Merge "checkpoint animations"
diff --git a/api/current.xml b/api/current.xml
index e6f3788..bb15b04 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1233,17 +1233,6 @@
visibility="public"
>
</field>
-<field name="WRITE_GEOLOCATION_PERMISSIONS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""com.android.browser.permission.WRITE_GEOLOCATION_PERMISSIONS""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="WRITE_GSERVICES"
type="java.lang.String"
transient="false"
@@ -11732,6 +11721,17 @@
visibility="public"
>
</field>
+<field name="custom"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16908331"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="cut"
type="int"
transient="false"
@@ -25508,6 +25508,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="flags" type="int">
+</parameter>
</method>
<method name="getCurrentModeType"
return="int"
@@ -25584,6 +25586,17 @@
visibility="public"
>
</field>
+<field name="DISABLE_CAR_MODE_GO_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MODE_NIGHT_AUTO"
type="int"
transient="false"
@@ -26862,11 +26875,22 @@
visibility="public"
>
</field>
+<field name="PASSWORD_QUALITY_ALPHABETIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="PASSWORD_QUALITY_ALPHANUMERIC"
type="int"
transient="false"
volatile="false"
- value="196608"
+ value="327680"
static="true"
final="true"
deprecated="not deprecated"
@@ -47612,6 +47636,32 @@
<parameter name="interestingChanges" type="int">
</parameter>
</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="setTo"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="android.content.res.Configuration">
+</parameter>
+</method>
<method name="setToDefaults"
return="void"
abstract="false"
@@ -80503,6 +80553,17 @@
visibility="public"
>
</method>
+<method name="isBluetoothScoAvailableOffCall"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isBluetoothScoOn"
return="boolean"
abstract="false"
@@ -80610,6 +80671,19 @@
<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener">
</parameter>
</method>
+<method name="registerMediaButtonEventReceiver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventReceiver" type="android.content.ComponentName">
+</parameter>
+</method>
<method name="requestAudioFocus"
return="int"
abstract="false"
@@ -80823,6 +80897,28 @@
<parameter name="vibrateType" type="int">
</parameter>
</method>
+<method name="startBluetoothSco"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stopBluetoothSco"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="unloadSoundEffects"
return="void"
abstract="false"
@@ -80847,6 +80943,19 @@
<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener">
</parameter>
</method>
+<method name="unregisterMediaButtonEventReceiver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventReceiver" type="android.content.ComponentName">
+</parameter>
+</method>
<field name="ACTION_AUDIO_BECOMING_NOISY"
type="java.lang.String"
transient="false"
@@ -80858,6 +80967,17 @@
visibility="public"
>
</field>
+<field name="ACTION_SCO_AUDIO_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.SCO_AUDIO_STATE_CHANGED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ADJUST_LOWER"
type="int"
transient="false"
@@ -80913,6 +81033,17 @@
visibility="public"
>
</field>
+<field name="AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="AUDIOFOCUS_LOSS"
type="int"
transient="false"
@@ -80935,6 +81066,17 @@
visibility="public"
>
</field>
+<field name="AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="AUDIOFOCUS_REQUEST_FAILED"
type="int"
transient="false"
@@ -80968,6 +81110,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_SCO_AUDIO_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.media.extra.SCO_AUDIO_STATE""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_VIBRATE_SETTING"
type="java.lang.String"
transient="false"
@@ -81331,6 +81484,39 @@
visibility="public"
>
</field>
+<field name="SCO_AUDIO_STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCO_AUDIO_STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCO_AUDIO_STATE_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="STREAM_ALARM"
type="int"
transient="false"
@@ -88229,7 +88415,7 @@
static="true"
final="false"
deprecated="not deprecated"
- visibility="private"
+ visibility="public"
>
<method name="startDownloadByUri"
return="long"
@@ -124012,21 +124198,6 @@
<parameter name="search" type="java.lang.String">
</parameter>
</method>
-<method name="allowGeolocation"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cr" type="android.content.ContentResolver">
-</parameter>
-<parameter name="origin" type="java.lang.String">
-</parameter>
-</method>
<method name="canClearHistory"
return="boolean"
abstract="false"
@@ -124040,21 +124211,6 @@
<parameter name="cr" type="android.content.ContentResolver">
</parameter>
</method>
-<method name="clearGeolocation"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cr" type="android.content.ContentResolver">
-</parameter>
-<parameter name="origin" type="java.lang.String">
-</parameter>
-</method>
<method name="clearHistory"
return="void"
abstract="false"
@@ -124254,16 +124410,6 @@
visibility="public"
>
</field>
-<field name="GEOLOCATION_URI"
- type="android.net.Uri"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="HISTORY_PROJECTION"
type="java.lang.String[]"
transient="false"
@@ -124536,34 +124682,6 @@
>
</field>
</class>
-<class name="Browser.GeolocationColumns"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Browser.GeolocationColumns"
- type="android.provider.Browser.GeolocationColumns"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<field name="ORIGIN"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""origin""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
<class name="Browser.SearchColumns"
extends="java.lang.Object"
abstract="false"
@@ -135751,6 +135869,17 @@
visibility="public"
>
</field>
+<field name="ALLOWED_GEOLOCATION_ORIGINS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""allowed_geolocation_origins""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ALLOW_MOCK_LOCATION"
type="java.lang.String"
transient="false"
@@ -136069,6 +136198,17 @@
visibility="public"
>
</field>
+<field name="TTS_ENABLED_PLUGINS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""tts_enabled_plugins""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TTS_USE_DEFAULTS"
type="java.lang.String"
transient="false"
@@ -177247,6 +177387,8 @@
</parameter>
<parameter name="maxOverscrollY" type="int">
</parameter>
+<parameter name="isTouchEvent" type="boolean">
+</parameter>
</method>
<method name="performClick"
return="boolean"
@@ -193476,6 +193618,17 @@
visibility="public"
>
</method>
+<method name="getPluginState"
+ return="android.webkit.WebSettings.PluginState"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPluginsEnabled"
return="boolean"
abstract="false"
@@ -193483,7 +193636,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -193575,7 +193728,7 @@
visibility="public"
>
</method>
-<method name="getUseSystemOverscrollBackground"
+<method name="getUseWebViewBackgroundForOverscrollBackground"
return="boolean"
abstract="false"
native="false"
@@ -194009,7 +194162,7 @@
<parameter name="flag" type="boolean">
</parameter>
</method>
-<method name="setPluginsEnabled"
+<method name="setPluginState"
return="void"
abstract="false"
native="false"
@@ -194019,6 +194172,19 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="state" type="android.webkit.WebSettings.PluginState">
+</parameter>
+</method>
+<method name="setPluginsEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
<parameter name="flag" type="boolean">
</parameter>
</method>
@@ -194165,7 +194331,7 @@
<parameter name="use" type="boolean">
</parameter>
</method>
-<method name="setUseSystemOverscrollBackground"
+<method name="setUseWebViewBackgroundForOverscrollBackground"
return="void"
abstract="false"
native="false"
@@ -194175,7 +194341,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="system" type="boolean">
+<parameter name="view" type="boolean">
</parameter>
</method>
<method name="setUseWideViewPort"
@@ -194328,6 +194494,39 @@
>
</method>
</class>
+<class name="WebSettings.PluginState"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.webkit.WebSettings.PluginState"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.webkit.WebSettings.PluginState[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
<class name="WebSettings.RenderPriority"
extends="java.lang.Enum"
abstract="false"
@@ -195227,7 +195426,7 @@
</parameter>
<parameter name="encoding" type="java.lang.String">
</parameter>
-<parameter name="failUrl" type="java.lang.String">
+<parameter name="historyUrl" type="java.lang.String">
</parameter>
</method>
<method name="loadUrl"
@@ -199560,6 +199759,8 @@
>
<implements name="android.widget.ExpandableListAdapter">
</implements>
+<implements name="android.widget.HeterogeneousExpandableList">
+</implements>
<constructor name="BaseExpandableListAdapter"
type="android.widget.BaseExpandableListAdapter"
static="false"
@@ -199579,6 +199780,32 @@
visibility="public"
>
</method>
+<method name="getChildType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getCombinedChildId"
return="long"
abstract="false"
@@ -199607,6 +199834,30 @@
<parameter name="groupId" type="long">
</parameter>
</method>
+<method name="getGroupType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isEmpty"
return="boolean"
abstract="false"
@@ -203349,6 +203600,64 @@
</parameter>
</method>
</class>
+<interface name="HeterogeneousExpandableList"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getChildType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGroupType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<class name="HorizontalScrollView"
extends="android.widget.FrameLayout"
abstract="false"
@@ -205409,6 +205718,331 @@
</parameter>
</method>
</interface>
+<class name="OverScroller"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</parameter>
+</constructor>
+<constructor name="OverScroller"
+ type="android.widget.OverScroller"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="interpolator" type="android.view.animation.Interpolator">
+</parameter>
+<parameter name="bounceCoefficientX" type="float">
+</parameter>
+<parameter name="bounceCoefficientY" type="float">
+</parameter>
+</constructor>
+<method name="abortAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="computeScrollOffset"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="fling"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="velocityX" type="int">
+</parameter>
+<parameter name="velocityY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="forceFinished"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="finished" type="boolean">
+</parameter>
+</method>
+<method name="getCurrX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFinalY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFinished"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isOverscrolled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="notifyHorizontalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="finalX" type="int">
+</parameter>
+<parameter name="overX" type="int">
+</parameter>
+</method>
+<method name="notifyVerticalEdgeReached"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="finalY" type="int">
+</parameter>
+<parameter name="overY" type="int">
+</parameter>
+</method>
+<method name="springback"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="minX" type="int">
+</parameter>
+<parameter name="maxX" type="int">
+</parameter>
+<parameter name="minY" type="int">
+</parameter>
+<parameter name="maxY" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+</method>
+<method name="startScroll"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startX" type="int">
+</parameter>
+<parameter name="startY" type="int">
+</parameter>
+<parameter name="dx" type="int">
+</parameter>
+<parameter name="dy" type="int">
+</parameter>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+</class>
<class name="PopupWindow"
extends="java.lang.Object"
abstract="false"
@@ -384004,7 +384638,17 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="x" type="org.json.JSONTokener">
+<parameter name="copyFrom" type="java.util.Collection">
+</parameter>
+</constructor>
+<constructor name="JSONArray"
+ type="org.json.JSONArray"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="readFrom" type="org.json.JSONTokener">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384016,21 +384660,11 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="string" type="java.lang.String">
+<parameter name="json" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
</constructor>
-<constructor name="JSONArray"
- type="org.json.JSONArray"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="collection" type="java.util.Collection">
-</parameter>
-</constructor>
<method name="get"
return="java.lang.Object"
abstract="false"
@@ -384228,7 +384862,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="defaultValue" type="boolean">
+<parameter name="fallback" type="boolean">
</parameter>
</method>
<method name="optDouble"
@@ -384256,7 +384890,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="defaultValue" type="double">
+<parameter name="fallback" type="double">
</parameter>
</method>
<method name="optInt"
@@ -384284,7 +384918,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="defaultValue" type="int">
+<parameter name="fallback" type="int">
</parameter>
</method>
<method name="optJSONArray"
@@ -384338,7 +384972,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="defaultValue" type="long">
+<parameter name="fallback" type="long">
</parameter>
</method>
<method name="optString"
@@ -384366,7 +385000,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="defaultValue" type="java.lang.String">
+<parameter name="fallback" type="java.lang.String">
</parameter>
</method>
<method name="put"
@@ -384546,7 +385180,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="indentFactor" type="int">
+<parameter name="indentSpaces" type="int">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384567,7 +385201,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="message" type="java.lang.String">
+<parameter name="s" type="java.lang.String">
</parameter>
</constructor>
</class>
@@ -384594,9 +385228,17 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="jo" type="org.json.JSONObject">
+<parameter name="copyFrom" type="java.util.Map">
</parameter>
-<parameter name="sa" type="java.lang.String[]">
+</constructor>
+<constructor name="JSONObject"
+ type="org.json.JSONObject"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="readFrom" type="org.json.JSONTokener">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384608,7 +385250,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="x" type="org.json.JSONTokener">
+<parameter name="json" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384620,17 +385262,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="map" type="java.util.Map">
+<parameter name="copyFrom" type="org.json.JSONObject">
</parameter>
-</constructor>
-<constructor name="JSONObject"
- type="org.json.JSONObject"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="string" type="java.lang.String">
+<parameter name="names" type="java.lang.String[]">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384645,7 +385279,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="java.lang.Object">
</parameter>
@@ -384662,7 +385296,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384677,7 +385311,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384692,7 +385326,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384707,7 +385341,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384722,7 +385356,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384737,7 +385371,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384752,7 +385386,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384767,7 +385401,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384782,7 +385416,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="isNull"
@@ -384795,7 +385429,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="keys"
@@ -384841,7 +385475,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="n" type="java.lang.Number">
+<parameter name="number" type="java.lang.Number">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -384856,7 +385490,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optBoolean"
@@ -384869,7 +385503,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optBoolean"
@@ -384882,9 +385516,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
-<parameter name="defaultValue" type="boolean">
+<parameter name="fallback" type="boolean">
</parameter>
</method>
<method name="optDouble"
@@ -384897,7 +385531,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optDouble"
@@ -384910,9 +385544,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
-<parameter name="defaultValue" type="double">
+<parameter name="fallback" type="double">
</parameter>
</method>
<method name="optInt"
@@ -384925,7 +385559,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optInt"
@@ -384938,9 +385572,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
-<parameter name="defaultValue" type="int">
+<parameter name="fallback" type="int">
</parameter>
</method>
<method name="optJSONArray"
@@ -384953,7 +385587,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optJSONObject"
@@ -384966,7 +385600,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optLong"
@@ -384979,7 +385613,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optLong"
@@ -384992,9 +385626,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
-<parameter name="defaultValue" type="long">
+<parameter name="fallback" type="long">
</parameter>
</method>
<method name="optString"
@@ -385007,7 +385641,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="optString"
@@ -385020,9 +385654,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
-<parameter name="defaultValue" type="java.lang.String">
+<parameter name="fallback" type="java.lang.String">
</parameter>
</method>
<method name="put"
@@ -385035,7 +385669,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="boolean">
</parameter>
@@ -385052,7 +385686,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="double">
</parameter>
@@ -385069,7 +385703,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="int">
</parameter>
@@ -385086,7 +385720,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="long">
</parameter>
@@ -385103,7 +385737,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="java.lang.Object">
</parameter>
@@ -385120,7 +385754,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<parameter name="value" type="java.lang.Object">
</parameter>
@@ -385137,7 +385771,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="string" type="java.lang.String">
+<parameter name="data" type="java.lang.String">
</parameter>
</method>
<method name="remove"
@@ -385150,7 +385784,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="key" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
</method>
<method name="toJSONArray"
@@ -385178,7 +385812,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="indentFactor" type="int">
+<parameter name="indentSpaces" type="int">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385259,7 +385893,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="s" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385287,7 +385921,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="b" type="boolean">
+<parameter name="value" type="java.lang.Object">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385302,7 +385936,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="d" type="double">
+<parameter name="value" type="boolean">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385317,7 +385951,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="l" type="long">
+<parameter name="value" type="double">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385332,7 +385966,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="java.lang.Object">
+<parameter name="value" type="long">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385353,7 +385987,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="s" type="java.lang.String">
+<parameter name="in" type="java.lang.String">
</parameter>
</constructor>
<method name="back"
@@ -385377,7 +386011,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="c" type="char">
+<parameter name="hex" type="char">
</parameter>
</method>
<method name="more"
@@ -385427,7 +386061,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="n" type="int">
+<parameter name="length" type="int">
</parameter>
<exception name="JSONException" type="org.json.JSONException">
</exception>
@@ -385470,7 +386104,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="d" type="char">
+<parameter name="excluded" type="java.lang.String">
</parameter>
</method>
<method name="nextTo"
@@ -385483,7 +386117,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="delimiters" type="java.lang.String">
+<parameter name="excluded" type="char">
</parameter>
</method>
<method name="nextValue"
@@ -385509,7 +386143,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="to" type="java.lang.String">
+<parameter name="thru" type="java.lang.String">
</parameter>
</method>
<method name="skipTo"
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index cc1167f..e8b5eaf 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -105,7 +105,7 @@
#ifdef FWDUMP_bcm4329
run_command("DUMP WIFI FIRMWARE LOG", 60,
- "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
+ "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
#endif
print_properties();
@@ -115,8 +115,8 @@
dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+ run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
- run_command("MOUNTED FILESYSTEMS", 10, "df", NULL);
run_command("PROCESSES", 10, "ps", "-P", NULL);
run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 654ee68..b8ba3f6 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -389,9 +389,10 @@
int cachesize = 0;
/* count the source apk as code -- but only if it's not
- * on the /system partition
+ * on the /system partition and its not on the sdcard.
*/
- if (strncmp(apkpath, "/system", 7) != 0) {
+ if (strncmp(apkpath, "/system", 7) != 0 &&
+ strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) {
if (stat(apkpath, &s) == 0) {
codesize += stat_size(&s);
}
@@ -403,17 +404,6 @@
codesize += stat_size(&s);
}
}
-
- /* count the source apk as code -- but only if it's not
- * installed on the sdcard
- */
- if (strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) {
- if (stat(apkpath, &s) == 0) {
- codesize += stat_size(&s);
- }
- }
-
-
/* count the cached dexfile as code */
if (!create_cache_path(path, apkpath)) {
if (stat(path, &s) == 0) {
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
index 8330f8e..141f69b 100644
--- a/cmds/keystore/keystore_get.h
+++ b/cmds/keystore/keystore_get.h
@@ -32,9 +32,11 @@
#endif
/* This function is provided for native components to get values from keystore.
- * Users are required to link against libcutils. The lengths of keys and values
- * are limited to KEYSTORE_MESSAGE_SIZE. This function returns the length of
- * the requested value or -1 if something goes wrong. */
+ * Users are required to link against libcutils. Keys are values are 8-bit safe.
+ * The first two arguments are the key and its length. The third argument
+ * specifies the buffer to store the retrieved value, which must be an array of
+ * KEYSTORE_MESSAGE_SIZE bytes. This function returns the length of the value or
+ * -1 if an error happens. */
static int keystore_get(const char *key, int length, char *value)
{
uint8_t bytes[2] = {length >> 8, length};
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index fc5707d..659d70f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,11 +92,6 @@
return;
}
- if ("mountsd".equals(op)) {
- runMountSd();
- return;
- }
-
if ("uninstall".equals(op)) {
runUninstall();
return;
@@ -602,6 +597,9 @@
} else if (opt.equals("-s")) {
// Override if -s option is specified.
installFlags |= PackageManager.INSTALL_EXTERNAL;
+ } else if (opt.equals("-f")) {
+ // Override if -s option is specified.
+ installFlags |= PackageManager.INSTALL_INTERNAL;
} else {
System.err.println("Error: Unknown option: " + opt);
showUsage();
@@ -643,37 +641,6 @@
}
}
- private void runMountSd() {
- String opt;
- boolean mount = false;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-m")) {
- String mountStr = nextOptionData();
- if (mountStr == null) {
- System.err.println("Error: no value specified for -m");
- showUsage();
- return;
- }
- if ("true".equalsIgnoreCase(mountStr)) {
- mount = true;
- } else if ("false".equalsIgnoreCase(mountStr)) {
- mount = false;
- } else {
- System.err.println("Error: no value specified for -m");
- showUsage();
- return;
- }
- }
- }
-
- try {
- mPm.updateExternalMediaStatus(mount);
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- }
- }
-
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -861,9 +828,8 @@
System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");
System.err.println(" pm list features");
System.err.println(" pm path PACKAGE");
- System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH");
+ System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
- System.err.println(" pm mountsd [-m true/false]");
System.err.println(" pm enable PACKAGE_OR_COMPONENT");
System.err.println(" pm disable PACKAGE_OR_COMPONENT");
System.err.println("");
@@ -895,6 +861,7 @@
System.err.println(" -t: allow test .apks to be installed.");
System.err.println(" -i: specify the installer package name.");
System.err.println(" -s: install package on sdcard.");
+ System.err.println(" -f: install package on internal flash.");
System.err.println("");
System.err.println("The uninstall command removes a package from the system. Options:");
System.err.println(" -k: keep the data and cache directories around.");
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp
index 394ce41..c4fa765 100644
--- a/cmds/rawbu/backup.cpp
+++ b/cmds/rawbu/backup.cpp
@@ -318,7 +318,7 @@
result = 0;
goto done;
}
- } else {
+ } else if (S_ISREG(statBuffer.st_mode)) {
printf("Saving file %s...\n", fullPath);
if (write_header(fh, TYPE_FILE, fullPath, &statBuffer) == 0) {
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 072a9d2..55bb581 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -47,7 +47,7 @@
struct timeval tv;
gettimeofday(&tv, NULL);
- return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+ return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
}
static void playSource(OMXClient *client, const sp<MediaSource> &source) {
@@ -370,7 +370,9 @@
const char *kMimeTypes[] = {
MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
- MEDIA_MIMETYPE_VIDEO_H263
+ MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
+ MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ MEDIA_MIMETYPE_AUDIO_MPEG
};
for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
diff --git a/common/Android.mk b/common/Android.mk
index 5c5b01b..1f78840 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -20,11 +20,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := android-common
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, java) \
+ $(call all-logtags-files-under, java)
include $(BUILD_STATIC_JAVA_LIBRARY)
# Include this library in the build server's output directory
$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):android-common.jar)
# Build the test package
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/common/java/com/android/common/GoogleLogTags.logtags b/common/java/com/android/common/GoogleLogTags.logtags
new file mode 100644
index 0000000..a5c9bb0
--- /dev/null
+++ b/common/java/com/android/common/GoogleLogTags.logtags
@@ -0,0 +1,100 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+option java_package com.android.common
+
+#####
+# This file contains definitions for event log (android.util.EventLog) tags
+# used by Google Mobile Services applications. These definitions are part of
+# the platform even when the Google applications are not.
+#
+# See system/core/logcat/event.logtags for a description of the file format.
+#
+# These event log tags must be assigned specific numbers (no "?") in the range
+# 200000-300000. If new tags are added, be aware that older platforms will be
+# missing the tag definitions, and may not be able to show them in their logs.
+
+#####
+# System Update (OTA)
+
+# System update status bits
+# [31- 9] Reserved for future use
+# [ 8- 7] package verified (0=not attempted, 1=succeeded, 2=failed)
+# [ 6] install approved
+# [ 5] download approved
+# [ 4- 0] status
+201001 system_update (status|1|5),(download_result|1|5),(bytes|2|2),(url|3)
+201002 system_update_user (action|3)
+
+#####
+# Android Market
+
+# @param changes Number of changes made to database in reconstruct
+202001 vending_reconstruct (changes|1)
+
+#####
+# Google Services Framework
+
+203001 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3)
+
+203002 google_http_request (elapsed|2|3),(status|1),(appname|3),(reused|1)
+
+#####
+# Google Talk Service
+
+# This event is logged when GTalkService encounters important events
+204001 gtalkservice (eventType|1)
+# This event is logged for GTalk connection state changes. The status field is an int, but
+# it really contains 4 separate values, each taking up a byte
+# (eventType << 24) + (connection state << 16) + (connection error << 8) + network state
+204002 gtalk_connection (status|1)
+
+# This event is logged when GTalk connection is closed.
+# The status field is an int, but contains 2 different values, it's represented as
+#
+# (networkType << 8) + connection error
+#
+# the possible error values are
+#
+# no_error=0, no_network=1, connection_failed=2, unknown_host=3, auth_failed=4,
+# auth_expired=5, heart_beat_timeout=6, server_error=7, server_reject_rate_limiting=8, unknown=10
+#
+# duration is the connection duration.
+204003 gtalk_conn_close (status|1),(duration|1)
+
+# This event is logged for GTalk heartbeat resets
+# interval_and_nt contains both the heartbeat interval and the network type, It's represented as
+# (networkType << 16) + interval
+# interval is in seconds; network type can be 0 (mobile) or 1 (wifi); ip is the host ip addr.
+204004 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3)
+
+# This event is logged when an Rmq v2 packet is sent or received.
+204005 push_messaging (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1)
+
+#####
+# Google Login Service and Setup Wizard
+
+# This event is for when communicating to the server times out during account setup
+205001 setup_server_timeout
+205002 setup_required_captcha (action|3)
+205003 setup_io_error (status|3)
+205004 setup_server_error
+205005 setup_retries_exhausted
+205006 setup_no_data_network
+205007 setup_completed
+
+205008 gls_account_tried (status|1)
+205009 gls_account_saved (status|1)
+205010 gls_authenticate (status|1),(service|3)
+205011 google_mail_switch (direction|1)
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 8356029..15c8856 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -227,11 +227,13 @@
* @hide for internal use only
*/
public static Bundle sanitizeResult(Bundle result) {
- if (result.containsKey(KEY_AUTHTOKEN)
- && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) {
- final Bundle newResult = new Bundle(result);
- newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>");
- return newResult;
+ if (result != null) {
+ if (result.containsKey(KEY_AUTHTOKEN)
+ && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) {
+ final Bundle newResult = new Bundle(result);
+ newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>");
+ return newResult;
+ }
}
return result;
}
@@ -251,6 +253,7 @@
* @return An {@link AccountManager} instance
*/
public static AccountManager get(Context context) {
+ if (context == null) throw new IllegalArgumentException("context is null");
return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
}
@@ -269,6 +272,7 @@
* @return The account's password, null if none or if the account doesn't exist
*/
public String getPassword(final Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
try {
return mService.getPassword(account);
} catch (RemoteException e) {
@@ -293,6 +297,8 @@
* @return The user data, null if the account or key doesn't exist
*/
public String getUserData(final Account account, final String key) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (key == null) throw new IllegalArgumentException("key is null");
try {
return mService.getUserData(account, key);
} catch (RemoteException e) {
@@ -393,6 +399,8 @@
public AccountManagerFuture<Boolean> hasFeatures(final Account account,
final String[] features,
AccountManagerCallback<Boolean> callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (features == null) throw new IllegalArgumentException("features is null");
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
mService.hasFeatures(mResponse, account, features);
@@ -436,13 +444,9 @@
public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
final String type, final String[] features,
AccountManagerCallback<Account[]> callback, Handler handler) {
+ if (type == null) throw new IllegalArgumentException("type is null");
return new Future2Task<Account[]>(handler, callback) {
public void doWork() throws RemoteException {
- if (type == null) {
- Log.e(TAG, "Type is null");
- set(new Account[0]);
- return;
- }
mService.getAccountsByFeatures(mResponse, type, features);
}
public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException {
@@ -476,6 +480,7 @@
* already exists, the account is null, or another error occurs.
*/
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
+ if (account == null) throw new IllegalArgumentException("account is null");
try {
return mService.addAccount(account, password, userdata);
} catch (RemoteException e) {
@@ -507,6 +512,7 @@
*/
public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
return new Future2Task<Boolean>(handler, callback) {
public void doWork() throws RemoteException {
mService.removeAccount(mResponse, account);
@@ -537,6 +543,8 @@
* @param authToken The auth token to invalidate
*/
public void invalidateAuthToken(final String accountType, final String authToken) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ if (authToken == null) throw new IllegalArgumentException("authToken is null");
try {
mService.invalidateAuthToken(accountType, authToken);
} catch (RemoteException e) {
@@ -563,13 +571,8 @@
* no auth token is cached or the account does not exist.
*/
public String peekAuthToken(final Account account, final String authTokenType) {
- if (account == null) {
- Log.e(TAG, "peekAuthToken: the account must not be null");
- return null;
- }
- if (authTokenType == null) {
- return null;
- }
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
try {
return mService.peekAuthToken(account, authTokenType);
} catch (RemoteException e) {
@@ -594,10 +597,7 @@
* @param password The password to set, null to clear the password
*/
public void setPassword(final Account account, final String password) {
- if (account == null) {
- Log.e(TAG, "the account must not be null");
- return;
- }
+ if (account == null) throw new IllegalArgumentException("account is null");
try {
mService.setPassword(account, password);
} catch (RemoteException e) {
@@ -621,10 +621,7 @@
* @param account The account whose password to clear
*/
public void clearPassword(final Account account) {
- if (account == null) {
- Log.e(TAG, "the account must not be null");
- return;
- }
+ if (account == null) throw new IllegalArgumentException("account is null");
try {
mService.clearPassword(account);
} catch (RemoteException e) {
@@ -649,14 +646,8 @@
* @param value The value to set, null to clear this userdata key
*/
public void setUserData(final Account account, final String key, final String value) {
- if (account == null) {
- Log.e(TAG, "the account must not be null");
- return;
- }
- if (key == null) {
- Log.e(TAG, "the key must not be null");
- return;
- }
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (key == null) throw new IllegalArgumentException("key is null");
try {
mService.setUserData(account, key, value);
} catch (RemoteException e) {
@@ -682,6 +673,8 @@
* @param authToken The auth token to add to the cache
*/
public void setAuthToken(Account account, final String authTokenType, final String authToken) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
try {
mService.setAuthToken(account, authTokenType, authToken);
} catch (RemoteException e) {
@@ -716,6 +709,8 @@
public String blockingGetAuthToken(Account account, String authTokenType,
boolean notifyAuthFailure)
throws OperationCanceledException, IOException, AuthenticatorException {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
null /* handler */).getResult();
if (bundle == null) {
@@ -786,7 +781,7 @@
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
- if (activity == null) throw new IllegalArgumentException("activity is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
@@ -930,14 +925,9 @@
final String authTokenType, final String[] requiredFeatures,
final Bundle addAccountOptions,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
- if (accountType == null) {
- Log.e(TAG, "the account must not be null");
- // to unblock caller waiting on Future.get()
- set(new Bundle());
- return;
- }
mService.addAcount(mResponse, accountType, authTokenType,
requiredFeatures, activity != null, addAccountOptions);
}
@@ -1004,6 +994,7 @@
final Activity activity,
final AccountManagerCallback<Bundle> callback,
final Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.confirmCredentials(mResponse, account, options, activity != null);
@@ -1064,6 +1055,7 @@
final Bundle options, final Activity activity,
final AccountManagerCallback<Bundle> callback,
final Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.updateCredentials(mResponse, account, authTokenType, activity != null,
@@ -1112,6 +1104,7 @@
public AccountManagerFuture<Bundle> editProperties(final String accountType,
final Activity activity, final AccountManagerCallback<Bundle> callback,
final Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.editProperties(mResponse, accountType, activity != null);
@@ -1734,10 +1727,7 @@
* @throws IllegalStateException if listener was not already added
*/
public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
- if (listener == null) {
- Log.e(TAG, "Missing listener");
- return;
- }
+ if (listener == null) throw new IllegalArgumentException("listener is null");
synchronized (mAccountsUpdatedListeners) {
if (!mAccountsUpdatedListeners.containsKey(listener)) {
Log.e(TAG, "Listener was not previously added");
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 16b603c..1d9e0f1 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -282,6 +282,7 @@
}
public String getPassword(Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
@@ -312,6 +313,8 @@
}
public String getUserData(Account account, String key) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (key == null) throw new IllegalArgumentException("key is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
try {
@@ -382,6 +385,7 @@
}
public boolean addAccount(Account account, String password, Bundle extras) {
+ if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
// fails if the account already exists
@@ -451,6 +455,9 @@
public void hasFeatures(IAccountManagerResponse response,
Account account, String[] features) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (features == null) throw new IllegalArgumentException("features is null");
checkReadAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -509,6 +516,8 @@
}
public void removeAccount(IAccountManagerResponse response, Account account) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -565,6 +574,8 @@
}
public void invalidateAuthToken(String accountType, String authToken) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ if (authToken == null) throw new IllegalArgumentException("authToken is null");
checkManageAccountsOrUseCredentialsPermissions();
long identityToken = clearCallingIdentity();
try {
@@ -658,6 +669,8 @@
}
public String peekAuthToken(Account account, String authTokenType) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
try {
@@ -668,6 +681,8 @@
}
public void setAuthToken(Account account, String authTokenType, String authToken) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
try {
@@ -678,6 +693,7 @@
}
public void setPassword(Account account, String password) {
+ if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
try {
@@ -714,6 +730,7 @@
}
public void clearPassword(Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -724,6 +741,8 @@
}
public void setUserData(Account account, String key, String value) {
+ if (key == null) throw new IllegalArgumentException("key is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
long identityToken = clearCallingIdentity();
if (account == null) {
@@ -786,6 +805,9 @@
public void getAuthToken(IAccountManagerResponse response, final Account account,
final String authTokenType, final boolean notifyOnAuthFailure,
final boolean expectActivityLaunch, final Bundle loginOptions) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final int callerUid = Binder.getCallingUid();
final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
@@ -955,6 +977,8 @@
public void addAcount(final IAccountManagerResponse response, final String accountType,
final String authTokenType, final String[] requiredFeatures,
final boolean expectActivityLaunch, final Bundle options) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -981,6 +1005,8 @@
public void confirmCredentials(IAccountManagerResponse response,
final Account account, final Bundle options, final boolean expectActivityLaunch) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -1002,6 +1028,9 @@
public void updateCredentials(IAccountManagerResponse response, final Account account,
final String authTokenType, final boolean expectActivityLaunch,
final Bundle loginOptions) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -1025,6 +1054,8 @@
public void editProperties(IAccountManagerResponse response, final String accountType,
final boolean expectActivityLaunch) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
try {
@@ -1142,6 +1173,8 @@
public void getAccountsByFeatures(IAccountManagerResponse response,
String type, String[] features) {
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (type == null) throw new IllegalArgumentException("accountType is null");
checkReadAccountsPermission();
if (features != null && type == null) {
if (response != null) {
@@ -1878,7 +1911,8 @@
* @hide
*/
public void grantAppPermission(Account account, String authTokenType, int uid) {
- if (account == null || authTokenType == null) {
+ if (account == null || authTokenType == null) {
+ Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
return;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -1908,7 +1942,8 @@
* @hide
*/
public void revokeAppPermission(Account account, String authTokenType, int uid) {
- if (account == null || authTokenType == null) {
+ if (account == null || authTokenType == null) {
+ Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
return;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
index f4b7258..fd340cbe 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -57,6 +57,14 @@
final Bundle extras = getIntent().getExtras();
mAccount = extras.getParcelable(EXTRAS_ACCOUNT);
mAuthTokenType = extras.getString(EXTRAS_AUTH_TOKEN_TYPE);
+
+ if (mAccount == null || mAuthTokenType == null) {
+ // we were somehow started with bad parameters. abort the activity.
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
mUid = extras.getInt(EXTRAS_REQUESTING_UID);
final String accountTypeLabel = extras.getString(EXTRAS_ACCOUNT_TYPE_LABEL);
final String[] packages = extras.getStringArray(EXTRAS_PACKAGES);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3c5a1c7..a962391 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1320,6 +1320,11 @@
}
mManagedCursors.clear();
}
+
+ // Close any open search dialog
+ if (mSearchManager != null) {
+ mSearchManager.stopSearch();
+ }
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e8ab51fd..b3223e5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.graphics.Bitmap;
@@ -859,6 +860,22 @@
}
/**
+ * Returns a list of application processes installed on external media
+ * that are running on the device.
+ *
+ * @return Returns a list of ApplicationInfo records, or null if none
+ * This list ordering is not specified.
+ * @hide
+ */
+ public List<ApplicationInfo> getRunningExternalApplications() {
+ try {
+ return ActivityManagerNative.getDefault().getRunningExternalApplications();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Returns a list of application processes that are running on the device.
*
* @return Returns a list of RunningAppProcessInfo records, or null if there are no
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6d5686a..f694285 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -168,6 +168,29 @@
return true;
}
+ case START_ACTIVITY_WITH_CONFIG_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
+ int grantedMode = data.readInt();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ boolean onlyIfNeeded = data.readInt() != 0;
+ boolean debug = data.readInt() != 0;
+ Configuration config = Configuration.CREATOR.createFromParcel(data);
+ int result = startActivityWithConfig(app, intent, resolvedType,
+ grantedUriPermissions, grantedMode, resultTo, resultWho,
+ requestCode, onlyIfNeeded, debug, config);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case START_ACTIVITY_INTENT_SENDER_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -468,6 +491,14 @@
return true;
}
+ case GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ List<ApplicationInfo> list = getRunningExternalApplications();
+ reply.writeNoException();
+ reply.writeTypedList(list);
+ return true;
+ }
+
case MOVE_TASK_TO_FRONT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int task = data.readInt();
@@ -980,10 +1011,11 @@
return true;
}
- case KILL_PIDS_FOR_MEMORY_TRANSACTION: {
+ case KILL_PIDS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int[] pids = data.createIntArray();
- boolean res = killPidsForMemory(pids);
+ String reason = data.readString();
+ boolean res = killPids(pids, reason);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -1294,6 +1326,32 @@
data.recycle();
return result;
}
+ public int startActivityWithConfig(IApplicationThread caller, Intent intent,
+ String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
+ IBinder resultTo, String resultWho,
+ int requestCode, boolean onlyIfNeeded,
+ boolean debug, Configuration config) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeTypedArray(grantedUriPermissions, 0);
+ data.writeInt(grantedMode);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(onlyIfNeeded ? 1 : 0);
+ data.writeInt(debug ? 1 : 0);
+ config.writeToParcel(data, 0);
+ mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -1666,6 +1724,19 @@
reply.recycle();
return list;
}
+ public List<ApplicationInfo> getRunningExternalApplications()
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ArrayList<ApplicationInfo> list
+ = reply.createTypedArrayList(ApplicationInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return list;
+ }
public void moveTaskToFront(int task) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -2393,12 +2464,13 @@
mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
data.recycle();
}
- public boolean killPidsForMemory(int[] pids) throws RemoteException {
+ public boolean killPids(int[] pids, String reason) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeIntArray(pids);
- mRemote.transact(KILL_PIDS_FOR_MEMORY_TRANSACTION, data, reply, 0);
+ data.writeString(reason);
+ mRemote.transact(KILL_PIDS_TRANSACTION, data, reply, 0);
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 35d1948..a556a32 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -278,6 +278,10 @@
int mClientCount = 0;
+ Application getApplication() {
+ return mApplication;
+ }
+
public PackageInfo(ActivityThread activityThread, ApplicationInfo aInfo,
ActivityThread mainThread, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode) {
@@ -648,7 +652,7 @@
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
-
+
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
@@ -3908,14 +3912,16 @@
}
}
- final void applyConfigurationToResourcesLocked(Configuration config) {
+ final boolean applyConfigurationToResourcesLocked(Configuration config) {
if (mResConfiguration == null) {
mResConfiguration = new Configuration();
}
if (!mResConfiguration.isOtherSeqNewer(config)) {
- return;
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ return false;
}
- mResConfiguration.updateFrom(config);
+ int changes = mResConfiguration.updateFrom(config);
DisplayMetrics dm = getDisplayMetricsLocked(true);
// set it for java, this also affects newly created Resources
@@ -3936,6 +3942,8 @@
WeakReference<Resources> v = it.next();
Resources r = v.get();
if (r != null) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Changing resources "
+ + r + " config to: " + config);
r.updateConfiguration(config, dm);
//Log.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
@@ -3944,6 +3952,8 @@
it.remove();
}
}
+
+ return changes != 0;
}
final void handleConfigurationChanged(Configuration config) {
@@ -4518,17 +4528,20 @@
ViewRoot.addConfigCallback(new ComponentCallbacks() {
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mPackages) {
- if (mPendingConfiguration == null ||
- mPendingConfiguration.isOtherSeqNewer(newConfig)) {
- mPendingConfiguration = newConfig;
-
- // We need to apply this change to the resources
- // immediately, because upon returning the view
- // hierarchy will be informed about it.
- applyConfigurationToResourcesLocked(newConfig);
+ // We need to apply this change to the resources
+ // immediately, because upon returning the view
+ // hierarchy will be informed about it.
+ if (applyConfigurationToResourcesLocked(newConfig)) {
+ // This actually changed the resources! Tell
+ // everyone about it.
+ if (mPendingConfiguration == null ||
+ mPendingConfiguration.isOtherSeqNewer(newConfig)) {
+ mPendingConfiguration = newConfig;
+
+ queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
+ }
}
}
- queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
public void onLowMemory() {
}
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 2603579..2714de5 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -35,12 +35,12 @@
/**
* A subclass of Dialog that can display one, two or three buttons. If you only want to
* display a String in this dialog box, use the setMessage() method. If you
- * want to display a more complex view, look up the FrameLayout called "body"
+ * want to display a more complex view, look up the FrameLayout called "custom"
* and add your view to it:
*
* <pre>
- * FrameLayout fl = (FrameLayout) findViewById(R.id.body);
- * fl.add(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ * FrameLayout fl = (FrameLayout) findViewById(android.R.id.custom);
+ * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
* </pre>
*
* <p>The AlertDialog class takes care of automatically setting
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4464ab9..4c2b36f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -260,7 +260,8 @@
@Override
public Context getApplicationContext() {
- return mMainThread.getApplication();
+ return (mPackageInfo != null) ?
+ mPackageInfo.getApplication() : mMainThread.getApplication();
}
@Override
@@ -2935,9 +2936,14 @@
private boolean writeFileLocked() {
// Rename the current file so it may be used as a backup during the next read
if (mFile.exists()) {
- if (!mFile.renameTo(mBackupFile)) {
- Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile);
- return false;
+ if (!mBackupFile.exists()) {
+ if (!mFile.renameTo(mBackupFile)) {
+ Log.e(TAG, "Couldn't rename file " + mFile
+ + " to backup file " + mBackupFile);
+ return false;
+ }
+ } else {
+ mFile.delete();
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 14571de..31f0a63 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -88,6 +88,10 @@
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug) throws RemoteException;
+ public int startActivityWithConfig(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo, String resultWho, int requestCode,
+ boolean onlyIfNeeded, boolean debug, Configuration newConfig) throws RemoteException;
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -241,7 +245,7 @@
public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
- public boolean killPidsForMemory(int[] pids) throws RemoteException;
+ public boolean killPids(int[] pids, String reason) throws RemoteException;
public void reportPss(IApplicationThread caller, int pss) throws RemoteException;
@@ -258,9 +262,13 @@
* SIGUSR1 is delivered. All others are ignored.
*/
public void signalPersistentProcesses(int signal) throws RemoteException;
- // Retrieve running application processes in the system
+ // Retrieve info of applications installed on external media that are currently
+ // running.
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses()
throws RemoteException;
+ // Retrieve running application processes in the system
+ public List<ApplicationInfo> getRunningExternalApplications()
+ throws RemoteException;
// Get device configuration
public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException;
@@ -476,7 +484,7 @@
int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76;
int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77;
int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
- int KILL_PIDS_FOR_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
+ int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
@@ -503,4 +511,6 @@
int IS_USER_A_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+103;
int START_ACTIVITY_AND_WAIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+104;
int WILL_ACTIVITY_BE_VISIBLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+105;
+ int START_ACTIVITY_WITH_CONFIG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+106;
+ int GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+107;
}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index bd637b7..7b668d2 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -30,7 +30,7 @@
/**
* Disables the car mode.
*/
- void disableCarMode();
+ void disableCarMode(int flags);
/**
* Return the current running mode.
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index ebc64d7..0ebe3ac 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -132,6 +132,15 @@
// Last known IME options value for the search edit text.
private int mSearchAutoCompleteImeOptions;
+ private BroadcastReceiver mConfChangeListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ onConfigurationChanged();
+ }
+ }
+ };
+
/**
* Constructor - fires it up and makes it look like the search UI.
*
@@ -149,16 +158,6 @@
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mSearchManager = searchManager;
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
- onConfigurationChanged();
- }
- }
- }, filter);
}
/**
@@ -297,10 +296,20 @@
show();
}
updateUI();
-
+
return true;
}
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // Register a listener for configuration change events.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ getContext().registerReceiver(mConfChangeListener, filter);
+ }
+
/**
* The search dialog is being dismissed, so handle all of the local shutdown operations.
*
@@ -311,6 +320,8 @@
public void onStop() {
super.onStop();
+ getContext().unregisterReceiver(mConfChangeListener);
+
closeSuggestionsAdapter();
// dump extra memory we're hanging on to
@@ -483,7 +494,7 @@
int textId = mSearchable.getSearchButtonText();
if (isBrowserSearch()){
iconLabel = getContext().getResources()
- .getDrawable(com.android.internal.R.drawable.ic_btn_search_play);
+ .getDrawable(com.android.internal.R.drawable.ic_btn_search_go);
} else if (textId != 0) {
textLabel = mActivityContext.getResources().getString(textId);
} else {
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 86224c5d..a1ca707 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -43,1176 +43,18 @@
*
* <p>In practice, you won't interact with this class directly, as search
* services are provided through methods in {@link android.app.Activity Activity}
- * methods and the the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
- * {@link android.content.Intent Intent}. This class does provide a basic
- * overview of search services and how to integrate them with your activities.
- * If you do require direct access to the SearchManager, do not instantiate
- * this class directly; instead, retrieve it through
+ * and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
+ * {@link android.content.Intent Intent}.
+ * If you do require direct access to the SearchManager, do not instantiate
+ * this class directly. Instead, retrieve it through
* {@link android.content.Context#getSystemService
* context.getSystemService(Context.SEARCH_SERVICE)}.
- *
- * <p>Topics covered here:
- * <ol>
- * <li><a href="#DeveloperGuide">Developer Guide</a>
- * <li><a href="#HowSearchIsInvoked">How Search Is Invoked</a>
- * <li><a href="#ImplementingSearchForYourApp">Implementing Search for Your App</a>
- * <li><a href="#Suggestions">Search Suggestions</a>
- * <li><a href="#ExposingSearchSuggestionsToQuickSearchBox">Exposing Search Suggestions to
- * Quick Search Box</a></li>
- * <li><a href="#ActionKeys">Action Keys</a>
- * <li><a href="#SearchabilityMetadata">Searchability Metadata</a>
- * <li><a href="#PassingSearchContext">Passing Search Context</a>
- * <li><a href="#ProtectingUserPrivacy">Protecting User Privacy</a>
- * </ol>
- *
- * <a name="DeveloperGuide"></a>
- * <h3>Developer Guide</h3>
- *
- * <p>The ability to search for user, system, or network based data is considered to be
- * a core user-level feature of the Android platform. At any time, the user should be
- * able to use a familiar command, button, or keystroke to invoke search, and the user
- * should be able to search any data which is available to them.
- *
- * <p>To make search appear to the user as a seamless system-wide feature, the application
- * framework centrally controls it, offering APIs to individual applications to control how they
- * are searched. Applications can customize how search is invoked, how the search dialog looks,
- * and what type of search results are available, including suggestions that are available as the
- * user types.
*
- * <p>Even applications which are not searchable will by default support the invocation of
- * search to trigger Quick Search Box, the system's 'global search'.
- *
- * <a name="HowSearchIsInvoked"></a>
- * <h3>How Search Is Invoked</h3>
- *
- * <p>Unless impossible or inapplicable, all applications should support
- * invoking the search UI. This means that when the user invokes the search command,
- * a search UI will be presented to them. The search command is currently defined as a menu
- * item called "Search" (with an alphabetic shortcut key of "S"), or on many devices, a dedicated
- * search button key.
- * <p>If your application is not inherently searchable, the default implementation will cause
- * the search UI to be invoked in a "global search" mode known as Quick Search Box. As the user
- * types, search suggestions from across the device and the web will be surfaced, and if they
- * click the "Search" button, this will bring the browser to the front and will launch a web-based
- * search. The user will be able to click the "Back" button and return to your application.
- * <p>In general this is implemented by your activity, or the {@link android.app.Activity Activity}
- * base class, which captures the search command and invokes the SearchManager to
- * display and operate the search UI. You can also cause the search UI to be presented in response
- * to user keystrokes in your activity (for example, to instantly start filter searching while
- * viewing a list and typing any key).
- * <p>The search UI is presented as a floating
- * window and does not cause any change in the activity stack. If the user
- * cancels search, the previous activity re-emerges. If the user launches a
- * search, this will be done by sending a search {@link android.content.Intent Intent} (see below),
- * and the normal intent-handling sequence will take place (your activity will pause,
- * etc.)
- * <p><b>What you need to do:</b> First, you should consider the way in which you want to
- * handle invoking search. There are four broad (and partially overlapping) categories for
- * you to choose from.
- * <ul><li>You can capture the search command yourself, by including a <i>search</i>
- * button or menu item - and invoking the search UI directly.</li>
- * <li>You can provide a <i>type-to-search</i> feature, in which search is invoked automatically
- * when the user enters any characters.</li>
- * <li>Even if your application is not inherently searchable, you can allow global search,
- * via the search key (or even via a search menu item).
- * <li>You can disable search entirely. This should only be used in very rare circumstances,
- * as search is a system-wide feature and users will expect it to be available in all contexts.</li>
- * </ul>
- *
- * <p><b>How to define a search menu.</b> The system provides the following resources which may
- * be useful when adding a search item to your menu:
- * <ul><li>android.R.drawable.ic_search_category_default is an icon you can use in your menu.</li>
- * <li>{@link #MENU_KEY SearchManager.MENU_KEY} is the recommended alphabetic shortcut.</li>
- * </ul>
- *
- * <p><b>How to invoke search directly.</b> In order to invoke search directly, from a button
- * or menu item, you can launch a generic search by calling
- * {@link android.app.Activity#onSearchRequested onSearchRequested} as shown:
- * <pre class="prettyprint">
- * onSearchRequested();</pre>
- *
- * <p><b>How to implement type-to-search.</b> While setting up your activity, call
- * {@link android.app.Activity#setDefaultKeyMode setDefaultKeyMode}:
- * <pre class="prettyprint">
- * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity
- * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre>
- *
- * <p><b>How to start global search.</b> In addition to searching within
- * your activity or application, you can also use the Search Manager to invoke a platform-global
- * search, which uses Quick Search Box to search across the device and the web.
- * Override {@link android.app.Activity#onSearchRequested} and call
- * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}.
- *
- * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users
- * will expect it to be available in all contexts. If your UI design absolutely precludes
- * launching search, override {@link android.app.Activity#onSearchRequested onSearchRequested}
- * as shown:
- * <pre class="prettyprint">
- * @Override
- * public boolean onSearchRequested() {
- * return false;
- * }</pre>
- *
- * <p><b>Managing focus and knowing if search is active.</b> The search UI is not a separate
- * activity, and when the UI is invoked or dismissed, your activity will not typically be paused,
- * resumed, or otherwise notified by the methods defined in
- * <a href="{@docRoot}guide/topics/fundamentals.html#actlife">Application Fundamentals:
- * Activity Lifecycle</a>. The search UI is
- * handled in the same way as other system UI elements which may appear from time to time, such as
- * notifications, screen locks, or other system alerts:
- * <p>When the search UI appears, your activity will lose input focus.
- * <p>When the search activity is dismissed, there are three possible outcomes:
- * <ul><li>If the user simply canceled the search UI, your activity will regain input focus and
- * proceed as before. See {@link #setOnDismissListener} and {@link #setOnCancelListener} if you
- * required direct notification of search dialog dismissals.</li>
- * <li>If the user launched a search, and this required switching to another activity to receive
- * and process the search {@link android.content.Intent Intent}, your activity will receive the
- * normal sequence of activity pause or stop notifications.</li>
- * <li>If the user launched a search, and the current activity is the recipient of the search
- * {@link android.content.Intent Intent}, you will receive notification via the
- * {@link android.app.Activity#onNewIntent onNewIntent()} method.</li></ul>
- * <p>This list is provided in order to clarify the ways in which your activities will interact with
- * the search UI. More details on searchable activities and search intents are provided in the
- * sections below.
- *
- * <a name="ImplementingSearchForYourApp"></a>
- * <h3>Implementing Search for Your App</h3>
- *
- * <p>The following steps are necessary in order to implement search.
- * <ul>
- * <li>Implement search invocation as described above. (Strictly speaking,
- * these are decoupled, but it would make little sense to be "searchable" but not
- * "search-invoking".)</li>
- * <li>Your application should have an activity that takes a search string and
- * converts it to a list of results. This could be your primary display activity
- * or it could be a dedicated search results activity. This is your <i>searchable</i>
- * activity and every query-search application must have one.</li>
- * <li>In the searchable activity, in onCreate(), you must receive and handle the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
- * {@link android.content.Intent Intent}. The text to search (query string) for is provided by
- * calling
- * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li>
- * <li>To identify and support your searchable activity, you'll need to
- * provide an XML file providing searchability configuration parameters, a reference to that
- * in your searchable activity's
- * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry, and an
- * intent-filter declaring that you can receive ACTION_SEARCH intents. This is described in more
- * detail in the <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
- * <li>Your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> also needs a
- * metadata entry providing a global reference to the searchable activity. This is the "glue"
- * directing the search UI, when invoked from any of your <i>other</i> activities, to use your
- * application as the default search context. This is also described in more detail in the
- * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li>
- * <li>Finally, you may want to define your search results activity as single-top with the
- * {@link android.R.attr#launchMode singleTop} launchMode flag. This allows the system
- * to launch searches from/to the same activity without creating a pile of them on the
- * activity stack. If you do this, be sure to also override
- * {@link android.app.Activity#onNewIntent onNewIntent} to handle the
- * updated intents (with new queries) as they arrive.</li>
- * </ul>
- *
- * <p>Code snippet showing handling of intents in your search activity:
- * <pre class="prettyprint">
- * @Override
- * protected void onCreate(Bundle icicle) {
- * super.onCreate(icicle);
- *
- * final Intent queryIntent = getIntent();
- * final String queryAction = queryIntent.getAction();
- * if (Intent.ACTION_SEARCH.equals(queryAction)) {
- * doSearchWithIntent(queryIntent);
- * }
- * }
- *
- * private void doSearchWithIntent(final Intent queryIntent) {
- * final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
- * doSearchWithQuery(queryString);
- * }</pre>
- *
- * <a name="Suggestions"></a>
- * <h3>Search Suggestions</h3>
- *
- * <p>A powerful feature of the search system is the ability of any application to easily provide
- * live "suggestions" in order to prompt the user. Each application implements suggestions in a
- * different, unique, and appropriate way. Suggestions be drawn from many sources, including but
- * not limited to:
- * <ul>
- * <li>Actual searchable results (e.g. names in the address book)</li>
- * <li>Recently entered queries</li>
- * <li>Recently viewed data or results</li>
- * <li>Contextually appropriate queries or results</li>
- * <li>Summaries of possible results</li>
- * </ul>
- *
- * <p>Once an application is configured to provide search suggestions, those same suggestions can
- * easily be made available to the system-wide Quick Search Box, providing faster access to its
- * content from one central prominent place. See
- * <a href="#ExposingSearchSuggestionsToQuickSearchBox">Exposing Search Suggestions to Quick Search
- * Box</a> for more details.
- *
- * <p>The primary form of suggestions is known as <i>queried suggestions</i> and is based on query
- * text that the user has already typed. This would generally be based on partial matches in
- * the available data. In certain situations - for example, when no query text has been typed yet -
- * an application may also opt to provide <i>zero-query suggestions</i>.
- * These would typically be drawn from the same data source, but because no partial query text is
- * available, they should be weighted based on other factors - for example, most recent queries
- * or most recent results.
- *
- * <p><b>Overview of how suggestions are provided.</b> Suggestions are accessed via a
- * {@link android.content.ContentProvider Content Provider}. When the search manager identifies a
- * particular activity as searchable, it will check for certain metadata which indicates that
- * there is also a source of suggestions. If suggestions are provided, the following steps are
- * taken.
- * <ul><li>Using formatting information found in the metadata, the user's query text (whatever
- * has been typed so far) will be formatted into a query and sent to the suggestions
- * {@link android.content.ContentProvider Content Provider}.</li>
- * <li>The suggestions {@link android.content.ContentProvider Content Provider} will create a
- * {@link android.database.Cursor Cursor} which can iterate over the possible suggestions.</li>
- * <li>The search manager will populate a list using display data found in each row of the cursor,
- * and display these suggestions to the user.</li>
- * <li>If the user types another key, or changes the query in any way, the above steps are repeated
- * and the suggestions list is updated or repopulated.</li>
- * <li>If the user clicks or touches the "GO" button, the suggestions are ignored and the search is
- * launched using the normal {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} type of
- * {@link android.content.Intent Intent}.</li>
- * <li>If the user uses the directional controls to navigate the focus into the suggestions list,
- * the query text will be updated while the user navigates from suggestion to suggestion. The user
- * can then click or touch the updated query and edit it further. If the user navigates back to
- * the edit field, the original typed query is restored.</li>
- * <li>If the user clicks or touches a particular suggestion, then a combination of data from the
- * cursor and
- * values found in the metadata are used to synthesize an Intent and send it to the application.
- * Depending on the design of the activity and the way it implements search, this might be a
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} (in order to launch a query), or it
- * might be a {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}, in order to proceed directly
- * to display of specific data.</li>
- * </ul>
- *
- * <p><b>Simple Recent-Query-Based Suggestions.</b> The Android framework provides a simple Search
- * Suggestions provider, which simply records and replays recent queries. For many applications,
- * this will be sufficient. The basic steps you will need to
- * do, in order to use the built-in recent queries suggestions provider, are as follows:
- * <ul>
- * <li>Implement and test query search, as described in the previous sections.</li>
- * <li>Create a Provider within your application by extending
- * {@link android.content.SearchRecentSuggestionsProvider}.</li>
- * <li>Create a manifest entry describing your provider.</li>
- * <li>Update your searchable activity's XML configuration file with information about your
- * provider.</li>
- * <li>In your searchable activities, capture any user-generated queries and record them
- * for future searches by calling {@link android.provider.SearchRecentSuggestions#saveRecentQuery}.
- * </li>
- * </ul>
- * <p>For complete implementation details, please refer to
- * {@link android.content.SearchRecentSuggestionsProvider}. The rest of the information in this
- * section should not be necessary, as it refers to custom suggestions providers.
- *
- * <p><b>Creating a Customized Suggestions Provider:</b> In order to create more sophisticated
- * suggestion providers, you'll need to take the following steps:
- * <ul>
- * <li>Implement and test query search, as described in the previous sections.</li>
- * <li>Decide how you wish to <i>receive</i> suggestions. Just like queries that the user enters,
- * suggestions will be delivered to your searchable activity as
- * {@link android.content.Intent Intent} messages; Unlike simple queries, you have quite a bit of
- * flexibility in forming those intents. A query search application will probably
- * wish to continue receiving the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
- * {@link android.content.Intent Intent}, which will launch a query search using query text as
- * provided by the suggestion. A filter search application will probably wish to
- * receive the {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}
- * {@link android.content.Intent Intent}, which will take the user directly to a selected entry.
- * Other interesting suggestions, including hybrids, are possible, and the suggestion provider
- * can easily mix-and-match results to provide a richer set of suggestions for the user. Finally,
- * you'll need to update your searchable activity (or other activities) to receive the intents
- * as you've defined them.</li>
- * <li>Implement a Content Provider that provides suggestions. If you already have one, and it
- * has access to your suggestions data, you can use that provider. If not, you'll have to create
- * one. You'll also provide information about your Content Provider in your
- * package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li>
- * <li>Update your searchable activity's XML configuration file. There are two categories of
- * information used for suggestions:
- * <ul><li>The first is (required) data that the search manager will
- * use to format the queries which are sent to the Content Provider.</li>
- * <li>The second is (optional) parameters to configure structure
- * if intents generated by suggestions.</li></li>
- * </ul>
- * </ul>
- *
- * <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of
- * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide
- * "live" (while-you-type) conversion of the user's query text into a set of zero or more
- * suggestions. Each application is free to define the conversion, and as described above there are
- * many possible solutions. This section simply defines how to communicate with the suggestion
- * provider.
- *
- * <p>The Search Manager must first determine if your package provides suggestions. This is done
- * by examination of your searchable meta-data XML file. The android:searchSuggestAuthority
- * attribute, if provided, is the signal to obtain & display suggestions.
- *
- * <p>Every query includes a Uri, and the Search Manager will format the Uri as shown:
- * <p><pre class="prettyprint">
- * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY
- * </pre>
- *
- * <p>Your Content Provider can receive the query text in one of two ways.
- * <ul>
- * <li><b>Query provided as a selection argument.</b> If you define the attribute value
- * android:searchSuggestSelection and include a string, this string will be passed as the
- * <i>selection</i> parameter to your Content Provider's query function. You must define a single
- * selection argument, using the '?' character. The user's query text will be passed to you
- * as the first element of the selection arguments array.</li>
- * <li><b>Query provided with Data Uri.</b> If you <i>do not</i> define the attribute value
- * android:searchSuggestSelection, then the Search Manager will append another "/" followed by
- * the user's query to the query Uri. The query will be encoding using Uri encoding rules - don't
- * forget to decode it. (See {@link android.net.Uri#getPathSegments} and
- * {@link android.net.Uri#getLastPathSegment} for helpful utilities you can use here.)</li>
- * </ul>
- *
- * <p><b>Providing access to Content Providers that require permissions.</b> If your content
- * provider declares an android:readPermission in your application's manifest, you must provide
- * access to the search infrastructure to the search suggestion path by including a path-permission
- * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access
- * explicitly to the search infrastructure ensures it will be able to access the search suggestions
- * without needing to know ahead of time any other details of the permissions protecting your
- * provider. Content providers that require no permissions are already available to the search
- * infrastructure. Here is an example of a provider that protects access to it with permissions,
- * and provides read access to the search infrastructure to the path that it expects to receive the
- * suggestion query on:
- * <pre class="prettyprint">
- * <provider android:name="MyProvider" android:authorities="myprovider"
- * android:readPermission="android.permission.READ_MY_DATA"
- * android:writePermission="android.permission.WRITE_MY_DATA">
- * <path-permission android:path="/search_suggest_query"
- * android:readPermission="android.permission.GLOBAL_SEARCH" />
- * </provider>
- * </pre>
- *
- * <p><b>Handling empty queries.</b> Your application should handle the "empty query"
- * (no user text entered) case properly, and generate useful suggestions in this case. There are a
- * number of ways to do this; Two are outlined here:
- * <ul><li>For a simple filter search of local data, you could simply present the entire dataset,
- * unfiltered. (example: People)</li>
- * <li>For a query search, you could simply present the most recent queries. This allows the user
- * to quickly repeat a recent search.</li></ul>
- *
- * <p><b>The Format of Individual Suggestions.</b> Your suggestions are communicated back to the
- * Search Manager by way of a {@link android.database.Cursor Cursor}. The Search Manager will
- * usually pass a null Projection, which means that your provider can simply return all appropriate
- * columns for each suggestion. The columns currently defined are:
- *
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Column Name</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tbody>
- * <tr><th>{@link #SUGGEST_COLUMN_FORMAT}</th>
- * <td><i>Unused - can be null.</i></td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_TEXT_1}</th>
- * <td>This is the line of text that will be presented to the user as the suggestion.</td>
- * <td align="center">Yes</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_TEXT_2}</th>
- * <td>If your cursor includes this column, then all suggestions will be provided in a
- * two-line format. The data in this column will be displayed as a second, smaller
- * line of text below the primary suggestion, or it can be null or empty to indicate no
- * text in this row's suggestion.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_ICON_1}</th>
- * <td>If your cursor includes this column, then all suggestions will be provided in an
- * icons+text format. This value should be a reference to the icon to
- * draw on the left side, or it can be null or zero to indicate no icon in this row.
- * </td>
- * <td align="center">No.</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_ICON_2}</th>
- * <td>If your cursor includes this column, then all suggestions will be provided in an
- * icons+text format. This value should be a reference to the icon to
- * draw on the right side, or it can be null or zero to indicate no icon in this row.
- * </td>
- * <td align="center">No.</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_INTENT_ACTION}</th>
- * <td>If this column exists <i>and</i> this element exists at the given row, this is the
- * action that will be used when forming the suggestion's intent. If the element is
- * not provided, the action will be taken from the android:searchSuggestIntentAction
- * field in your XML metadata. <i>At least one of these must be present for the
- * suggestion to generate an intent.</i> Note: If your action is the same for all
- * suggestions, it is more efficient to specify it using XML metadata and omit it from
- * the cursor.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA}</th>
- * <td>If this column exists <i>and</i> this element exists at the given row, this is the
- * data that will be used when forming the suggestion's intent. If the element is not
- * provided, the data will be taken from the android:searchSuggestIntentData field in
- * your XML metadata. If neither source is provided, the Intent's data field will be
- * null. Note: If your data is the same for all suggestions, or can be described
- * using a constant part and a specific ID, it is more efficient to specify it using
- * XML metadata and omit it from the cursor.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_INTENT_DATA_ID}</th>
- * <td>If this column exists <i>and</i> this element exists at the given row, then "/" and
- * this value will be appended to the data field in the Intent. This should only be
- * used if the data field has already been set to an appropriate base string.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA}</th>
- * <td>If this column exists <i>and</i> this element exists at a given row, this is the
- * data that will be used when forming the suggestion's intent. If not provided,
- * the Intent's extra data field will be null. This column allows suggestions to
- * provide additional arbitrary data which will be included as an extra under the
- * key {@link #EXTRA_DATA_KEY}.</td>
- * <td align="center">No.</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_QUERY}</th>
- * <td>If this column exists <i>and</i> this element exists at the given row, this is the
- * data that will be used when forming the suggestion's query.</td>
- * <td align="center">Required if suggestion's action is
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_SHORTCUT_ID}</th>
- * <td>This column is used to indicate whether a search suggestion should be stored as a
- * shortcut, and whether it should be validated. Shortcuts are usually formed when the
- * user clicks a suggestion from Quick Search Box. If missing, the result will be
- * stored as a shortcut and never refreshed. If set to
- * {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
- * Otherwise, the shortcut id will be used to check back for for an up to date
- * suggestion using {@link #SUGGEST_URI_PATH_SHORTCUT}. Read more about shortcut
- * refreshing in the section about
- * <a href="#ExposingSearchSuggestionsToQuickSearchBox">exposing search suggestions to
- * Quick Search Box</a>.</td>
- * <td align="center">No. Only applicable to sources included in Quick Search Box.</td>
- * </tr>
- *
- * <tr><th>{@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</th>
- * <td>This column is used to specify that a spinner should be shown in lieu of an icon2
- * while the shortcut of this suggestion is being refreshed in Quick Search Box.</td>
- * <td align="center">No. Only applicable to sources included in Quick Search Box.</td>
- * </tr>
- *
- * <tr><th><i>Other Columns</i></th>
- * <td>Finally, if you have defined any <a href="#ActionKeys">Action Keys</a> and you wish
- * for them to have suggestion-specific definitions, you'll need to define one
- * additional column per action key. The action key will only trigger if the
- * currently-selection suggestion has a non-empty string in the corresponding column.
- * See the section on <a href="#ActionKeys">Action Keys</a> for additional details and
- * implementation steps.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <p>Clearly there are quite a few permutations of your suggestion data, but in the next section
- * we'll look at a few simple combinations that you'll select from.
- *
- * <p><b>The Format Of Intents Sent By Search Suggestions.</b> Although there are many ways to
- * configure these intents, this document will provide specific information on just a few of them.
- * <ul><li><b>Launch a query.</b> In this model, each suggestion represents a query that your
- * searchable activity can perform, and the {@link android.content.Intent Intent} will be formatted
- * exactly like those sent when the user enters query text and clicks the "GO" button:
- * <ul>
- * <li><b>Action:</b> {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} provided
- * using your XML metadata (android:searchSuggestIntentAction).</li>
- * <li><b>Data:</b> empty (not used).</li>
- * <li><b>Query:</b> query text supplied by the cursor.</li>
- * </ul>
- * </li>
- * <li><b>Go directly to a result, using a complete Data Uri.</b> In this model, the user will be
- * taken directly to a specific result.
- * <ul>
- * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
- * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.
- * </li>
- * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
- * </ul>
- * </li>
- * <li><b>Go directly to a result, using a synthesized Data Uri.</b> This has the same result
- * as the previous suggestion, but provides the Data Uri in a different way.
- * <ul>
- * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
- * <li><b>Data:</b> The search manager will assemble a Data Uri using the following elements:
- * a Uri fragment provided in your XML metadata (android:searchSuggestIntentData), followed by
- * a single "/", followed by the value found in the {@link #SUGGEST_COLUMN_INTENT_DATA_ID}
- * entry in your cursor.</li>
- * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
- * </ul>
- * </li>
- * </ul>
- * <p>This list is not meant to be exhaustive. Applications should feel free to define other types
- * of suggestions. For example, you could reduce long lists of results to summaries, and use one
- * of the above intents (or one of your own) with specially formatted Data Uri's to display more
- * detailed results. Or you could display textual shortcuts as suggestions, but launch a display
- * in a more data-appropriate format such as media artwork.
- *
- * <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI
- * may temporarily rewrite the user's query with a query that matches the currently selected
- * suggestion. This enables the user to see what query is being suggested, and also allows the user
- * to click or touch in the entry EditText element and make further edits to the query before
- * dispatching it. In order to perform this correctly, the Search UI needs to know exactly what
- * text to rewrite the query with.
- *
- * <p>For each suggestion, the following logic is used to select a new query string:
- * <ul><li>If the suggestion provides an explicit value in the {@link #SUGGEST_COLUMN_QUERY}
- * column, this value will be used.</li>
- * <li>If the metadata includes the queryRewriteFromData flag, and the suggestion provides an
- * explicit value for the intent Data field, this Uri will be used. Note that this should only be
- * used with Uri's that are intended to be user-visible, such as HTTP. Internal Uri schemes should
- * not be used in this way.</li>
- * <li>If the metadata includes the queryRewriteFromText flag, the text in
- * {@link #SUGGEST_COLUMN_TEXT_1} will be used. This should be used for suggestions in which no
- * query text is provided and the SUGGEST_COLUMN_INTENT_DATA values are not suitable for user
- * inspection and editing.</li></ul>
- *
- * <a name="ExposingSearchSuggestionsToQuickSearchBox"></a>
- * <h3>Exposing Search Suggestions to Quick Search Box</h3>
- *
- * <p>Once your application is set up to provide search suggestions, making them available to the
- * globally accessable Quick Search Box is as easy as setting android:includeInGlobalSearch to
- * "true" in your searchable metadata file. Beyond that, here are some more details of how
- * suggestions interact with Quick Search Box, and optional ways that you may customize suggestions
- * for your application.
- *
- * <p><b>Important Note:</b> By default, your application will not be enabled as a suggestion
- * provider (or "searchable item") in Quick Search Box. Once your app is installed, the user must
- * enable it as a "searchable item" in the Search settings in order to receive your app's
- * suggestions in Quick Search Box. You should consider how to message this to users of your app -
- * perhaps with a note to the user the first time they launch the app about how to enable search
- * suggestions. This gives your app a chance to be queried for suggestions as the user types into
- * Quick Search Box, though exactly how or if your suggestions will be surfaced is decided by Quick
- * Search Box.
- *
- * <p><b>Source Ranking:</b> Once your application's search results are made available to Quick
- * Search Box, how they surface to the user for a particular query will be determined as appropriate
- * by Quick Search Box ranking. This may depend on how many other apps have results for that query,
- * and how often the user has clicked on your results compared to the other apps - but there is no
- * guarantee about how ranking will occur, or whether your app's suggestions will show at all for
- * a given query. In general, you can expect that providing quality results will increase the
- * likelihood that your app's suggestions are provided in a prominent position, and apps that
- * provide lower quality suggestions will be more likely to be ranked lower and/or not displayed.
- *
- * <p><b>Search Settings:</b> Each app that is available to Quick Search Box has an entry in the
- * system settings where the user can enable or disable the inclusion of its results. Below the
- * name of the application, each application may provide a brief description of what kind of
- * information will be made available via a search settings description string pointed to by the
- * android:searchSettingsDescription attribute in the searchable metadata. Note that the
- * user will need to visit this settings menu to enable search suggestions for your app before your
- * app will have a chance to provide search suggestions to Quick Search Box - see the section
- * called "Important Note" above.
- *
- * <p><b>Shortcuts:</b> Suggestions that are clicked on by the user may be automatically made into
- * shortcuts, which are suggestions that have been copied from your provider in order to be quickly
- * displayed without the need to re-query the original sources. Shortcutted suggestions may be
- * displayed for the query that yielded the suggestion and for any prefixes of that query. You can
- * request how to have your app's suggestions made into shortcuts, and whether they should be
- * refreshed, using the {@link #SUGGEST_COLUMN_SHORTCUT_ID} column:
- * <ul><li>Suggestions that do not include a shortcut id column will be made into shortcuts and
- * never refreshed. This makes sense for suggestions that refer to data that will never be changed
- * or removed.</li>
- * <li>Suggestions that include a shortcut id will be re-queried for a fresh version of the
- * suggestion each time the shortcut is displayed. The shortcut will be quickly displayed with
- * whatever data was most recently available until the refresh query returns, after which the
- * suggestion will be dynamically refreshed with the up to date information. The shortcut refresh
- * query will be sent to your suggestion provider with a uri of {@link #SUGGEST_URI_PATH_SHORTCUT}.
- * The result should contain one suggestion using the same columns as the suggestion query, or be
- * empty, indicating that the shortcut is no longer valid. Shortcut ids make sense when referring
- * to data that may change over time, such as a contact's presence status. If a suggestion refers
- * to data that could take longer to refresh, such as a network based refresh of a stock quote, you
- * may include {@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} to show a progress spinner for the
- * right hand icon until the refresh is complete.</li>
- * <li>Finally, to prevent a suggestion from being copied into a shortcut, you may provide a
- * shortcut id with a value of {@link #SUGGEST_NEVER_MAKE_SHORTCUT}.</li></ul>
- *
- * Note that Quick Search Box will ultimately decide whether to shortcut your app's suggestions,
- * considering these values as a strong request from your application.
- *
- * <a name="ActionKeys"></a>
- * <h3>Action Keys</h3>
- *
- * <p>Searchable activities may also wish to provide shortcuts based on the various action keys
- * available on the device. The most basic example of this is the contacts app, which enables the
- * green "dial" key for quick access during searching. Not all action keys are available on
- * every device, and not all are allowed to be overriden in this way. (For example, the "Home"
- * key must always return to the home screen, with no exceptions.)
- *
- * <p>In order to define action keys for your searchable application, you must do two things.
- *
- * <ul>
- * <li>You'll add one or more <i>actionkey</i> elements to your searchable metadata configuration
- * file. Each element defines one of the keycodes you are interested in,
- * defines the conditions under which they are sent, and provides details
- * on how to communicate the action key event back to your searchable activity.</li>
- * <li>In your broadcast receiver, if you wish, you can check for action keys by checking the
- * extras field of the {@link android.content.Intent Intent}.</li>
- * </ul>
- *
- * <p><b>Updating metadata.</b> For each keycode of interest, you must add an <actionkey>
- * element. Within this element you must define two or three attributes. The first attribute,
- * <android:keycode>, is required; It is the key code of the action key event, as defined in
- * {@link android.view.KeyEvent}. The remaining two attributes define the value of the actionkey's
- * <i>message</i>, which will be passed to your searchable activity in the
- * {@link android.content.Intent Intent} (see below for more details). Although each of these
- * attributes is optional, you must define one or both for the action key to have any effect.
- * <android:queryActionMsg> provides the message that will be sent if the action key is
- * pressed while the user is simply entering query text. <android:suggestActionMsgColumn>
- * is used when action keys are tied to specific suggestions. This attribute provides the name
- * of a <i>column</i> in your suggestion cursor; The individual suggestion, in that column,
- * provides the message. (If the cell is empty or null, that suggestion will not work with that
- * action key.)
- * <p>See the <a href="#SearchabilityMetadata">Searchability Metadata</a> section for more details
- * and examples.
- *
- * <p><b>Receiving Action Keys</b> Intents launched by action keys will be specially marked
- * using a combination of values. This enables your searchable application to examine the intent,
- * if necessary, and perform special processing. For example, clicking a suggested contact might
- * simply display them; Selecting a suggested contact and clicking the dial button might
- * immediately call them.
- *
- * <p>When a search {@link android.content.Intent Intent} is launched by an action key, two values
- * will be added to the extras field.
- * <ul>
- * <li>To examine the key code, use {@link android.content.Intent#getIntExtra
- * getIntExtra(SearchManager.ACTION_KEY)}.</li>
- * <li>To examine the message string, use {@link android.content.Intent#getStringExtra
- * getStringExtra(SearchManager.ACTION_MSG)}</li>
- * </ul>
- *
- * <a name="SearchabilityMetadata"></a>
- * <h3>Searchability Metadata</h3>
- *
- * <p>Every activity that is searchable must provide a small amount of additional information
- * in order to properly configure the search system. This controls the way that your search
- * is presented to the user, and controls for the various modalities described previously.
- *
- * <p>If your application is not searchable,
- * then you do not need to provide any search metadata, and you can skip the rest of this section.
- * When this search metadata cannot be found, the search manager will assume that the activity
- * does not implement search. (Note: to implement web-based search, you will need to add
- * the android.app.default_searchable metadata to your manifest, as shown below.)
- *
- * <p>Values you supply in metadata apply only to each local searchable activity. Each
- * searchable activity can define a completely unique search experience relevant to its own
- * capabilities and user experience requirements, and a single application can even define multiple
- * searchable activities.
- *
- * <p><b>Metadata for searchable activity.</b> As with your search implementations described
- * above, you must first identify which of your activities is searchable. In the
- * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for this activity, you must
- * provide two elements:
- * <ul><li>An intent-filter specifying that you can receive and process the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}.
- * </li>
- * <li>A reference to a small XML file (typically called "searchable.xml") which contains the
- * remaining configuration information for how your application implements search.</li></ul>
- *
- * <p>Here is a snippet showing the necessary elements in the
- * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for your searchable activity.
- * <pre class="prettyprint">
- * <!-- Search Activity - searchable -->
- * <activity android:name="MySearchActivity"
- * android:label="Search"
- * android:launchMode="singleTop">
- * <intent-filter>
- * <action android:name="android.intent.action.SEARCH" />
- * <category android:name="android.intent.category.DEFAULT" />
- * </intent-filter>
- * <meta-data android:name="android.app.searchable"
- * android:resource="@xml/searchable" />
- * </activity></pre>
- *
- * <p>Next, you must provide the rest of the searchability configuration in
- * the small XML file, stored in the ../xml/ folder in your build. The XML file is a
- * simple enumeration of the search configuration parameters for searching within this activity,
- * application, or package. Here is a sample XML file (named searchable.xml, for use with
- * the above manifest) for a query-search activity.
- *
- * <pre class="prettyprint">
- * <searchable xmlns:android="http://schemas.android.com/apk/res/android"
- * android:label="@string/search_label"
- * android:hint="@string/search_hint" >
- * </searchable></pre>
- *
- * <p>Note that all user-visible strings <i>must</i> be provided in the form of "@string"
- * references. Hard-coded strings, which cannot be localized, will not work properly in search
- * metadata.
- *
- * <p>Attributes you can set in search metadata:
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tbody>
- * <tr><th>android:label</th>
- * <td>This is the name for your application that will be presented to the user in a
- * list of search targets, or in the search box as a label.</td>
- * <td align="center">Yes</td>
- * </tr>
- *
- * <tr><th>android:icon</th>
- * <td><strong>This is deprecated.</strong><br/>The default
- * application icon is now always used, so this attribute is
- * obsolete.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:hint</th>
- * <td>This is the text to display in the search text field when no text
- * has been entered by the user.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:searchMode</th>
- * <td>If provided and non-zero, sets additional modes for control of the search
- * presentation. The following mode bits are defined:
- * <table border="2" align="center" frame="hsides" rules="rows">
- * <tbody>
- * <tr><th>showSearchLabelAsBadge</th>
- * <td>If set, this flag enables the display of the search target (label)
- * above the search box. As an alternative, you may
- * want to instead use "hint" text in the search box.
- * See the "android:hint" attribute above.</td>
- * </tr>
- * <tr><th>showSearchIconAsBadge</th>
- * <td><strong>This is deprecated.</strong><br/>The default
- * application icon is now always used, so this
- * option is obsolete.</td>
- * </tr>
- * <tr><th>queryRewriteFromData</th>
- * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA
- * to be considered as the text for suggestion query rewriting. This should
- * only be used when the values in SUGGEST_COLUMN_INTENT_DATA are suitable
- * for user inspection and editing - typically, HTTP/HTTPS Uri's.</td>
- * </tr>
- * <tr><th>queryRewriteFromText</th>
- * <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_TEXT_1 to
- * be considered as the text for suggestion query rewriting. This should
- * be used for suggestions in which no query text is provided and the
- * SUGGEST_COLUMN_INTENT_DATA values are not suitable for user inspection
- * and editing.</td>
- * </tr>
- * </tbody>
- * </table>
- * Note that the icon of your app will likely be shown alongside any badge you specify,
- * to differentiate search in your app from Quick Search Box. The display of this icon
- * is not under the app's control.
- * </td>
- *
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:inputType</th>
- * <td>If provided, supplies a hint about the type of search text the user will be
- * entering. For most searches, in which free form text is expected, this attribute
- * need not be provided. Suitable values for this attribute are described in the
- * <a href="../R.attr.html#inputType">inputType</a> attribute.</td>
- * <td align="center">No</td>
- * </tr>
- * <tr><th>android:imeOptions</th>
- * <td>If provided, supplies additional options for the input method.
- * For most searches, in which free form text is expected, this attribute
- * need not be provided, and will default to "actionSearch".
- * Suitable values for this attribute are described in the
- * <a href="../R.attr.html#imeOptions">imeOptions</a> attribute.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <p><b>Styleable Resources in your Metadata.</b> It's possible to provide alternate strings
- * for your searchable application, in order to provide localization and/or to better visual
- * presentation on different device configurations. Each searchable activity has a single XML
- * metadata file, but any resource references can be replaced at runtime based on device
- * configuration, language setting, and other system inputs.
- *
- * <p>A concrete example is the "hint" text you supply using the android:searchHint attribute.
- * In portrait mode you'll have less screen space and may need to provide a shorter string, but
- * in landscape mode you can provide a longer, more descriptive hint. To do this, you'll need to
- * define two or more strings.xml files, in the following directories:
- * <ul><li>.../res/values-land/strings.xml</li>
- * <li>.../res/values-port/strings.xml</li>
- * <li>.../res/values/strings.xml</li></ul>
- *
- * <p>For more complete documentation on this capability, see
- * <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">Resources and
- * Internationalization: Alternate Resources</a>.
- *
- * <p><b>Metadata for non-searchable activities.</b> Activities which are part of a searchable
- * application, but don't implement search itself, require a bit of "glue" in order to cause
- * them to invoke search using your searchable activity as their primary context. If this is not
- * provided, then searches from these activities will use the system default search context.
- *
- * <p>The simplest way to specify this is to add a <i>search reference</i> element to the
- * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file.
- * The value of this reference should be the name of your searchable activity.
- * It is typically prefixed by '.' to indicate that it's in the same package.
- *
- * <p>Here is a snippet showing the necessary addition to the manifest entry for your
- * non-searchable activities.
- * <pre class="prettyprint">
- * <application>
- * <meta-data android:name="android.app.default_searchable"
- * android:value=".MySearchActivity" />
- *
- * <!-- followed by activities, providers, etc... -->
- * </application></pre>
- *
- * <p>You can also specify android.app.default_searchable on a per-activity basis, by including
- * the meta-data element (as shown above) in one or more activity sections. If found, these will
- * override the reference in the application section. The only reason to configure your application
- * this way would be if you wish to partition it into separate sections with different search
- * behaviors; Otherwise this configuration is not recommended.
- *
- * <p><b>Additional metadata for search suggestions.</b> If you have defined a content provider
- * to generate search suggestions, you'll need to publish it to the system, and you'll need to
- * provide a bit of additional XML metadata in order to configure communications with it.
- *
- * <p>First, in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>, you'll add the
- * following lines.
- * <pre class="prettyprint">
- * <!-- Content provider for search suggestions -->
- * <provider android:name="YourSuggestionProviderClass"
- * android:authorities="your.suggestion.authority" /></pre>
- *
- * <p>Next, you'll add a few lines to your XML metadata file, as shown:
- * <pre class="prettyprint">
- * <!-- Required attribute for any suggestions provider -->
- * android:searchSuggestAuthority="your.suggestion.authority"
- *
- * <!-- Optional attribute for configuring queries -->
- * android:searchSuggestSelection="field =?"
- *
- * <!-- Optional attributes for configuring intent construction -->
- * android:searchSuggestIntentAction="intent action string"
- * android:searchSuggestIntentData="intent data Uri" /></pre>
- *
- * <p>Elements of search metadata that support suggestions:
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tbody>
- * <tr><th>android:searchSuggestAuthority</th>
- * <td>This value must match the authority string provided in the <i>provider</i> section
- * of your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</td>
- * <td align="center">Yes</td>
- * </tr>
- *
- * <tr><th>android:searchSuggestPath</th>
- * <td>If provided, this will be inserted in the suggestions query Uri, after the authority
- * you have provide but before the standard suggestions path. This is only required if
- * you have a single content provider issuing different types of suggestions (e.g. for
- * different data types) and you need a way to disambiguate the suggestions queries
- * when they are received.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:searchSuggestSelection</th>
- * <td>If provided, this value will be passed into your query function as the
- * <i>selection</i> parameter. Typically this will be a WHERE clause for your database,
- * and will contain a single question mark, which represents the actual query string
- * that has been typed by the user. However, you can also use any non-null value
- * to simply trigger the delivery of the query text (via selection arguments), and then
- * use the query text in any way appropriate for your provider (ignoring the actual
- * text of the selection parameter.)</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:searchSuggestIntentAction</th>
- * <td>If provided, and not overridden by the selected suggestion, this value will be
- * placed in the action field of the {@link android.content.Intent Intent} when the
- * user clicks a suggestion.</td>
- * <td align="center">No</td>
- *
- * <tr><th>android:searchSuggestIntentData</th>
- * <td>If provided, and not overridden by the selected suggestion, this value will be
- * placed in the data field of the {@link android.content.Intent Intent} when the user
- * clicks a suggestion.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <p>Elements of search metadata that configure search suggestions being available to Quick Search
- * Box:
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tr><th>android:includeInGlobalSearch</th>
- * <td>If true, indicates the search suggestions provided by your application should be
- * included in the globally accessible Quick Search Box. The attributes below are only
- * applicable if this is set to true.</td>
- * <td align="center">Yes</td>
- * </tr>
- *
- * <tr><th>android:searchSettingsDescription</th>
- * <td>If provided, provides a brief description of the search suggestions that are provided
- * by your application to Quick Search Box, and will be displayed in the search settings
- * entry for your application.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:queryAfterZeroResults</th>
- * <td>Indicates whether a source should be invoked for supersets of queries it has
- * returned zero results for in the past. For example, if a source returned zero
- * results for "bo", it would be ignored for "bob". If set to false, this source
- * will only be ignored for a single session; the next time the search dialog is
- * invoked, all sources will be queried. The default value is false.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:searchSuggestThreshold</th>
- * <td>Indicates the minimum number of characters needed to trigger a source from Quick
- * Search Box. Only guarantees that a source will not be queried for anything shorter
- * than the threshold. The default value is 0.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <p><b>Additional metadata for search action keys.</b> For each action key that you would like to
- * define, you'll need to add an additional element defining that key, and using the attributes
- * discussed in <a href="#ActionKeys">Action Keys</a>. A simple example is shown here:
- *
- * <pre class="prettyprint"><actionkey
- * android:keycode="KEYCODE_CALL"
- * android:queryActionMsg="call"
- * android:suggestActionMsg="call"
- * android:suggestActionMsgColumn="call_column" /></pre>
- *
- * <p>Elements of search metadata that support search action keys. Note that although each of the
- * action message elements are marked as <i>optional</i>, at least one must be present for the
- * action key to have any effect.
- *
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tbody>
- * <tr><th>android:keycode</th>
- * <td>This attribute denotes the action key you wish to respond to. Note that not
- * all action keys are actually supported using this mechanism, as many of them are
- * used for typing, navigation, or system functions. This will be added to the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
- * your searchable activity. To examine the key code, use
- * {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.
- * <p>Note, in addition to the keycode, you must also provide one or more of the action
- * specifier attributes.</td>
- * <td align="center">Yes</td>
- * </tr>
- *
- * <tr><th>android:queryActionMsg</th>
- * <td>If you wish to handle an action key during normal search query entry, you
- * must define an action string here. This will be added to the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to your
- * searchable activity. To examine the string, use
- * {@link android.content.Intent#getStringExtra
- * getStringExtra(SearchManager.ACTION_MSG)}.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:suggestActionMsg</th>
- * <td>If you wish to handle an action key while a suggestion is being displayed <i>and
- * selected</i>, there are two ways to handle this. If <i>all</i> of your suggestions
- * can handle the action key, you can simply define the action message using this
- * attribute. This will be added to the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
- * your searchable activity. To examine the string, use
- * {@link android.content.Intent#getStringExtra
- * getStringExtra(SearchManager.ACTION_MSG)}.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:suggestActionMsgColumn</th>
- * <td>If you wish to handle an action key while a suggestion is being displayed <i>and
- * selected</i>, but you do not wish to enable this action key for every suggestion,
- * then you can use this attribute to control it on a suggestion-by-suggestion basis.
- * First, you must define a column (and name it here) where your suggestions will
- * include the action string. Then, in your content provider, you must provide this
- * column, and when desired, provide data in this column.
- * The search manager will look at your suggestion cursor, using the string
- * provided here in order to select a column, and will use that to select a string from
- * the cursor. That string will be added to the
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} intent that is passed to
- * your searchable activity. To examine the string, use
- * {@link android.content.Intent#getStringExtra
- * getStringExtra(SearchManager.ACTION_MSG)}. <i>If the data does not exist for the
- * selection suggestion, the action key will be ignored.</i></td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <p><b>Additional metadata for enabling voice search.</b> To enable voice search for your
- * activity, you can add fields to the metadata that enable and configure voice search. When
- * enabled (and available on the device), a voice search button will be displayed in the
- * Search UI. Clicking this button will launch a voice search activity. When the user has
- * finished speaking, the voice search phrase will be transcribed into text and presented to the
- * searchable activity as if it were a typed query.
- *
- * <p>Elements of search metadata that support voice search:
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- *
- * <thead>
- * <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
- * </thead>
- *
- * <tr><th>android:voiceSearchMode</th>
- * <td>If provided and non-zero, enables voice search. (Voice search may not be
- * provided by the device, in which case these flags will have no effect.) The
- * following mode bits are defined:
- * <table border="2" align="center" frame="hsides" rules="rows">
- * <tbody>
- * <tr><th>showVoiceSearchButton</th>
- * <td>If set, display a voice search button. This only takes effect if voice
- * search is available on the device. If set, then launchWebSearch or
- * launchRecognizer must also be set.</td>
- * </tr>
- * <tr><th>launchWebSearch</th>
- * <td>If set, the voice search button will take the user directly to a
- * built-in voice web search activity. Most applications will not use this
- * flag, as it will take the user away from the activity in which search
- * was invoked.</td>
- * </tr>
- * <tr><th>launchRecognizer</th>
- * <td>If set, the voice search button will take the user directly to a
- * built-in voice recording activity. This activity will prompt the user
- * to speak, transcribe the spoken text, and forward the resulting query
- * text to the searchable activity, just as if the user had typed it into
- * the search UI and clicked the search button.</td>
- * </tr>
- * </tbody>
- * </table></td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:voiceLanguageModel</th>
- * <td>If provided, this specifies the language model that should be used by the voice
- * recognition system.
- * See {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL}
- * for more information. If not provided, the default value
- * {@link android.speech.RecognizerIntent#LANGUAGE_MODEL_FREE_FORM} will be used.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:voicePromptText</th>
- * <td>If provided, this specifies a prompt that will be displayed during voice input.
- * (If not provided, a default prompt will be displayed.)</td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:voiceLanguage</th>
- * <td>If provided, this specifies the spoken language to be expected. This is only
- * needed if it is different from the current value of
- * {@link java.util.Locale#getDefault()}.
- * </td>
- * <td align="center">No</td>
- * </tr>
- *
- * <tr><th>android:voiceMaxResults</th>
- * <td>If provided, enforces the maximum number of results to return, including the "best"
- * result which will always be provided as the SEARCH intent's primary query. Must be
- * one or greater. Use {@link android.speech.RecognizerIntent#EXTRA_RESULTS}
- * to get the results from the intent. If not provided, the recognizer will choose
- * how many results to return.</td>
- * <td align="center">No</td>
- * </tr>
- *
- * </tbody>
- * </table>
- *
- * <a name="PassingSearchContext"></a>
- * <h3>Passing Search Context</h3>
- *
- * <p>In order to improve search experience, an application may wish to specify
- * additional data along with the search, such as local history or context. For
- * example, a maps search would be improved by including the current location.
- * In order to simplify the structure of your activities, this can be done using
- * the search manager.
- *
- * <p>Any data can be provided at the time the search is launched, as long as it
- * can be stored in a {@link android.os.Bundle Bundle} object.
- *
- * <p>To pass application data into the Search Manager, you'll need to override
- * {@link android.app.Activity#onSearchRequested onSearchRequested} as follows:
- *
- * <pre class="prettyprint">
- * @Override
- * public boolean onSearchRequested() {
- * Bundle appData = new Bundle();
- * appData.put...();
- * appData.put...();
- * startSearch(null, false, appData, false);
- * return true;
- * }</pre>
- *
- * <p>To receive application data from the Search Manager, you'll extract it from
- * the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
- * {@link android.content.Intent Intent} as follows:
- *
- * <pre class="prettyprint">
- * final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA);
- * if (appData != null) {
- * appData.get...();
- * appData.get...();
- * }</pre>
- *
- * <a name="ProtectingUserPrivacy"></a>
- * <h3>Protecting User Privacy</h3>
- *
- * <p>Many users consider their activities on the phone, including searches, to be private
- * information. Applications that implement search should take steps to protect users' privacy
- * wherever possible. This section covers two areas of concern, but you should consider your search
- * design carefully and take any additional steps necessary.
- *
- * <p><b>Don't send personal information to servers, and if you do, don't log it.</b>
- * "Personal information" is information that can personally identify your users, such as name,
- * email address or billing information, or other data which can be reasonably linked to such
- * information. If your application implements search with the assistance of a server, try to
- * avoid sending personal information with your searches. For example, if you are searching for
- * businesses near a zip code, you don't need to send the user ID as well - just send the zip code
- * to the server. If you do need to send personal information, you should take steps to avoid
- * logging it. If you must log it, you should protect that data very carefully, and erase it as
- * soon as possible.
- *
- * <p><b>Provide the user with a way to clear their search history.</b> The Search Manager helps
- * your application provide context-specific suggestions. Sometimes these suggestions are based
- * on previous searches, or other actions taken by the user in an earlier session. A user may not
- * wish for previous searches to be revealed to other users, for instance if they share their phone
- * with a friend. If your application provides suggestions that can reveal previous activities,
- * you should implement a "Clear History" menu, preference, or button. If you are using
- * {@link android.provider.SearchRecentSuggestions}, you can simply call its
- * {@link android.provider.SearchRecentSuggestions#clearHistory() clearHistory()} method from
- * your "Clear History" UI. If you are implementing your own form of recent suggestions, you'll
- * need to provide a similar a "clear history" API in your provider, and call it from your
- * "Clear History" UI.
+ * <div class="special">
+ * <p>For a guide to using the search dialog and adding search
+ * suggestions in your application, see the Dev Guide topic about <strong><a
+ * href="{@docRoot}guide/topics/search/index.html">Search</a></strong>.</p>
+ * </div>
*/
public class SearchManager
implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 82da983..0007f58 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -116,12 +116,22 @@
}
/**
- * Turn off special mode if currently in car mode.
+ * Flag for use with {@link #disableCarMode(int)}: go to the normal
+ * home activity as part of the disable. Disabling this way ensures
+ * a clean transition between the current activity (in car mode) and
+ * the original home activity (which was typically last running without
+ * being in car mode).
*/
- public void disableCarMode() {
+ public static final int DISABLE_CAR_MODE_GO_HOME = 0x0001;
+
+ /**
+ * Turn off special mode if currently in car mode.
+ * @param flags May be 0 or {@link #DISABLE_CAR_MODE_GO_HOME}.
+ */
+ public void disableCarMode(int flags) {
if (mService != null) {
try {
- mService.disableCarMode();
+ mService.disableCarMode(flags);
} catch (RemoteException e) {
Log.e(TAG, "disableCarMode: RemoteException", e);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2d9b415..35e7ee6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,7 +20,6 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.app.admin.IDevicePolicyManager.Stub;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -192,11 +191,19 @@
/**
* Constant for {@link #setPasswordQuality}: the user must have entered a
+ * password containing at least alphabetic (or other symbol) characters.
+ * Note that quality constants are ordered so that higher values are more
+ * restrictive.
+ */
+ public static final int PASSWORD_QUALITY_ALPHABETIC = 0x40000;
+
+ /**
+ * Constant for {@link #setPasswordQuality}: the user must have entered a
* password containing at least <em>both></em> numeric <em>and</em>
- * alphabeter (or other symbol) characters. Note that quality constants are
+ * alphabetic (or other symbol) characters. Note that quality constants are
* ordered so that higher values are more restrictive.
*/
- public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x30000;
+ public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000;
/**
* Called by an application that is administering the device to set the
@@ -219,7 +226,8 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param quality The new desired quality. One of
* {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING},
- * {@link #PASSWORD_QUALITY_NUMERIC}, or {@link #PASSWORD_QUALITY_ALPHANUMERIC}.
+ * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
+ * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}.
*/
public void setPasswordQuality(ComponentName admin, int quality) {
if (mService != null) {
@@ -257,7 +265,8 @@
* take place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
* constraint is only imposed if the administrator has also requested either
- * {@link #PASSWORD_QUALITY_NUMERIC} or {@link #PASSWORD_QUALITY_ALPHANUMERIC}
+ * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
+ * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}
* with {@link #setPasswordQuality}.
*
* <p>The calling device admin must have requested
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 251813e..fff75ca 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -112,10 +112,6 @@
/** Default priority when not set or when the device is unpaired */
public static final int PRIORITY_UNDEFINED = -1;
- /** Set this to true to prevent the bluetooth headset from
- * activating the VoiceDialer. */
- public static final boolean DISABLE_BT_VOICE_DIALING = false;
-
/**
* An interface for notifying BluetoothHeadset IPC clients when they have
* been connected to the BluetoothHeadset service.
@@ -384,6 +380,16 @@
return -1;
}
+ /**
+ * Indicates if current platform supports voice dialing over bluetooth SCO.
+ * @return true if voice dialing over bluetooth is supported, false otherwise.
+ * @hide
+ */
+ public static boolean isBluetoothVoiceDialingEnabled(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_bluetooth_sco_off_call);
+ }
+
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/ScoSocket.java b/core/java/android/bluetooth/ScoSocket.java
index 116310a..b65a99a 100644
--- a/core/java/android/bluetooth/ScoSocket.java
+++ b/core/java/android/bluetooth/ScoSocket.java
@@ -86,14 +86,14 @@
/** Connect this SCO socket to the given BT address.
* Does not block.
*/
- public synchronized boolean connect(String address) {
+ public synchronized boolean connect(String address, String name) {
if (DBG) log("connect() " + this);
if (mState != STATE_READY) {
if (DBG) log("connect(): Bad state");
return false;
}
acquireWakeLock();
- if (connectNative(address)) {
+ if (connectNative(address, name)) {
mState = STATE_CONNECTING;
return true;
} else {
@@ -102,7 +102,7 @@
return false;
}
}
- private native boolean connectNative(String address);
+ private native boolean connectNative(String address, String name);
/** Accept incoming SCO connections.
* Does not block.
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index c04625d..75787cd 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -66,7 +66,7 @@
* Creates a set of values copied from the given HashMap. This is used
* by the Parcel unmarshalling code.
*
- * @param from the values to start with
+ * @param values the values to start with
* {@hide}
*/
private ContentValues(HashMap<String, Object> values) {
@@ -248,7 +248,7 @@
*/
public String getAsString(String key) {
Object value = mValues.get(key);
- return value != null ? mValues.get(key).toString() : null;
+ return value != null ? value.toString() : null;
}
/**
diff --git a/core/java/android/content/EventLogTags.logtags b/core/java/android/content/EventLogTags.logtags
index a815b95..0a8c9b0 100644
--- a/core/java/android/content/EventLogTags.logtags
+++ b/core/java/android/content/EventLogTags.logtags
@@ -4,3 +4,4 @@
52002 content_query_operation (uri|3),(projection|3),(selection|3),(sortorder|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
52003 content_update_operation (uri|3),(operation|3),(selection|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
+52004 binder_operation (descriptor|3),(method_num|1|5),(time|1|3),(blocking_package|3),(sample_percent|1|6)
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 840a714..5c8ee18 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -215,7 +215,9 @@
// the accounts are not set yet
sendCheckAlarmsMessage();
- mSyncStorageEngine.doDatabaseCleanup(accounts);
+ if (mBootCompleted) {
+ mSyncStorageEngine.doDatabaseCleanup(accounts);
+ }
if (accounts.length > 0) {
// If this is the first time this was called after a bootup then
@@ -1317,6 +1319,7 @@
private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
public void onBootCompleted() {
mBootCompleted = true;
+ mSyncStorageEngine.doDatabaseCleanup(AccountManager.get(mContext).getAccounts());
if (mReadyToRunLatch != null) {
mReadyToRunLatch.countDown();
}
@@ -1610,7 +1613,9 @@
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(syncAdapterType);
if (syncAdapterInfo == null) {
- Log.d(TAG, "can't find a sync adapter for " + syncAdapterType);
+ Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ + ", removing settings for it");
+ mSyncStorageEngine.removeAuthority(op.account, op.authority);
runStateIdle();
return;
}
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 0ec24530..daad95c 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -122,7 +122,15 @@
private static final boolean SYNC_ENABLED_DEFAULT = false;
// the version of the accounts xml file format
- private static final int ACCOUNTS_VERSION = 1;
+ private static final int ACCOUNTS_VERSION = 2;
+
+ private static HashMap<String, String> sAuthorityRenames;
+
+ static {
+ sAuthorityRenames = new HashMap<String, String>();
+ sAuthorityRenames.put("contacts", "com.android.contacts");
+ sAuthorityRenames.put("calendar", "com.android.calendar");
+ }
public static class PendingOperation {
final Account account;
@@ -643,6 +651,12 @@
}
}
+ public void removeAuthority(Account account, String authority) {
+ synchronized (mAuthorities) {
+ removeAuthorityLocked(account, authority);
+ }
+ }
+
public AuthorityInfo getAuthority(int authorityId) {
synchronized (mAuthorities) {
return mAuthorities.get(authorityId);
@@ -1272,6 +1286,17 @@
return authority;
}
+ private void removeAuthorityLocked(Account account, String authorityName) {
+ AccountInfo accountInfo = mAccounts.get(account);
+ if (accountInfo != null) {
+ final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
+ if (authorityInfo != null) {
+ mAuthorities.remove(authorityInfo.ident);
+ writeAccountInfoLocked();
+ }
+ }
+ }
+
public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
synchronized (mAuthorities) {
return getOrCreateSyncStatusLocked(authority.ident);
@@ -1392,11 +1417,61 @@
}
}
+ if (maybeMigrateSettingsForRenamedAuthorities()) {
+ writeNeeded = true;
+ }
+
if (writeNeeded) {
writeAccountInfoLocked();
}
}
+ /**
+ * some authority names have changed. copy over their settings and delete the old ones
+ * @return true if a change was made
+ */
+ private boolean maybeMigrateSettingsForRenamedAuthorities() {
+ boolean writeNeeded = false;
+
+ ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
+ final int N = mAuthorities.size();
+ for (int i=0; i<N; i++) {
+ AuthorityInfo authority = mAuthorities.valueAt(i);
+ // skip this authority if it isn't one of the renamed ones
+ final String newAuthorityName = sAuthorityRenames.get(authority.authority);
+ if (newAuthorityName == null) {
+ continue;
+ }
+
+ // remember this authority so we can remove it later. we can't remove it
+ // now without messing up this loop iteration
+ authoritiesToRemove.add(authority);
+
+ // this authority isn't enabled, no need to copy it to the new authority name since
+ // the default is "disabled"
+ if (!authority.enabled) {
+ continue;
+ }
+
+ // if we already have a record of this new authority then don't copy over the settings
+ if (getAuthorityLocked(authority.account, newAuthorityName, "cleanup") != null) {
+ continue;
+ }
+
+ AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
+ newAuthorityName, -1 /* ident */, false /* doWrite */);
+ newAuthority.enabled = true;
+ writeNeeded = true;
+ }
+
+ for (AuthorityInfo authorityInfo : authoritiesToRemove) {
+ removeAuthorityLocked(authorityInfo.account, authorityInfo.authority);
+ writeNeeded = true;
+ }
+
+ return writeNeeded;
+ }
+
private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
AuthorityInfo authority = null;
int id = -1;
@@ -1409,14 +1484,15 @@
Log.e(TAG, "the id of the authority is null", e);
}
if (id >= 0) {
+ String authorityName = parser.getAttributeValue(null, "authority");
+ String enabled = parser.getAttributeValue(null, "enabled");
+ String syncable = parser.getAttributeValue(null, "syncable");
String accountName = parser.getAttributeValue(null, "account");
String accountType = parser.getAttributeValue(null, "type");
if (accountType == null) {
accountType = "com.google";
+ syncable = "unknown";
}
- String authorityName = parser.getAttributeValue(null, "authority");
- String enabled = parser.getAttributeValue(null, "enabled");
- String syncable = parser.getAttributeValue(null, "syncable");
authority = mAuthorities.get(id);
if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
+ accountName + " auth=" + authorityName
@@ -1441,7 +1517,7 @@
authority.syncable = -1;
} else {
authority.syncable =
- (syncable == null || Boolean.parseBoolean(enabled)) ? 1 : 0;
+ (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
}
} else {
Log.w(TAG, "Failure adding authority: account="
@@ -1531,13 +1607,11 @@
out.attribute(null, "account", authority.account.name);
out.attribute(null, "type", authority.account.type);
out.attribute(null, "authority", authority.authority);
- if (!authority.enabled) {
- out.attribute(null, "enabled", "false");
- }
+ out.attribute(null, "enabled", Boolean.toString(authority.enabled));
if (authority.syncable < 0) {
out.attribute(null, "syncable", "unknown");
- } else if (authority.syncable == 0) {
- out.attribute(null, "syncable", "false");
+ } else {
+ out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
}
for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
out.startTag(null, "periodicSync");
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 399a87d..c638d04 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -309,7 +309,7 @@
* MountService uses this to call into the package manager to update
* status of sdcard.
*/
- boolean updateExternalMediaStatus(boolean mounted);
+ void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
String nextPackageToClean(String lastPackage);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index e9a9f31..af327c3 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -153,6 +153,12 @@
* the {@link android.R.attr#installLocation} attribute.
* @hide
*/
+ public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+ /**
+ * Constant corresponding to <code>auto</code> in
+ * the {@link android.R.attr#installLocation} attribute.
+ * @hide
+ */
public static final int INSTALL_LOCATION_AUTO = 0;
/**
* Constant corresponding to <code>internalOnly</code> in
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 271f477..0318b6c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -233,7 +233,7 @@
/**
* Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
* indicate that this package should be installed as forward locked, i.e. only the app itself
- * should have access to it's code and non-resource assets.
+ * should have access to its code and non-resource assets.
* @hide
*/
public static final int INSTALL_FORWARD_LOCK = 0x00000001;
@@ -261,6 +261,13 @@
public static final int INSTALL_EXTERNAL = 0x00000008;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this
+ * package has to be installed on the sdcard.
+ * @hide
+ */
+ public static final int INSTALL_INTERNAL = 0x00000010;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -439,6 +446,15 @@
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package couldn't be installed in the specified install
+ * location because the media is not available.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
@@ -586,6 +602,14 @@
public static final int MOVE_FAILED_INVALID_LOCATION = -5;
/**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * if the specified package cannot be moved to the specified location.
+ * @hide
+ */
+ public static final int MOVE_FAILED_INTERNAL_ERROR = -6;
+
+ /**
* Flag parameter for {@link #movePackage} to indicate that
* the package should be moved to internal storage if its
* been installed on external media.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c33f305..3be4cebd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -96,7 +96,8 @@
private static final Object mSync = new Object();
private static WeakReference<byte[]> mReadBuffer;
- private static boolean sCompatibilityModeEnabled = true;
+ private static boolean sCompatibilityModeEnabled = true;
+ private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
static class ParsePackageItemArgs {
final Package owner;
@@ -352,6 +353,7 @@
public final static int PARSE_IGNORE_PROCESSES = 1<<3;
public final static int PARSE_FORWARD_LOCK = 1<<4;
public final static int PARSE_ON_SDCARD = 1<<5;
+ public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
public int getParseError() {
return mParseError;
@@ -707,12 +709,12 @@
+ pkgName + "\": " + nameError;
return null;
}
- int installLocation = PackageInfo.INSTALL_LOCATION_AUTO;
+ int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
- PackageInfo.INSTALL_LOCATION_AUTO);
+ PARSE_DEFAULT_INSTALL_LOCATION);
break;
}
}
@@ -778,7 +780,7 @@
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
- PackageInfo.INSTALL_LOCATION_AUTO);
+ PARSE_DEFAULT_INSTALL_LOCATION);
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6598264..1a0c867 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -219,6 +219,10 @@
* Makes a deep copy suitable for modification.
*/
public Configuration(Configuration o) {
+ setTo(o);
+ }
+
+ public void setTo(Configuration o) {
fontScale = o.fontScale;
mcc = o.mcc;
mnc = o.mnc;
@@ -237,7 +241,7 @@
uiMode = o.uiMode;
seq = o.seq;
}
-
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("{ scale=");
@@ -552,21 +556,7 @@
dest.writeInt(seq);
}
- public static final Parcelable.Creator<Configuration> CREATOR
- = new Parcelable.Creator<Configuration>() {
- public Configuration createFromParcel(Parcel source) {
- return new Configuration(source);
- }
-
- public Configuration[] newArray(int size) {
- return new Configuration[size];
- }
- };
-
- /**
- * Construct this Configuration object, reading from the Parcel.
- */
- private Configuration(Parcel source) {
+ public void readFromParcel(Parcel source) {
fontScale = source.readFloat();
mcc = source.readInt();
mnc = source.readInt();
@@ -586,6 +576,24 @@
uiMode = source.readInt();
seq = source.readInt();
}
+
+ public static final Parcelable.Creator<Configuration> CREATOR
+ = new Parcelable.Creator<Configuration>() {
+ public Configuration createFromParcel(Parcel source) {
+ return new Configuration(source);
+ }
+
+ public Configuration[] newArray(int size) {
+ return new Configuration[size];
+ }
+ };
+
+ /**
+ * Construct this Configuration object, reading from the Parcel.
+ */
+ private Configuration(Parcel source) {
+ readFromParcel(source);
+ }
public int compareTo(Configuration that) {
int n;
diff --git a/core/java/android/database/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 7e91159..722d707 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -185,6 +185,7 @@
mCursors[i].deactivate();
}
}
+ super.deactivate();
}
@Override
@@ -194,6 +195,7 @@
if (mCursors[i] == null) continue;
mCursors[i].close();
}
+ super.close();
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 816f8a8..72ceb9b 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -139,7 +139,10 @@
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
}
- Log.w(TAG, "finalizer should never be called on sql: " + mSqlStmt, mStackTrace);
+ int len = mSqlStmt.length();
+ Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
+ "that you explicitly call close() on your cursor: " +
+ mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
releaseSqlStatement();
} finally {
super.finalize();
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 3f0fcb1..6e5b3e1 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -579,10 +579,12 @@
try {
// if the cursor hasn't been closed yet, close it first
if (mWindow != null) {
- close();
+ int len = mQuery.mSql.length();
Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
"database = " + mDatabase.getPath() + ", table = " + mEditTable +
- ", query = " + mQuery.mSql, mStackTrace);
+ ", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
+ mStackTrace);
+ close();
SQLiteDebug.notifyActiveCursorFinalized();
} else {
if (Config.LOGV) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index b232ff9..3b7416e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -213,16 +213,23 @@
// Things related to query logging/sampling for debugging
// slow/frequent queries during development. Always log queries
- // which take 500ms+; shorter queries are sampled accordingly.
- // Commit statements, which are typically slow, are logged
- // together with the most recently executed SQL statement, for
- // disambiguation.
- private static final int QUERY_LOG_TIME_IN_MILLIS = 500;
+ // which take (by default) 500ms+; shorter queries are sampled
+ // accordingly. Commit statements, which are typically slow, are
+ // logged together with the most recently executed SQL statement,
+ // for disambiguation. The 500ms value is configurable via a
+ // SystemProperty, but developers actively debugging database I/O
+ // should probably use the regular log tunable,
+ // LOG_SLOW_QUERIES_PROPERTY, defined below.
+ private static int sQueryLogTimeInMillis = 0; // lazily initialized
private static final int QUERY_LOG_SQL_LENGTH = 64;
private static final String COMMIT_SQL = "COMMIT;";
private final Random mRandom = new Random();
private String mLastSqlStatement = null;
+ // String prefix for slow database query EventLog records that show
+ // lock acquistions of the database.
+ /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
+
/** Used by native code, do not rename */
/* package */ int mNativeHandle = 0;
@@ -340,15 +347,18 @@
private boolean mLockingEnabled = true;
/* package */ void onCorruption() {
+ Log.e(TAG, "Removing corrupt database: " + mPath);
+ EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
try {
// Close the database (if we can), which will cause subsequent operations to fail.
close();
} finally {
- Log.e(TAG, "Removing corrupt database: " + mPath);
- EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
// Delete the corrupt file. Don't re-create it now -- that would just confuse people
// -- but the next time someone tries to open it, they can set it up from scratch.
- new File(mPath).delete();
+ if (!mPath.equalsIgnoreCase(":memory")) {
+ // delete is only for non-memory database files
+ new File(mPath).delete();
+ }
}
}
@@ -490,6 +500,9 @@
* {@link #yieldIfContendedSafely}.
*/
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
lockForced();
boolean ok = false;
try {
@@ -535,6 +548,9 @@
* are committed and rolled back.
*/
public void endTransaction() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
if (!mLock.isHeldByCurrentThread()) {
throw new IllegalStateException("no transaction pending");
}
@@ -595,6 +611,9 @@
* transaction is already marked as successful.
*/
public void setTransactionSuccessful() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
if (!mLock.isHeldByCurrentThread()) {
throw new IllegalStateException("no transaction pending");
}
@@ -807,7 +826,10 @@
// TODO: should we do this for other open failures?
Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
EventLog.writeEvent(EVENT_DB_CORRUPT, path);
- new File(path).delete();
+ if (!path.equalsIgnoreCase(":memory")) {
+ // delete is only for non-memory database files
+ new File(path).delete();
+ }
sqliteDatabase = new SQLiteDatabase(path, factory, flags);
}
ActiveDatabases.getInstance().mActiveDatabases.add(
@@ -849,10 +871,14 @@
* Close the database.
*/
public void close() {
+ if (!isOpen()) {
+ return; // already closed
+ }
lock();
try {
closeClosable();
- releaseReference();
+ // close this database instance - regardless of its reference count value
+ onAllReferencesReleased();
} finally {
unlock();
}
@@ -889,6 +915,9 @@
* @return the database version
*/
public int getVersion() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
SQLiteStatement prog = null;
lock();
try {
@@ -907,6 +936,9 @@
* @param version the new database version
*/
public void setVersion(int version) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
execSQL("PRAGMA user_version = " + version);
}
@@ -916,6 +948,9 @@
* @return the new maximum database size
*/
public long getMaximumSize() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
SQLiteStatement prog = null;
lock();
try {
@@ -937,6 +972,9 @@
* @return the new maximum database size
*/
public long setMaximumSize(long numBytes) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
SQLiteStatement prog = null;
lock();
try {
@@ -962,6 +1000,9 @@
* @return the database page size, in bytes
*/
public long getPageSize() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
SQLiteStatement prog = null;
lock();
try {
@@ -983,6 +1024,9 @@
* @param numBytes the database page size, in bytes
*/
public void setPageSize(long numBytes) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
execSQL("PRAGMA page_size = " + numBytes);
}
@@ -1099,6 +1143,9 @@
* @return a pre-compiled statement object.
*/
public SQLiteStatement compileStatement(String sql) throws SQLException {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
lock();
try {
return new SQLiteStatement(this, sql);
@@ -1179,6 +1226,9 @@
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);
@@ -1285,6 +1335,9 @@
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
long timeStart = 0;
if (Config.LOGV || mSlowQueryThreshold != -1) {
@@ -1294,19 +1347,19 @@
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
Cursor cursor = null;
+ int count = 0;
try {
cursor = driver.query(
cursorFactory != null ? cursorFactory : mFactory,
selectionArgs);
+
+ // Force query execution
+ if (cursor != null) {
+ count = cursor.getCount();
+ }
} finally {
if (Config.LOGV || mSlowQueryThreshold != -1) {
- // Force query execution
- if (cursor != null) {
- cursor.moveToFirst();
- cursor.moveToPosition(-1);
- }
-
long duration = System.currentTimeMillis() - timeStart;
if (Config.LOGV || duration >= mSlowQueryThreshold) {
@@ -1314,7 +1367,7 @@
"query (" + duration + " ms): " + driver.toString() + ", args are "
+ (selectionArgs != null
? TextUtils.join(",", selectionArgs)
- : "<null>"));
+ : "<null>") + ", count is " + count);
}
}
}
@@ -1671,8 +1724,12 @@
* @throws SQLException If the SQL string is invalid for some reason
*/
public void execSQL(String sql) throws SQLException {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
long timeStart = SystemClock.uptimeMillis();
lock();
+ logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
try {
native_execSQL(sql);
} catch (SQLiteDatabaseCorruptException e) {
@@ -1686,9 +1743,9 @@
// SQL statement for disambiguation. Note that instance
// equality to COMMIT_SQL is safe here.
if (sql == COMMIT_SQL) {
- logTimeStat(sql + mLastSqlStatement, timeStart);
+ logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
} else {
- logTimeStat(sql, timeStart);
+ logTimeStat(sql, timeStart, null);
}
}
@@ -1702,6 +1759,9 @@
* @throws SQLException If the SQL string is invalid for some reason
*/
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
if (bindArgs == null) {
throw new IllegalArgumentException("Empty bindArgs");
}
@@ -1805,9 +1865,11 @@
return mPath;
}
-
-
/* package */ void logTimeStat(String sql, long beginMillis) {
+ logTimeStat(sql, beginMillis, null);
+ }
+
+ /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
// Keep track of the last statement executed here, as this is
// the common funnel through which all methods of hitting
// libsqlite eventually flow.
@@ -1819,13 +1881,27 @@
int samplePercent;
long durationMillis = SystemClock.uptimeMillis() - beginMillis;
- if (durationMillis >= QUERY_LOG_TIME_IN_MILLIS) {
+ if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
+ // The common case is locks being uncontended. Don't log those,
+ // even at 1%, which is our default below.
+ return;
+ }
+ if (sQueryLogTimeInMillis == 0) {
+ sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
+ }
+ if (durationMillis >= sQueryLogTimeInMillis) {
samplePercent = 100;
- } else {
- samplePercent = (int) (100 * durationMillis / QUERY_LOG_TIME_IN_MILLIS) + 1;
+ } else {;
+ samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
if (mRandom.nextInt(100) >= samplePercent) return;
}
+ // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
+ // it here so we avoid allocating in the common case.
+ if (prefix != null) {
+ sql = prefix + sql;
+ }
+
if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
// ActivityThread.currentPackageName() only returns non-null if the
@@ -2055,6 +2131,10 @@
static ActiveDatabases getInstance() {return activeDatabases;}
}
+ /**
+ * this method is used to collect data about ALL open databases in the current process.
+ * bugreport is a user of this data.
+ */
/* package */ static ArrayList<DbStats> getDbStats() {
ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
@@ -2072,6 +2152,9 @@
// get list of attached dbs and for each db, get its size and pagesize
ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
+ if (attachedDbs == null) {
+ continue;
+ }
for (int i = 0; i < attachedDbs.size(); i++) {
Pair<String, String> p = attachedDbs.get(i);
long pageCount = getPragmaVal(db, p.first + ".page_count;");
@@ -2091,7 +2174,10 @@
dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
}
}
- dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(), lookasideUsed));
+ if (pageCount > 0) {
+ dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+ lookasideUsed));
+ }
}
}
return dbStatsList;
@@ -2104,6 +2190,9 @@
* TODO: use this to do all pragma's in this class
*/
private static long getPragmaVal(SQLiteDatabase db, String pragma) {
+ if (!db.isOpen()) {
+ return 0;
+ }
SQLiteStatement prog = null;
try {
prog = new SQLiteStatement(db, "PRAGMA " + pragma);
@@ -2120,6 +2209,9 @@
* TODO: move this to {@link DatabaseUtils}
*/
private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
+ if (!dbObj.isOpen()) {
+ return null;
+ }
ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
Cursor c = dbObj.rawQuery("pragma database_list;", null);
while (c.moveToNext()) {
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index 7cd9561..43d2fac 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -51,20 +51,20 @@
/**
* Reads rows into a buffer. This method acquires the database lock.
- *
+ *
* @param window The window to fill into
* @return number of total rows in the query
*/
- /* package */ int fillWindow(CursorWindow window,
+ /* package */ int fillWindow(CursorWindow window,
int maxRead, int lastPos) {
long timeStart = SystemClock.uptimeMillis();
mDatabase.lock();
-
+ mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
try {
acquireReference();
try {
window.acquireReference();
- // if the start pos is not equal to 0, then most likely window is
+ // if the start pos is not equal to 0, then most likely window is
// too small for the data set, loading by another thread
// is not safe in this situation. the native code will ignore maxRead
int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
@@ -83,7 +83,7 @@
mDatabase.onCorruption();
throw e;
} finally {
- window.releaseReference();
+ window.releaseReference();
}
} finally {
releaseReference();
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index f29b69f..7f484ff 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -58,11 +58,10 @@
}
/**
- * Execute this SQL statement and return the ID of the most
- * recently inserted row. The SQL statement should probably be an
- * INSERT for this to be a useful call.
+ * Execute this SQL statement and return the ID of the row inserted due to this call.
+ * The SQL statement should be an INSERT for this to be a useful call.
*
- * @return the row ID of the last row inserted.
+ * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
*
* @throws android.database.SQLException If the SQL string is invalid for
* some reason
@@ -75,7 +74,7 @@
try {
native_execute();
mDatabase.logTimeStat(mSql, timeStart);
- return mDatabase.lastInsertRow();
+ return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
} finally {
releaseReference();
mDatabase.unlock();
diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index b4c04b1..96fbe77 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -26,8 +26,9 @@
* <p>This uses the World Magnetic Model produced by the United States National
* Geospatial-Intelligence Agency. More details about the model can be found at
* <a href="http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml">http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml</a>.
- * This class currently uses WMM-2005 which is valid until 2010, but should
- * produce acceptable results for several years after that.
+ * This class currently uses WMM-2010 which is valid until 2015, but should
+ * produce acceptable results for several years after that. Future versions of
+ * Android may use a newer version of the model.
*/
public class GeomagneticField {
// The magnetic field at a given point, in nonoteslas in geodetic
@@ -43,75 +44,73 @@
// Constants from WGS84 (the coordinate system used by GPS)
static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f;
- static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f;
+ static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523142f;
static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f;
// These coefficients and the formulae used below are from:
- // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010
+ // NOAA Technical Report: The US/UK World Magnetic Model for 2010-2015
static private final float[][] G_COEFF = new float[][] {
- { 0f },
- { -29556.8f, -1671.7f },
- { -2340.6f, 3046.9f, 1657.0f },
- { 1335.4f, -2305.1f, 1246.7f, 674.0f },
- { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f },
- { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f },
- { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f },
- { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f },
- { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f },
- { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f },
- { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f },
- { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f },
- { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f,
- -0.3f, -0.1f } };
+ { 0.0f },
+ { -29496.6f, -1586.3f },
+ { -2396.6f, 3026.1f, 1668.6f },
+ { 1340.1f, -2326.2f, 1231.9f, 634.0f },
+ { 912.6f, 808.9f, 166.7f, -357.1f, 89.4f },
+ { -230.9f, 357.2f, 200.3f, -141.1f, -163.0f, -7.8f },
+ { 72.8f, 68.6f, 76.0f, -141.4f, -22.8f, 13.2f, -77.9f },
+ { 80.5f, -75.1f, -4.7f, 45.3f, 13.9f, 10.4f, 1.7f, 4.9f },
+ { 24.4f, 8.1f, -14.5f, -5.6f, -19.3f, 11.5f, 10.9f, -14.1f, -3.7f },
+ { 5.4f, 9.4f, 3.4f, -5.2f, 3.1f, -12.4f, -0.7f, 8.4f, -8.5f, -10.1f },
+ { -2.0f, -6.3f, 0.9f, -1.1f, -0.2f, 2.5f, -0.3f, 2.2f, 3.1f, -1.0f, -2.8f },
+ { 3.0f, -1.5f, -2.1f, 1.7f, -0.5f, 0.5f, -0.8f, 0.4f, 1.8f, 0.1f, 0.7f, 3.8f },
+ { -2.2f, -0.2f, 0.3f, 1.0f, -0.6f, 0.9f, -0.1f, 0.5f, -0.4f, -0.4f, 0.2f, -0.8f, 0.0f } };
static private final float[][] H_COEFF = new float[][] {
- { 0f },
- { 0.0f, 5079.8f },
- { 0.0f, -2594.7f, -516.7f },
- { 0.0f, -199.9f, 269.3f, -524.2f },
- { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f },
- { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f },
- { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f },
- { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f },
- { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f },
- { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f },
- { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f },
- { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f },
- { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f,
- 0.8f } };
+ { 0.0f },
+ { 0.0f, 4944.4f },
+ { 0.0f, -2707.7f, -576.1f },
+ { 0.0f, -160.2f, 251.9f, -536.6f },
+ { 0.0f, 286.4f, -211.2f, 164.3f, -309.1f },
+ { 0.0f, 44.6f, 188.9f, -118.2f, 0.0f, 100.9f },
+ { 0.0f, -20.8f, 44.1f, 61.5f, -66.3f, 3.1f, 55.0f },
+ { 0.0f, -57.9f, -21.1f, 6.5f, 24.9f, 7.0f, -27.7f, -3.3f },
+ { 0.0f, 11.0f, -20.0f, 11.9f, -17.4f, 16.7f, 7.0f, -10.8f, 1.7f },
+ { 0.0f, -20.5f, 11.5f, 12.8f, -7.2f, -7.4f, 8.0f, 2.1f, -6.1f, 7.0f },
+ { 0.0f, 2.8f, -0.1f, 4.7f, 4.4f, -7.2f, -1.0f, -3.9f, -2.0f, -2.0f, -8.3f },
+ { 0.0f, 0.2f, 1.7f, -0.6f, -1.8f, 0.9f, -0.4f, -2.5f, -1.3f, -2.1f, -1.9f, -1.8f },
+ { 0.0f, -0.9f, 0.3f, 2.1f, -2.5f, 0.5f, 0.6f, 0.0f, 0.1f, 0.3f, -0.9f, -0.2f, 0.9f } };
static private final float[][] DELTA_G = new float[][] {
- { 0f },
- { 8.0f, 10.6f },
- { -15.1f, -7.8f, -0.8f },
- { 0.4f, -2.6f, -1.2f, -6.5f },
- { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f },
- { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f },
- { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f },
- { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f },
- { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+ { 0.0f },
+ { 11.6f, 16.5f },
+ { -12.1f, -4.4f, 1.9f },
+ { 0.4f, -4.1f, -2.9f, -7.7f },
+ { -1.8f, 2.3f, -8.7f, 4.6f, -2.1f },
+ { -1.0f, 0.6f, -1.8f, -1.0f, 0.9f, 1.0f },
+ { -0.2f, -0.2f, -0.1f, 2.0f, -1.7f, -0.3f, 1.7f },
+ { 0.1f, -0.1f, -0.6f, 1.3f, 0.4f, 0.3f, -0.7f, 0.6f },
+ { -0.1f, 0.1f, -0.6f, 0.2f, -0.2f, 0.3f, 0.3f, -0.6f, 0.2f },
+ { 0.0f, -0.1f, 0.0f, 0.3f, -0.4f, -0.3f, 0.1f, -0.1f, -0.4f, -0.2f },
+ { 0.0f, 0.0f, -0.1f, 0.2f, 0.0f, -0.1f, -0.2f, 0.0f, -0.1f, -0.2f, -0.2f },
+ { 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f, 0.0f },
+ { 0.0f, 0.0f, 0.1f, 0.1f, -0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.1f, 0.1f } };
static private final float[][] DELTA_H = new float[][] {
- { 0f },
- { 0.0f, -20.9f },
- { 0.0f, -23.2f, -14.6f },
- { 0.0f, 5.0f, -7.0f, -0.6f },
- { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f },
- { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f },
- { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f },
- { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f },
- { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+ { 0.0f },
+ { 0.0f, -25.9f },
+ { 0.0f, -22.5f, -11.8f },
+ { 0.0f, 7.3f, -3.9f, -2.6f },
+ { 0.0f, 1.1f, 2.7f, 3.9f, -0.8f },
+ { 0.0f, 0.4f, 1.8f, 1.2f, 4.0f, -0.6f },
+ { 0.0f, -0.2f, -2.1f, -0.4f, -0.6f, 0.5f, 0.9f },
+ { 0.0f, 0.7f, 0.3f, -0.1f, -0.1f, -0.8f, -0.3f, 0.3f },
+ { 0.0f, -0.1f, 0.2f, 0.4f, 0.4f, 0.1f, -0.1f, 0.4f, 0.3f },
+ { 0.0f, 0.0f, -0.2f, 0.0f, -0.1f, 0.1f, 0.0f, -0.2f, 0.3f, 0.2f },
+ { 0.0f, 0.1f, -0.1f, 0.0f, -0.1f, -0.1f, 0.0f, -0.1f, -0.2f, 0.0f, -0.1f },
+ { 0.0f, 0.0f, 0.1f, 0.0f, 0.1f, 0.0f, 0.1f, 0.0f, -0.1f, -0.1f, 0.0f, -0.1f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
static private final long BASE_TIME =
- new GregorianCalendar(2005, 1, 1).getTimeInMillis();
+ new GregorianCalendar(2010, 1, 1).getTimeInMillis();
// The ratio between the Gauss-normalized associated Legendre functions and
// the Schmid quasi-normalized ones. Compute these once staticly since they
@@ -191,7 +190,7 @@
// We now compute the magnetic field strength given the geocentric
// location. The magnetic field is the derivative of the potential
// function defined by the model. See NOAA Technical Report: The US/UK
- // World Magnetic Model for 2005-2010 for the derivation.
+ // World Magnetic Model for 2010-2015 for the derivation.
float gcX = 0.0f; // Geocentric northwards component.
float gcY = 0.0f; // Geocentric eastwards component.
float gcZ = 0.0f; // Geocentric downwards component.
@@ -406,4 +405,4 @@
}
return schmidtQuasiNorm;
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9201e3b..4b48409 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -175,6 +175,7 @@
private boolean mShowTouchPoints = true;
private int mPopupPreviewX;
private int mPopupPreviewY;
+ private int mWindowY;
private int mLastX;
private int mLastY;
@@ -909,20 +910,36 @@
getLocationInWindow(mOffsetInWindow);
mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+ int[] mWindowLocation = new int[2];
+ getLocationOnScreen(mWindowLocation);
+ mWindowY = mWindowLocation[1];
}
// Set the preview background state
mPreviewText.getBackground().setState(
key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+ mPopupPreviewX += mOffsetInWindow[0];
+ mPopupPreviewY += mOffsetInWindow[1];
+
+ // If the popup cannot be shown above the key, put it on the side
+ if (mPopupPreviewY + mWindowY < 0) {
+ // If the key you're pressing is on the left side of the keyboard, show the popup on
+ // the right, offset by enough to see at least one key to the left/right.
+ if (key.x + key.width <= getWidth() / 2) {
+ mPopupPreviewX += (int) (key.width * 2.5);
+ } else {
+ mPopupPreviewX -= (int) (key.width * 2.5);
+ }
+ mPopupPreviewY += popupHeight;
+ }
+
if (previewPopup.isShowing()) {
- previewPopup.update(mPopupPreviewX + mOffsetInWindow[0],
- mPopupPreviewY + mOffsetInWindow[1],
+ previewPopup.update(mPopupPreviewX, mPopupPreviewY,
popupWidth, popupHeight);
} else {
previewPopup.setWidth(popupWidth);
previewPopup.setHeight(popupHeight);
previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
- mPopupPreviewX + mOffsetInWindow[0],
- mPopupPreviewY + mOffsetInWindow[1]);
+ mPopupPreviewX, mPopupPreviewY);
}
mPreviewText.setVisibility(VISIBLE);
}
diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java
index 72106c8..3867385 100644
--- a/core/java/android/net/Downloads.java
+++ b/core/java/android/net/Downloads.java
@@ -469,7 +469,7 @@
/**
* Base class with common functionality for the various download classes
*/
- private static class DownloadBase {
+ public static class DownloadBase {
/** @hide */
DownloadBase() {}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 22e432563..98f32b3 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -369,7 +369,11 @@
}
// else fall through
case Phone.APN_TYPE_NOT_AVAILABLE:
- mEnabled = false;
+ // Default is always available, but may be off due to
+ // AirplaneMode or E-Call or whatever..
+ if (mApnType != Phone.APN_TYPE_DEFAULT) {
+ mEnabled = false;
+ }
break;
default:
Log.e(TAG, "Error in reconnect - unexpected response.");
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index f2ea539..eca4569 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1598,6 +1598,8 @@
end = query.length();
}
return decode(query.substring(equalsIndex + 1, end));
+ } else {
+ encodedKeySearchIndex = equalsIndex + 1;
}
}
return null;
diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java
index b8e17da..43fb5f1 100644
--- a/core/java/android/net/http/Connection.java
+++ b/core/java/android/net/http/Connection.java
@@ -222,6 +222,12 @@
}
}
+ /* we have a connection, let the event handler
+ * know of any associated certificate,
+ * potentially none.
+ */
+ req.mEventHandler.certificate(mCertificate);
+
try {
/* FIXME: don't increment failure count if old
connection? There should not be a penalty for
diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java
index a035c19..2aa05eb 100644
--- a/core/java/android/net/http/EventHandler.java
+++ b/core/java/android/net/http/EventHandler.java
@@ -125,8 +125,8 @@
public void endData();
/**
- * SSL certificate callback called every time a resource is
- * loaded via a secure connection
+ * SSL certificate callback called before resource request is
+ * made, which will be null for insecure connection.
*/
public void certificate(SslCertificate certificate);
diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java
index b0923d1..09f6f4f 100644
--- a/core/java/android/net/http/Headers.java
+++ b/core/java/android/net/http/Headers.java
@@ -72,6 +72,7 @@
public final static String SET_COOKIE = "set-cookie";
public final static String PRAGMA = "pragma";
public final static String REFRESH = "refresh";
+ public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies";
// following hash are generated by String.hashCode()
private final static int HASH_TRANSFER_ENCODING = 1274458357;
@@ -92,6 +93,7 @@
private final static int HASH_SET_COOKIE = 1237214767;
private final static int HASH_PRAGMA = -980228804;
private final static int HASH_REFRESH = 1085444827;
+ private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014;
// keep any headers that require direct access in a presized
// string array
@@ -113,8 +115,9 @@
private final static int IDX_SET_COOKIE = 15;
private final static int IDX_PRAGMA = 16;
private final static int IDX_REFRESH = 17;
+ private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18;
- private final static int HEADER_COUNT = 18;
+ private final static int HEADER_COUNT = 19;
/* parsed values */
private long transferEncoding;
@@ -141,7 +144,8 @@
ETAG,
SET_COOKIE,
PRAGMA,
- REFRESH
+ REFRESH,
+ X_PERMITTED_CROSS_DOMAIN_POLICIES
};
// Catch-all for headers not explicitly handled
@@ -287,6 +291,11 @@
mHeaders[IDX_REFRESH] = val;
}
break;
+ case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES:
+ if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) {
+ mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val;
+ }
+ break;
default:
mExtraHeaderNames.add(name);
mExtraHeaderValues.add(val);
@@ -361,6 +370,10 @@
return mHeaders[IDX_REFRESH];
}
+ public String getXPermittedCrossDomainPolicies() {
+ return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES];
+ }
+
public void setContentLength(long value) {
this.contentLength = value;
}
@@ -409,6 +422,10 @@
mHeaders[IDX_ETAG] = value;
}
+ public void setXPermittedCrossDomainPolicies(String value) {
+ mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value;
+ }
+
public interface HeaderCallback {
public void header(String name, String value);
}
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index a67fd9a..e512a1df 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -308,12 +308,6 @@
SslError error = CertificateChainValidator.getInstance().
doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName());
- EventHandler eventHandler = req.getEventHandler();
-
- // Update the certificate info (to be consistent, it is better to do it
- // here, before we start handling SSL errors, if any)
- eventHandler.certificate(mCertificate);
-
// Inform the user if there is a problem
if (error != null) {
// handleSslErrorRequest may immediately unsuspend if it wants to
@@ -325,7 +319,7 @@
mSuspended = true;
}
// don't hold the lock while calling out to the event handler
- boolean canHandle = eventHandler.handleSslErrorRequest(error);
+ boolean canHandle = req.getEventHandler().handleSslErrorRequest(error);
if(!canHandle) {
throw new IOException("failed to handle "+ error);
}
diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java
index 1b6568e..6dbf325 100644
--- a/core/java/android/net/http/Request.java
+++ b/core/java/android/net/http/Request.java
@@ -82,6 +82,9 @@
/* Used to synchronize waitUntilComplete() requests */
private final Object mClientResource = new Object();
+ /** True if loading should be paused **/
+ private boolean mLoadingPaused = false;
+
/**
* Processor used to set content-length and transfer-encoding
* headers.
@@ -133,6 +136,18 @@
}
/**
+ * @param pause True if the load should be paused.
+ */
+ synchronized void setLoadingPaused(boolean pause) {
+ mLoadingPaused = pause;
+
+ // Wake up the paused thread if we're unpausing the load.
+ if (!mLoadingPaused) {
+ notify();
+ }
+ }
+
+ /**
* @param connection Request served by this connection
*/
void setConnection(Connection connection) {
@@ -271,7 +286,24 @@
int len = 0;
int lowWater = buf.length / 2;
while (len != -1) {
+ synchronized(this) {
+ while (mLoadingPaused) {
+ // Put this (network loading) thread to sleep if WebCore
+ // has asked us to. This can happen with plugins for
+ // example, if we are streaming data but the plugin has
+ // filled its internal buffers.
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ HttpLog.e("Interrupted exception whilst "
+ + "network thread paused at WebCore's request."
+ + " " + e.getMessage());
+ }
+ }
+ }
+
len = nis.read(buf, count, buf.length - count);
+
if (len != -1) {
count += len;
}
@@ -316,10 +348,16 @@
*
* Called by RequestHandle from non-network thread
*/
- void cancel() {
+ synchronized void cancel() {
if (HttpLog.LOGV) {
HttpLog.v("Request.cancel(): " + getUri());
}
+
+ // Ensure that the network thread is not blocked by a hanging request from WebCore to
+ // pause the load.
+ mLoadingPaused = false;
+ notify();
+
mCancelled = true;
if (mConnection != null) {
mConnection.cancel();
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 77cd544..103fd94 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -101,6 +101,16 @@
}
/**
+ * Pauses the loading of this request. For example, called from the WebCore thread
+ * when the plugin can take no more data.
+ */
+ public void pauseRequest(boolean pause) {
+ if (mRequest != null) {
+ mRequest.setLoadingPaused(pause);
+ }
+ }
+
+ /**
* Handles SSL error(s) on the way down from the user (the user
* has already provided their feedback).
*/
@@ -190,7 +200,7 @@
if (mBodyProvider != null) mBodyProvider.reset();
} catch (java.io.IOException ex) {
if (HttpLog.LOGV) {
- HttpLog.v("setupAuthResponse() failed to reset body provider");
+ HttpLog.v("setupRedirect() failed to reset body provider");
}
return false;
}
@@ -433,6 +443,16 @@
* Creates and queues new request.
*/
private void createAndQueueNewRequest() {
+ // mConnection is non-null if and only if the requests are synchronous.
+ if (mConnection != null) {
+ RequestHandle newHandle = mRequestQueue.queueSynchronousRequest(
+ mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
+ mBodyProvider, mBodyLength);
+ mRequest = newHandle.mRequest;
+ mConnection = newHandle.mConnection;
+ newHandle.processRequest();
+ return;
+ }
mRequest = mRequestQueue.queueRequest(
mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
mBodyProvider,
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index df10c6a..c9df567 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -73,30 +73,31 @@
public static final native int getCallingUid();
/**
- * Reset the identity of the incoming IPC to the local process. This can
+ * Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
* on interfaces of other objects that may be local to your process and
* need to do permission checks on the calls coming into them (so they
* will check the permission of your own local process, and not whatever
* process originally called you).
- *
+ *
* @return Returns an opaque token that can be used to restore the
* original calling identity by passing it to
* {@link #restoreCallingIdentity(long)}.
- *
+ *
* @see #getCallingPid()
* @see #getCallingUid()
* @see #restoreCallingIdentity(long)
*/
public static final native long clearCallingIdentity();
-
+
/**
- * Restore the identity of the incoming IPC back to a previously identity
- * that was returned by {@link #clearCallingIdentity}.
- *
+ * Restore the identity of the incoming IPC on the current thread
+ * back to a previously identity that was returned by {@link
+ * #clearCallingIdentity}.
+ *
* @param token The opaque token that was previously returned by
* {@link #clearCallingIdentity}.
- *
+ *
* @see #clearCallingIdentity
*/
public static final native void restoreCallingIdentity(long token);
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4780cf3..a17b7fe 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -115,6 +115,9 @@
*/
public static boolean copyToFile(InputStream inputStream, File destFile) {
try {
+ if (destFile.exists()) {
+ destFile.delete();
+ }
OutputStream out = new FileOutputStream(destFile);
try {
byte[] buffer = new byte[4096];
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index d1d8b0a..e970e7e 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -19,6 +19,7 @@
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.wifi.WifiConfiguration;
/**
* @hide
@@ -77,8 +78,9 @@
/**
* Start tethering services with the specified dhcp server range
+ * arg is a set of start end pairs defining the ranges.
*/
- void startTethering(String dhcpRangeStart, String dhcpRangeEnd);
+ void startTethering(in String[] dhcpRanges);
/**
* Stop currently running tethering services
@@ -166,11 +168,15 @@
/**
* Start Wifi Access Point
*/
- void startAccessPoint();
+ void startAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface);
/**
* Stop Wifi Access Point
*/
void stopAccessPoint();
+ /**
+ * Set Access Point config
+ */
+ void setAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface);
}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index 9742b05..a81e16b 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -273,7 +273,8 @@
* @hide
*/
public ParcelFileDescriptor getParcelFileDescriptor() throws IOException {
- return new ParcelFileDescriptor(getFileDescriptor());
+ FileDescriptor fd = getFileDescriptor();
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
}
/**
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 3fcb18e..0a3b2cf 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -113,7 +113,7 @@
}
FileDescriptor fd = Parcel.openFileDescriptor(path, mode);
- return new ParcelFileDescriptor(fd);
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
}
/**
@@ -127,7 +127,7 @@
*/
public static ParcelFileDescriptor fromSocket(Socket socket) {
FileDescriptor fd = getFileDescriptorFromSocket(socket);
- return new ParcelFileDescriptor(fd);
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
}
// Extracts the file descriptor from the specified socket and returns it untouched
@@ -163,7 +163,10 @@
* If an error occurs attempting to close this ParcelFileDescriptor.
*/
public void close() throws IOException {
- mClosed = true;
+ synchronized (this) {
+ if (mClosed) return;
+ mClosed = true;
+ }
if (mParcelDescriptor != null) {
// If this is a proxy to another file descriptor, just call through to its
// close method.
@@ -235,6 +238,9 @@
/*package */ParcelFileDescriptor(FileDescriptor descriptor) {
super();
+ if (descriptor == null) {
+ throw new NullPointerException("descriptor must not be null");
+ }
mFileDescriptor = descriptor;
mParcelDescriptor = null;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e4eaf45b..f4ca8bc 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -361,6 +361,9 @@
*/
public WakeLock newWakeLock(int flags, String tag)
{
+ if (tag == null) {
+ throw new NullPointerException("tag is null in PowerManager.newWakeLock");
+ }
return new WakeLock(flags, tag);
}
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index 75455ab..4862f80 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -84,7 +84,7 @@
int[] getStorageUsers(String path);
/**
- * Gets the state of an volume via it's mountpoint.
+ * Gets the state of a volume via its mountpoint.
*/
String getVolumeState(String mountPoint);
@@ -146,4 +146,10 @@
* Invokes call back once the shutdown is complete.
*/
void shutdown(IMountShutdownObserver observer);
+
+ /**
+ * Call into MountService by PackageManager to notify that its done
+ * processing the media status update request.
+ */
+ void finishMediaUpdate();
}
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 61012c9..e40a7baa 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -1055,6 +1055,8 @@
public Uri pushIntoContentResolver(ContentResolver resolver) {
ArrayList<ContentProviderOperation> operationList =
new ArrayList<ContentProviderOperation>();
+ // After applying the batch the first result's Uri is returned so it is important that
+ // the RawContact is the first operation that gets inserted into the list
ContentProviderOperation.Builder builder =
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
String myGroupsId = null;
@@ -1290,8 +1292,11 @@
ContentProviderResult[] results = resolver.applyBatch(
ContactsContract.AUTHORITY, operationList);
// the first result is always the raw_contact. return it's uri so
- // that it can be found later
- return results[0].uri;
+ // that it can be found later. do null checking for badly behaving
+ // ContentResolvers
+ return (results == null || results.length == 0 || results[0] == null)
+ ? null
+ : results[0].uri;
} catch (RemoteException e) {
Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 837ce91..726793d 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -24,7 +24,6 @@
import android.os.Handler;
import android.os.Message;
import android.view.View;
-import android.view.Window;
/**
* Shows a hierarchy of {@link Preference} objects as
@@ -81,6 +80,8 @@
private PreferenceManager mPreferenceManager;
+ private Bundle mSavedInstanceState;
+
/**
* The starting request code given out to preference framework.
*/
@@ -137,15 +138,19 @@
@Override
protected void onRestoreInstanceState(Bundle state) {
- super.onRestoreInstanceState(state);
-
Bundle container = state.getBundle(PREFERENCES_TAG);
if (container != null) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.restoreHierarchyState(container);
+ mSavedInstanceState = state;
+ return;
}
}
+
+ // Only call this if we didn't save the instance state for later.
+ // If we did save it, it will be restored when we bind the adapter.
+ super.onRestoreInstanceState(state);
}
@Override
@@ -176,6 +181,10 @@
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.bind(getListView());
+ if (mSavedInstanceState != null) {
+ super.onRestoreInstanceState(mSavedInstanceState);
+ mSavedInstanceState = null;
+ }
}
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 27092ae..b466b40 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -121,15 +121,6 @@
private static final int MAX_HISTORY_COUNT = 250;
/**
- * URI for writing geolocation permissions. This requires the
- * {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}.
- */
- public static final Uri GEOLOCATION_URI =
- Uri.parse("content://browser/geolocation");
-
- private static final String GEOLOCATION_WHERE_CLAUSE = GeolocationColumns.ORIGIN + " = ?";
-
- /**
* Open the AddBookmark activity to save a bookmark. Launch with
* and/or url, which can be edited by the user before saving.
* @param c Context used to launch the AddBookmark activity.
@@ -276,71 +267,47 @@
public static final void updateVisitedHistory(ContentResolver cr,
String url, boolean real) {
long now = new Date().getTime();
- new UpdateVisitedHistory(cr, url, real, now).execute();
- }
-
- /**
- * Runnable to perform the update in a separate thread.
- */
- private static class UpdateVisitedHistory extends AsyncTask<Void, Void, Void> {
- private final ContentResolver mContentResolver;
- private final String mUrl;
- private final boolean mReal;
- private final long mNow;
-
- public UpdateVisitedHistory(ContentResolver cr, String url,
- boolean real, long now) {
- mContentResolver = cr;
- mUrl = url;
- mReal = real;
- mNow = now;
- }
-
- protected Void doInBackground(Void... unused) {
- Cursor c = null;
- try {
- c = getVisitedLike(mContentResolver, mUrl);
- /* We should only get one answer that is exactly the same. */
- if (c.moveToFirst()) {
- ContentValues map = new ContentValues();
- if (mReal) {
- map.put(BookmarkColumns.VISITS, c
- .getInt(HISTORY_PROJECTION_VISITS_INDEX) + 1);
- } else {
- map.put(BookmarkColumns.USER_ENTERED, 1);
- }
- map.put(BookmarkColumns.DATE, mNow);
- String[] projection = new String[]
- { Integer.valueOf(c.getInt(0)).toString() };
- mContentResolver.update(BOOKMARKS_URI, map, "_id = ?",
- projection);
+ Cursor c = null;
+ try {
+ c = getVisitedLike(cr, url);
+ /* We should only get one answer that is exactly the same. */
+ if (c.moveToFirst()) {
+ ContentValues map = new ContentValues();
+ if (real) {
+ map.put(BookmarkColumns.VISITS, c
+ .getInt(HISTORY_PROJECTION_VISITS_INDEX) + 1);
} else {
- truncateHistory(mContentResolver);
- ContentValues map = new ContentValues();
- int visits;
- int user_entered;
- if (mReal) {
- visits = 1;
- user_entered = 0;
- } else {
- visits = 0;
- user_entered = 1;
- }
- map.put(BookmarkColumns.URL, mUrl);
- map.put(BookmarkColumns.VISITS, visits);
- map.put(BookmarkColumns.DATE, mNow);
- map.put(BookmarkColumns.BOOKMARK, 0);
- map.put(BookmarkColumns.TITLE, mUrl);
- map.put(BookmarkColumns.CREATED, 0);
- map.put(BookmarkColumns.USER_ENTERED, user_entered);
- mContentResolver.insert(BOOKMARKS_URI, map);
+ map.put(BookmarkColumns.USER_ENTERED, 1);
}
- } catch (IllegalStateException e) {
- Log.e(LOGTAG, "updateVisitedHistory", e);
- } finally {
- if (c != null) c.close();
+ map.put(BookmarkColumns.DATE, now);
+ String[] projection = new String[]
+ { Integer.valueOf(c.getInt(0)).toString() };
+ cr.update(BOOKMARKS_URI, map, "_id = ?", projection);
+ } else {
+ truncateHistory(cr);
+ ContentValues map = new ContentValues();
+ int visits;
+ int user_entered;
+ if (real) {
+ visits = 1;
+ user_entered = 0;
+ } else {
+ visits = 0;
+ user_entered = 1;
+ }
+ map.put(BookmarkColumns.URL, url);
+ map.put(BookmarkColumns.VISITS, visits);
+ map.put(BookmarkColumns.DATE, now);
+ map.put(BookmarkColumns.BOOKMARK, 0);
+ map.put(BookmarkColumns.TITLE, url);
+ map.put(BookmarkColumns.CREATED, 0);
+ map.put(BookmarkColumns.USER_ENTERED, user_entered);
+ cr.insert(BOOKMARKS_URI, map);
}
- return null;
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "updateVisitedHistory", e);
+ } finally {
+ if (c != null) c.close();
}
}
@@ -638,42 +605,6 @@
}
}
- /**
- * Allows geolocation for the specified origin.
- * This requires the {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}
- * permission.
- *
- * @param origin The origin to allow geolocation for, e.g. "http://www.google.com". The string
- * should not include a trailing slash.
- */
- public static void allowGeolocation(ContentResolver cr, String origin) {
- try {
- ContentValues map = new ContentValues();
- map.put(GeolocationColumns.ORIGIN, origin);
- cr.insert(GEOLOCATION_URI, map);
- } catch (IllegalStateException e) {
- Log.e(LOGTAG, "allowGeolocation", e);
- return;
- }
- }
-
- /**
- * Clears the geolocation permission state for the specified origin.
- * This requires the {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}
- * permission.
- *
- * @param origin The origin to allow geolocation for, e.g. "http://www.google.com". The string
- * should not include a trailing slash.
- */
- public static void clearGeolocation(ContentResolver cr, String origin) {
- try {
- String[] whereArgs = { origin };
- cr.delete(GEOLOCATION_URI, GEOLOCATION_WHERE_CLAUSE, whereArgs);
- } catch (IllegalStateException e) {
- Log.e(LOGTAG, "clearGeolocation", e);
- }
- }
-
public static class BookmarkColumns implements BaseColumns {
public static final String URL = "url";
public static final String VISITS = "visits";
@@ -701,8 +632,4 @@
public static final String SEARCH = "search";
public static final String DATE = "date";
}
-
- public static class GeolocationColumns {
- public static final String ORIGIN = "origin";
- }
}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 1c5fca3..ba63a66 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -1279,9 +1279,9 @@
}
Intent intent = new Intent(EVENT_REMINDER_ACTION);
+ intent.setData(ContentUris.withAppendedId(Calendar.CONTENT_URI, alarmTime));
intent.putExtra(ALARM_TIME, alarmTime);
- PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
manager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index dda9018..1d27828 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -870,6 +870,13 @@
public static final String ARTIST = "artist";
/**
+ * The artist credited for the album that contains the audio file
+ * <P>Type: TEXT</P>
+ * @hide
+ */
+ public static final String ALBUM_ARTIST = "album_artist";
+
+ /**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
* <P>Type: TEXT</P>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 503d8b8d..b8e5747 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2313,6 +2313,14 @@
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
/**
+ * List of system input methods that are currently disabled. This is a string
+ * containing the IDs of all disabled input methods, each ID separated
+ * by ':'.
+ * @hide
+ */
+ public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
+
+ /**
* Host name and port for a user-selected proxy.
*/
public static final String HTTP_PROXY = "http_proxy";
@@ -2458,6 +2466,11 @@
public static final String TTS_DEFAULT_VARIANT = "tts_default_variant";
/**
+ * Space delimited list of plugin packages that are enabled.
+ */
+ public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
+
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
@@ -2495,11 +2508,34 @@
public static final String WIFI_ON = "wifi_on";
/**
- * Whether the Wi-Fi AP should be on.
+ * Used to save the Wifi_ON state prior to tethering.
+ * This state will be checked to restore Wifi after
+ * the user turns off tethering.
*
* @hide
*/
- public static final String WIFI_AP_ON = "wifi_ap_on";
+ public static final String WIFI_SAVED_STATE = "wifi_saved_state";
+
+ /**
+ * AP SSID
+ *
+ * @hide
+ */
+ public static final String WIFI_AP_SSID = "wifi_ap_ssid";
+
+ /**
+ * AP security
+ *
+ * @hide
+ */
+ public static final String WIFI_AP_SECURITY = "wifi_ap_security";
+
+ /**
+ * AP passphrase
+ *
+ * @hide
+ */
+ public static final String WIFI_AP_PASSWD = "wifi_ap_passwd";
/**
* The acceptable packet loss percentage (range 0 - 100) before trying
@@ -2596,6 +2632,13 @@
public static final String BACKGROUND_DATA = "background_data";
/**
+ * Origins for which browsers should allow geolocation by default.
+ * The value is a space-separated list of origins.
+ */
+ public static final String ALLOWED_GEOLOCATION_ORIGINS
+ = "allowed_geolocation_origins";
+
+ /**
* Whether mobile data connections are allowed by the user. See
* ConnectivityManager for more info.
* @hide
@@ -2676,14 +2719,6 @@
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
/**
- * Flag for allowing service provider to use location information to improve products and
- * services.
- * Type: int ( 0 = disallow, 1 = allow )
- * @hide
- */
- public static final String USE_LOCATION_FOR_SERVICES = "use_location";
-
- /**
* Controls whether settings backup is enabled.
* Type: int ( 0 = disabled, 1 = enabled )
* @hide
@@ -2877,6 +2912,12 @@
public static final String DROPBOX_AGE_SECONDS =
"dropbox_age_seconds";
/**
+ * Maximum number of entry files which {@link android.os.IDropBox} will keep around.
+ * @hide
+ */
+ public static final String DROPBOX_MAX_FILES =
+ "dropbox_max_files";
+ /**
* Maximum amount of disk space used by {@link android.os.IDropBox} no matter what.
* @hide
*/
@@ -3248,6 +3289,14 @@
INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
/**
+ * The current night mode that has been selected by the user. Owned
+ * and controlled by UiModeManagerService. Constants are as per
+ * UiModeManager.
+ * @hide
+ */
+ public static final String UI_NIGHT_MODE = "ui_night_mode";
+
+ /**
* @hide
*/
public static final String[] SETTINGS_TO_BACKUP = {
@@ -3265,6 +3314,7 @@
TTS_DEFAULT_SYNTH,
TTS_DEFAULT_LANG,
TTS_DEFAULT_COUNTRY,
+ TTS_ENABLED_PLUGINS,
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
WIFI_NUM_ALLOWED_CHANNELS,
@@ -3272,7 +3322,8 @@
MOUNT_PLAY_NOTIFICATION_SND,
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
- MOUNT_UMS_NOTIFY_ENABLED
+ MOUNT_UMS_NOTIFY_ENABLED,
+ UI_NIGHT_MODE
};
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 444135a..f47a8d0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -141,6 +141,7 @@
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mContentInsets = new Rect();
+ final Configuration mConfiguration = new Configuration();
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
@@ -494,7 +495,7 @@
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, false, mWinFrame, mContentInsets,
- mVisibleInsets, mSurfaceHolder.mSurface);
+ mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
diff --git a/core/java/android/speech/RecognitionManager.java b/core/java/android/speech/RecognitionManager.java
index 16b1f89..9ed9941 100644
--- a/core/java/android/speech/RecognitionManager.java
+++ b/core/java/android/speech/RecognitionManager.java
@@ -180,10 +180,11 @@
}
/**
- * Factory method to create a new {@code RecognitionManager}, please note that
- * {@link #setRecognitionListener(RecognitionListener)} must be called before dispatching any
- * command to the created {@code RecognitionManager}.
- *
+ * Factory method to create a new {@code RecognitionManager}. Please note that
+ * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
+ * command to the created {@code RecognitionManager}, otherwise no notifications will be
+ * received.
+ *
* @param context in which to create {@code RecognitionManager}
* @return a new {@code RecognitionManager}
*/
@@ -192,14 +193,15 @@
}
/**
- * Factory method to create a new {@code RecognitionManager}, please note that
- * {@link #setRecognitionListener(RecognitionListener)} must be called before dispatching any
- * command to the created {@code RecognitionManager}.
- *
+ * Factory method to create a new {@code RecognitionManager}. Please note that
+ * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
+ * command to the created {@code RecognitionManager}, otherwise no notifications will be
+ * received.
+ *
* Use this version of the method to specify a specific service to direct this
* {@link RecognitionManager} to. Normally you would not use this; use
- * {@link #createRecognitionManager(Context)} instead to use the system default
- * recognition service.
+ * {@link #createRecognitionManager(Context)} instead to use the system default recognition
+ * service.
*
* @param context in which to create {@code RecognitionManager}
* @param serviceComponent the {@link ComponentName} of a specific service to direct this
@@ -230,9 +232,9 @@
/**
* Starts listening for speech. Please note that
- * {@link #setRecognitionListener(RecognitionListener)} must be called beforehand, otherwise a
- * {@link RuntimeException} will be thrown.
- *
+ * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
+ * no notifications will be received.
+ *
* @param recognizerIntent contains parameters for the recognition to be performed. The intent
* may also contain optional extras, see {@link RecognizerIntent}. If these values are
* not set explicitly, default values will be used by the recognizer.
@@ -280,8 +282,8 @@
* determines speech has completed. However, you can manipulate endpointer parameters directly
* using the intent extras defined in {@link RecognizerIntent}, in which case you may sometimes
* want to manually call this method to stop listening sooner. Please note that
- * {@link #setRecognitionListener(RecognitionListener)} must be called beforehand, otherwise a
- * {@link RuntimeException} will be thrown.
+ * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
+ * no notifications will be received.
*/
public void stopListening() {
checkIsCalledFromMainThread();
@@ -290,8 +292,8 @@
/**
* Cancels the speech recognition. Please note that
- * {@link #setRecognitionListener(RecognitionListener)} must be called beforehand, otherwise a
- * {@link RuntimeException} will be thrown.
+ * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
+ * no notifications will be received.
*/
public void cancel() {
checkIsCalledFromMainThread();
@@ -371,8 +373,7 @@
}
/**
- * Destroys the {@code RecognitionManager} object. Note that after calling this method all
- * method calls on this object will fail, triggering {@link RecognitionListener#onError}.
+ * Destroys the {@code RecognitionManager} object.
*/
public void destroy() {
if (mConnection != null) {
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
index 2ed660a..df8ea0c 100755
--- a/core/java/android/speech/tts/ITts.aidl
+++ b/core/java/android/speech/tts/ITts.aidl
@@ -61,5 +61,7 @@
int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
- int setEngineByPackageName(in String enginePackageName);
+ int setEngineByPackageName(in String enginePackageName);
+
+ String getDefaultEngine();
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 80b3a74..69b60db 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1340,4 +1340,43 @@
}
}
+
+ /**
+ * Gets the packagename of the default speech synthesis engine.
+ *
+ * @return Packagename of the TTS engine that the user has chosen as their default.
+ *
+ * @hide
+ */
+ public String getDefaultEngine() {
+ synchronized (mStartLock) {
+ String engineName = "";
+ if (!mStarted) {
+ return engineName;
+ }
+ try {
+ engineName = mITts.getDefaultEngine();
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - setEngineByPackageName", "RemoteException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (NullPointerException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - setEngineByPackageName", "NullPointerException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (IllegalStateException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - setEngineByPackageName", "IllegalStateException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } finally {
+ return engineName;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index b6b009b..01f07d6 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -17,6 +17,7 @@
package android.view;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@@ -63,6 +64,9 @@
* contents to make sure the user can see it. This is different than
* <var>outContentInsets</var> in that these insets change transiently,
* so complex relayout of the window should not happen based on them.
+ * @param outConfiguration New configuration of window, if it is now
+ * becoming visible and the global configuration has changed since it
+ * was last displayed.
* @param outSurface Object in which is placed the new display surface.
*
* @return int Result flags: {@link WindowManagerImpl#RELAYOUT_SHOW_FOCUS},
@@ -71,7 +75,8 @@
int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
- out Rect outVisibleInsets, out Surface outSurface);
+ out Rect outVisibleInsets, out Configuration outConfig,
+ out Surface outSurface);
/**
* Give the window manager a hint of the part of the window that is
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 183fce3..255e317 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -28,6 +28,7 @@
*/
public class Surface implements Parcelable {
private static final String LOG_TAG = "Surface";
+ private static final boolean DEBUG_RELEASE = false;
/* flags used in constructor (keep in sync with ISurfaceComposer.h) */
@@ -155,6 +156,9 @@
// non compatibility mode.
private Matrix mCompatibleMatrix;
+ @SuppressWarnings("unused")
+ private Exception mCreationStack;
+
/**
* Exception thrown when a surface couldn't be created or resized
*/
@@ -181,6 +185,9 @@
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
+ if (DEBUG_RELEASE) {
+ mCreationStack = new Exception();
+ }
mCanvas = new CompatibleCanvas();
init(s,pid,null,display,w,h,format,flags);
}
@@ -192,6 +199,9 @@
public Surface(SurfaceSession s,
int pid, String name, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
+ if (DEBUG_RELEASE) {
+ mCreationStack = new Exception();
+ }
mCanvas = new CompatibleCanvas();
init(s,pid,name,display,w,h,format,flags);
}
@@ -202,6 +212,9 @@
* {@hide}
*/
public Surface() {
+ if (DEBUG_RELEASE) {
+ mCreationStack = new Exception();
+ }
mCanvas = new CompatibleCanvas();
}
@@ -407,6 +420,15 @@
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
+ if (mSurface != 0 || mSurfaceControl != 0) {
+ if (DEBUG_RELEASE) {
+ Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
+ + mSurface + ", " + mSurfaceControl + ")", mCreationStack);
+ } else {
+ Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
+ + mSurface + ", " + mSurfaceControl + ")");
+ }
+ }
release();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0722699..7f7d207 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -39,7 +39,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
-import java.lang.ref.WeakReference;
/**
* Provides a dedicated drawing surface embedded inside of a view hierarchy.
@@ -102,7 +101,8 @@
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mContentInsets = new Rect();
-
+ final Configuration mConfiguration = new Configuration();
+
static final int KEEP_SCREEN_ON_MSG = 1;
static final int GET_NEW_SURFACE_MSG = 2;
@@ -124,6 +124,13 @@
}
};
+ final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+ = new ViewTreeObserver.OnScrollChangedListener() {
+ public void onScrollChanged() {
+ updateWindow(false);
+ }
+ };
+
boolean mRequestedVisible = false;
boolean mWindowVisibility = false;
boolean mViewVisibility = false;
@@ -180,6 +187,7 @@
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView");
mViewVisibility = getVisibility() == VISIBLE;
+ getViewTreeObserver().addOnScrollChangedListener(mScrollChangedListener);
}
@Override
@@ -200,6 +208,7 @@
@Override
protected void onDetachedFromWindow() {
+ getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener);
mRequestedVisible = false;
updateWindow(false);
mHaveFrame = false;
@@ -224,12 +233,6 @@
}
@Override
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- super.onScrollChanged(l, t, oldl, oldt);
- updateWindow(false);
- }
-
- @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateWindow(false);
@@ -419,7 +422,7 @@
if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
reportSurfaceDestroyed();
}
-
+
mNewSurfaceNeeded = false;
mSurfaceLock.lock();
@@ -428,7 +431,7 @@
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
- mVisibleInsets, mSurface);
+ mVisibleInsets, mConfiguration, mSurface);
if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
@@ -467,6 +470,8 @@
c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
}
}
+ } else {
+ mSurface.release();
}
} finally {
mIsCreating = false;
@@ -503,6 +508,17 @@
updateWindow(false);
}
+ /**
+ * Check to see if the surface has fixed size dimensions or if the surface's
+ * dimensions are dimensions are dependent on its current layout.
+ *
+ * @return true if the surface has dimensions that are fixed in size
+ * @hide
+ */
+ public boolean isFixedSize() {
+ return (mRequestedWidth != -1 || mRequestedHeight != -1);
+ }
+
private static class MyWindow extends BaseIWindow {
private final WeakReference<SurfaceView> mSurfaceView;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e83b9b5..7e748c0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1541,7 +1541,7 @@
/**
* Controls the overscroll mode for this view.
- * See {@link #overscrollBy(int, int, int, int, int, int, int, int)},
+ * See {@link #overscrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVERSCROLL_ALWAYS}, {@link #OVERSCROLL_IF_CONTENT_SCROLLS},
* and {@link #OVERSCROLL_NEVER}.
*/
@@ -5728,7 +5728,7 @@
* of the thumb within the scrollbar's track.</p>
*
* <p>The range is expressed in arbitrary units that must be the same as the
- * units used by {@link #computeHorizontalScrollRange()} and
+ * units used by {@link #computeVerticalScrollRange()} and
* {@link #computeVerticalScrollOffset()}.</p>
*
* <p>The default extent is the drawing height of this view.</p>
@@ -8686,13 +8686,15 @@
* along the X axis.
* @param maxOverscrollY Number of pixels to overscroll by in either direction
* along the Y axis.
+ * @param isTouchEvent true if this scroll operation is the result of a touch event.
* @return true if scrolling was clamped to an overscroll boundary along either
* axis, false otherwise.
*/
protected boolean overscrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
- int maxOverscrollX, int maxOverscrollY) {
+ int maxOverscrollX, int maxOverscrollY,
+ boolean isTouchEvent) {
final int overscrollMode = mOverscrollMode;
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
@@ -8770,10 +8772,11 @@
clampedY = true;
}
- // Bump the device with some haptic feedback if we're at the edge
- // and didn't start there.
- if ((overscrollHorizontal && clampedX && scrollX != left && scrollX != right) ||
- (overscrollVertical && clampedY && scrollY != top && scrollY != bottom)) {
+ // Bump the device with some haptic feedback if we're at the edge,
+ // didn't start there, and the scroll is the result of a touch event.
+ if (isTouchEvent &&
+ ((overscrollHorizontal && clampedX && scrollX != left && scrollX != right) ||
+ (overscrollVertical && clampedY && scrollY != top && scrollY != bottom))) {
performHapticFeedback(HapticFeedbackConstants.SCROLL_BARRIER);
}
@@ -8783,7 +8786,7 @@
}
/**
- * Called by {@link #overscrollBy(int, int, int, int, int, int, int, int)} to
+ * Called by {@link #overscrollBy(int, int, int, int, int, int, int, int, boolean)} to
* respond to the results of an overscroll operation.
*
* @param scrollX New X scroll value in pixels
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8311bdc..d2563a8 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -546,6 +546,9 @@
recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
try {
+ if (recyclerDump.exists()) {
+ recyclerDump.delete();
+ }
final FileOutputStream file = new FileOutputStream(recyclerDump);
final DataOutputStream out = new DataOutputStream(file);
@@ -1037,7 +1040,7 @@
clientStream.writeInt(outRect.width());
clientStream.writeInt(outRect.height());
- captureViewLayer(root, clientStream);
+ captureViewLayer(root, clientStream, true);
clientStream.write(2);
} finally {
@@ -1045,9 +1048,11 @@
}
}
- private static void captureViewLayer(View view, DataOutputStream clientStream)
+ private static void captureViewLayer(View view, DataOutputStream clientStream, boolean visible)
throws IOException {
+ final boolean localVisible = view.getVisibility() == View.VISIBLE && visible;
+
if ((view.mPrivateFlags & View.SKIP_DRAW) != View.SKIP_DRAW) {
final int id = view.getId();
String name = view.getClass().getSimpleName();
@@ -1057,7 +1062,7 @@
clientStream.write(1);
clientStream.writeUTF(name);
- clientStream.writeByte(view.getVisibility() == View.VISIBLE ? 1 : 0);
+ clientStream.writeByte(localVisible ? 1 : 0);
int[] position = new int[2];
// XXX: Should happen on the UI thread
@@ -1083,7 +1088,7 @@
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- captureViewLayer(group.getChildAt(i), clientStream);
+ captureViewLayer(group.getChildAt(i), clientStream, localVisible);
}
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 597d583..006aff8 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1274,13 +1274,27 @@
@Override
Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
- int oldCount = mChildrenCount;
+ int count = mChildrenCount;
+ int[] visibilities = null;
+
if (skipChildren) {
- mChildrenCount = 0;
+ visibilities = new int[count];
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ visibilities[i] = child.getVisibility();
+ if (visibilities[i] == View.VISIBLE) {
+ child.setVisibility(INVISIBLE);
+ }
+ }
}
Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
- mChildrenCount = oldCount;
+
+ if (skipChildren) {
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).setVisibility(visibilities[i]);
+ }
+ }
return b;
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 484922d..ed83bca 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -80,6 +80,7 @@
private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
+ private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
private static final boolean WATCH_POINTER = false;
private static final boolean MEASURE_LATENCY = false;
@@ -176,6 +177,9 @@
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
+ final Configuration mLastConfiguration = new Configuration();
+ final Configuration mPendingConfiguration = new Configuration();
+
class ResizedInfo {
Rect coveredInsets;
Rect visibleInsets;
@@ -719,6 +723,7 @@
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mKeepScreenOn = false;
viewVisibilityChanged = false;
+ mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
@@ -904,6 +909,13 @@
+ " visible=" + mPendingVisibleInsets.toShortString()
+ " surface=" + mSurface);
+ if (mPendingConfiguration.seq != 0) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+ + mPendingConfiguration);
+ updateConfiguration(mPendingConfiguration, !mFirst);
+ mPendingConfiguration.seq = 0;
+ }
+
contentInsetsChanged = !mPendingContentInsets.equals(
mAttachInfo.mContentInsets);
visibleInsetsChanged = !mPendingVisibleInsets.equals(
@@ -1488,8 +1500,10 @@
focus.getFocusedRect(mTempRect);
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
+ ": focusRect=" + mTempRect.toShortString());
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(
- focus, mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focus, mTempRect);
+ }
if (DEBUG_INPUT_RESIZE) Log.v(TAG,
"Focus in window: focusRect="
+ mTempRect.toShortString()
@@ -1627,6 +1641,30 @@
}
}
+ void updateConfiguration(Configuration config, boolean force) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG,
+ "Applying new config to window "
+ + mWindowAttributes.getTitle()
+ + ": " + config);
+ synchronized (sConfigCallbacks) {
+ for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
+ sConfigCallbacks.get(i).onConfigurationChanged(config);
+ }
+ }
+ if (mView != null) {
+ // At this point the resources have been updated to
+ // have the most recent config, whatever that is. Use
+ // the on in them which may be newer.
+ if (mView != null) {
+ config = mView.getResources().getConfiguration();
+ }
+ if (force || mLastConfiguration.diff(config) != 0) {
+ mLastConfiguration.setTo(config);
+ mView.dispatchConfigurationChanged(config);
+ }
+ }
+ }
+
/**
* Return true if child is an ancestor of parent, (or equal to the parent).
*/
@@ -1639,6 +1677,16 @@
return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
}
+ private static void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
public final static int DO_TRAVERSAL = 1000;
public final static int DIE = 1001;
@@ -1807,7 +1855,8 @@
if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
&& mPendingContentInsets.equals(ri.coveredInsets)
- && mPendingVisibleInsets.equals(ri.visibleInsets)) {
+ && mPendingVisibleInsets.equals(ri.visibleInsets)
+ && ((ResizedInfo)msg.obj).newConfig == null) {
break;
}
// fall through...
@@ -1815,14 +1864,7 @@
if (mAdded) {
Configuration config = ((ResizedInfo)msg.obj).newConfig;
if (config != null) {
- synchronized (sConfigCallbacks) {
- for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
- sConfigCallbacks.get(i).onConfigurationChanged(config);
- }
- }
- if (mView != null) {
- mView.dispatchConfigurationChanged(config);
- }
+ updateConfiguration(config, false);
}
mWinFrame.left = 0;
mWinFrame.right = msg.arg1;
@@ -1833,6 +1875,10 @@
if (msg.what == RESIZED_REPORT) {
mReportNextDraw = true;
}
+
+ if (mView != null) {
+ forceLayout(mView);
+ }
requestLayout();
}
break;
@@ -2448,8 +2494,12 @@
// of previous focused into the coord system of
// newly focused view
focused.getFocusedRect(mTempRect);
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(focused, mTempRect);
- ((ViewGroup) mView).offsetRectIntoDescendantCoords(v, mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focused, mTempRect);
+ ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+ v, mTempRect);
+ }
focusPassed = v.requestFocus(direction, mTempRect);
}
@@ -2500,12 +2550,14 @@
if (params != null) {
if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
+ mPendingConfiguration.seq = 0;
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
- mPendingContentInsets, mPendingVisibleInsets, mSurface);
+ mPendingContentInsets, mPendingVisibleInsets,
+ mPendingConfiguration, mSurface);
if (restore) {
params.restore();
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0af31f05..88d0c25 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -142,6 +142,29 @@
* @see #TYPE_INPUT_METHOD
* @see #TYPE_INPUT_METHOD_DIALOG
*/
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = TYPE_BASE_APPLICATION, to = "TYPE_BASE_APPLICATION"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION, to = "TYPE_APPLICATION"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_STARTING, to = "TYPE_APPLICATION_STARTING"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG, to = "TYPE_APPLICATION_ATTACHED_DIALOG"),
+ @ViewDebug.IntToString(from = TYPE_STATUS_BAR, to = "TYPE_STATUS_BAR"),
+ @ViewDebug.IntToString(from = TYPE_SEARCH_BAR, to = "TYPE_SEARCH_BAR"),
+ @ViewDebug.IntToString(from = TYPE_PHONE, to = "TYPE_PHONE"),
+ @ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT, to = "TYPE_SYSTEM_ALERT"),
+ @ViewDebug.IntToString(from = TYPE_KEYGUARD, to = "TYPE_KEYGUARD"),
+ @ViewDebug.IntToString(from = TYPE_TOAST, to = "TYPE_TOAST"),
+ @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY, to = "TYPE_SYSTEM_OVERLAY"),
+ @ViewDebug.IntToString(from = TYPE_PRIORITY_PHONE, to = "TYPE_PRIORITY_PHONE"),
+ @ViewDebug.IntToString(from = TYPE_STATUS_BAR_PANEL, to = "TYPE_STATUS_BAR_PANEL"),
+ @ViewDebug.IntToString(from = TYPE_SYSTEM_DIALOG, to = "TYPE_SYSTEM_DIALOG"),
+ @ViewDebug.IntToString(from = TYPE_KEYGUARD_DIALOG, to = "TYPE_KEYGUARD_DIALOG"),
+ @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"),
+ @ViewDebug.IntToString(from = TYPE_INPUT_METHOD, to = "TYPE_INPUT_METHOD"),
+ @ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG, to = "TYPE_INPUT_METHOD_DIALOG")
+ })
public int type;
/**
@@ -367,6 +390,30 @@
* @see #FLAG_FORCE_NOT_FULLSCREEN
* @see #FLAG_IGNORE_CHEEK_PRESSES
*/
+ @ViewDebug.ExportedProperty(flagMapping = {
+ @ViewDebug.FlagToString(mask = FLAG_BLUR_BEHIND, equals = FLAG_BLUR_BEHIND,
+ name = "FLAG_BLUR_BEHIND"),
+ @ViewDebug.FlagToString(mask = FLAG_DIM_BEHIND, equals = FLAG_DIM_BEHIND,
+ name = "FLAG_DIM_BEHIND"),
+ @ViewDebug.FlagToString(mask = FLAG_NOT_FOCUSABLE, equals = FLAG_NOT_FOCUSABLE,
+ name = "FLAG_NOT_FOCUSABLE"),
+ @ViewDebug.FlagToString(mask = FLAG_NOT_TOUCHABLE, equals = FLAG_NOT_TOUCHABLE,
+ name = "FLAG_NOT_TOUCHABLE"),
+ @ViewDebug.FlagToString(mask = FLAG_NOT_TOUCH_MODAL, equals = FLAG_NOT_TOUCH_MODAL,
+ name = "FLAG_NOT_TOUCH_MODAL"),
+ @ViewDebug.FlagToString(mask = FLAG_LAYOUT_IN_SCREEN, equals = FLAG_LAYOUT_IN_SCREEN,
+ name = "FLAG_LAYOUT_IN_SCREEN"),
+ @ViewDebug.FlagToString(mask = FLAG_DITHER, equals = FLAG_DITHER,
+ name = "FLAG_DITHER"),
+ @ViewDebug.FlagToString(mask = FLAG_KEEP_SCREEN_ON, equals = FLAG_KEEP_SCREEN_ON,
+ name = "FLAG_KEEP_SCREEN_ON"),
+ @ViewDebug.FlagToString(mask = FLAG_FULLSCREEN, equals = FLAG_FULLSCREEN,
+ name = "FLAG_FULLSCREEN"),
+ @ViewDebug.FlagToString(mask = FLAG_FORCE_NOT_FULLSCREEN,
+ equals = FLAG_FORCE_NOT_FULLSCREEN, name = "FLAG_FORCE_NOT_FULLSCREEN"),
+ @ViewDebug.FlagToString(mask = FLAG_IGNORE_CHEEK_PRESSES,
+ equals = FLAG_IGNORE_CHEEK_PRESSES, name = "FLAG_IGNORE_CHEEK_PRESSES")
+ })
public int flags;
/** Window flag: as long as this window is visible to the user, allow
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 176169e..2709cff 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -28,11 +28,11 @@
public class DecelerateInterpolator implements Interpolator {
public DecelerateInterpolator() {
}
-
+
/**
* Constructor
*
- * @param factor Degree to which the animation should be eased. Seting factor to 1.0f produces
+ * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
* an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
* ease-out effect (i.e., it starts even faster and ends evens slower)
*/
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index af42ce0..beac0b8 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -17,8 +17,10 @@
package android.webkit;
import android.app.ActivityManager;
+import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.AssetManager;
+import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.ParseException;
@@ -31,12 +33,15 @@
import android.util.Log;
import android.util.TypedValue;
import android.view.Surface;
-import android.view.WindowOrientationListener;
+import android.view.ViewRoot;
+import android.view.WindowManager;
import junit.framework.Assert;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.net.URLEncoder;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
@@ -73,9 +78,6 @@
// Attached Javascript interfaces
private Map<String, Object> mJSInterfaceMap;
- // Orientation listener
- private WindowOrientationListener mOrientationListener;
-
// message ids
// a message posted when a frame loading is completed
static final int FRAME_COMPLETED = 1001;
@@ -105,6 +107,70 @@
// requests from WebCore.
static JWebCoreJavaBridge sJavaBridge;
+ private static class ConfigCallback implements ComponentCallbacks {
+ private final ArrayList<WeakReference<Handler>> mHandlers =
+ new ArrayList<WeakReference<Handler>>();
+ private final WindowManager mWindowManager;
+
+ ConfigCallback(WindowManager wm) {
+ mWindowManager = wm;
+ }
+
+ public synchronized void addHandler(Handler h) {
+ // No need to ever remove a Handler. If the BrowserFrame is
+ // destroyed, it will be collected and the WeakReference set to
+ // null. If it happens to still be around during a configuration
+ // change, the message will be ignored.
+ mHandlers.add(new WeakReference<Handler>(h));
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mHandlers.size() == 0) {
+ return;
+ }
+ int orientation =
+ mWindowManager.getDefaultDisplay().getOrientation();
+ switch (orientation) {
+ case Surface.ROTATION_90:
+ orientation = 90;
+ break;
+ case Surface.ROTATION_180:
+ orientation = 180;
+ break;
+ case Surface.ROTATION_270:
+ orientation = -90;
+ break;
+ case Surface.ROTATION_0:
+ orientation = 0;
+ break;
+ default:
+ break;
+ }
+ synchronized (this) {
+ // Create a list of handlers to remove. Go ahead and make it
+ // the same size to avoid resizing.
+ ArrayList<WeakReference> handlersToRemove =
+ new ArrayList<WeakReference>(mHandlers.size());
+ for (WeakReference<Handler> wh : mHandlers) {
+ Handler h = wh.get();
+ if (h != null) {
+ h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED,
+ orientation, 0));
+ } else {
+ handlersToRemove.add(wh);
+ }
+ }
+ // Now remove all the null references.
+ for (WeakReference weak : handlersToRemove) {
+ mHandlers.remove(weak);
+ }
+ }
+ }
+
+ public void onLowMemory() {}
+ }
+ static ConfigCallback sConfigCallback;
+
/**
* Create a new BrowserFrame to be used in an application.
* @param context An application context to use when retrieving assets.
@@ -138,6 +204,15 @@
// create PluginManager with current Context
PluginManager.getInstance(appContext);
}
+
+ if (sConfigCallback == null) {
+ sConfigCallback = new ConfigCallback(
+ (WindowManager) context.getSystemService(
+ Context.WINDOW_SERVICE));
+ ViewRoot.addConfigCallback(sConfigCallback);
+ }
+ sConfigCallback.addHandler(this);
+
mJSInterfaceMap = javascriptInterfaces;
mSettings = settings;
@@ -152,31 +227,6 @@
if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
}
-
- mOrientationListener = new WindowOrientationListener(context) {
- @Override
- public void onOrientationChanged(int orientation) {
- switch (orientation) {
- case Surface.ROTATION_90:
- orientation = 90;
- break;
- case Surface.ROTATION_180:
- orientation = 180;
- break;
- case Surface.ROTATION_270:
- orientation = -90;
- break;
- case Surface.ROTATION_0:
- orientation = 0;
- break;
- default:
- break;
- }
- sendMessage(
- obtainMessage(ORIENTATION_CHANGED, orientation, 0));
- }
- };
- mOrientationListener.enable();
}
/**
@@ -213,21 +263,19 @@
/**
* Load the content as if it was loaded by the provided base URL. The
- * failUrl is used as the history entry for the load data. If null or
- * an empty string is passed for the failUrl, then no history entry is
- * created.
+ * historyUrl is used as the history entry for the load data.
*
* @param baseUrl Base URL used to resolve relative paths in the content
* @param data Content to render in the browser
* @param mimeType Mimetype of the data being passed in
* @param encoding Character set encoding of the provided data.
- * @param failUrl URL to use if the content fails to load or null.
+ * @param historyUrl URL to use as the history entry.
*/
public void loadData(String baseUrl, String data, String mimeType,
- String encoding, String failUrl) {
+ String encoding, String historyUrl) {
mLoadInitFromJava = true;
- if (failUrl == null) {
- failUrl = "";
+ if (historyUrl == null || historyUrl.length() == 0) {
+ historyUrl = "about:blank";
}
if (data == null) {
data = "";
@@ -241,7 +289,7 @@
if (mimeType == null || mimeType.length() == 0) {
mimeType = "text/html";
}
- nativeLoadData(baseUrl, data, mimeType, encoding, failUrl);
+ nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl);
mLoadInitFromJava = false;
}
@@ -385,7 +433,6 @@
* Destroy all native components of the BrowserFrame.
*/
public void destroy() {
- mOrientationListener.disable();
nativeDestroyFrame();
mBlockMessages = true;
removeCallbacksAndMessages(null);
@@ -527,20 +574,12 @@
*/
private int getFileSize(String uri) {
int size = 0;
- Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
- new String[] { OpenableColumns.SIZE },
- null,
- null,
- null);
- if (cursor != null) {
- try {
- if (cursor.moveToNext()) {
- size = cursor.getInt(0);
- }
- } finally {
- cursor.close();
- }
- }
+ try {
+ InputStream stream = mContext.getContentResolver()
+ .openInputStream(Uri.parse(uri));
+ size = stream.available();
+ stream.close();
+ } catch (Exception e) {}
return size;
}
@@ -586,7 +625,10 @@
* @param headers The http headers.
* @param postData If the method is "POST" postData is sent as the request
* body. Is null when empty.
- * @param cacheMode The cache mode to use when loading this resource.
+ * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
+ * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
+ * @param mainResource True if the this resource is the main request, not a supporting resource
+ * @param userGesture
* @param synchronous True if the load is synchronous.
* @return A newly created LoadListener object.
*/
@@ -599,7 +641,9 @@
int cacheMode,
boolean mainResource,
boolean userGesture,
- boolean synchronous) {
+ boolean synchronous,
+ String username,
+ String password) {
PerfChecker checker = new PerfChecker();
if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) {
@@ -672,7 +716,7 @@
// Create a LoadListener
LoadListener loadListener = LoadListener.getLoadListener(mContext,
this, url, loaderHandle, synchronous, isMainFramePage,
- mainResource, userGesture, postDataIdentifier);
+ mainResource, userGesture, postDataIdentifier, username, password);
mCallbackProxy.onLoadResource(url);
@@ -921,7 +965,7 @@
private native void nativePostUrl(String url, byte[] postData);
private native void nativeLoadData(String baseUrl, String data,
- String mimeType, String encoding, String failUrl);
+ String mimeType, String encoding, String historyUrl);
/**
* Stop loading the current page.
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index aeb537c..05c02b0 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -67,5 +67,9 @@
if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) {
headers.setContentDisposition(mCacheResult.contentdisposition);
}
+
+ if (!TextUtils.isEmpty(mCacheResult.crossDomain)) {
+ headers.setXPermittedCrossDomainPolicies(mCacheResult.crossDomain);
+ }
}
}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 1c59c10..d19805e 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -99,6 +99,7 @@
String location;
String encoding;
String contentdisposition;
+ String crossDomain;
// these fields are NOT saved to the database
InputStream inStream;
@@ -733,11 +734,20 @@
ret.contentdisposition = contentDisposition;
}
+ String crossDomain = headers.getXPermittedCrossDomainPolicies();
+ if (crossDomain != null) {
+ ret.crossDomain = crossDomain;
+ }
+
+ // lastModified and etag may be set back to http header. So they can't
+ // be empty string.
String lastModified = headers.getLastModified();
- if (lastModified != null) ret.lastModified = lastModified;
+ if (lastModified != null && lastModified.length() > 0) {
+ ret.lastModified = lastModified;
+ }
String etag = headers.getEtag();
- if (etag != null) ret.etag = etag;
+ if (etag != null && etag.length() > 0) ret.etag = etag;
String cacheControl = headers.getCacheControl();
if (cacheControl != null) {
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index a013a9b..92ce4f0 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -111,6 +111,7 @@
private static final int OPEN_FILE_CHOOSER = 134;
private static final int ADD_HISTORY_ITEM = 135;
private static final int HISTORY_INDEX_CHANGED = 136;
+ private static final int AUTH_CREDENTIALS = 137;
// Message triggered by the client to resume execution
private static final int NOTIFY = 200;
@@ -252,6 +253,13 @@
// 32-bit reads and writes.
switch (msg.what) {
case PAGE_STARTED:
+ // every time we start a new page, we want to reset the
+ // WebView certificate:
+ // if the new site is secure, we will reload it and get a
+ // new certificate set;
+ // if the new site is not secure, the certificate must be
+ // null, and that will be the case
+ mWebView.setCertificate(null);
if (mWebViewClient != null) {
mWebViewClient.onPageStarted(mWebView,
msg.getData().getString("url"),
@@ -390,6 +398,7 @@
notify();
}
}
+ mWebView.dismissZoomControl();
}
break;
@@ -726,6 +735,16 @@
(WebHistoryItem) msg.obj, msg.arg1);
}
break;
+ case AUTH_CREDENTIALS:
+ if (mWebViewClient != null) {
+ String host = msg.getData().getString("host");
+ String realm = msg.getData().getString("realm");
+ username = msg.getData().getString("username");
+ password = msg.getData().getString("password");
+ mWebViewClient.onReceivedHttpAuthCredentials(
+ mWebView, host, realm, username, password);
+ }
+ break;
}
}
@@ -909,6 +928,20 @@
msg.getData().putString("realm", realmName);
sendMessage(msg);
}
+
+ public void onReceivedHttpAuthCredentials(String host, String realm,
+ String username, String password) {
+ if (mWebViewClient == null) {
+ return;
+ }
+ Message msg = obtainMessage(AUTH_CREDENTIALS);
+ msg.getData().putString("host", host);
+ msg.getData().putString("realm", realm);
+ msg.getData().putString("username", username);
+ msg.getData().putString("password", password);
+ sendMessage(msg);
+ }
+
/**
* @hide - hide this because it contains a parameter of type SslError.
* SslError is located in a hidden package.
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 427ce76..74a648e 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -230,14 +230,8 @@
return 1;
}
- diff = cookie2.name.hashCode() - cookie1.name.hashCode();
- if (diff != 0) return diff;
-
- // cookie1 and cookie2 both have non-null values so we emit a
- // warning and treat them as the same.
- Log.w(LOGTAG, "Found two cookies with the same value."
- + "cookie1=" + cookie1 + " , cookie2=" + cookie2);
- return 0;
+ // Fallback to comparing the name to ensure consistent order.
+ return cookie1.name.compareTo(cookie2.name);
}
}
@@ -255,7 +249,7 @@
* first.
*
* @return CookieManager
-= */
+ */
public static synchronized CookieManager getInstance() {
if (sRef == null) {
sRef = new CookieManager();
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 303e417..906264a 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -262,9 +262,13 @@
CacheLoader cacheLoader =
new CacheLoader(mListener, result);
mListener.setCacheLoader(cacheLoader);
- // Load the cached file in a separate thread
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget();
+ if (mListener.isSynchronous()) {
+ cacheLoader.load();
+ } else {
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget();
+ }
}
/*
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index 1c17575..6a8d88d 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.util.Log;
import java.util.ListIterator;
import java.util.LinkedList;
@@ -52,6 +53,14 @@
private static final int AUTH_PROCEED = 100;
private static final int AUTH_CANCEL = 200;
+ // Use to synchronize when making synchronous calls to
+ // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
+ // both the lock and the state, because Boolean is immutable.
+ Object mRequestInFlightLock = new Object();
+ boolean mRequestInFlight;
+ String mUsername;
+ String mPassword;
+
/**
* Creates a new HTTP authentication handler with an empty
* loader queue
@@ -70,6 +79,7 @@
synchronized (mLoaderQueue) {
loader = mLoaderQueue.poll();
}
+ assert(loader.isSynchronous() == false);
switch (msg.what) {
case AUTH_PROCEED:
@@ -87,25 +97,70 @@
processNextLoader();
}
+ /**
+ * Helper method used to unblock handleAuthRequest(), which in the case of a
+ * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
+ * call back to either proceed() or cancel().
+ *
+ * @param username The username to use for authentication
+ * @param password The password to use for authentication
+ * @return True if the request is synchronous and handleAuthRequest() has
+ * been unblocked
+ */
+ private boolean handleResponseForSynchronousRequest(String username, String password) {
+ LoadListener loader = null;
+ synchronized (mLoaderQueue) {
+ loader = mLoaderQueue.peek();
+ }
+ if (loader.isSynchronous()) {
+ mUsername = username;
+ mPassword = password;
+ return true;
+ }
+ return false;
+ }
+
+ private void signalRequestComplete() {
+ synchronized (mRequestInFlightLock) {
+ assert(mRequestInFlight);
+ mRequestInFlight = false;
+ mRequestInFlightLock.notify();
+ }
+ }
/**
* Proceed with the authorization with the given credentials
*
+ * May be called on the UI thread, rather than the WebCore thread.
+ *
* @param username The username to use for authentication
* @param password The password to use for authentication
*/
public void proceed(String username, String password) {
+ if (handleResponseForSynchronousRequest(username, password)) {
+ signalRequestComplete();
+ return;
+ }
Message msg = obtainMessage(AUTH_PROCEED);
msg.getData().putString("username", username);
msg.getData().putString("password", password);
sendMessage(msg);
+ signalRequestComplete();
}
/**
* Cancel the authorization request
+ *
+ * May be called on the UI thread, rather than the WebCore thread.
+ *
*/
public void cancel() {
+ if (handleResponseForSynchronousRequest(null, null)) {
+ signalRequestComplete();
+ return;
+ }
sendMessage(obtainMessage(AUTH_CANCEL));
+ signalRequestComplete();
}
/**
@@ -132,6 +187,34 @@
* authentication request
*/
/* package */ void handleAuthRequest(LoadListener loader) {
+ // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
+ // the request is synchronous, we must block here until we have a
+ // response.
+ if (loader.isSynchronous()) {
+ // If there's a request in flight, wait for it to complete. The
+ // response will queue a message on this thread.
+ waitForRequestToComplete();
+ // Make a request to the proxy for this request, jumping the queue.
+ // We use the queue so that the loader is present in
+ // useHttpAuthUsernamePassword().
+ synchronized (mLoaderQueue) {
+ mLoaderQueue.addFirst(loader);
+ }
+ processNextLoader();
+ // Wait for this request to complete.
+ waitForRequestToComplete();
+ // Pop the loader from the queue.
+ synchronized (mLoaderQueue) {
+ assert(mLoaderQueue.peek() == loader);
+ mLoaderQueue.poll();
+ }
+ // Call back.
+ loader.handleAuthResponse(mUsername, mPassword);
+ // The message queued by the response from the last asynchronous
+ // request, if present, will start the next request.
+ return;
+ }
+
boolean processNext = false;
synchronized (mLoaderQueue) {
@@ -146,6 +229,21 @@
}
/**
+ * Wait for the request in flight, if any, to complete
+ */
+ private void waitForRequestToComplete() {
+ synchronized (mRequestInFlightLock) {
+ while (mRequestInFlight) {
+ try {
+ mRequestInFlightLock.wait();
+ } catch(InterruptedException e) {
+ Log.e(LOGTAG, "Interrupted while waiting for request to complete");
+ }
+ }
+ }
+ }
+
+ /**
* Process the next loader in the queue (helper method)
*/
private void processNextLoader() {
@@ -154,6 +252,11 @@
loader = mLoaderQueue.peek();
}
if (loader != null) {
+ synchronized (mRequestInFlightLock) {
+ assert(mRequestInFlight == false);
+ mRequestInFlight = true;
+ }
+
CallbackProxy proxy = loader.getFrame().getCallbackProxy();
String hostname = loader.proxyAuthenticate() ?
@@ -164,4 +267,14 @@
proxy.onReceivedHttpAuthRequest(this, hostname, realm);
}
}
+
+ /**
+ * Informs the proxy of a new set of credentials.
+ * @hide Pending API council review
+ */
+ public static void onReceivedCredentials(LoadListener loader,
+ String host, String realm, String username, String password) {
+ CallbackProxy proxy = loader.getFrame().getCallbackProxy();
+ proxy.onReceivedHttpAuthCredentials(host, realm, username, password);
+ }
}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 4bdab0a..1dfadde 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -121,11 +121,15 @@
// Does this loader correspond to the main-frame top-level page?
private boolean mIsMainPageLoader;
+ // Does this loader correspond to the main content (as opposed to a supporting resource)
private final boolean mIsMainResourceLoader;
private final boolean mUserGesture;
private Headers mHeaders;
+ private final String mUsername;
+ private final String mPassword;
+
// =========================================================================
// Public functions
// =========================================================================
@@ -133,11 +137,13 @@
public static LoadListener getLoadListener(Context context,
BrowserFrame frame, String url, int nativeLoader,
boolean synchronous, boolean isMainPageLoader,
- boolean isMainResource, boolean userGesture, long postIdentifier) {
+ boolean isMainResource, boolean userGesture, long postIdentifier,
+ String username, String password) {
sNativeLoaderCount += 1;
return new LoadListener(context, frame, url, nativeLoader, synchronous,
- isMainPageLoader, isMainResource, userGesture, postIdentifier);
+ isMainPageLoader, isMainResource, userGesture, postIdentifier,
+ username, password);
}
public static int getNativeLoaderCount() {
@@ -146,7 +152,8 @@
LoadListener(Context context, BrowserFrame frame, String url,
int nativeLoader, boolean synchronous, boolean isMainPageLoader,
- boolean isMainResource, boolean userGesture, long postIdentifier) {
+ boolean isMainResource, boolean userGesture, long postIdentifier,
+ String username, String password) {
if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener constructor url=" + url);
}
@@ -162,6 +169,8 @@
mIsMainResourceLoader = isMainResource;
mUserGesture = userGesture;
mPostIdentifier = postIdentifier;
+ mUsername = username;
+ mPassword = password;
}
/**
@@ -401,7 +410,7 @@
// if we tried to authenticate ourselves last time
if (mAuthHeader != null) {
- // we failed, if we must to authenticate again now and
+ // we failed, if we must authenticate again now and
// we have a proxy-ness match
mAuthFailed = (mustAuthenticate &&
isProxyAuthRequest == mAuthHeader.isProxy());
@@ -520,23 +529,28 @@
}
/**
- * Implementation of certificate handler for EventHandler.
- * Called every time a resource is loaded via a secure
- * connection. In this context, can be called multiple
- * times if we have redirects
- * @param certificate The SSL certifcate
- * IMPORTANT: as this is called from network thread, can't call native
- * directly
+ * Implementation of certificate handler for EventHandler. Called
+ * before a resource is requested. In this context, can be called
+ * multiple times if we have redirects
+ *
+ * IMPORTANT: as this is called from network thread, can't call
+ * native directly
+ *
+ * @param certificate The SSL certifcate or null if the request
+ * was not secure
*/
public void certificate(SslCertificate certificate) {
+ if (DebugFlags.LOAD_LISTENER) {
+ Log.v(LOGTAG, "LoadListener.certificate: " + certificate);
+ }
sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate));
}
// Handle the certificate on the WebCore thread.
private void handleCertificate(SslCertificate certificate) {
- // if this is the top-most main-frame page loader
- if (mIsMainPageLoader) {
- // update the browser frame (ie, the main frame)
+ // if this is main resource of the top frame
+ if (mIsMainPageLoader && mIsMainResourceLoader) {
+ // update the browser frame with certificate
mBrowserFrame.certificate(certificate);
}
}
@@ -646,7 +660,18 @@
if (mAuthHeader != null &&
(Network.getInstance(mContext).isValidProxySet() ||
!mAuthHeader.isProxy())) {
- Network.getInstance(mContext).handleAuthRequest(this);
+ // If this is the first attempt to authenticate, try again with the username and
+ // password supplied in the URL, if present.
+ if (!mAuthFailed && mUsername != null && mPassword != null) {
+ String host = mAuthHeader.isProxy() ?
+ Network.getInstance(mContext).getProxyHostname() :
+ mUri.mHost;
+ HttpAuthHandler.onReceivedCredentials(this, host,
+ mAuthHeader.getRealm(), mUsername, mPassword);
+ makeAuthResponse(mUsername, mPassword);
+ } else {
+ Network.getInstance(mContext).handleAuthRequest(this);
+ }
return;
}
break; // use default
@@ -656,10 +681,14 @@
// ask for it, so make sure we have a valid CacheLoader
// before calling it.
if (mCacheLoader != null) {
- // Load the cached file in a separate thread
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
- .sendToTarget();
+ if (isSynchronous()) {
+ mCacheLoader.load();
+ } else {
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+ .sendToTarget();
+ }
mFromCache = true;
if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener cache load url=" + url());
@@ -718,10 +747,14 @@
Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
"and usable: " + url());
}
- // Load the cached file in a separate thread
- WebViewWorker.getHandler().obtainMessage(
- WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
- .sendToTarget();
+ if (isSynchronous()) {
+ mCacheLoader.load();
+ } else {
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+ .sendToTarget();
+ }
mFromCache = true;
return true;
}
@@ -830,37 +863,8 @@
+ " username: " + username
+ " password: " + password);
}
-
- // create and queue an authentication-response
if (username != null && password != null) {
- if (mAuthHeader != null && mRequestHandle != null) {
- mAuthHeader.setUsername(username);
- mAuthHeader.setPassword(password);
-
- int scheme = mAuthHeader.getScheme();
- if (scheme == HttpAuthHeader.BASIC) {
- // create a basic response
- boolean isProxy = mAuthHeader.isProxy();
-
- mRequestHandle.setupBasicAuthResponse(isProxy,
- username, password);
- } else {
- if (scheme == HttpAuthHeader.DIGEST) {
- // create a digest response
- boolean isProxy = mAuthHeader.isProxy();
-
- String realm = mAuthHeader.getRealm();
- String nonce = mAuthHeader.getNonce();
- String qop = mAuthHeader.getQop();
- String algorithm = mAuthHeader.getAlgorithm();
- String opaque = mAuthHeader.getOpaque();
-
- mRequestHandle.setupDigestAuthResponse
- (isProxy, username, password, realm,
- nonce, qop, algorithm, opaque);
- }
- }
- }
+ makeAuthResponse(username, password);
} else {
// Commit whatever data we have and tear down the loader.
commitLoad();
@@ -868,6 +872,35 @@
}
}
+ void makeAuthResponse(String username, String password) {
+ if (mAuthHeader == null || mRequestHandle == null) {
+ return;
+ }
+
+ mAuthHeader.setUsername(username);
+ mAuthHeader.setPassword(password);
+
+ int scheme = mAuthHeader.getScheme();
+ if (scheme == HttpAuthHeader.BASIC) {
+ // create a basic response
+ boolean isProxy = mAuthHeader.isProxy();
+
+ mRequestHandle.setupBasicAuthResponse(isProxy, username, password);
+ } else if (scheme == HttpAuthHeader.DIGEST) {
+ // create a digest response
+ boolean isProxy = mAuthHeader.isProxy();
+
+ String realm = mAuthHeader.getRealm();
+ String nonce = mAuthHeader.getNonce();
+ String qop = mAuthHeader.getQop();
+ String algorithm = mAuthHeader.getAlgorithm();
+ String opaque = mAuthHeader.getOpaque();
+
+ mRequestHandle.setupDigestAuthResponse(isProxy, username, password,
+ realm, nonce, qop, algorithm, opaque);
+ }
+ }
+
/**
* This is called when a request can be satisfied by the cache, however,
* the cache result could be a redirect. In this case we need to issue
@@ -1184,6 +1217,16 @@
}
/**
+ * Pause the load. For example, if a plugin is unable to accept more data,
+ * we pause reading from the request. Called directly from the WebCore thread.
+ */
+ void pauseLoad(boolean pause) {
+ if (mRequestHandle != null) {
+ mRequestHandle.pauseRequest(pause);
+ }
+ }
+
+ /**
* Cancel a request.
* FIXME: This will only work if the request has yet to be handled. This
* is in no way guarenteed if requests are served in a separate thread.
@@ -1575,7 +1618,7 @@
// from http thread. Then it is called again from WebViewCore thread
// after the load is completed. So make sure the queue is cleared but
// don't set it to null.
- for (int size = mMessageQueue.size(); size > 0; size--) {
+ while (!mMessageQueue.isEmpty()) {
handleMessage(mMessageQueue.remove(0));
}
}
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 3ed9851..034c88a 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -484,6 +484,7 @@
sMimeTypeMap.loadEntry("text/x-tex", "cls");
sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs");
sMimeTypeMap.loadEntry("text/x-vcard", "vcf");
+ sMimeTypeMap.loadEntry("video/3gpp", "3gpp");
sMimeTypeMap.loadEntry("video/3gpp", "3gp");
sMimeTypeMap.loadEntry("video/3gpp", "3g2");
sMimeTypeMap.loadEntry("video/dl", "dl");
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index df7d0c4..cdcb662 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -165,7 +165,6 @@
continue;
}
-/* temporarily disable signatures checking
// check to ensure the plugin is properly signed
Signature signatures[] = pkgInfo.signatures;
if (signatures == null) {
@@ -185,7 +184,7 @@
continue;
}
}
-*/
+
// determine the type of plugin from the manifest
if (serviceInfo.metaData == null) {
Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined");
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
index 02f452b..23cf6b8 100644
--- a/core/java/android/webkit/ViewManager.java
+++ b/core/java/android/webkit/ViewManager.java
@@ -18,6 +18,7 @@
import android.view.SurfaceView;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AbsoluteLayout;
import java.util.ArrayList;
@@ -27,9 +28,10 @@
private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
private boolean mHidden;
private boolean mReadyToDraw;
+ private boolean mZoomInProgress = false;
// Threshold at which a surface is prevented from further increasing in size
- private static final int MAX_SURFACE_THRESHOLD = 1000000;
+ private final int MAX_SURFACE_THRESHOLD;
class ChildView {
int x;
@@ -38,11 +40,6 @@
int height;
View mView; // generic view to show
- /* set to true if the view is a surface and it has exceeded the pixel
- threshold specified in MAX_SURFACE_THRESHOLD.
- */
- boolean isFixedSize = false;
-
ChildView() {
}
@@ -65,19 +62,17 @@
// already attached, just set the new LayoutParams,
// otherwise attach the view and add it to the list of
// children.
- AbsoluteLayout.LayoutParams lp = computeLayout(ChildView.this);
+ requestLayout(ChildView.this);
- if (mView.getParent() != null) {
- mView.setLayoutParams(lp);
- } else {
- attachViewOnUIThread(lp);
+ if (mView.getParent() == null) {
+ attachViewOnUIThread();
}
}
});
}
- private void attachViewOnUIThread(AbsoluteLayout.LayoutParams lp) {
- mWebView.addView(mView, lp);
+ private void attachViewOnUIThread() {
+ mWebView.addView(mView);
mChildren.add(this);
if (!mReadyToDraw) {
mView.setVisibility(View.GONE);
@@ -103,6 +98,14 @@
ViewManager(WebView w) {
mWebView = w;
+
+ int pixelArea = w.getResources().getDisplayMetrics().widthPixels *
+ w.getResources().getDisplayMetrics().heightPixels;
+ /* set the threshold to be 275% larger than the screen size. The
+ percentage is simply an estimation and is not based on anything but
+ basic trial-and-error tests run on multiple devices.
+ */
+ MAX_SURFACE_THRESHOLD = (int)(pixelArea * 2.75);
}
ChildView createView() {
@@ -137,34 +140,87 @@
/**
* This should only be called from the UI thread.
*/
- private AbsoluteLayout.LayoutParams computeLayout(ChildView v) {
+ private void requestLayout(ChildView v) {
- // if the surface has exceed a predefined threshold then fix the size
- // of the surface.
- if (!v.isFixedSize && (v.width * v.height) > MAX_SURFACE_THRESHOLD
- && v.mView instanceof SurfaceView) {
- ((SurfaceView)v.mView).getHolder().setFixedSize(v.width, v.height);
- v.isFixedSize = true;
+ int width = ctvD(v.width);
+ int height = ctvD(v.height);
+ int x = ctvX(v.x);
+ int y = ctvY(v.y);
+
+ AbsoluteLayout.LayoutParams lp;
+ ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams();
+
+ if (layoutParams instanceof AbsoluteLayout.LayoutParams) {
+ lp = (AbsoluteLayout.LayoutParams) layoutParams;
+ lp.width = width;
+ lp.height = height;
+ lp.x = x;
+ lp.y = y;
+ } else {
+ lp = new AbsoluteLayout.LayoutParams(width, height, x, y);
}
- AbsoluteLayout.LayoutParams lp =
- (AbsoluteLayout.LayoutParams) v.mView.getLayoutParams();
+ // apply the layout to the view
+ v.mView.setLayoutParams(lp);
- if (lp == null)
- lp = new AbsoluteLayout.LayoutParams(ctvD(v.width), ctvD(v.height),
- ctvX(v.x), ctvY(v.y));
- else {
- lp.width = ctvD(v.width);
- lp.height = ctvD(v.height);
- lp.x = ctvX(v.x);
- lp.y = ctvY(v.y);
+ if(v.mView instanceof SurfaceView) {
+
+ final SurfaceView sView = (SurfaceView) v.mView;
+ boolean exceedThreshold = (width * height) > MAX_SURFACE_THRESHOLD;
+
+ /* If the surface has exceeded a predefined threshold or the webview
+ * is currently zoom then fix the size of the surface.
+ *
+ * NOTE: plugins (e.g. Flash) must not explicitly fix the size of
+ * their surface. The logic below will result in unexpected behavior
+ * for the plugin if they attempt to fix the size of the surface.
+ */
+ if (!sView.isFixedSize() && (exceedThreshold || mZoomInProgress)) {
+ sView.getHolder().setFixedSize(width, height);
+ }
+ else if (sView.isFixedSize() && !exceedThreshold && !mZoomInProgress) {
+ /* The changing of visibility is a hack to get around a bug in
+ * the framework that causes the surface to revert to the size
+ * it was prior to being fixed before it redraws using the
+ * values currently in its layout.
+ *
+ * The surface is destroyed when it is set to invisible and then
+ * recreated at the new dimensions when it is made visible. The
+ * same destroy/create step occurs without the change in
+ * visibility, but then exhibits the behavior described in the
+ * previous paragraph.
+ */
+ if (sView.getVisibility() == View.VISIBLE) {
+ sView.setVisibility(View.INVISIBLE);
+ sView.getHolder().setSizeFromLayout();
+ sView.setVisibility(View.VISIBLE);
+ } else {
+ sView.getHolder().setSizeFromLayout();
+ }
+ }
+ else if (sView.isFixedSize() && exceedThreshold) {
+ sView.requestLayout();
+ }
}
- return lp;
+ }
+
+ void startZoom() {
+ mZoomInProgress = true;
+ for (ChildView v : mChildren) {
+ requestLayout(v);
+ }
+ }
+
+ void endZoom() {
+ mZoomInProgress = false;
+ for (ChildView v : mChildren) {
+ requestLayout(v);
+ }
}
void scaleAll() {
for (ChildView v : mChildren) {
- v.mView.setLayoutParams(computeLayout(v));
+ requestLayout(v);
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index fb15f78..1b801d4 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -120,6 +120,21 @@
LOW
}
+ /**
+ * The plugin state effects how plugins are treated on a page. ON means
+ * that any object will be loaded even if a plugin does not exist to handle
+ * the content. ON_DEMAND means that if there is a plugin installed that
+ * can handle the content, a placeholder is shown until the user clicks on
+ * the placeholder. Once clicked, the plugin will be enabled on the page.
+ * OFF means that all plugins will be turned off and any fallback content
+ * will be used.
+ */
+ public enum PluginState {
+ ON,
+ ON_DEMAND,
+ OFF
+ }
+
// WebView associated with this WebSettings.
private WebView mWebView;
// BrowserFrame used to access the native frame pointer.
@@ -157,7 +172,7 @@
private boolean mBlockNetworkImage = false;
private boolean mBlockNetworkLoads;
private boolean mJavaScriptEnabled = false;
- private boolean mPluginsEnabled = false;
+ private PluginState mPluginState = PluginState.OFF;
private boolean mJavaScriptCanOpenWindowsAutomatically = false;
private boolean mUseDoubleTree = false;
private boolean mUseWideViewport = false;
@@ -192,7 +207,7 @@
private boolean mBuiltInZoomControls = false;
private boolean mAllowFileAccess = true;
private boolean mLoadWithOverviewMode = false;
- private boolean mUseSystemOverscrollBackground = false;
+ private boolean mUseWebViewBackgroundOverscrollBackground = true;
// private WebSettings, not accessible by the host activity
static private int mDoubleTapToastCount = 3;
@@ -471,20 +486,20 @@
}
/**
- * Set whether the WebView uses system background for over scroll
- * background. If false, it will use the WebView's background. Default is
- * false.
+ * Set whether the WebView uses its background for over scroll background.
+ * If true, it will use the WebView's background. If false, it will use an
+ * internal pattern. Default is true.
*/
- public void setUseSystemOverscrollBackground(boolean system) {
- mUseSystemOverscrollBackground = system;
+ public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+ mUseWebViewBackgroundOverscrollBackground = view;
}
/**
- * Returns true if this WebView uses system background instead of WebView
- * background for over scroll background.
+ * Returns true if this WebView uses WebView's background instead of
+ * internal pattern for over scroll background.
*/
- public boolean getUseSystemOverscrollBackground() {
- return mUseSystemOverscrollBackground;
+ public boolean getUseWebViewBackgroundForOverscrollBackground() {
+ return mUseWebViewBackgroundOverscrollBackground;
}
/**
@@ -1011,10 +1026,23 @@
/**
* Tell the WebView to enable plugins.
* @param flag True if the WebView should load plugins.
+ * @deprecated This method has been deprecated in favor of
+ * {@link #setPluginState}
*/
public synchronized void setPluginsEnabled(boolean flag) {
- if (mPluginsEnabled != flag) {
- mPluginsEnabled = flag;
+ setPluginState(PluginState.ON);
+ }
+
+ /**
+ * Tell the WebView to enable, disable, or have plugins on demand. On
+ * demand mode means that if a plugin exists that can handle the embedded
+ * content, a placeholder icon will be shown instead of the plugin. When
+ * the placeholder is clicked, the plugin will be enabled.
+ * @param state One of the PluginState values.
+ */
+ public synchronized void setPluginState(PluginState state) {
+ if (mPluginState != state) {
+ mPluginState = state;
postSync();
}
}
@@ -1176,9 +1204,18 @@
/**
* Return true if plugins are enabled.
* @return True if plugins are enabled.
+ * @deprecated This method has been replaced by {@link #getPluginState}
*/
public synchronized boolean getPluginsEnabled() {
- return mPluginsEnabled;
+ return mPluginState == PluginState.ON;
+ }
+
+ /**
+ * Return the current plugin state.
+ * @return A value corresponding to the enum PluginState.
+ */
+ public synchronized PluginState getPluginState() {
+ return mPluginState;
}
/**
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 9314d7b..5345879 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -340,6 +340,15 @@
}
/**
+ * Sets the maximum size of the ApplicationCache.
+ * This should only ever be called on the WebKit thread.
+ * @hide Pending API council approval
+ */
+ public void setAppCacheMaximumSize(long size) {
+ nativeSetAppCacheMaximumSize(size);
+ }
+
+ /**
* Utility function to send a message to our handler
*/
private synchronized void postMessage(Message msg) {
@@ -402,4 +411,5 @@
private static native void nativeSetQuotaForOrigin(String origin, long quota);
private static native void nativeDeleteOrigin(String origin);
private static native void nativeDeleteAllData();
+ private static native void nativeSetAppCacheMaximumSize(long size);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 52bc8be1..ff1a685 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -468,6 +468,7 @@
private OverScroller mScroller;
private boolean mInOverScrollMode = false;
private static Paint mOverScrollBackground;
+ private static Paint mOverScrollBorder;
private boolean mWrapContent;
private static final int MOTIONLESS_FALSE = 0;
@@ -536,9 +537,11 @@
static final int RETURN_LABEL = 125;
static final int FIND_AGAIN = 126;
static final int CENTER_FIT_RECT = 127;
+ static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
- private static final int LAST_PACKAGE_MSG_ID = CENTER_FIT_RECT;
+ private static final int LAST_PACKAGE_MSG_ID
+ = REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID;
static final String[] HandlerPrivateDebugString = {
"REMEMBER_PASSWORD", // = 1;
@@ -580,7 +583,8 @@
"SET_ROOT_LAYER_MSG_ID", // = 124;
"RETURN_LABEL", // = 125;
"FIND_AGAIN", // = 126;
- "CENTER_FIT_RECT" // = 127;
+ "CENTER_FIT_RECT", // = 127;
+ "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID" // = 128;
};
// If the site doesn't use the viewport meta tag to specify the viewport,
@@ -759,6 +763,8 @@
private ExtendedZoomControls mZoomControls;
private Runnable mZoomControlRunnable;
+ // mZoomButtonsController will be lazy initialized in
+ // getZoomButtonsController() to get better performance.
private ZoomButtonsController mZoomButtonsController;
// These keep track of the center point of the zoom. They are used to
@@ -840,18 +846,6 @@
mDatabase = WebViewDatabase.getInstance(context);
mScroller = new OverScroller(context);
- mZoomButtonsController = new ZoomButtonsController(this);
- mZoomButtonsController.setOnZoomListener(mZoomListener);
- // ZoomButtonsController positions the buttons at the bottom, but in
- // the middle. Change their layout parameters so they appear on the
- // right.
- View controls = mZoomButtonsController.getZoomControls();
- ViewGroup.LayoutParams params = controls.getLayoutParams();
- if (params instanceof FrameLayout.LayoutParams) {
- FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
- params;
- frameParams.gravity = Gravity.RIGHT;
- }
updateMultiTouchSupport(context);
}
@@ -871,15 +865,16 @@
private void updateZoomButtonsEnabled() {
boolean canZoomIn = mActualScale < mMaxZoomScale;
boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
+ ZoomButtonsController controller = getZoomButtonsController();
if (!canZoomIn && !canZoomOut) {
// Hide the zoom in and out buttons, as well as the fit to page
// button, if the page cannot zoom
- mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
+ controller.getZoomControls().setVisibility(View.GONE);
} else {
// Set each one individually, as a page may be able to zoom in
// or out.
- mZoomButtonsController.setZoomInEnabled(canZoomIn);
- mZoomButtonsController.setZoomOutEnabled(canZoomOut);
+ controller.setZoomInEnabled(canZoomIn);
+ controller.setZoomOutEnabled(canZoomOut);
}
}
@@ -1085,6 +1080,9 @@
* Sets the SSL certificate for the main top-level page.
*/
public void setCertificate(SslCertificate certificate) {
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "setCertificate=" + certificate);
+ }
// here, the certificate can be null (if the site is not secure)
mCertificate = certificate;
}
@@ -1477,10 +1475,8 @@
/**
* Load the given data into the WebView, use the provided URL as the base
* URL for the content. The base URL is the URL that represents the page
- * that is loaded through this interface. As such, it is used for the
- * history entry and to resolve any relative URLs. The failUrl is used if
- * browser fails to load the data provided. If it is empty or null, and the
- * load fails, then no history entry is created.
+ * that is loaded through this interface. As such, it is used to resolve any
+ * relative URLs. The historyUrl is used for the history entry.
* <p>
* Note for post 1.0. Due to the change in the WebKit, the access to asset
* files through "file:///android_asset/" for the sub resources is more
@@ -1495,10 +1491,10 @@
* @param mimeType The MIMEType of the data. i.e. text/html. If null,
* defaults to "text/html"
* @param encoding The encoding of the data. i.e. utf-8, us-ascii
- * @param failUrl URL to use if the content fails to load or null.
+ * @param historyUrl URL to use as the history entry. Can be null.
*/
public void loadDataWithBaseURL(String baseUrl, String data,
- String mimeType, String encoding, String failUrl) {
+ String mimeType, String encoding, String historyUrl) {
if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
loadData(data, mimeType, encoding);
@@ -1510,7 +1506,7 @@
arg.mData = data;
arg.mMimeType = mimeType;
arg.mEncoding = encoding;
- arg.mFailUrl = failUrl;
+ arg.mHistoryUrl = historyUrl;
mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
clearTextEntry(false);
}
@@ -1608,13 +1604,6 @@
}
private void goBackOrForward(int steps, boolean ignoreSnapshot) {
- // every time we go back or forward, we want to reset the
- // WebView certificate:
- // if the new site is secure, we will reload it and get a
- // new certificate set;
- // if the new site is not secure, the certificate must be
- // null, and that will be the case
- mCertificate = null;
if (steps != 0) {
clearTextEntry(false);
mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
@@ -1669,7 +1658,7 @@
}
nativeClearCursor(); // start next trackball movement from page edge
if (bottom) {
- return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
+ return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
}
// Page down.
int h = getHeight();
@@ -1762,7 +1751,7 @@
}
clearTextEntry(false);
if (getSettings().getBuiltInZoomControls()) {
- mZoomButtonsController.setVisible(true);
+ getZoomButtonsController().setVisible(true);
} else {
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
mPrivateHandler.postDelayed(mZoomControlRunnable,
@@ -1901,21 +1890,14 @@
// Expects x in view coordinates
private int pinLocX(int x) {
if (mInOverScrollMode) return x;
- return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
+ return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
}
// Expects y in view coordinates
private int pinLocY(int y) {
if (mInOverScrollMode) return y;
- int titleH = getTitleHeight();
- // if the titlebar is still visible, just pin against 0
- if (y <= titleH) {
- return Math.max(y, 0);
- }
- // convert to 0-based coordinate (subtract the title height)
- // pin(), and then add the title height back in
- return pinLoc(y - titleH, getViewHeight(),
- computeVerticalScrollRange()) + titleH;
+ return pinLoc(y, getViewHeightWithTitle(),
+ computeRealVerticalScrollRange() + getTitleHeight());
}
/**
@@ -2266,8 +2248,7 @@
return false;
}
- @Override
- protected int computeHorizontalScrollRange() {
+ private int computeRealHorizontalScrollRange() {
if (mDrawHistory) {
return mHistoryWidth;
} else {
@@ -2277,7 +2258,27 @@
}
@Override
- protected int computeVerticalScrollRange() {
+ protected int computeHorizontalScrollRange() {
+ int range = computeRealHorizontalScrollRange();
+
+ // Adjust reported range if overscrolled to compress the scroll bars
+ final int scrollX = mScrollX;
+ final int overscrollRight = computeMaxScrollX();
+ if (scrollX < 0) {
+ range -= scrollX;
+ } else if (scrollX > overscrollRight) {
+ range += scrollX - overscrollRight;
+ }
+
+ return range;
+ }
+
+ @Override
+ protected int computeHorizontalScrollOffset() {
+ return Math.max(mScrollX, 0);
+ }
+
+ private int computeRealVerticalScrollRange() {
if (mDrawHistory) {
return mHistoryHeight;
} else {
@@ -2287,6 +2288,22 @@
}
@Override
+ protected int computeVerticalScrollRange() {
+ int range = computeRealVerticalScrollRange();
+
+ // Adjust reported range if overscrolled to compress the scroll bars
+ final int scrollY = mScrollY;
+ final int overscrollBottom = computeMaxScrollY();
+ if (scrollY < 0) {
+ range -= scrollY;
+ } else if (scrollY > overscrollBottom) {
+ range += scrollY - overscrollBottom;
+ }
+
+ return range;
+ }
+
+ @Override
protected int computeVerticalScrollOffset() {
return Math.max(mScrollY - getTitleHeight(), 0);
}
@@ -2305,20 +2322,13 @@
scrollBar.draw(canvas);
}
- private boolean canOverscrollHorizontally() {
- return (Math.abs(mMinZoomScale - mMaxZoomScale) >= MINIMUM_SCALE_INCREMENT)
- && getSettings().supportZoom()
- && getSettings().getUseWideViewPort();
- }
-
@Override
protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY) {
mInOverScrollMode = false;
int maxX = computeMaxScrollX();
- if (maxX == 0 && !canOverscrollHorizontally()) {
- // do not over scroll x if the page just fits the screen and it
- // can't zoom or the view doesn't use wide viewport
+ if (maxX == 0) {
+ // do not over scroll x if the page just fits the screen
scrollX = pinLocX(scrollX);
} else if (scrollX < 0 || scrollX > maxX) {
mInOverScrollMode = true;
@@ -2633,6 +2643,9 @@
* @hide
*/
public void notifyFindDialogDismissed() {
+ if (mWebViewCore == null) {
+ return;
+ }
clearMatches();
setFindIsUp(false);
recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
@@ -2677,7 +2690,7 @@
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY,
computeMaxScrollX(), computeMaxScrollY(),
- getViewWidth() / 3, getViewHeight() / 3);
+ getViewWidth() / 3, getViewHeight() / 3, false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
} else {
@@ -3131,29 +3144,35 @@
}
int saveCount = canvas.save();
- if (mInOverScrollMode
- && getSettings().getUseSystemOverscrollBackground()) {
+ if (mInOverScrollMode && !getSettings()
+ .getUseWebViewBackgroundForOverscrollBackground()) {
if (mOverScrollBackground == null) {
mOverScrollBackground = new Paint();
Bitmap bm = BitmapFactory.decodeResource(
mContext.getResources(),
- com.android.internal.R.drawable.pattern_underwear);
+ com.android.internal.R.drawable.status_bar_background);
mOverScrollBackground.setShader(new BitmapShader(bm,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ mOverScrollBorder = new Paint();
+ mOverScrollBorder.setStyle(Paint.Style.STROKE);
+ mOverScrollBorder.setStrokeWidth(0);
+ mOverScrollBorder.setColor(0xffbbbbbb);
}
+
int top = getTitleHeight();
+ int right = computeRealHorizontalScrollRange();
+ int bottom = top + computeRealVerticalScrollRange();
// first draw the background and anchor to the top of the view
canvas.save();
canvas.translate(mScrollX, mScrollY);
- canvas.clipRect(-mScrollX, top - mScrollY,
- computeHorizontalScrollRange() - mScrollX, top
- + computeVerticalScrollRange() - mScrollY,
- Region.Op.DIFFERENCE);
+ canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
+ - mScrollY, Region.Op.DIFFERENCE);
canvas.drawPaint(mOverScrollBackground);
canvas.restore();
+ // then draw the border
+ canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
// next clip the region for the content
- canvas.clipRect(0, top, computeHorizontalScrollRange(), top
- + computeVerticalScrollRange());
+ canvas.clipRect(0, top, right, bottom);
}
if (mTitleBar != null) {
canvas.translate(0, (int) mTitleBar.getHeight());
@@ -3858,7 +3877,6 @@
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
if (nativeFocusIsPlugin()) {
mShiftIsPressed = false;
- return true;
} else if (commitCopy()) {
return true;
}
@@ -3995,8 +4013,7 @@
protected void onDetachedFromWindow() {
clearTextEntry(false);
super.onDetachedFromWindow();
- // Clean up the zoom controller
- mZoomButtonsController.setVisible(false);
+ dismissZoomControl();
}
/**
@@ -4046,7 +4063,7 @@
// false for the first parameter
}
} else {
- if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
+ if (getSettings().getBuiltInZoomControls() && !getZoomButtonsController().isVisible()) {
/*
* The zoom controls come in their own window, so our window
* loses focus. Our policy is to not draw the cursor ring if
@@ -4195,6 +4212,8 @@
}
}
+ dismissZoomControl();
+
// onSizeChanged() is called during WebView layout. And any
// requestLayout() is blocked during layout. As setNewZoomScale() will
// call its child View to reposition itself through ViewManager's
@@ -4207,12 +4226,14 @@
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
- sendOurVisibleRect();
- // update WebKit if visible title bar height changed. The logic is same
- // as getVisibleTitleHeight.
- int titleHeight = getTitleHeight();
- if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
- sendViewSizeZoom();
+ if (!mInOverScrollMode) {
+ sendOurVisibleRect();
+ // update WebKit if visible title bar height changed. The logic is same
+ // as getVisibleTitleHeight.
+ int titleHeight = getTitleHeight();
+ if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
+ sendViewSizeZoom();
+ }
}
}
@@ -4282,7 +4303,7 @@
public DragTrackerHandler(float x, float y, DragTracker proxy) {
mProxy = proxy;
- int docBottom = computeVerticalScrollRange() + getTitleHeight();
+ int docBottom = computeRealVerticalScrollRange() + getTitleHeight();
int viewTop = getScrollY();
int viewBottom = viewTop + getHeight();
@@ -4295,7 +4316,7 @@
" up/down= " + mMinDY + " " + mMaxDY);
}
- int docRight = computeHorizontalScrollRange();
+ int docRight = computeRealHorizontalScrollRange();
int viewLeft = getScrollX();
int viewRight = viewLeft + getWidth();
mStartX = x;
@@ -4462,9 +4483,7 @@
public boolean onScaleBegin(ScaleGestureDetector detector) {
// cancel the single touch handling
cancelTouch();
- if (mZoomButtonsController.isVisible()) {
- mZoomButtonsController.setVisible(false);
- }
+ dismissZoomControl();
// reset the zoom overview mode so that the page won't auto grow
mInZoomOverview = false;
// If it is in password mode, turn it off so it does not draw
@@ -4472,6 +4491,9 @@
if (inEditingMode() && nativeFocusCandidateIsPassword()) {
mWebTextView.setInPassword(false);
}
+
+ mViewManager.startZoom();
+
return true;
}
@@ -4505,6 +4527,8 @@
mConfirmMove = true;
startTouch(detector.getFocusX(), detector.getFocusY(),
mLastTouchTime);
+
+ mViewManager.endZoom();
}
public boolean onScale(ScaleGestureDetector detector) {
@@ -4529,9 +4553,14 @@
}
private boolean hitFocusedPlugin(int contentX, int contentY) {
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
+ Rect r = nativeFocusNodeBounds();
+ Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
+ + ", " + r.right + ", " + r.bottom + ")");
+ }
return nativeFocusIsPlugin()
- && nativePointInNavCache(contentX, contentY, mNavSlop)
- && nativeCacheHitNodePointer() == nativeFocusNodePointer();
+ && nativeFocusNodeBounds().contains(contentX, contentY);
}
private boolean shouldForwardTouchEvent() {
@@ -5012,7 +5041,7 @@
WebSettings settings = getSettings();
if (settings.supportZoom()
&& settings.getBuiltInZoomControls()
- && !mZoomButtonsController.isVisible()
+ && !getZoomButtonsController().isVisible()
&& mMinZoomScale < mMaxZoomScale) {
mZoomButtonsController.setVisible(true);
int count = settings.getDoubleTapToastCount();
@@ -5029,7 +5058,7 @@
if ((deltaX | deltaY) != 0) {
overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
computeMaxScrollX(), computeMaxScrollY(),
- getViewWidth() / 3, getViewHeight() / 3);
+ getViewWidth() / 3, getViewHeight() / 3, true);
}
if (!getSettings().getBuiltInZoomControls()) {
boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
@@ -5366,12 +5395,12 @@
}
private int computeMaxScrollX() {
- return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+ return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
}
private int computeMaxScrollY() {
- return Math.max(computeVerticalScrollRange() + getTitleHeight()
- - getViewHeightWithTitle(), getTitleHeight());
+ return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
+ - getViewHeightWithTitle(), 0);
}
public void flingScroll(int vx, int vy) {
@@ -5398,9 +5427,6 @@
vx = 0;
}
}
- if (maxX == 0 && !canOverscrollHorizontally()) {
- vx = 0;
- }
if (true /* EMG release: make our fling more like Maps' */) {
// maps cuts their velocity in half
vx = vx * 3 / 4;
@@ -5439,8 +5465,9 @@
mLastVelY = vy;
mLastVelocity = (float) Math.hypot(vx, vy);
+ // no horizontal overscroll if the content just fits
mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
- getViewWidth() / 3, getViewHeight() / 3);
+ maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3);
// TODO: duration is calculated based on velocity, if the range is
// small, the animation will stop before duration is up. We may
// want to calculate how long the animation is going to run to precisely
@@ -5557,6 +5584,19 @@
* @hide
*/
public ZoomButtonsController getZoomButtonsController() {
+ if (mZoomButtonsController == null) {
+ mZoomButtonsController = new ZoomButtonsController(this);
+ mZoomButtonsController.setOnZoomListener(mZoomListener);
+ // ZoomButtonsController positions the buttons at the bottom, but in
+ // the middle. Change their layout parameters so they appear on the
+ // right.
+ View controls = mZoomButtonsController.getZoomControls();
+ ViewGroup.LayoutParams params = controls.getLayoutParams();
+ if (params instanceof FrameLayout.LayoutParams) {
+ FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams) params;
+ frameParams.gravity = Gravity.RIGHT;
+ }
+ }
return mZoomButtonsController;
}
@@ -5761,11 +5801,11 @@
contentToViewY(docY + docHeight / 2) - viewHeight / 2,
true, 0);
} else {
- int oldScreenX = contentToViewX(docX) - mScrollX;
- int rectViewX = (int) (docX * scale);
- int rectViewWidth = (int) (docWidth * scale);
- int newMaxWidth = (int) (mContentWidth * scale);
- int newScreenX = (viewWidth - rectViewWidth) / 2;
+ float oldScreenX = docX * mActualScale - mScrollX;
+ float rectViewX = docX * scale;
+ float rectViewWidth = docWidth * scale;
+ float newMaxWidth = mContentWidth * scale;
+ float newScreenX = (viewWidth - rectViewWidth) / 2;
// pin the newX to the WebView
if (newScreenX > rectViewX) {
newScreenX = rectViewX;
@@ -5774,12 +5814,12 @@
}
mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale)
/ (scale - mActualScale);
- int oldScreenY = contentToViewY(docY) - mScrollY;
- int rectViewY = (int) (docY * scale) + getTitleHeight();
- int rectViewHeight = (int) (docHeight * scale);
- int newMaxHeight = (int) (mContentHeight * scale)
- + getTitleHeight();
- int newScreenY = (viewHeight - rectViewHeight) / 2;
+ float oldScreenY = docY * mActualScale + getTitleHeight()
+ - mScrollY;
+ float rectViewY = docY * scale + getTitleHeight();
+ float rectViewHeight = docHeight * scale;
+ float newMaxHeight = mContentHeight * scale + getTitleHeight();
+ float newScreenY = (viewHeight - rectViewHeight) / 2;
// pin the newY to the WebView
if (newScreenY > rectViewY) {
newScreenY = rectViewY;
@@ -5792,6 +5832,33 @@
}
}
+ void dismissZoomControl() {
+ if (mWebViewCore == null) {
+ // maybe called after WebView's destroy(). As we can't get settings,
+ // just hide zoom control for both styles.
+ if (mZoomButtonsController != null) {
+ mZoomButtonsController.setVisible(false);
+ }
+ if (mZoomControls != null) {
+ mZoomControls.hide();
+ }
+ return;
+ }
+ WebSettings settings = getSettings();
+ if (settings.getBuiltInZoomControls()) {
+ if (getZoomButtonsController().isVisible()) {
+ mZoomButtonsController.setVisible(false);
+ }
+ } else {
+ if (mZoomControlRunnable != null) {
+ mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+ }
+ if (mZoomControls != null) {
+ mZoomControls.hide();
+ }
+ }
+ }
+
// Rule for double tap:
// 1. if the current scale is not same as the text wrap scale and layout
// algorithm is NARROW_COLUMNS, fit to column;
@@ -5806,20 +5873,9 @@
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
WebSettings settings = getSettings();
- // remove the zoom control after double tap
- if (settings.getBuiltInZoomControls()) {
- if (mZoomButtonsController.isVisible()) {
- mZoomButtonsController.setVisible(false);
- }
- } else {
- if (mZoomControlRunnable != null) {
- mPrivateHandler.removeCallbacks(mZoomControlRunnable);
- }
- if (mZoomControls != null) {
- mZoomControls.hide();
- }
- }
settings.setDoubleTapToastCount(0);
+ // remove the zoom control after double tap
+ dismissZoomControl();
ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
if (plugin != null) {
if (isPluginFitOnScreen(plugin)) {
@@ -6310,19 +6366,18 @@
}
}
break;
+ case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
+ displaySoftKeyboard(true);
+ updateTextSelectionFromMessage(msg.arg1, msg.arg2,
+ (WebViewCore.TextSelectionData) msg.obj);
+ break;
case UPDATE_TEXT_SELECTION_MSG_ID:
// If no textfield was in focus, and the user touched one,
// causing it to send this message, then WebTextView has not
// been set up yet. Rebuild it so it can set its selection.
rebuildWebTextView();
- if (inEditingMode()
- && mWebTextView.isSameTextField(msg.arg1)
- && msg.arg2 == mTextGeneration) {
- WebViewCore.TextSelectionData tData
- = (WebViewCore.TextSelectionData) msg.obj;
- mWebTextView.setSelectionFromWebKit(tData.mStart,
- tData.mEnd);
- }
+ updateTextSelectionFromMessage(msg.arg1, msg.arg2,
+ (WebViewCore.TextSelectionData) msg.obj);
break;
case RETURN_LABEL:
if (inEditingMode()
@@ -6478,7 +6533,7 @@
if (msg.arg1 == 0) {
hideSoftKeyboard();
} else {
- displaySoftKeyboard(1 == msg.arg2);
+ displaySoftKeyboard(false);
}
break;
@@ -6601,6 +6656,19 @@
}
}
+ /**
+ * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
+ * and UPDATE_TEXT_SELECTION_MSG_ID. Update the selection of WebTextView.
+ */
+ private void updateTextSelectionFromMessage(int nodePointer,
+ int textGeneration, WebViewCore.TextSelectionData data) {
+ if (inEditingMode()
+ && mWebTextView.isSameTextField(nodePointer)
+ && textGeneration == mTextGeneration) {
+ mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
+ }
+ }
+
// Class used to use a dropdown for a <select> element
private class InvokeListBox implements Runnable {
// Whether the listbox allows multiple selection.
@@ -7177,6 +7245,7 @@
*/
private native int nativeFocusCandidateType();
private native boolean nativeFocusIsPlugin();
+ private native Rect nativeFocusNodeBounds();
/* package */ native int nativeFocusNodePointer();
private native Rect nativeGetCursorRingBounds();
private native String nativeGetSelection();
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 02c7210..4ac660e 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -197,6 +197,22 @@
}
/**
+ * Notify the host application that authentication credentials have been
+ * supplied from Script.
+ * The default behavior is to do nothing.
+ * @hide Pending API council review
+ *
+ * @param view The WebView that is initiating the callback.
+ * @param host The host requiring authentication.
+ * @param realm A description to help store user credentials for future
+ * @param username The username
+ * @param password The password
+ */
+ public void onReceivedHttpAuthCredentials(WebView view, String host,
+ String realm, String username, String password) {
+ }
+
+ /**
* Give the host application a chance to handle the key event synchronously.
* e.g. menu shortcut key events need to be filtered this way. If return
* true, WebView will not handle the key event. If return false, WebView
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4073e37..410227b 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -627,7 +627,7 @@
String mData;
String mMimeType;
String mEncoding;
- String mFailUrl;
+ String mHistoryUrl;
}
static class CursorData {
@@ -981,7 +981,7 @@
loadParams.mData,
loadParams.mMimeType,
loadParams.mEncoding,
- loadParams.mFailUrl);
+ loadParams.mHistoryUrl);
break;
case STOP_LOADING:
@@ -2301,11 +2301,21 @@
}
// called by JNI
- private void requestKeyboard(boolean showKeyboard, boolean isTextView) {
+ private void requestKeyboardWithSelection(int pointer, int selStart,
+ int selEnd, int textGeneration) {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
- WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0,
- isTextView ? 1 : 0)
+ WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+ textGeneration, new TextSelectionData(selStart, selEnd))
+ .sendToTarget();
+ }
+ }
+
+ // called by JNI
+ private void requestKeyboard(boolean showKeyboard) {
+ if (mWebView != null) {
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
.sendToTarget();
}
}
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index a870931..b18419d 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -51,9 +51,10 @@
// 7 -> 8 Move cache to its own db
// 8 -> 9 Store both scheme and host when storing passwords
// 9 -> 10 Update httpauth table UNIQUE
- private static final int CACHE_DATABASE_VERSION = 3;
+ private static final int CACHE_DATABASE_VERSION = 4;
// 1 -> 2 Add expires String
// 2 -> 3 Add content-disposition
+ // 3 -> 4 Add crossdomain (For x-permitted-cross-domain-policies header)
private static WebViewDatabase mInstance = null;
@@ -126,6 +127,8 @@
private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition";
+ private static final String CACHE_CROSSDOMAIN_COL = "crossdomain";
+
// column id strings for "password" table
private static final String PASSWORD_HOST_COL = "host";
@@ -166,6 +169,7 @@
private static int mCacheLocationColIndex;
private static int mCacheContentLengthColIndex;
private static int mCacheContentDispositionColIndex;
+ private static int mCacheCrossDomainColIndex;
private static int mCacheTransactionRefcount;
@@ -269,6 +273,8 @@
.getColumnIndex(CACHE_CONTENTLENGTH_COL);
mCacheContentDispositionColIndex = mCacheInserter
.getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
+ mCacheCrossDomainColIndex = mCacheInserter
+ .getColumnIndex(CACHE_CROSSDOMAIN_COL);
}
}
@@ -378,6 +384,7 @@
+ " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
+ CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
+ " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, "
+ + CACHE_CROSSDOMAIN_COL + " TEXT,"
+ " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);");
mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache ("
+ CACHE_URL_COL + ")");
@@ -389,10 +396,17 @@
return false;
}
- Cursor cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
- null, null, null, null, null);
- boolean ret = cursor.moveToFirst() == true;
- cursor.close();
+ Cursor cursor = null;
+ boolean ret = false;
+ try {
+ cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
+ null, null, null, null, null);
+ ret = cursor.moveToFirst() == true;
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "hasEntries", e);
+ } finally {
+ if (cursor != null) cursor.close();
+ }
return ret;
}
@@ -420,33 +434,39 @@
};
final String selection = "(" + COOKIES_DOMAIN_COL
+ " GLOB '*' || ?)";
- Cursor cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
- columns, selection, new String[] { domain }, null, null,
- null);
- if (cursor.moveToFirst()) {
- int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
- int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
- int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
- int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
- int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
- int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
- do {
- Cookie cookie = new Cookie();
- cookie.domain = cursor.getString(domainCol);
- cookie.path = cursor.getString(pathCol);
- cookie.name = cursor.getString(nameCol);
- cookie.value = cursor.getString(valueCol);
- if (cursor.isNull(expiresCol)) {
- cookie.expires = -1;
- } else {
- cookie.expires = cursor.getLong(expiresCol);
- }
- cookie.secure = cursor.getShort(secureCol) != 0;
- cookie.mode = Cookie.MODE_NORMAL;
- list.add(cookie);
- } while (cursor.moveToNext());
+ Cursor cursor = null;
+ try {
+ cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
+ columns, selection, new String[] { domain }, null, null,
+ null);
+ if (cursor.moveToFirst()) {
+ int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
+ int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
+ int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
+ int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
+ int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
+ int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
+ do {
+ Cookie cookie = new Cookie();
+ cookie.domain = cursor.getString(domainCol);
+ cookie.path = cursor.getString(pathCol);
+ cookie.name = cursor.getString(nameCol);
+ cookie.value = cursor.getString(valueCol);
+ if (cursor.isNull(expiresCol)) {
+ cookie.expires = -1;
+ } else {
+ cookie.expires = cursor.getLong(expiresCol);
+ }
+ cookie.secure = cursor.getShort(secureCol) != 0;
+ cookie.mode = Cookie.MODE_NORMAL;
+ list.add(cookie);
+ } while (cursor.moveToNext());
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getCookiesForDomain", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return list;
}
}
@@ -604,12 +624,12 @@
return null;
}
- Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
- + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
- + "contentdisposition FROM cache WHERE url = ?",
- new String[] { url });
-
+ Cursor cursor = null;
+ final String query = "SELECT filepath, lastmodify, etag, expires, "
+ + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
+ + "contentdisposition, crossdomain FROM cache WHERE url = ?";
try {
+ cursor = mCacheDatabase.rawQuery(query, new String[] { url });
if (cursor.moveToFirst()) {
CacheResult ret = new CacheResult();
ret.localPath = cursor.getString(0);
@@ -623,8 +643,11 @@
ret.location = cursor.getString(8);
ret.contentLength = cursor.getLong(9);
ret.contentdisposition = cursor.getString(10);
+ ret.crossDomain = cursor.getString(11);
return ret;
}
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getCache", e);
} finally {
if (cursor != null) cursor.close();
}
@@ -669,6 +692,7 @@
mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
mCacheInserter.bind(mCacheContentDispositionColIndex,
c.contentdisposition);
+ mCacheInserter.bind(mCacheCrossDomainColIndex, c.crossDomain);
mCacheInserter.execute();
}
@@ -688,78 +712,108 @@
return false;
}
- Cursor cursor = mCacheDatabase.query("cache", ID_PROJECTION,
- null, null, null, null, null);
- boolean ret = cursor.moveToFirst() == true;
- cursor.close();
+ Cursor cursor = null;
+ boolean ret = false;
+ try {
+ cursor = mCacheDatabase.query("cache", ID_PROJECTION,
+ null, null, null, null, null);
+ ret = cursor.moveToFirst() == true;
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "hasCache", e);
+ } finally {
+ if (cursor != null) cursor.close();
+ }
return ret;
}
long getCacheTotalSize() {
long size = 0;
- Cursor cursor = mCacheDatabase.rawQuery(
- "SELECT SUM(contentlength) as sum FROM cache", null);
- if (cursor.moveToFirst()) {
- size = cursor.getLong(0);
+ Cursor cursor = null;
+ final String query = "SELECT SUM(contentlength) as sum FROM cache";
+ try {
+ cursor = mCacheDatabase.rawQuery(query, null);
+ if (cursor.moveToFirst()) {
+ size = cursor.getLong(0);
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getCacheTotalSize", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return size;
}
List<String> trimCache(long amount) {
ArrayList<String> pathList = new ArrayList<String>(100);
- Cursor cursor = mCacheDatabase.rawQuery(
- "SELECT contentlength, filepath FROM cache ORDER BY expires ASC",
- null);
- if (cursor.moveToFirst()) {
- int batchSize = 100;
- StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
- pathStr.append("DELETE FROM cache WHERE filepath IN (?");
- for (int i = 1; i < batchSize; i++) {
- pathStr.append(", ?");
- }
- pathStr.append(")");
- SQLiteStatement statement = mCacheDatabase.compileStatement(pathStr
- .toString());
- // as bindString() uses 1-based index, initialize index to 1
- int index = 1;
- do {
- long length = cursor.getLong(0);
- if (length == 0) {
- continue;
+ Cursor cursor = null;
+ final String query = "SELECT contentlength, filepath FROM cache ORDER BY expires ASC";
+ try {
+ cursor = mCacheDatabase.rawQuery(query, null);
+ if (cursor.moveToFirst()) {
+ int batchSize = 100;
+ StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
+ pathStr.append("DELETE FROM cache WHERE filepath IN (?");
+ for (int i = 1; i < batchSize; i++) {
+ pathStr.append(", ?");
}
- amount -= length;
- String filePath = cursor.getString(1);
- statement.bindString(index, filePath);
- pathList.add(filePath);
- if (index++ == batchSize) {
- statement.execute();
- statement.clearBindings();
- index = 1;
+ pathStr.append(")");
+ SQLiteStatement statement = null;
+ try {
+ statement = mCacheDatabase.compileStatement(
+ pathStr.toString());
+ // as bindString() uses 1-based index, initialize index to 1
+ int index = 1;
+ do {
+ long length = cursor.getLong(0);
+ if (length == 0) {
+ continue;
+ }
+ amount -= length;
+ String filePath = cursor.getString(1);
+ statement.bindString(index, filePath);
+ pathList.add(filePath);
+ if (index++ == batchSize) {
+ statement.execute();
+ statement.clearBindings();
+ index = 1;
+ }
+ } while (cursor.moveToNext() && amount > 0);
+ if (index > 1) {
+ // there may be old bindings from the previous statement
+ // if index is less than batchSize, which is Ok.
+ statement.execute();
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "trimCache SQLiteStatement", e);
+ } finally {
+ if (statement != null) statement.close();
}
- } while (cursor.moveToNext() && amount > 0);
- if (index > 1) {
- // there may be old bindings from the previous statement if
- // index is less than batchSize, which is Ok.
- statement.execute();
}
- statement.close();
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "trimCache Cursor", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return pathList;
}
List<String> getAllCacheFileNames() {
ArrayList<String> pathList = null;
- Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
- null);
- if (cursor != null && cursor.moveToFirst()) {
- pathList = new ArrayList<String>(cursor.getCount());
- do {
- pathList.add(cursor.getString(0));
- } while (cursor.moveToNext());
+ Cursor cursor = null;
+ try {
+ cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ pathList = new ArrayList<String>(cursor.getCount());
+ do {
+ pathList.add(cursor.getString(0));
+ } while (cursor.moveToNext());
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getAllCacheFileNames", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return pathList;
}
@@ -809,17 +863,23 @@
final String selection = "(" + PASSWORD_HOST_COL + " == ?)";
synchronized (mPasswordLock) {
String[] ret = null;
- Cursor cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID],
- columns, selection, new String[] { schemePlusHost }, null,
- null, null);
- if (cursor.moveToFirst()) {
- ret = new String[2];
- ret[0] = cursor.getString(
- cursor.getColumnIndex(PASSWORD_USERNAME_COL));
- ret[1] = cursor.getString(
- cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
+ Cursor cursor = null;
+ try {
+ cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID],
+ columns, selection, new String[] { schemePlusHost }, null,
+ null, null);
+ if (cursor.moveToFirst()) {
+ ret = new String[2];
+ ret[0] = cursor.getString(
+ cursor.getColumnIndex(PASSWORD_USERNAME_COL));
+ ret[1] = cursor.getString(
+ cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getUsernamePassword", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return ret;
}
}
@@ -900,17 +960,23 @@
+ HTTPAUTH_REALM_COL + " == ?)";
synchronized (mHttpAuthLock) {
String[] ret = null;
- Cursor cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
- columns, selection, new String[] { host, realm }, null,
- null, null);
- if (cursor.moveToFirst()) {
- ret = new String[2];
- ret[0] = cursor.getString(
- cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
- ret[1] = cursor.getString(
- cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
+ Cursor cursor = null;
+ try {
+ cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
+ columns, selection, new String[] { host, realm }, null,
+ null, null);
+ if (cursor.moveToFirst()) {
+ ret = new String[2];
+ ret[0] = cursor.getString(
+ cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
+ ret[1] = cursor.getString(
+ cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return ret;
}
}
@@ -958,18 +1024,24 @@
final String selection = "(" + FORMURL_URL_COL + " == ?)";
synchronized (mFormLock) {
long urlid = -1;
- Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
- ID_PROJECTION, selection, new String[] { url }, null, null,
- null);
- if (cursor.moveToFirst()) {
- urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
- } else {
- ContentValues c = new ContentValues();
- c.put(FORMURL_URL_COL, url);
- urlid = mDatabase.insert(
- mTableNames[TABLE_FORMURL_ID], null, c);
+ Cursor cursor = null;
+ try {
+ cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
+ ID_PROJECTION, selection, new String[] { url }, null, null,
+ null);
+ if (cursor.moveToFirst()) {
+ urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
+ } else {
+ ContentValues c = new ContentValues();
+ c.put(FORMURL_URL_COL, url);
+ urlid = mDatabase.insert(
+ mTableNames[TABLE_FORMURL_ID], null, c);
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "setFormData", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
if (urlid >= 0) {
Set<Entry<String, String>> set = formdata.entrySet();
Iterator<Entry<String, String>> iter = set.iterator();
@@ -1002,27 +1074,39 @@
final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND ("
+ FORMDATA_NAME_COL + " == ?)";
synchronized (mFormLock) {
- Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
- ID_PROJECTION, urlSelection, new String[] { url }, null,
- null, null);
- if (cursor.moveToFirst()) {
- long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
- Cursor dataCursor = mDatabase.query(
- mTableNames[TABLE_FORMDATA_ID],
- new String[] { ID_COL, FORMDATA_VALUE_COL },
- dataSelection,
- new String[] { Long.toString(urlid), name }, null,
+ Cursor cursor = null;
+ try {
+ cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
+ ID_PROJECTION, urlSelection, new String[] { url }, null,
null, null);
- if (dataCursor.moveToFirst()) {
- int valueCol =
- dataCursor.getColumnIndex(FORMDATA_VALUE_COL);
- do {
- values.add(dataCursor.getString(valueCol));
- } while (dataCursor.moveToNext());
+ if (cursor.moveToFirst()) {
+ long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
+ Cursor dataCursor = null;
+ try {
+ dataCursor = mDatabase.query(
+ mTableNames[TABLE_FORMDATA_ID],
+ new String[] { ID_COL, FORMDATA_VALUE_COL },
+ dataSelection,
+ new String[] { Long.toString(urlid), name },
+ null, null, null);
+ if (dataCursor.moveToFirst()) {
+ int valueCol = dataCursor.getColumnIndex(
+ FORMDATA_VALUE_COL);
+ do {
+ values.add(dataCursor.getString(valueCol));
+ } while (dataCursor.moveToNext());
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getFormData dataCursor", e);
+ } finally {
+ if (dataCursor != null) dataCursor.close();
+ }
}
- dataCursor.close();
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "getFormData cursor", e);
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
return values;
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ec7424ba..bd6105f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -32,6 +32,7 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -1160,6 +1161,14 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
+ if (changed) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ getChildAt(i).forceLayout();
+ }
+ mRecycler.markChildrenDirty();
+ }
+
layoutChildren();
mInLayout = false;
@@ -1826,9 +1835,11 @@
mSelectedPosition < mAdapter.getCount()) {
final View view = getChildAt(mSelectedPosition - mFirstPosition);
- performItemClick(view, mSelectedPosition, mSelectedRowId);
+ if (view != null) {
+ performItemClick(view, mSelectedPosition, mSelectedRowId);
+ view.setPressed(false);
+ }
setPressed(false);
- if (view != null) view.setPressed(false);
return true;
}
break;
@@ -2125,7 +2136,7 @@
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
overscrollBy(0, overscroll, 0, mScrollY, 0, 0,
- 0, getOverscrollMax());
+ 0, getOverscrollMax(), true);
mTouchMode = TOUCH_MODE_OVERSCROLL;
invalidate();
}
@@ -2170,7 +2181,7 @@
}
} else {
overscrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
- 0, getOverscrollMax());
+ 0, getOverscrollMax(), true);
invalidate();
}
mLastY = y;
@@ -2380,6 +2391,7 @@
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
mScrollY = scrollY;
+
if (clampedY) {
// Velocity is broken by hitting the limit; don't start a fling off of this.
if (mVelocityTracker != null) {
@@ -2553,7 +2565,7 @@
/**
* Tracks the decay of a fling scroll
*/
- private OverScroller mScroller;
+ private final OverScroller mScroller;
/**
* Y value reported by mScroller on the previous fling
@@ -2585,11 +2597,22 @@
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
+ } else {
+ mTouchMode = TOUCH_MODE_REST;
}
}
void startOverfling(int initialVelocity) {
- mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight());
+ final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0;
+ final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE;
+ mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight());
+ mTouchMode = TOUCH_MODE_OVERFLING;
+ invalidate();
+ post(this);
+ }
+
+ void edgeReached() {
+ mScroller.notifyVerticalEdgeReached(mScrollY, 0, getOverscrollMax());
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
@@ -2666,13 +2689,10 @@
if (motionView != null) {
// Tweak the scroll for how far we overshot
int overshoot = -(delta - (motionView.getTop() - oldTop));
- overscrollBy(0, overshoot, 0, mScrollY, 0, 0, 0, getOverscrollMax());
+ overscrollBy(0, overshoot, 0, mScrollY, 0, 0,
+ 0, getOverscrollMax(), false);
}
- float vel = scroller.getCurrVelocity();
- if (delta > 0) {
- vel = -vel;
- }
- startOverfling(Math.round(vel));
+ edgeReached();
break;
}
@@ -2698,7 +2718,8 @@
if (scroller.computeScrollOffset()) {
final int scrollY = mScrollY;
final int deltaY = scroller.getCurrY() - scrollY;
- if (overscrollBy(0, deltaY, 0, scrollY, 0, 0, 0, getOverscrollMax())) {
+ if (overscrollBy(0, deltaY, 0, scrollY, 0, 0,
+ 0, getOverscrollMax(), false)) {
startSpringback();
} else {
invalidate();
@@ -2728,7 +2749,7 @@
private int mBoundPos;
private int mLastSeenPos;
private int mScrollDuration;
- private int mExtraScroll;
+ private final int mExtraScroll;
PositionScroller() {
mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
@@ -3967,7 +3988,7 @@
for (int i = 0; i < count; i++) {
if (activeViews[i] != null) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
"AbsListView " + this + " has a view in its active recycler: " +
activeViews[i]);
}
@@ -3995,12 +4016,12 @@
final View view = scrap.get(i);
if (view.getParent() != null) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
" has a view in its scrap heap still attached to a parent: " + view);
}
if (indexOfChild(view) >= 0) {
result = false;
- android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
" has a view in its scrap heap that is also a direct child: " + view);
}
}
@@ -4142,6 +4163,25 @@
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
+
+ public void markChildrenDirty() {
+ if (mViewTypeCount == 1) {
+ final ArrayList<View> scrap = mCurrentScrap;
+ final int scrapCount = scrap.size();
+ for (int i = 0; i < scrapCount; i++) {
+ scrap.get(i).forceLayout();
+ }
+ } else {
+ final int typeCount = mViewTypeCount;
+ for (int i = 0; i < typeCount; i++) {
+ final ArrayList<View> scrap = mScrapViews[i];
+ final int scrapCount = scrap.size();
+ for (int j = 0; j < scrapCount; j++) {
+ scrap.get(j).forceLayout();
+ }
+ }
+ }
+ }
public boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 1156e15..e15a520 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Editable;
@@ -91,6 +92,13 @@
private static final int HINT_VIEW_ID = 0x17;
+ /**
+ * This value controls the length of time that the user
+ * must leave a pointer down without scrolling to expand
+ * the autocomplete dropdown list to cover the IME.
+ */
+ private static final int EXPAND_LIST_TIMEOUT = 250;
+
private CharSequence mHintText;
private int mHintResource;
@@ -129,10 +137,12 @@
private boolean mBlockCompletion;
- private AutoCompleteTextView.ListSelectorHider mHideSelector;
+ private ListSelectorHider mHideSelector;
private Runnable mShowDropDownRunnable;
+ private Runnable mResizePopupRunnable = new ResizePopupRunnable();
- private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
+ private PassThroughClickListener mPassThroughClickListener;
+ private PopupDataSetObserver mObserver;
public AutoCompleteTextView(Context context) {
this(context, null);
@@ -218,14 +228,6 @@
}
/**
- * Sets this to be single line; a separate method so
- * MultiAutoCompleteTextView can skip this.
- */
- /* package */ void finishInit() {
- setSingleLine();
- }
-
- /**
* <p>Sets the optional hint text that is displayed at the bottom of the
* the matching list. This can be used as a cue to the user on how to
* best use the list, or to provide extra information.</p>
@@ -448,7 +450,7 @@
public boolean isDropDownDismissedOnCompletion() {
return mDropDownDismissedOnCompletion;
}
-
+
/**
* Sets whether the drop-down is dismissed when a suggestion is clicked. This is
* true by default.
@@ -590,10 +592,16 @@
* @see android.widget.ListAdapter
*/
public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
+ if (mObserver == null) {
+ mObserver = new PopupDataSetObserver();
+ } else if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(mObserver);
+ }
mAdapter = adapter;
if (mAdapter != null) {
//noinspection unchecked
mFilter = ((Filterable) mAdapter).getFilter();
+ adapter.registerDataSetObserver(mObserver);
} else {
mFilter = null;
}
@@ -660,14 +668,20 @@
final boolean below = !mPopup.isAboveAnchor();
- final ListAdapter adapter = mDropDownList.getAdapter();
- final boolean allEnabled = adapter.areAllItemsEnabled();
+ final ListAdapter adapter = mAdapter;
+
+ boolean allEnabled;
+ int firstItem = Integer.MAX_VALUE;
+ int lastItem = Integer.MIN_VALUE;
- final int firstItem = allEnabled ? 0 :
- mDropDownList.lookForSelectablePosition(0, true);
- final int lastItem = allEnabled ? adapter.getCount() - 1 :
- mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);
-
+ if (adapter != null) {
+ allEnabled = adapter.areAllItemsEnabled();
+ firstItem = allEnabled ? 0 :
+ mDropDownList.lookForSelectablePosition(0, true);
+ lastItem = allEnabled ? adapter.getCount() - 1 :
+ mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);
+ }
+
if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
(!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
// When the selection is at the top, we block the key
@@ -866,16 +880,6 @@
return ListView.INVALID_POSITION;
}
-
- /**
- * @hide
- * @return {@link android.widget.ListView#getChildCount()} of the drop down if it is showing,
- * otherwise 0.
- */
- protected int getDropDownChildCount() {
- return mDropDownList == null ? 0 : mDropDownList.getChildCount();
- }
-
/**
* <p>Starts filtering the content of the drop down list. The filtering
* pattern is the content of the edit box. Subclasses should override this
@@ -976,25 +980,7 @@
mBlockCompletion = false;
}
}
-
- /**
- * Like {@link #setTextKeepState(CharSequence)}, except that it can disable filtering.
- *
- * @param filter If <code>false</code>, no filtering will be performed
- * as a result of this call.
- *
- * @hide Pending API council approval.
- */
- public void setTextKeepState(CharSequence text, boolean filter) {
- if (filter) {
- setTextKeepState(text);
- } else {
- mBlockCompletion = true;
- setTextKeepState(text);
- mBlockCompletion = false;
- }
- }
-
+
/**
* <p>Performs the text completion by replacing the current text by the
* selected item. Subclasses should override this method to avoid replacing
@@ -1013,6 +999,11 @@
/** {@inheritDoc} */
public void onFilterComplete(int count) {
+ updateDropDownForFilter(count);
+
+ }
+
+ private void updateDropDownForFilter(int count) {
// Not attached to window, don't update drop-down
if (getWindowVisibility() == View.GONE) return;
@@ -1242,18 +1233,30 @@
ViewGroup dropDownView;
int otherHeights = 0;
- if (mAdapter != null) {
+ final ListAdapter adapter = mAdapter;
+ if (adapter != null) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
- int N = mAdapter.getCount();
- if (N > 20) N = 20;
- CompletionInfo[] completions = new CompletionInfo[N];
- for (int i = 0; i < N; i++) {
- Object item = mAdapter.getItem(i);
- long id = mAdapter.getItemId(i);
- completions[i] = new CompletionInfo(id, i,
- convertSelectionToString(item));
+ final int count = Math.min(adapter.getCount(), 20);
+ CompletionInfo[] completions = new CompletionInfo[count];
+ int realCount = 0;
+
+ for (int i = 0; i < count; i++) {
+ if (adapter.isEnabled(i)) {
+ realCount++;
+ Object item = adapter.getItem(i);
+ long id = adapter.getItemId(i);
+ completions[i] = new CompletionInfo(id, i,
+ convertSelectionToString(item));
+ }
}
+
+ if (realCount != count) {
+ CompletionInfo[] tmp = new CompletionInfo[realCount];
+ System.arraycopy(completions, 0, tmp, 0, realCount);
+ completions = tmp;
+ }
+
imm.displayCompletions(this, completions);
}
}
@@ -1281,7 +1284,7 @@
mDropDownList = new DropDownListView(context);
mDropDownList.setSelector(mDropDownListHighlight);
- mDropDownList.setAdapter(mAdapter);
+ mDropDownList.setAdapter(adapter);
mDropDownList.setVerticalFadingEdgeEnabled(true);
mDropDownList.setOnItemClickListener(mDropDownItemClickListener);
mDropDownList.setFocusable(true);
@@ -1302,6 +1305,7 @@
public void onNothingSelected(AdapterView<?> parent) {
}
});
+ mDropDownList.setOnScrollListener(new PopupScrollListener());
if (mItemSelectedListener != null) {
mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
@@ -1442,17 +1446,41 @@
}
}
+ private class ResizePopupRunnable implements Runnable {
+ public void run() {
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ showDropDown();
+ }
+ }
+
private class PopupTouchInterceptor implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN &&
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN &&
mPopup != null && mPopup.isShowing()) {
- mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
- showDropDown();
+ postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
+ } else if (action == MotionEvent.ACTION_UP) {
+ removeCallbacks(mResizePopupRunnable);
}
return false;
}
}
+ private class PopupScrollListener implements ListView.OnScrollListener {
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+
+ }
+
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
+ !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
+ removeCallbacks(mResizePopupRunnable);
+ mResizePopupRunnable.run();
+ }
+ }
+ }
+
private class DropDownItemClickListener implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView parent, View v, int position, long id) {
performCompletion(v, position, id);
@@ -1523,24 +1551,6 @@
return view;
}
- /**
- * <p>Returns the top padding of the currently selected view.</p>
- *
- * @return the height of the top padding for the selection
- */
- public int getSelectionPaddingTop() {
- return mSelectionTopPadding;
- }
-
- /**
- * <p>Returns the bottom padding of the currently selected view.</p>
- *
- * @return the height of the bottom padding for the selection
- */
- public int getSelectionPaddingBottom() {
- return mSelectionBottomPadding;
- }
-
@Override
public boolean isInTouchMode() {
// WARNING: Please read the comment where mListSelectionHidden is declared
@@ -1638,5 +1648,36 @@
if (mWrapped != null) mWrapped.onClick(v);
}
}
-
+
+ private class PopupDataSetObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ if (isPopupShowing()) {
+ // This will resize the popup to fit the new adapter's content
+ showDropDown();
+ } else if (mAdapter != null) {
+ // If the popup is not showing already, showing it will cause
+ // the list of data set observers attached to the adapter to
+ // change. We can't do it from here, because we are in the middle
+ // of iterating throught he list of observers.
+ post(new Runnable() {
+ public void run() {
+ final ListAdapter adapter = mAdapter;
+ if (adapter != null) {
+ updateDropDownForFilter(adapter.getCount());
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onInvalidated() {
+ if (!mDropDownAlwaysVisible) {
+ // There's no data to display so make sure we're not showing
+ // the drop down and its list
+ dismissDropDown();
+ }
+ }
+ }
}
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 1bba7f0..396b7ae 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -18,7 +18,6 @@
import android.database.DataSetObservable;
import android.database.DataSetObserver;
-import android.view.KeyEvent;
/**
* Base class for a {@link ExpandableListAdapter} used to provide data and Views
@@ -31,7 +30,8 @@
* @see SimpleExpandableListAdapter
* @see SimpleCursorTreeAdapter
*/
-public abstract class BaseExpandableListAdapter implements ExpandableListAdapter {
+public abstract class BaseExpandableListAdapter implements ExpandableListAdapter,
+ HeterogeneousExpandableList {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
@@ -102,5 +102,37 @@
public boolean isEmpty() {
return getGroupCount() == 0;
}
-
+
+
+ /**
+ * {@inheritDoc}
+ * @return 0 for any group or child position, since only one child type count is declared.
+ */
+ public int getChildType(int groupPosition, int childPosition) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1 as a default value in BaseExpandableListAdapter.
+ */
+ public int getChildTypeCount() {
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 0 for any groupPosition, since only one group type count is declared.
+ */
+ public int getGroupType(int groupPosition) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1 as a default value in BaseExpandableListAdapter.
+ */
+ public int getGroupTypeCount() {
+ return 1;
+ }
}
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 3dab9f2..6b845b0 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -206,7 +206,12 @@
if (format == null || "".equals(format)) {
return DateFormat.getDateInstance(DateFormat.SHORT);
} else {
- return new SimpleDateFormat(format);
+ try {
+ return new SimpleDateFormat(format);
+ } catch (IllegalArgumentException e) {
+ // If we tried to use a bad format string, fall back to a default.
+ return DateFormat.getDateInstance(DateFormat.SHORT);
+ }
}
}
diff --git a/core/java/android/widget/ExpandableListAdapter.java b/core/java/android/widget/ExpandableListAdapter.java
index b75983c..7f6781b 100644
--- a/core/java/android/widget/ExpandableListAdapter.java
+++ b/core/java/android/widget/ExpandableListAdapter.java
@@ -17,7 +17,6 @@
package android.widget;
import android.database.DataSetObserver;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -108,7 +107,7 @@
/**
* Gets a View that displays the given group. This View is only for the
* group--the Views for the group's children will be fetched using
- * getChildrenView.
+ * {@link #getChildView(int, int, boolean, View, ViewGroup)}.
*
* @param groupPosition the position of the group for which the View is
* returned
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 01d3a4a..2ff6b70 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -442,8 +442,8 @@
View retValue;
if (posMetadata.position.type == ExpandableListPosition.GROUP) {
- retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos, posMetadata
- .isExpanded(), convertView, parent);
+ retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos,
+ posMetadata.isExpanded(), convertView, parent);
} else if (posMetadata.position.type == ExpandableListPosition.CHILD) {
final boolean isLastChild = posMetadata.groupMetadata.lastChildFlPos == flatListPos;
@@ -464,10 +464,21 @@
final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position;
int retValue;
- if (pos.type == ExpandableListPosition.GROUP) {
- retValue = 0;
+ if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+ HeterogeneousExpandableList adapter =
+ (HeterogeneousExpandableList) mExpandableListAdapter;
+ if (pos.type == ExpandableListPosition.GROUP) {
+ retValue = adapter.getGroupType(pos.groupPos);
+ } else {
+ final int childType = adapter.getChildType(pos.groupPos, pos.childPos);
+ retValue = adapter.getGroupTypeCount() + childType;
+ }
} else {
- retValue = 1;
+ if (pos.type == ExpandableListPosition.GROUP) {
+ retValue = 0;
+ } else {
+ retValue = 1;
+ }
}
pos.recycle();
@@ -477,7 +488,13 @@
@Override
public int getViewTypeCount() {
- return 2;
+ if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+ HeterogeneousExpandableList adapter =
+ (HeterogeneousExpandableList) mExpandableListAdapter;
+ return adapter.getGroupTypeCount() + adapter.getChildTypeCount();
+ } else {
+ return 2;
+ }
}
@Override
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 65a4673..e27bb4fe 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -304,8 +304,8 @@
childLeft = parentLeft + lp.leftMargin;
break;
case Gravity.CENTER_HORIZONTAL:
- childLeft = parentLeft + (parentRight - parentLeft + lp.leftMargin +
- lp.rightMargin - width) / 2;
+ childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
+ lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentRight - width - lp.rightMargin;
@@ -319,8 +319,8 @@
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
- childTop = parentTop + (parentBottom - parentTop + lp.topMargin +
- lp.bottomMargin - height) / 2;
+ childTop = parentTop + (parentBottom - parentTop - height) / 2 +
+ lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
diff --git a/core/java/android/widget/HeaderViewListAdapter.java b/core/java/android/widget/HeaderViewListAdapter.java
index 981996a..e2a269e 100644
--- a/core/java/android/widget/HeaderViewListAdapter.java
+++ b/core/java/android/widget/HeaderViewListAdapter.java
@@ -28,19 +28,24 @@
* associated data objects.
*<p>This is intended as a base class; you will probably not need to
* use this class directly in your own code.
- *
*/
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
- private ListAdapter mAdapter;
+ private final ListAdapter mAdapter;
// These two ArrayList are assumed to NOT be null.
- // They are indeed created when declared in ListView and then shared.
+ // They are indeed created when declared in ListView and then shared.
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
+
+ // Used as a placeholder in case the provided info views are indeed null.
+ // Currently only used by some CTS tests, which may be removed.
+ static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
+ new ArrayList<ListView.FixedViewInfo>();
+
boolean mAreAllFixedViewsSelectable;
- private boolean mIsFilterable;
+ private final boolean mIsFilterable;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
@@ -48,8 +53,17 @@
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
- mHeaderViewInfos = headerViewInfos;
- mFooterViewInfos = footerViewInfos;
+ if (headerViewInfos == null) {
+ mHeaderViewInfos = EMPTY_INFO_LIST;
+ } else {
+ mHeaderViewInfos = headerViewInfos;
+ }
+
+ if (footerViewInfos == null) {
+ mFooterViewInfos = EMPTY_INFO_LIST;
+ } else {
+ mFooterViewInfos = footerViewInfos;
+ }
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
@@ -130,43 +144,53 @@
}
public boolean isEnabled(int position) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
- if (mAdapter != null && position >= numHeaders) {
- int adjPosition = position - numHeaders;
- int adapterCount = mAdapter.getCount();
- if (adjPosition >= adapterCount) {
- return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
- } else {
- return mAdapter.isEnabled(adjPosition);
- }
- } else if (position < numHeaders) {
+ if (position < numHeaders) {
return mHeaderViewInfos.get(position).isSelectable;
}
- return true;
+
+ // Adapter
+ final int adjPosition = position - numHeaders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.isEnabled(adjPosition);
+ }
+ }
+
+ // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
+ return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
}
public Object getItem(int position) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
- if (mAdapter != null && position >= numHeaders) {
- int adjPosition = position - numHeaders;
- int adapterCount = mAdapter.getCount();
- if (adjPosition >= adapterCount) {
- return mFooterViewInfos.get(adjPosition - adapterCount).data;
- } else {
- return mAdapter.getItem(adjPosition);
- }
- } else if (position < numHeaders) {
+ if (position < numHeaders) {
return mHeaderViewInfos.get(position).data;
}
- return null;
+
+ // Adapter
+ final int adjPosition = position - numHeaders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getItem(adjPosition);
+ }
+ }
+
+ // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
+ return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
public long getItemId(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
- int adapterCnt = mAdapter.getCount();
- if (adjPosition < adapterCnt) {
+ int adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
return mAdapter.getItemId(adjPosition);
}
}
@@ -181,19 +205,24 @@
}
public View getView(int position, View convertView, ViewGroup parent) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
- if (mAdapter != null && position >= numHeaders) {
- int adjPosition = position - numHeaders;
- int adapterCount = mAdapter.getCount();
- if (adjPosition >= adapterCount) {
- return mFooterViewInfos.get(adjPosition - adapterCount).view;
- } else {
- return mAdapter.getView(adjPosition, convertView, parent);
- }
- } else if (position < numHeaders) {
+ if (position < numHeaders) {
return mHeaderViewInfos.get(position).view;
}
- return null;
+
+ // Adapter
+ final int adjPosition = position - numHeaders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getView(adjPosition, convertView, parent);
+ }
+ }
+
+ // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
+ return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
public int getItemViewType(int position) {
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
new file mode 100644
index 0000000..1292733
--- /dev/null
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Additional methods that when implemented make an
+ * {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
+ * mechanism.
+ *
+ * An {@link ExpandableListAdapter} declares one view type for its group items
+ * and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
+ * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
+ * different types of group and/or child item views, should use an adapter that implements this
+ * interface. This way, the recycled views that will be provided to
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * and
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * will be of the appropriate group or child type, resulting in a more efficient reuse of the
+ * previously created views.
+ */
+public interface HeterogeneousExpandableList {
+ /**
+ * Get the type of group View that will be created by
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . for the specified group item.
+ *
+ * @param groupPosition the position of the group for which the type should be returned.
+ * @return An integer representing the type of group View. Two group views should share the same
+ * type if one can be converted to the other in
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
+ * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+ * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+ * @see getGroupTypeCount()
+ */
+ int getGroupType(int groupPosition);
+
+ /**
+ * Get the type of child View that will be created by
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * for the specified child item.
+ *
+ * @param groupPosition the position of the group that the child resides in
+ * @param childPosition the position of the child with respect to other children in the group
+ * @return An integer representing the type of child View. Two child views should share the same
+ * type if one can be converted to the other in
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
+ * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+ * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+ * @see getChildTypeCount()
+ */
+ int getChildType(int groupPosition, int childPosition);
+
+ /**
+ * <p>
+ * Returns the number of types of group Views that will be created by
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . Each type represents a set of views that can be converted in
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * . If the adapter always returns the same type of View for all group items, this method should
+ * return 1.
+ * </p>
+ * <p>
+ * This method will only be called when the adapter is set on the {@link AdapterView}.
+ * </p>
+ *
+ * @return The number of types of group Views that will be created by this adapter.
+ * @see getChildTypeCount()
+ * @see getGroupType()
+ */
+ int getGroupTypeCount();
+
+ /**
+ * <p>
+ * Returns the number of types of child Views that will be created by
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * . Each type represents a set of views that can be converted in
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * , for any group. If the adapter always returns the same type of View for
+ * all child items, this method should return 1.
+ * </p>
+ * <p>
+ * This method will only be called when the adapter is set on the {@link AdapterView}.
+ * </p>
+ *
+ * @return The total number of types of child Views that will be created by this adapter.
+ * @see getGroupTypeCount()
+ * @see getChildType()
+ */
+ int getChildTypeCount();
+}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d80a5e3..3bd4767 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -372,11 +372,12 @@
private boolean inChild(int x, int y) {
if (getChildCount() > 0) {
+ final int scrollX = mScrollX;
final View child = getChildAt(0);
return !(y < child.getTop()
|| y >= child.getBottom()
- || x < child.getLeft()
- || x >= child.getRight());
+ || x < child.getLeft() - scrollX
+ || x >= child.getRight() - scrollX);
}
return false;
}
@@ -410,7 +411,13 @@
* Locally do absolute value. mLastMotionX is set to the x value
* of the down event.
*/
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final int activePointerId = mActivePointerId;
+ if (activePointerId == INVALID_POINTER) {
+ // If we don't have a valid id, the touch down wasn't on content.
+ break;
+ }
+
+ final int pointerIndex = ev.findPointerIndex(activePointerId);
final float x = ev.getX(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
if (xDiff > mTouchSlop) {
@@ -449,6 +456,9 @@
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+ invalidate();
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -480,21 +490,22 @@
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
-
final float x = ev.getX();
if (!(mIsBeingDragged = inChild((int) x, (int) ev.getY()))) {
return false;
}
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
// Remember where the motion event started
mLastMotionX = x;
+ mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
@@ -508,7 +519,7 @@
final int oldX = mScrollX;
final int oldY = mScrollY;
overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0,
- getOverscrollMax(), 0);
+ getOverscrollMax(), 0, true);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
break;
@@ -538,6 +549,19 @@
}
}
break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
+ invalidate();
+ }
+ mActivePointerId = INVALID_POINTER;
+ mIsBeingDragged = false;
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+ break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
@@ -575,6 +599,7 @@
} else {
super.scrollTo(scrollX, scrollY);
}
+ awakenScrollBars();
}
private int getOverscrollMax() {
@@ -937,7 +962,6 @@
dx = Math.max(0, Math.min(scrollX + dx, maxX)) - scrollX;
mScroller.startScroll(scrollX, mScrollY, dx, 0);
- awakenScrollBars(mScroller.getDuration());
invalidate();
} else {
if (!mScroller.isFinished()) {
@@ -1042,7 +1066,7 @@
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
- getOverscrollMax(), 0);
+ getOverscrollMax(), 0, false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
@@ -1295,7 +1319,6 @@
mScrollViewMovedFocus = false;
}
- awakenScrollBars(mScroller.getDuration());
invalidate();
}
}
diff --git a/core/java/android/widget/ListAdapter.java b/core/java/android/widget/ListAdapter.java
index a035145..0fd2e70 100644
--- a/core/java/android/widget/ListAdapter.java
+++ b/core/java/android/widget/ListAdapter.java
@@ -36,6 +36,9 @@
/**
* Returns true if the item at the specified position is not a separator.
* (A separator is a non-selectable, non-clickable item).
+ *
+ * The result is unspecified if position is invalid. An {@link ArrayIndexOutOfBoundsException}
+ * should be thrown in that case for fast failure.
*
* @param position Index of the item
* @return True if the item is not a separator
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 8469c8b..e8d92de 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -17,207 +17,282 @@
package android.widget;
import android.content.Context;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
+import android.util.FloatMath;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.Scroller.MagneticScroller;
/**
* This class encapsulates scrolling with the ability to overshoot the bounds
- * of a scrolling operation. This class attempts to be a drop-in replacement
- * for {@link android.widget.Scroller} in most cases.
- *
- * @hide Pending API approval
+ * of a scrolling operation. This class is a drop-in replacement for
+ * {@link android.widget.Scroller} in most cases.
*/
public class OverScroller {
- private static final int SPRINGBACK_DURATION = 150;
- private static final int OVERFLING_DURATION = 150;
-
- private static final int MODE_DEFAULT = 0;
- private static final int MODE_OVERFLING = 1;
- private static final int MODE_SPRINGBACK = 2;
-
- private Scroller mDefaultScroller;
- private Scroller mDecelScroller;
- private Scroller mAccelDecelScroller;
- private Scroller mCurrScroller;
-
- private int mScrollMode = MODE_DEFAULT;
-
- private int mMinimumX;
- private int mMinimumY;
- private int mMaximumX;
- private int mMaximumY;
-
- public OverScroller(Context context) {
- mDefaultScroller = new Scroller(context);
- mDecelScroller = new Scroller(context, new DecelerateInterpolator());
- mAccelDecelScroller = new Scroller(context, new AccelerateDecelerateInterpolator());
- mCurrScroller = mDefaultScroller;
- }
-
+ private int mMode;
+
+ private MagneticOverScroller mScrollerX;
+ private MagneticOverScroller mScrollerY;
+
+ private final Interpolator mInterpolator;
+
+ private static final int DEFAULT_DURATION = 250;
+ private static final int SCROLL_MODE = 0;
+ private static final int FLING_MODE = 1;
+
/**
- * Call this when you want to know the new location. If it returns true,
- * the animation is not yet finished. loc will be altered to provide the
- * new location.
- */
- public boolean computeScrollOffset() {
- boolean inProgress = mCurrScroller.computeScrollOffset();
-
- switch (mScrollMode) {
- case MODE_OVERFLING:
- if (!inProgress) {
- // Overfling ended
- if (springback(mCurrScroller.getCurrX(), mCurrScroller.getCurrY(),
- mMinimumX, mMaximumX, mMinimumY, mMaximumY, mAccelDecelScroller)) {
- return mCurrScroller.computeScrollOffset();
- } else {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- }
- }
- break;
-
- case MODE_SPRINGBACK:
- if (!inProgress) {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- }
- break;
-
- case MODE_DEFAULT:
- // Fling/autoscroll - did we go off the edge?
- if (inProgress) {
- Scroller scroller = mCurrScroller;
- final int x = scroller.getCurrX();
- final int y = scroller.getCurrY();
- final int minX = mMinimumX;
- final int maxX = mMaximumX;
- final int minY = mMinimumY;
- final int maxY = mMaximumY;
- if (x < minX || x > maxX || y < minY || y > maxY) {
- final int startx = scroller.getStartX();
- final int starty = scroller.getStartY();
- final int time = scroller.timePassed();
- final float timeSecs = time / 1000.f;
- final float xvel = ((x - startx) / timeSecs);
- final float yvel = ((y - starty) / timeSecs);
-
- if ((x < minX && xvel > 0) || (y < minY && yvel > 0) ||
- (x > maxX && xvel < 0) || (y > maxY && yvel < 0)) {
- // If our velocity would take us back into valid areas,
- // try to springback rather than overfling.
- if (springback(x, y, minX, maxX, minY, maxY)) {
- return mCurrScroller.computeScrollOffset();
- }
- } else {
- overfling(x, y, xvel, yvel);
- return mCurrScroller.computeScrollOffset();
- }
- }
- }
- break;
- }
-
- return inProgress;
- }
-
- private void overfling(int startx, int starty, float xvel, float yvel) {
- Scroller scroller = mDecelScroller;
- final float durationSecs = (OVERFLING_DURATION / 1000.f);
- int dx = (int)(xvel * durationSecs) / 8;
- int dy = (int)(yvel * durationSecs) / 8;
- mCurrScroller.abortAnimation();
- scroller.startScroll(startx, starty, dx, dy, OVERFLING_DURATION);
- mCurrScroller = scroller;
- mScrollMode = MODE_OVERFLING;
- }
-
- /**
- * Call this when you want to 'spring back' into a valid coordinate range.
- *
- * @param startX Starting X coordinate
- * @param startY Starting Y coordinate
- * @param minX Minimum valid X value
- * @param maxX Maximum valid X value
- * @param minY Minimum valid Y value
- * @param maxY Minimum valid Y value
- * @return true if a springback was initiated, false if startX/startY was
- * already within the valid range.
+ * Creates an OverScroller with a viscous fluid scroll interpolator.
+ * @param context
*/
- public boolean springback(int startX, int startY, int minX, int maxX,
- int minY, int maxY) {
- return springback(startX, startY, minX, maxX, minY, maxY, mDecelScroller);
- }
-
- private boolean springback(int startX, int startY, int minX, int maxX,
- int minY, int maxY, Scroller scroller) {
- int xoff = 0;
- int yoff = 0;
- if (startX < minX) {
- xoff = minX - startX;
- } else if (startX > maxX) {
- xoff = maxX - startX;
- }
- if (startY < minY) {
- yoff = minY - startY;
- } else if (startY > maxY) {
- yoff = maxY - startY;
- }
-
- if (xoff != 0 || yoff != 0) {
- mCurrScroller.abortAnimation();
- scroller.startScroll(startX, startY, xoff, yoff, SPRINGBACK_DURATION);
- mCurrScroller = scroller;
- mScrollMode = MODE_SPRINGBACK;
- return true;
- }
-
- return false;
+ public OverScroller(Context context) {
+ this(context, null);
}
/**
- *
+ * Creates an OverScroller with default edge bounce coefficients.
+ * @param context The context of this application.
+ * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
+ * be used.
+ */
+ public OverScroller(Context context, Interpolator interpolator) {
+ this(context, interpolator, MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT,
+ MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT);
+ }
+
+ /**
+ * Creates an OverScroller.
+ * @param context The context of this application.
+ * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
+ * be used.
+ * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
+ * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
+ * means no bounce.
+ * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
+ */
+ public OverScroller(Context context, Interpolator interpolator,
+ float bounceCoefficientX, float bounceCoefficientY) {
+ mInterpolator = interpolator;
+ mScrollerX = new MagneticOverScroller();
+ mScrollerY = new MagneticOverScroller();
+ MagneticScroller.initializeFromContext(context);
+
+ mScrollerX.setBounceCoefficient(bounceCoefficientX);
+ mScrollerY.setBounceCoefficient(bounceCoefficientY);
+ }
+
+ /**
+ *
* Returns whether the scroller has finished scrolling.
- *
+ *
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
- return mCurrScroller.isFinished();
+ return mScrollerX.mFinished && mScrollerY.mFinished;
}
/**
- * Returns the current X offset in the scroll.
- *
+ * Force the finished field to a particular value. Contrary to
+ * {@link #abortAnimation()}, forcing the animation to finished
+ * does NOT cause the scroller to move to the final x and y
+ * position.
+ *
+ * @param finished The new finished value.
+ */
+ public final void forceFinished(boolean finished) {
+ mScrollerX.mFinished = mScrollerY.mFinished = finished;
+ }
+
+ /**
+ * Returns the current X offset in the scroll.
+ *
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
- return mCurrScroller.getCurrX();
+ return mScrollerX.mCurrentPosition;
}
-
+
/**
- * Returns the current Y offset in the scroll.
- *
+ * Returns the current Y offset in the scroll.
+ *
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
- return mCurrScroller.getCurrY();
+ return mScrollerY.mCurrentPosition;
}
-
+
/**
- * Stops the animation, resets any springback/overfling and completes
- * any standard flings/scrolls in progress.
+ * @hide
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
*/
- public void abortAnimation() {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mCurrScroller.abortAnimation();
+ public float getCurrVelocity() {
+ float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+ squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+ return FloatMath.sqrt(squaredNorm);
}
-
+
+ /**
+ * Returns the start X offset in the scroll.
+ *
+ * @return The start X offset as an absolute distance from the origin.
+ */
+ public final int getStartX() {
+ return mScrollerX.mStart;
+ }
+
+ /**
+ * Returns the start Y offset in the scroll.
+ *
+ * @return The start Y offset as an absolute distance from the origin.
+ */
+ public final int getStartY() {
+ return mScrollerY.mStart;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final X offset as an absolute distance from the origin.
+ */
+ public final int getFinalX() {
+ return mScrollerX.mFinal;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final Y offset as an absolute distance from the origin.
+ */
+ public final int getFinalY() {
+ return mScrollerY.mFinal;
+ }
+
+ /**
+ * Returns how long the scroll event will take, in milliseconds.
+ *
+ * @return The duration of the scroll in milliseconds.
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScrollers don't necessarily have a fixed duration.
+ * This function will lie to the best of its ability.
+ */
+ public final int getDuration() {
+ return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
+ }
+
+ /**
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+ *
+ * @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScrollers don't necessarily have a fixed duration.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ public void extendDuration(int extend) {
+ mScrollerX.extendDuration(extend);
+ mScrollerY.extendDuration(extend);
+ }
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScroller's final position may change during an animation.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ public void setFinalX(int newX) {
+ mScrollerX.setFinalPosition(newX);
+ }
+
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ *
+ * @hide Pending removal once nothing depends on it
+ * @deprecated OverScroller's final position may change during an animation.
+ * Instead of setting a new final position and extending
+ * the duration of an existing scroll, use startScroll
+ * to begin a new animation.
+ */
+ public void setFinalY(int newY) {
+ mScrollerY.setFinalPosition(newY);
+ }
+
+ /**
+ * Call this when you want to know the new location. If it returns true, the
+ * animation is not yet finished.
+ */
+ public boolean computeScrollOffset() {
+ if (isFinished()) {
+ return false;
+ }
+
+ switch (mMode) {
+ case SCROLL_MODE:
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ // Any scroller can be used for time, since they were started
+ // together in scroll mode. We use X here.
+ final long elapsedTime = time - mScrollerX.mStartTime;
+
+ final int duration = mScrollerX.mDuration;
+ if (elapsedTime < duration) {
+ float q = (float) (elapsedTime) / duration;
+
+ if (mInterpolator == null)
+ q = Scroller.viscousFluid(q);
+ else
+ q = mInterpolator.getInterpolation(q);
+
+ mScrollerX.updateScroll(q);
+ mScrollerY.updateScroll(q);
+ } else {
+ abortAnimation();
+ }
+ break;
+
+ case FLING_MODE:
+ if (!mScrollerX.mFinished) {
+ if (!mScrollerX.update()) {
+ if (!mScrollerX.continueWhenFinished()) {
+ mScrollerX.finish();
+ }
+ }
+ }
+
+ if (!mScrollerY.mFinished) {
+ if (!mScrollerY.update()) {
+ if (!mScrollerY.continueWhenFinished()) {
+ mScrollerY.finish();
+ }
+ }
+ }
+
+ break;
+ }
+
+ return true;
+ }
+
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
- * duration. This version does not spring back to boundaries.
- *
+ * duration.
+ *
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
@@ -228,51 +303,12 @@
* content up.
*/
public void startScroll(int startX, int startY, int dx, int dy) {
- final int minX = Math.min(startX, startX + dx);
- final int maxX = Math.max(startX, startX + dx);
- final int minY = Math.min(startY, startY + dy);
- final int maxY = Math.max(startY, startY + dy);
- startScroll(startX, startY, dx, dy, minX, maxX, minY, maxY);
- }
-
- /**
- * Start scrolling by providing a starting point and the distance to travel.
- * The scroll will use the default value of 250 milliseconds for the
- * duration. This version will spring back to the provided boundaries if
- * the scroll value would take it too far.
- *
- * @param startX Starting horizontal scroll offset in pixels. Positive
- * numbers will scroll the content to the left.
- * @param startY Starting vertical scroll offset in pixels. Positive numbers
- * will scroll the content up.
- * @param dx Horizontal distance to travel. Positive numbers will scroll the
- * content to the left.
- * @param dy Vertical distance to travel. Positive numbers will scroll the
- * content up.
- * @param minX Minimum X value. The scroller will not scroll past this
- * point.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
- */
- public void startScroll(int startX, int startY, int dx, int dy,
- int minX, int maxX, int minY, int maxY) {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = minX;
- mMaximumX = maxX;
- mMinimumY = minY;
- mMaximumY = maxY;
- mCurrScroller.startScroll(startX, startY, dx, dy);
+ startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
/**
* Start scrolling by providing a starting point and the distance to travel.
- *
+ *
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
@@ -284,166 +320,363 @@
* @param duration Duration of the scroll in milliseconds.
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
- mCurrScroller.abortAnimation();
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = Math.min(startX, startX + dx);
- mMinimumY = Math.min(startY, startY + dy);
- mMaximumX = Math.max(startX, startX + dx);
- mMaximumY = Math.max(startY, startY + dy);
- mCurrScroller.startScroll(startX, startY, dx, dy, duration);
- }
-
- /**
- * Returns the duration of the active scroll in progress; standard, fling,
- * springback, or overfling. Does not account for any overflings or springback
- * that may result.
- */
- public int getDuration() {
- return mCurrScroller.getDuration();
+ mMode = SCROLL_MODE;
+ mScrollerX.startScroll(startX, dx, duration);
+ mScrollerY.startScroll(startY, dy, duration);
}
/**
- * Start scrolling based on a fling gesture. The distance travelled will
- * depend on the initial velocity of the fling.
- *
- * @param startX Starting point of the scroll (X)
- * @param startY Starting point of the scroll (Y)
- * @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
- * @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
- * @param minX Minimum X value. The scroller will not scroll past this
- * point.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
+ * Call this when you want to 'spring back' into a valid coordinate range.
+ *
+ * @param startX Starting X coordinate
+ * @param startY Starting Y coordinate
+ * @param minX Minimum valid X value
+ * @param maxX Maximum valid X value
+ * @param minY Minimum valid Y value
+ * @param maxY Minimum valid Y value
+ * @return true if a springback was initiated, false if startX and startY were
+ * already within the valid range.
*/
+ public boolean springback(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+ mMode = FLING_MODE;
+
+ // Make sure both methods are called.
+ final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
+ final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
+ return spingbackX || spingbackY;
+ }
+
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
- this.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
+ fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
/**
- * Start scrolling based on a fling gesture. The distance travelled will
+ * Start scrolling based on a fling gesture. The distance traveled will
* depend on the initial velocity of the fling.
- *
+ *
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
+ * second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
- * @param minX Minimum X value. The scroller will not scroll past this
- * point unless overX > 0. If overfling is allowed, it will use minX
- * as a springback boundary.
- * @param maxX Maximum X value. The scroller will not scroll past this
- * point unless overX > 0. If overfling is allowed, it will use maxX
- * as a springback boundary.
- * @param minY Minimum Y value. The scroller will not scroll past this
- * point unless overY > 0. If overfling is allowed, it will use minY
- * as a springback boundary.
- * @param maxY Maximum Y value. The scroller will not scroll past this
- * point unless overY > 0. If overfling is allowed, it will use maxY
- * as a springback boundary.
+ * second
+ * @param minX Minimum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use minX as
+ * a springback boundary.
+ * @param maxX Maximum X value. The scroller will not scroll past this point
+ * unless overX > 0. If overfling is allowed, it will use maxX as
+ * a springback boundary.
+ * @param minY Minimum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use minY as
+ * a springback boundary.
+ * @param maxY Maximum Y value. The scroller will not scroll past this point
+ * unless overY > 0. If overfling is allowed, it will use maxY as
+ * a springback boundary.
* @param overX Overfling range. If > 0, horizontal overfling in either
- * direction will be possible.
+ * direction will be possible.
* @param overY Overfling range. If > 0, vertical overfling in either
- * direction will be possible.
+ * direction will be possible.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY, int overX, int overY) {
- mCurrScroller = mDefaultScroller;
- mScrollMode = MODE_DEFAULT;
- mMinimumX = minX;
- mMaximumX = maxX;
- mMinimumY = minY;
- mMaximumY = maxY;
- mCurrScroller.fling(startX, startY, velocityX, velocityY,
- minX - overX, maxX + overX, minY - overY, maxY + overY);
+ mMode = FLING_MODE;
+ mScrollerX.fling(startX, velocityX, minX, maxX, overX);
+ mScrollerY.fling(startY, velocityY, minY, maxY, overY);
}
/**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
- *
- * @return The final X offset as an absolute distance from the origin.
+ * Notify the scroller that we've reached a horizontal boundary.
+ * Normally the information to handle this will already be known
+ * when the animation is started, such as in a call to one of the
+ * fling functions. However there are cases where this cannot be known
+ * in advance. This function will transition the current motion and
+ * animate from startX to finalX as appropriate.
+ *
+ * @param startX Starting/current X position
+ * @param finalX Desired final X position
+ * @param overX Magnitude of overscroll allowed. This should be the maximum
+ * desired distance from finalX. Absolute value - must be positive.
*/
- public int getFinalX() {
- return mCurrScroller.getFinalX();
+ public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
+ mScrollerX.notifyEdgeReached(startX, finalX, overX);
}
-
+
/**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
- *
- * @return The final Y offset as an absolute distance from the origin.
+ * Notify the scroller that we've reached a vertical boundary.
+ * Normally the information to handle this will already be known
+ * when the animation is started, such as in a call to one of the
+ * fling functions. However there are cases where this cannot be known
+ * in advance. This function will animate a parabolic motion from
+ * startY to finalY.
+ *
+ * @param startY Starting/current Y position
+ * @param finalY Desired final Y position
+ * @param overY Magnitude of overscroll allowed. This should be the maximum
+ * desired distance from finalY.
*/
- public int getFinalY() {
- return mCurrScroller.getFinalY();
+ public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
+ mScrollerY.notifyEdgeReached(startY, finalY, overY);
}
-
+
/**
+ * Returns whether the current Scroller is currently returning to a valid position.
+ * Valid bounds were provided by the
+ * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
+ *
+ * One should check this value before calling
+ * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
+ * to restore a valid position will then be stopped. The caller has to take into account
+ * the fact that the started scroll will start from an overscrolled position.
+ *
+ * @return true when the current position is overscrolled and in the process of
+ * interpolating back to a valid value.
+ */
+ public boolean isOverscrolled() {
+ return ((!mScrollerX.mFinished &&
+ mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
+ (!mScrollerY.mFinished &&
+ mScrollerY.mState != MagneticOverScroller.TO_EDGE));
+ }
+
+ /**
+ * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+ * aborting the animating causes the scroller to move to the final x and y
+ * positions.
+ *
+ * @see #forceFinished(boolean)
+ */
+ public void abortAnimation() {
+ mScrollerX.finish();
+ mScrollerY.finish();
+ }
+
+ /**
+ * Returns the time elapsed since the beginning of the scrolling.
+ *
+ * @return The elapsed time in milliseconds.
+ *
* @hide
- * Returns the current velocity.
- *
- * @return The original velocity less the deceleration. Result may be
- * negative.
*/
- public float getCurrVelocity() {
- return mCurrScroller.getCurrVelocity();
+ public int timePassed() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+ return (int) (time - startTime);
}
-
- /**
- * Extend the scroll animation. This allows a running animation to scroll
- * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
- *
- * @param extend Additional time to scroll in milliseconds.
- * @see #setFinalX(int)
- * @see #setFinalY(int)
- */
- public void extendDuration(int extend) {
- if (mScrollMode == MODE_DEFAULT) {
- mDefaultScroller.extendDuration(extend);
+
+ static class MagneticOverScroller extends Scroller.MagneticScroller {
+ private static final int TO_EDGE = 0;
+ private static final int TO_BOUNDARY = 1;
+ private static final int TO_BOUNCE = 2;
+
+ private int mState = TO_EDGE;
+
+ // The allowed overshot distance before boundary is reached.
+ private int mOver;
+
+ // Duration in milliseconds to go back from edge to edge. Springback is half of it.
+ private static final int OVERSCROLL_SPRINGBACK_DURATION = 200;
+
+ // Oscillation period
+ private static final float TIME_COEF =
+ 1000.0f * (float) Math.PI / OVERSCROLL_SPRINGBACK_DURATION;
+
+ // If the velocity is smaller than this value, no bounce is triggered
+ // when the edge limits are reached (would result in a zero pixels
+ // displacement anyway).
+ private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f;
+
+ // Proportion of the velocity that is preserved when the edge is reached.
+ private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f;
+
+ private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
+
+ void setBounceCoefficient(float coefficient) {
+ mBounceCoefficient = coefficient;
}
- }
-
- /**
- * Sets the final position (X) for this scroller.
- *
- * @param newX The new X offset as an absolute distance from the origin.
- * @see #extendDuration(int)
- * @see #setFinalY(int)
- */
- public void setFinalX(int newX) {
- if (mScrollMode == MODE_DEFAULT) {
- if (newX < mMinimumX) {
- mMinimumX = newX;
+
+ boolean springback(int start, int min, int max) {
+ mFinished = true;
+
+ mStart = start;
+ mVelocity = 0;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = 0;
+
+ if (start < min) {
+ startSpringback(start, min, false);
+ } else if (start > max) {
+ startSpringback(start, max, true);
}
- if (newX > mMaximumX) {
- mMaximumX = newX;
- }
- mDefaultScroller.setFinalX(newX);
+
+ return !mFinished;
}
- }
-
- /**
- * Sets the final position (Y) for this scroller.
- *
- * @param newY The new Y offset as an absolute distance from the origin.
- * @see #extendDuration(int)
- * @see #setFinalX(int)
- */
- public void setFinalY(int newY) {
- if (mScrollMode == MODE_DEFAULT) {
- if (newY < mMinimumY) {
- mMinimumY = newY;
+
+ private void startSpringback(int start, int end, boolean positive) {
+ mFinished = false;
+ mState = TO_BOUNCE;
+ mStart = mFinal = end;
+ mDuration = OVERSCROLL_SPRINGBACK_DURATION;
+ mStartTime -= OVERSCROLL_SPRINGBACK_DURATION / 2;
+ mVelocity = (int) (Math.abs(end - start) * TIME_COEF * (positive ? 1.0 : -1.0f));
+ }
+
+ void fling(int start, int velocity, int min, int max, int over) {
+ mState = TO_EDGE;
+ mOver = over;
+
+ super.fling(start, velocity, min, max);
+
+ if (start > max) {
+ if (start >= max + over) {
+ springback(max + over, min, max);
+ } else {
+ if (velocity <= 0) {
+ springback(start, min, max);
+ } else {
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ final double durationSinceEdge =
+ Math.atan((start-max) * TIME_COEF / velocity) / TIME_COEF;
+ mStartTime = (int) (time - 1000.0f * durationSinceEdge);
+
+ // Simulate a bounce that started from edge
+ mStart = max;
+
+ mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
+
+ onEdgeReached();
+ }
+ }
+ } else {
+ if (start < min) {
+ if (start <= min - over) {
+ springback(min - over, min, max);
+ } else {
+ if (velocity >= 0) {
+ springback(start, min, max);
+ } else {
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ final double durationSinceEdge =
+ Math.atan((start-min) * TIME_COEF / velocity) / TIME_COEF;
+ mStartTime = (int) (time - 1000.0f * durationSinceEdge);
+
+ // Simulate a bounce that started from edge
+ mStart = min;
+
+ mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
+
+ onEdgeReached();
+ }
+
+ }
+ }
}
- if (newY > mMaximumY) {
- mMaximumY = newY;
+ }
+
+ void notifyEdgeReached(int start, int end, int over) {
+ mDeceleration = getDeceleration(mVelocity);
+
+ // Local time, used to compute edge crossing time.
+ float timeCurrent = mCurrVelocity / mDeceleration;
+ final int distance = end - start;
+ float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration)
+ + (timeCurrent * timeCurrent));
+
+ mVelocity = (int) (mDeceleration * timeEdge);
+
+ // Simulate a symmetric bounce that started from edge
+ mStart = end;
+
+ mOver = over;
+
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge));
+
+ onEdgeReached();
+ }
+
+ private void onEdgeReached() {
+ // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
+ final float distance = mVelocity / TIME_COEF;
+
+ if (Math.abs(distance) < mOver) {
+ // Spring force will bring us back to final position
+ mState = TO_BOUNCE;
+ mFinal = mStart;
+ mDuration = OVERSCROLL_SPRINGBACK_DURATION;
+ } else {
+ // Velocity is too high, we will hit the boundary limit
+ mState = TO_BOUNDARY;
+ int over = mVelocity > 0 ? mOver : -mOver;
+ mFinal = mStart + over;
+ mDuration = (int) (1000.0f * Math.asin(over / distance) / TIME_COEF);
}
- mDefaultScroller.setFinalY(newY);
+ }
+
+ @Override
+ boolean continueWhenFinished() {
+ switch (mState) {
+ case TO_EDGE:
+ // Duration from start to null velocity
+ int duration = (int) (-1000.0f * mVelocity / mDeceleration);
+ if (mDuration < duration) {
+ // If the animation was clamped, we reached the edge
+ mStart = mFinal;
+ // Speed when edge was reached
+ mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f);
+ mStartTime += mDuration;
+ onEdgeReached();
+ } else {
+ // Normal stop, no need to continue
+ return false;
+ }
+ break;
+ case TO_BOUNDARY:
+ mStartTime += mDuration;
+ startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), mVelocity > 0);
+ break;
+ case TO_BOUNCE:
+ //mVelocity = (int) (mVelocity * BOUNCE_COEFFICIENT);
+ mVelocity = (int) (mVelocity * mBounceCoefficient);
+ if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
+ return false;
+ }
+ mStartTime += mDuration;
+ break;
+ }
+
+ update();
+ return true;
+ }
+
+ /*
+ * Update the current position and velocity for current time. Returns
+ * true if update has been done and false if animation duration has been
+ * reached.
+ */
+ @Override
+ boolean update() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long duration = time - mStartTime;
+
+ if (duration > mDuration) {
+ return false;
+ }
+
+ double distance;
+ final float t = duration / 1000.0f;
+ if (mState == TO_EDGE) {
+ mCurrVelocity = mVelocity + mDeceleration * t;
+ distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+ } else {
+ final float d = t * TIME_COEF;
+ mCurrVelocity = mVelocity * (float)Math.cos(d);
+ distance = mVelocity / TIME_COEF * Math.sin(d);
+ }
+
+ mCurrentPosition = mStart + (int) distance;
+ return true;
}
}
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index cf2ed86..0378328 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1048,16 +1048,18 @@
if (isShowing() && mPopupView != null) {
unregisterForScrollChanged();
- mWindowManager.removeView(mPopupView);
-
- if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
- ((ViewGroup) mPopupView).removeView(mContentView);
- }
- mPopupView = null;
- mIsShowing = false;
-
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss();
+ try {
+ mWindowManager.removeView(mPopupView);
+ } finally {
+ if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
+ ((ViewGroup) mPopupView).removeView(mContentView);
+ }
+ mPopupView = null;
+ mIsShowing = false;
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index d7dfa86..23e1234 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -370,9 +370,10 @@
private boolean inChild(int x, int y) {
if (getChildCount() > 0) {
+ final int scrollY = mScrollY;
final View child = getChildAt(0);
- return !(y < child.getTop()
- || y >= child.getBottom()
+ return !(y < child.getTop() - scrollY
+ || y >= child.getBottom() - scrollY
|| x < child.getLeft()
|| x >= child.getRight());
}
@@ -408,7 +409,13 @@
* Locally do absolute value. mLastMotionY is set to the y value
* of the down event.
*/
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final int activePointerId = mActivePointerId;
+ if (activePointerId == INVALID_POINTER) {
+ // If we don't have a valid id, the touch down wasn't on content.
+ break;
+ }
+
+ final int pointerIndex = ev.findPointerIndex(activePointerId);
final float y = ev.getY(pointerIndex);
final int yDiff = (int) Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop) {
@@ -446,6 +453,9 @@
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -477,19 +487,19 @@
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
-
final float y = ev.getY();
if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) {
return false;
}
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
// Remember where the motion event started
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
@@ -506,7 +516,7 @@
final int oldX = mScrollX;
final int oldY = mScrollY;
overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
- 0, getOverscrollMax());
+ 0, getOverscrollMax(), true);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
break;
@@ -536,6 +546,19 @@
}
}
break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
+ invalidate();
+ }
+ mActivePointerId = INVALID_POINTER;
+ mIsBeingDragged = false;
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+ break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
@@ -573,6 +596,7 @@
} else {
super.scrollTo(scrollX, scrollY);
}
+ awakenScrollBars();
}
private int getOverscrollMax() {
@@ -939,7 +963,6 @@
dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
mScroller.startScroll(mScrollX, scrollY, 0, dy);
- awakenScrollBars(mScroller.getDuration());
invalidate();
} else {
if (!mScroller.isFinished()) {
@@ -1044,7 +1067,7 @@
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
- 0, getOverscrollMax());
+ 0, getOverscrollMax(), false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
@@ -1272,7 +1295,7 @@
* Fling the scroll view
*
* @param velocityY The initial velocity in the Y direction. Positive
- * numbers mean that the finger/curor is moving down the screen,
+ * numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
public void fling(int velocityY) {
@@ -1297,7 +1320,6 @@
mScrollViewMovedFocus = false;
}
- awakenScrollBars(mScroller.getDuration());
invalidate();
}
}
@@ -1307,6 +1329,7 @@
*
* <p>This version also clamps the scrolling to the bounds of our child.
*/
+ @Override
public void scrollTo(int x, int y) {
// we rely on the fact the View.scrollBy calls scrollTo.
if (getChildCount() > 0) {
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 11dab02..abb3e32 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -16,8 +16,10 @@
package android.widget;
+
import android.content.Context;
import android.hardware.SensorManager;
+import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -25,48 +27,34 @@
/**
* This class encapsulates scrolling. The duration of the scroll
- * can be passed in the constructor and specifies the maximum time that
- * the scrolling animation should take. Past this time, the scrolling is
- * automatically moved to its final stage and computeScrollOffset()
- * will always return false to indicate that scrolling is over.
+ * is either specified along with the distance or depends on the initial fling velocity.
+ * Past this time, the scrolling is automatically moved to its final stage and
+ * computeScrollOffset() will always return false to indicate that scrolling is over.
*/
public class Scroller {
private int mMode;
- private int mStartX;
- private int mStartY;
- private int mFinalX;
- private int mFinalY;
+ private MagneticScroller mScrollerX;
+ private MagneticScroller mScrollerY;
- private int mMinX;
- private int mMaxX;
- private int mMinY;
- private int mMaxY;
-
- private int mCurrX;
- private int mCurrY;
- private long mStartTime;
- private int mDuration;
- private float mDurationReciprocal;
- private float mDeltaX;
- private float mDeltaY;
- private float mViscousFluidScale;
- private float mViscousFluidNormalize;
- private boolean mFinished;
- private Interpolator mInterpolator;
-
- private float mCoeffX = 0.0f;
- private float mCoeffY = 1.0f;
- private float mVelocity;
+ private final Interpolator mInterpolator;
private static final int DEFAULT_DURATION = 250;
private static final int SCROLL_MODE = 0;
private static final int FLING_MODE = 1;
- private final float mDeceleration;
+ // This controls the viscous fluid effect (how much of it)
+ private final static float VISCOUS_FLUID_SCALE = 8.0f;
+ private static float VISCOUS_FLUID_NORMALIZE;
+
+ static {
+ // Set a neutral value that will be used in the next call to viscousFluid().
+ VISCOUS_FLUID_NORMALIZE = 1.0f;
+ VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
+ }
/**
- * Create a Scroller with the default duration and interpolator.
+ * Create a Scroller with a viscous fluid scroll interpolator.
*/
public Scroller(Context context) {
this(context, null);
@@ -77,15 +65,13 @@
* null, the default (viscous) interpolator will be used.
*/
public Scroller(Context context, Interpolator interpolator) {
- mFinished = true;
+ mScrollerX = new MagneticScroller();
+ mScrollerY = new MagneticScroller();
+ MagneticScroller.initializeFromContext(context);
+
mInterpolator = interpolator;
- float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
- mDeceleration = SensorManager.GRAVITY_EARTH // g (m/s^2)
- * 39.37f // inch/meter
- * ppi // pixels per inch
- * ViewConfiguration.getScrollFriction();
}
-
+
/**
*
* Returns whether the scroller has finished scrolling.
@@ -93,150 +79,148 @@
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
- return mFinished;
+ return mScrollerX.mFinished && mScrollerY.mFinished;
}
-
+
/**
* Force the finished field to a particular value.
- *
+ *
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
- mFinished = finished;
+ mScrollerX.mFinished = mScrollerY.mFinished = finished;
}
-
+
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
- return mDuration;
+ return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
}
-
+
/**
- * Returns the current X offset in the scroll.
+ * Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
- return mCurrX;
+ return mScrollerX.mCurrentPosition;
}
-
+
/**
- * Returns the current Y offset in the scroll.
+ * Returns the current Y offset in the scroll.
*
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
- return mCurrY;
- }
-
- /**
- * @hide
- * Returns the current velocity.
- *
- * @return The original velocity less the deceleration. Result may be
- * negative.
- */
- public float getCurrVelocity() {
- return mVelocity - mDeceleration * timePassed() / 2000.0f;
+ return mScrollerY.mCurrentPosition;
}
/**
- * Returns the start X offset in the scroll.
+ * @hide
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
+ */
+ public float getCurrVelocity() {
+ float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
+ squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
+ return FloatMath.sqrt(squaredNorm);
+ }
+
+ /**
+ * Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final int getStartX() {
- return mStartX;
+ return mScrollerX.mStart;
}
-
+
/**
- * Returns the start Y offset in the scroll.
+ * Returns the start Y offset in the scroll.
*
* @return The start Y offset as an absolute distance from the origin.
*/
public final int getStartY() {
- return mStartY;
+ return mScrollerY.mStart;
}
-
+
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final X offset as an absolute distance from the origin.
*/
public final int getFinalX() {
- return mFinalX;
+ return mScrollerX.mFinal;
}
-
+
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final Y offset as an absolute distance from the origin.
*/
public final int getFinalY() {
- return mFinalY;
+ return mScrollerY.mFinal;
}
/**
- * Call this when you want to know the new location. If it returns true,
- * the animation is not yet finished. loc will be altered to provide the
- * new location.
- */
+ * Call this when you want to know the new location. If it returns true, the
+ * animation is not yet finished.
+ */
public boolean computeScrollOffset() {
- if (mFinished) {
+ if (isFinished()) {
return false;
}
- int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
-
- if (timePassed < mDuration) {
- switch (mMode) {
+ switch (mMode) {
case SCROLL_MODE:
- float x = (float)timePassed * mDurationReciprocal;
-
- if (mInterpolator == null)
- x = viscousFluid(x);
- else
- x = mInterpolator.getInterpolation(x);
-
- mCurrX = mStartX + Math.round(x * mDeltaX);
- mCurrY = mStartY + Math.round(x * mDeltaY);
- if ((mCurrX == mFinalX) && (mCurrY == mFinalY)) {
- mFinished = true;
- }
- break;
- case FLING_MODE:
- float timePassedSeconds = timePassed / 1000.0f;
- float distance = (mVelocity * timePassedSeconds)
- - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
-
- mCurrX = mStartX + Math.round(distance * mCoeffX);
- // Pin to mMinX <= mCurrX <= mMaxX
- mCurrX = Math.min(mCurrX, mMaxX);
- mCurrX = Math.max(mCurrX, mMinX);
-
- mCurrY = mStartY + Math.round(distance * mCoeffY);
- // Pin to mMinY <= mCurrY <= mMaxY
- mCurrY = Math.min(mCurrY, mMaxY);
- mCurrY = Math.max(mCurrY, mMinY);
+ long time = AnimationUtils.currentAnimationTimeMillis();
+ // Any scroller can be used for time, since they were started
+ // together in scroll mode. We use X here.
+ final long elapsedTime = time - mScrollerX.mStartTime;
- if (mCurrX == mFinalX && mCurrY == mFinalY) {
- mFinished = true;
+ final int duration = mScrollerX.mDuration;
+ if (elapsedTime < duration) {
+ float q = (float) (elapsedTime) / duration;
+
+ if (mInterpolator == null)
+ q = viscousFluid(q);
+ else
+ q = mInterpolator.getInterpolation(q);
+
+ mScrollerX.updateScroll(q);
+ mScrollerY.updateScroll(q);
+ } else {
+ abortAnimation();
}
-
break;
- }
+
+ case FLING_MODE:
+ if (!mScrollerX.mFinished) {
+ if (!mScrollerX.update()) {
+ if (!mScrollerX.continueWhenFinished()) {
+ mScrollerX.finish();
+ }
+ }
+ }
+
+ if (!mScrollerY.mFinished) {
+ if (!mScrollerY.update()) {
+ if (!mScrollerY.continueWhenFinished()) {
+ mScrollerY.finish();
+ }
+ }
+ }
+
+ break;
}
- else {
- mCurrX = mFinalX;
- mCurrY = mFinalY;
- mFinished = true;
- }
+
return true;
}
-
+
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
@@ -270,83 +254,39 @@
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
- mFinished = false;
- mDuration = duration;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStartX = startX;
- mStartY = startY;
- mFinalX = startX + dx;
- mFinalY = startY + dy;
- mDeltaX = dx;
- mDeltaY = dy;
- mDurationReciprocal = 1.0f / (float) mDuration;
- // This controls the viscous fluid effect (how much of it)
- mViscousFluidScale = 8.0f;
- // must be set to 1.0 (used in viscousFluid())
- mViscousFluidNormalize = 1.0f;
- mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+ mScrollerX.startScroll(startX, dx, duration);
+ mScrollerY.startScroll(startY, dy, duration);
}
/**
- * Start scrolling based on a fling gesture. The distance travelled will
- * depend on the initial velocity of the fling.
+ * Start scrolling based on a fling gesture. The distance traveled will
+ * depend on the initial velocity of the fling. Velocity is slowed down by a
+ * constant deceleration until it reaches 0 or the limits are reached.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
- * second.
+ * second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
- * second
+ * second.
* @param minX Minimum X value. The scroller will not scroll past this
- * point.
+ * point.
* @param maxX Maximum X value. The scroller will not scroll past this
- * point.
+ * point.
* @param minY Minimum Y value. The scroller will not scroll past this
- * point.
+ * point.
* @param maxY Maximum Y value. The scroller will not scroll past this
- * point.
+ * point.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
mMode = FLING_MODE;
- mFinished = false;
-
- float velocity = (float)Math.hypot(velocityX, velocityY);
-
- mVelocity = velocity;
- mDuration = (int) (1000 * velocity / mDeceleration); // Duration is in
- // milliseconds
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStartX = startX;
- mStartY = startY;
-
- mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity;
- mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
-
- int totalDistance = (int) ((velocity * velocity) / (2 * mDeceleration));
-
- mMinX = minX;
- mMaxX = maxX;
- mMinY = minY;
- mMaxY = maxY;
-
-
- mFinalX = startX + Math.round(totalDistance * mCoeffX);
- // Pin to mMinX <= mFinalX <= mMaxX
- mFinalX = Math.min(mFinalX, mMaxX);
- mFinalX = Math.max(mFinalX, mMinX);
-
- mFinalY = startY + Math.round(totalDistance * mCoeffY);
- // Pin to mMinY <= mFinalY <= mMaxY
- mFinalY = Math.min(mFinalY, mMaxY);
- mFinalY = Math.max(mFinalY, mMinY);
+ mScrollerX.fling(startX, velocityX, minX, maxX);
+ mScrollerY.fling(startY, velocityY, minY, maxY);
}
-
-
-
- private float viscousFluid(float x)
- {
- x *= mViscousFluidScale;
+
+ static float viscousFluid(float x) {
+ x *= VISCOUS_FLUID_SCALE;
if (x < 1.0f) {
x -= (1.0f - (float)Math.exp(-x));
} else {
@@ -354,70 +294,237 @@
x = 1.0f - (float)Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
- x *= mViscousFluidNormalize;
+ x *= VISCOUS_FLUID_NORMALIZE;
return x;
}
-
+
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
- *
+ *
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
- mCurrX = mFinalX;
- mCurrY = mFinalY;
- mFinished = true;
+ mScrollerX.finish();
+ mScrollerY.finish();
}
-
+
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
- *
+ *
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*/
public void extendDuration(int extend) {
- int passed = timePassed();
- mDuration = passed + extend;
- mDurationReciprocal = 1.0f / (float)mDuration;
- mFinished = false;
+ mScrollerX.extendDuration(extend);
+ mScrollerY.extendDuration(extend);
}
/**
* Returns the time elapsed since the beginning of the scrolling.
- *
+ *
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
- return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
+ return (int) (time - startTime);
}
/**
* Sets the final position (X) for this scroller.
- *
+ *
* @param newX The new X offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalY(int)
*/
public void setFinalX(int newX) {
- mFinalX = newX;
- mDeltaX = mFinalX - mStartX;
- mFinished = false;
+ mScrollerX.setFinalPosition(newX);
}
/**
* Sets the final position (Y) for this scroller.
- *
+ *
* @param newY The new Y offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalX(int)
*/
public void setFinalY(int newY) {
- mFinalY = newY;
- mDeltaY = mFinalY - mStartY;
- mFinished = false;
+ mScrollerY.setFinalPosition(newY);
+ }
+
+ static class MagneticScroller {
+ // Initial position
+ int mStart;
+
+ // Current position
+ int mCurrentPosition;
+
+ // Final position
+ int mFinal;
+
+ // Initial velocity
+ int mVelocity;
+
+ // Current velocity
+ float mCurrVelocity;
+
+ // Constant current deceleration
+ float mDeceleration;
+
+ // Animation starting time, in system milliseconds
+ long mStartTime;
+
+ // Animation duration, in milliseconds
+ int mDuration;
+
+ // Whether the animation is currently in progress
+ boolean mFinished;
+
+ // Constant gravity value, used to scale deceleration
+ static float GRAVITY;
+
+ static void initializeFromContext(Context context) {
+ final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
+ GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * ppi // pixels per inch
+ * ViewConfiguration.getScrollFriction();
+ }
+
+ MagneticScroller() {
+ mFinished = true;
+ }
+
+ void updateScroll(float q) {
+ mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
+ }
+
+ /*
+ * Update the current position and velocity for current time. Returns
+ * true if update has been done and false if animation duration has been
+ * reached.
+ */
+ boolean update() {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final long duration = time - mStartTime;
+
+ if (duration > mDuration) {
+ return false;
+ }
+
+ final float t = duration / 1000.0f;
+ mCurrVelocity = mVelocity + mDeceleration * t;
+ final float distance = mVelocity * t + mDeceleration * t * t / 2.0f;
+ mCurrentPosition = mStart + (int) distance;
+
+ return true;
+ }
+
+ /*
+ * Get a signed deceleration that will reduce the velocity.
+ */
+ static float getDeceleration(int velocity) {
+ return velocity > 0 ? -GRAVITY : GRAVITY;
+ }
+
+ /*
+ * Returns the time (in milliseconds) it will take to go from start to end.
+ */
+ static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
+ final int distance = start - end;
+ final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
+ * distance;
+ if (discriminant >= 0.0f) {
+ float delta = (float) Math.sqrt(discriminant);
+ if (deceleration < 0.0f) {
+ delta = -delta;
+ }
+ return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
+ }
+
+ // End position can not be reached
+ return 0;
+ }
+
+ void startScroll(int start, int distance, int duration) {
+ mFinished = false;
+
+ mStart = start;
+ mFinal = start + distance;
+
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mDuration = duration;
+
+ // Unused
+ mDeceleration = 0.0f;
+ mVelocity = 0;
+ }
+
+ void fling(int start, int velocity, int min, int max) {
+ mFinished = false;
+
+ mStart = start;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+
+ mVelocity = velocity;
+
+ mDeceleration = getDeceleration(velocity);
+
+ // A start from an invalid position immediately brings back to a valid position
+ if (mStart < min) {
+ mDuration = 0;
+ mFinal = min;
+ return;
+ }
+
+ if (mStart > max) {
+ mDuration = 0;
+ mFinal = max;
+ return;
+ }
+
+ // Duration are expressed in milliseconds
+ mDuration = (int) (-1000.0f * velocity / mDeceleration);
+
+ mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
+
+ // Clamp to a valid final position
+ if (mFinal < min) {
+ mFinal = min;
+ mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
+ }
+
+ if (mFinal > max) {
+ mFinal = max;
+ mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
+ }
+ }
+
+ void finish() {
+ mCurrentPosition = mFinal;
+ // Not reset since WebView relies on this value for fast fling.
+ // mCurrVelocity = 0.0f;
+ mFinished = true;
+ }
+
+ boolean continueWhenFinished() {
+ return false;
+ }
+
+ void setFinalPosition(int position) {
+ mFinal = position;
+ mFinished = false;
+ }
+
+ void extendDuration(int extend) {
+ final long time = AnimationUtils.currentAnimationTimeMillis();
+ final int elapsedTime = (int) (time - mStartTime);
+ mDuration = elapsedTime + extend;
+ mFinished = false;
+ }
}
}
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index 479965a..4b17a92 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -172,6 +172,10 @@
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
+ } else if (v instanceof TextView) {
+ // Note: keep the instanceof TextView check at the bottom of these
+ // ifs since a lot of views are TextViews (e.g. CheckBoxes).
+ setViewText((TextView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() +
" should be bound to a Boolean, not a " +
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 3328c13..52a560c 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -216,23 +216,23 @@
}
/**
- * Sets the drawable to use as the left part of the strip below the
+ * Sets the drawable to use as the right part of the strip below the
* tab indicators.
- * @param drawable the left strip drawable
+ * @param drawable the right strip drawable
*/
public void setRightStripDrawable(Drawable drawable) {
- mBottomLeftStrip = drawable;
+ mBottomRightStrip = drawable;
requestLayout();
invalidate(); }
/**
- * Sets the drawable to use as the left part of the strip below the
+ * Sets the drawable to use as the right part of the strip below the
* tab indicators.
* @param resId the resource identifier of the drawable to use as the
- * left strip drawable
+ * right strip drawable
*/
public void setRightStripDrawable(int resId) {
- mBottomLeftStrip = mContext.getResources().getDrawable(resId);
+ mBottomRightStrip = mContext.getResources().getDrawable(resId);
requestLayout();
invalidate();
}
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ded0559..531d9fe 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -64,6 +64,7 @@
private static final int STATE_PLAYBACK_COMPLETED = 5;
private static final int STATE_SUSPEND = 6;
private static final int STATE_RESUME = 7;
+ private static final int STATE_SUSPEND_UNSUPPORTED = 8;
// mCurrentState is a VideoView object's current state.
// mTargetState is the state that a method caller intends to reach.
@@ -586,8 +587,9 @@
mCurrentState = STATE_SUSPEND;
mTargetState = STATE_SUSPEND;
} else {
- Log.w(TAG, "Unable to suspend video");
- mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+ release(false);
+ mCurrentState = STATE_SUSPEND_UNSUPPORTED;
+ Log.w(TAG, "Unable to suspend video. Release MediaPlayer.");
}
}
}
@@ -603,8 +605,11 @@
mTargetState = mStateWhenSuspended;
} else {
Log.w(TAG, "Unable to resume video");
- mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
}
+ return;
+ }
+ if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) {
+ openVideo();
}
}
diff --git a/core/java/com/android/internal/app/DisableCarModeActivity.java b/core/java/com/android/internal/app/DisableCarModeActivity.java
index 95dc1f9..7943c61 100644
--- a/core/java/com/android/internal/app/DisableCarModeActivity.java
+++ b/core/java/com/android/internal/app/DisableCarModeActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.IUiModeManager;
+import android.app.UiModeManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -32,7 +33,7 @@
try {
IUiModeManager uiModeManager = IUiModeManager.Stub.asInterface(
ServiceManager.getService("uimode"));
- uiModeManager.disableCarMode();
+ uiModeManager.disableCarMode(UiModeManager.DISABLE_CAR_MODE_GO_HOME);
} catch (RemoteException e) {
Log.e(TAG, "Failed to disable car mode", e);
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index fd1fd58..0f817b7 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -26,5 +26,6 @@
String key, String resFileName);
boolean copyResource(in Uri packageURI,
in ParcelFileDescriptor outStream);
- PackageInfoLite getMinimalPackageInfo(in Uri fileUri);
+ PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
+ boolean checkFreeStorage(boolean external, in Uri fileUri);
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 24818a8..03b0957 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -30,7 +30,6 @@
import android.widget.Toast;
import android.util.Log;
import android.location.LocationManager;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
/**
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 9e4b606..b436363 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -105,6 +105,9 @@
+ " key64=" + base64Key);
if (dataSize >= 0) {
+ if (entityFile.exists()) {
+ entityFile.delete();
+ }
FileOutputStream entity = new FileOutputStream(entityFile);
if (dataSize > bufSize) {
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 80efca4..4d0a9e0 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -38,6 +38,7 @@
public static final int RECOMMEND_FAILED_INVALID_APK = -2;
public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3;
public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
+ public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
private static final boolean localLOGV = true;
private static final String TAG = "PackageHelper";
// App installation location settings values
@@ -70,7 +71,7 @@
try {
int rc = mountService.createSecureContainer(
- cid, mbLen, "vfat", sdEncKey, uid);
+ cid, mbLen, "fat", sdEncKey, uid);
if (rc != StorageResultCode.OperationSucceeded) {
Log.e(TAG, "Failed to create secure container " + cid);
return null;
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
index ca0345f..e675ef0 100644
--- a/core/java/com/android/internal/os/AtomicFile.java
+++ b/core/java/com/android/internal/os/AtomicFile.java
@@ -45,12 +45,13 @@
public FileOutputStream startWrite() throws IOException {
// Rename the current file so it may be used as a backup during the next read
if (mBaseName.exists()) {
- if (!mBaseName.renameTo(mBackupName)) {
- mBackupName.delete();
+ if (!mBackupName.exists()) {
if (!mBaseName.renameTo(mBackupName)) {
Log.w("AtomicFile", "Couldn't rename file " + mBaseName
+ " to backup file " + mBackupName);
}
+ } else {
+ mBaseName.delete();
}
}
FileOutputStream str = null;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 71ccb3b..24275ec 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import com.android.internal.util.JournaledFile;
+
import android.bluetooth.BluetoothHeadset;
import android.net.TrafficStats;
import android.os.BatteryStats;
@@ -30,6 +32,7 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
+import android.util.Slog;
import android.util.SparseArray;
import java.io.BufferedReader;
@@ -43,6 +46,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* All information we are collecting about things that can happen that impact
@@ -57,7 +61,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 42;
+ private static final int VERSION = 43;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -68,8 +72,7 @@
private static int sNumSpeedSteps;
- private final File mFile;
- private final File mBackupFile;
+ private final JournaledFile mFile;
/**
* The statistics we have collected organized by uids.
@@ -216,7 +219,7 @@
// For debugging
public BatteryStatsImpl() {
- mFile = mBackupFile = null;
+ mFile = null;
}
public static interface Unpluggable {
@@ -228,14 +231,15 @@
* State for keeping track of counting information.
*/
public static class Counter extends BatteryStats.Counter implements Unpluggable {
- int mCount;
+ final AtomicInteger mCount = new AtomicInteger();
int mLoadedCount;
int mLastCount;
int mUnpluggedCount;
int mPluggedCount;
Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
- mPluggedCount = mCount = in.readInt();
+ mPluggedCount = in.readInt();
+ mCount.set(mPluggedCount);
mLoadedCount = in.readInt();
mLastCount = in.readInt();
mUnpluggedCount = in.readInt();
@@ -247,18 +251,19 @@
}
public void writeToParcel(Parcel out) {
- out.writeInt(mCount);
+ out.writeInt(mCount.get());
out.writeInt(mLoadedCount);
out.writeInt(mLastCount);
out.writeInt(mUnpluggedCount);
}
public void unplug(long batteryUptime, long batteryRealtime) {
- mUnpluggedCount = mCount = mPluggedCount;
+ mUnpluggedCount = mPluggedCount;
+ mCount.set(mPluggedCount);
}
public void plug(long batteryUptime, long batteryRealtime) {
- mPluggedCount = mCount;
+ mPluggedCount = mCount.get();
}
/**
@@ -283,7 +288,7 @@
if (which == STATS_LAST) {
val = mLastCount;
} else {
- val = mCount;
+ val = mCount.get();
if (which == STATS_UNPLUGGED) {
val -= mUnpluggedCount;
} else if (which != STATS_TOTAL) {
@@ -295,25 +300,27 @@
}
public void logState(Printer pw, String prefix) {
- pw.println(prefix + "mCount=" + mCount
+ pw.println(prefix + "mCount=" + mCount.get()
+ " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ " mUnpluggedCount=" + mUnpluggedCount
+ " mPluggedCount=" + mPluggedCount);
}
- void stepLocked() {
- mCount++;
+ void stepAtomic() {
+ mCount.incrementAndGet();
}
void writeSummaryFromParcelLocked(Parcel out) {
- out.writeInt(mCount);
- out.writeInt(mCount - mLoadedCount);
+ int count = mCount.get();
+ out.writeInt(count);
+ out.writeInt(count - mLoadedCount);
}
void readSummaryFromParcelLocked(Parcel in) {
- mCount = mLoadedCount = in.readInt();
+ mLoadedCount = in.readInt();
+ mCount.set(mLoadedCount);
mLastCount = in.readInt();
- mUnpluggedCount = mPluggedCount = mCount;
+ mUnpluggedCount = mPluggedCount = mLoadedCount;
}
}
@@ -327,8 +334,8 @@
super(unpluggables);
}
- public void addCountLocked(long count) {
- mCount += count;
+ public void addCountAtomic(long count) {
+ mCount.addAndGet((int)count);
}
}
@@ -1122,8 +1129,8 @@
}
}
- public void noteInputEventLocked() {
- mInputEventCounter.stepLocked();
+ public void noteInputEventAtomic() {
+ mInputEventCounter.stepAtomic();
}
public void noteUserActivityLocked(int uid, int event) {
@@ -1678,7 +1685,7 @@
}
if (type < 0) type = 0;
else if (type >= NUM_USER_ACTIVITY_TYPES) type = NUM_USER_ACTIVITY_TYPES-1;
- mUserActivityCounters[type].stepLocked();
+ mUserActivityCounters[type].stepAtomic();
}
@Override
@@ -2170,7 +2177,7 @@
/* Called by ActivityManagerService when CPU times are updated. */
public void addSpeedStepTimes(long[] values) {
for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
- mSpeedBins[i].addCountLocked(values[i]);
+ mSpeedBins[i].addCountAtomic(values[i]);
}
}
@@ -2704,8 +2711,7 @@
}
public BatteryStatsImpl(String filename) {
- mFile = new File(filename);
- mBackupFile = new File(filename + ".bak");
+ mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
mStartCount++;
mScreenOnTimer = new StopwatchTimer(-1, null, mUnpluggables);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -2736,7 +2742,7 @@
}
public BatteryStatsImpl(Parcel p) {
- mFile = mBackupFile = null;
+ mFile = null;
readFromParcel(p);
}
@@ -2799,7 +2805,7 @@
if (m == null) {
// Not crashing might make board bringup easier.
- Log.w(TAG, "Couldn't get kernel wake lock stats");
+ Slog.w(TAG, "Couldn't get kernel wake lock stats");
return;
}
@@ -3047,26 +3053,19 @@
return u.getServiceStatsLocked(pkg, name);
}
+ private static JournaledFile makeJournaledFile() {
+ final String base = "/data/system/device_policies.xml";
+ return new JournaledFile(new File(base), new File(base + ".tmp"));
+ }
+
public void writeLocked() {
- if ((mFile == null) || (mBackupFile == null)) {
- Log.w("BatteryStats", "writeLocked: no file associated with this instance");
+ if (mFile == null) {
+ Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
return;
}
- // Keep the old file around until we know the new one has
- // been successfully written.
- if (mFile.exists()) {
- if (mBackupFile.exists()) {
- mBackupFile.delete();
- }
- if (!mFile.renameTo(mBackupFile)) {
- Log.w("BatteryStats", "Failed to back up file before writing new stats");
- return;
- }
- }
-
try {
- FileOutputStream stream = new FileOutputStream(mFile);
+ FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
Parcel out = Parcel.obtain();
writeSummaryToParcel(out);
stream.write(out.marshall());
@@ -3074,18 +3073,14 @@
stream.flush();
stream.close();
- mBackupFile.delete();
+ mFile.commit();
mLastWriteTime = SystemClock.elapsedRealtime();
return;
} catch (IOException e) {
- Log.w("BatteryStats", "Error writing battery statistics", e);
+ Slog.w("BatteryStats", "Error writing battery statistics", e);
}
- if (mFile.exists()) {
- if (!mFile.delete()) {
- Log.w(TAG, "Failed to delete mangled file " + mFile);
- }
- }
+ mFile.rollback();
}
static byte[] readFully(FileInputStream stream) throws java.io.IOException {
@@ -3112,29 +3107,19 @@
}
public void readLocked() {
- if ((mFile == null) || (mBackupFile == null)) {
- Log.w("BatteryStats", "readLocked: no file associated with this instance");
+ if (mFile == null) {
+ Slog.w("BatteryStats", "readLocked: no file associated with this instance");
return;
}
mUidStats.clear();
- FileInputStream stream = null;
- if (mBackupFile.exists()) {
- try {
- stream = new FileInputStream(mBackupFile);
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
-
try {
- if (stream == null) {
- if (!mFile.exists()) {
- return;
- }
- stream = new FileInputStream(mFile);
+ File file = mFile.chooseForRead();
+ if (!file.exists()) {
+ return;
}
+ FileInputStream stream = new FileInputStream(file);
byte[] raw = readFully(stream);
Parcel in = Parcel.obtain();
@@ -3144,7 +3129,7 @@
readSummaryFromParcel(in);
} catch(java.io.IOException e) {
- Log.e("BatteryStats", "Error reading battery statistics", e);
+ Slog.e("BatteryStats", "Error reading battery statistics", e);
}
}
@@ -3155,7 +3140,7 @@
private void readSummaryFromParcel(Parcel in) {
final int version = in.readInt();
if (version != VERSION) {
- Log.w("BatteryStats", "readFromParcel: version got " + version
+ Slog.w("BatteryStats", "readFromParcel: version got " + version
+ ", expected " + VERSION + "; erasing old stats");
return;
}
@@ -3197,6 +3182,10 @@
mBluetoothOnTimer.readSummaryFromParcelLocked(in);
int NKW = in.readInt();
+ if (NKW > 10000) {
+ Slog.w(TAG, "File corrupt: too many kernel wake locks " + NKW);
+ return;
+ }
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String kwltName = in.readString();
@@ -3207,6 +3196,10 @@
sNumSpeedSteps = in.readInt();
final int NU = in.readInt();
+ if (NU > 10000) {
+ Slog.w(TAG, "File corrupt: too many uids " + NU);
+ return;
+ }
for (int iu = 0; iu < NU; iu++) {
int uid = in.readInt();
Uid u = new Uid(uid);
@@ -3235,6 +3228,10 @@
}
int NW = in.readInt();
+ if (NW > 10000) {
+ Slog.w(TAG, "File corrupt: too many wake locks " + NW);
+ return;
+ }
for (int iw = 0; iw < NW; iw++) {
String wlName = in.readString();
if (in.readInt() != 0) {
@@ -3249,6 +3246,10 @@
}
int NP = in.readInt();
+ if (NP > 10000) {
+ Slog.w(TAG, "File corrupt: too many sensors " + NP);
+ return;
+ }
for (int is = 0; is < NP; is++) {
int seNumber = in.readInt();
if (in.readInt() != 0) {
@@ -3258,6 +3259,10 @@
}
NP = in.readInt();
+ if (NP > 10000) {
+ Slog.w(TAG, "File corrupt: too many processes " + NP);
+ return;
+ }
for (int ip = 0; ip < NP; ip++) {
String procName = in.readString();
Uid.Proc p = u.getProcessStatsLocked(procName);
@@ -3270,6 +3275,10 @@
}
NP = in.readInt();
+ if (NP > 10000) {
+ Slog.w(TAG, "File corrupt: too many packages " + NP);
+ return;
+ }
for (int ip = 0; ip < NP; ip++) {
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
diff --git a/core/java/com/android/internal/util/HierarchicalState.java b/core/java/com/android/internal/util/HierarchicalState.java
index 002338a..b37f46c 100644
--- a/core/java/com/android/internal/util/HierarchicalState.java
+++ b/core/java/com/android/internal/util/HierarchicalState.java
@@ -21,10 +21,9 @@
/**
* {@hide}
*
- * The abstract class for implementing states in a
- * HierarchicalStateMachine and HandlerStateMachine.
+ * The class for implementing states in a HierarchicalStateMachine
*/
-public abstract class HierarchicalState {
+public class HierarchicalState {
/**
* Constructor
@@ -54,7 +53,9 @@
* if the parent state's processMessage should
* be invoked.
*/
- abstract protected boolean processMessage(Message msg);
+ protected boolean processMessage(Message msg) {
+ return false;
+ }
/**
* Called when a state is exited.
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index bebf051..9911f48 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -574,23 +574,41 @@
}
/**
- * Process the message abiding by the hierarchical semantics.
+ * Process the message abiding by the hierarchical semantics
+ * and perform any requested transitions.
*/
processMsg(msg);
+ performTransitions();
+ if (mDbg) Log.d(TAG, "handleMessage: X");
+ }
+
+ /**
+ * Do any transitions
+ */
+ private void performTransitions() {
/**
* If transitionTo has been called, exit and then enter
- * the appropriate states.
+ * the appropriate states. We loop on this to allow
+ * enter and exit methods to use transitionTo.
*/
- if (mDestState != null) {
+ HierarchicalState destState = null;
+ while (mDestState != null) {
if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
/**
+ * Save mDestState locally and set to null
+ * to know if enter/exit use transitionTo.
+ */
+ destState = mDestState;
+ mDestState = null;
+
+ /**
* Determine the states to exit and enter and return the
* common ancestor state of the enter/exit states. Then
* invoke the exit methods then the enter methods.
*/
- StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(mDestState);
+ StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
invokeExitMethods(commonStateInfo);
int stateStackEnteringIndex = moveTempStateStackToStateStack();
invokeEnterMethods(stateStackEnteringIndex);
@@ -603,25 +621,31 @@
* message queue.
*/
moveDeferredMessageAtFrontOfQueue();
+ }
- /**
- * Call halting() if we've transitioned to the halting
- * state. All subsequent messages will be processed in
- * in the halting state which invokes haltedProcessMessage(msg);
- */
- if (mDestState == mQuittingState) {
+ /**
+ * After processing all transitions check and
+ * see if the last transition was to quit or halt.
+ */
+ if (destState != null) {
+ if (destState == mQuittingState) {
+ /**
+ * We are quitting so ignore all messages.
+ */
mHsm.quitting();
if (mHsm.mHsmThread != null) {
// If we made the thread then quit looper
getLooper().quit();
}
- } else if (mDestState == mHaltingState) {
+ } else if (destState == mHaltingState) {
+ /**
+ * Call halting() if we've transitioned to the halting
+ * state. All subsequent messages will be processed in
+ * in the halting state which invokes haltedProcessMessage(msg);
+ */
mHsm.halting();
}
- mDestState = null;
}
-
- if (mDbg) Log.d(TAG, "handleMessage: X");
}
/**
@@ -657,6 +681,11 @@
mIsConstructionCompleted = true;
invokeEnterMethods(0);
+ /**
+ * Perform any transitions requested by the enter methods
+ */
+ performTransitions();
+
if (mDbg) Log.d(TAG, "completeConstruction: X");
}
@@ -1170,6 +1199,20 @@
/**
* Enqueue a message to this state machine.
*/
+ public final void sendMessage(int what) {
+ mHsmHandler.sendMessage(obtainMessage(what));
+ }
+
+ /**
+ * Enqueue a message to this state machine.
+ */
+ public final void sendMessage(int what, Object obj) {
+ mHsmHandler.sendMessage(obtainMessage(what,obj));
+ }
+
+ /**
+ * Enqueue a message to this state machine.
+ */
public final void sendMessage(Message msg) {
mHsmHandler.sendMessage(msg);
}
@@ -1177,6 +1220,20 @@
/**
* Enqueue a message to this state machine after a delay.
*/
+ public final void sendMessageDelayed(int what, long delayMillis) {
+ mHsmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
+ }
+
+ /**
+ * Enqueue a message to this state machine after a delay.
+ */
+ public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
+ mHsmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+ }
+
+ /**
+ * Enqueue a message to this state machine after a delay.
+ */
public final void sendMessageDelayed(Message msg, long delayMillis) {
mHsmHandler.sendMessageDelayed(msg, delayMillis);
}
@@ -1185,6 +1242,22 @@
* Enqueue a message to the front of the queue for this state machine.
* Protected, may only be called by instances of HierarchicalStateMachine.
*/
+ protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
+ mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
+ }
+
+ /**
+ * Enqueue a message to the front of the queue for this state machine.
+ * Protected, may only be called by instances of HierarchicalStateMachine.
+ */
+ protected final void sendMessageAtFrontOfQueue(int what) {
+ mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
+ }
+
+ /**
+ * Enqueue a message to the front of the queue for this state machine.
+ * Protected, may only be called by instances of HierarchicalStateMachine.
+ */
protected final void sendMessageAtFrontOfQueue(Message msg) {
mHsmHandler.sendMessageAtFrontOfQueue(msg);
}
diff --git a/services/java/com/android/server/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
similarity index 98%
rename from services/java/com/android/server/JournaledFile.java
rename to core/java/com/android/internal/util/JournaledFile.java
index 3d1f52d..af0c6c6 100644
--- a/services/java/com/android/server/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.internal.util;
import java.io.File;
import java.io.IOException;
diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java
index fa47ff6..23e2277 100644
--- a/core/java/com/android/internal/widget/DigitalClock.java
+++ b/core/java/com/android/internal/widget/DigitalClock.java
@@ -30,7 +30,7 @@
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import java.text.DateFormatSymbols;
@@ -39,7 +39,7 @@
/**
* Displays the time
*/
-public class DigitalClock extends LinearLayout {
+public class DigitalClock extends RelativeLayout {
private final static String M12 = "h:mm";
private final static String M24 = "kk:mm";
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9b7f487..dbbd286 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -47,7 +47,6 @@
public class LockPatternUtils {
private static final String TAG = "LockPatternUtils";
- private static final boolean LDEBUG = false;
private static final String LOCK_PATTERN_FILE = "/system/gesture.key";
private static final String LOCK_PASSWORD_FILE = "/system/password.key";
@@ -81,17 +80,6 @@
public static final int MIN_LOCK_PATTERN_SIZE = 4;
/**
- * Type of password being stored.
- * pattern = pattern screen
- * pin = digit-only password
- * password = alphanumeric password
- */
- public static final int MODE_UNSPECIFIED = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- public static final int MODE_PATTERN = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- public static final int MODE_PIN = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
- public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-
- /**
* The minimum number of dots the user must include in a wrong pattern
* attempt for it to be counted against the counts that affect
* {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
@@ -110,7 +98,7 @@
private static String sLockPatternFilename;
private static String sLockPasswordFilename;
- DevicePolicyManager getDevicePolicyManager() {
+ public DevicePolicyManager getDevicePolicyManager() {
if (mDevicePolicyManager == null) {
mDevicePolicyManager =
(DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
@@ -141,23 +129,13 @@
return getDevicePolicyManager().getPasswordMinimumLength(null);
}
+
/**
* Gets the device policy password mode. If the mode is non-specific, returns
* MODE_PATTERN which allows the user to choose anything.
- *
- * @return
*/
- public int getRequestedPasswordMode() {
- int policyMode = getDevicePolicyManager().getPasswordQuality(null);
- switch (policyMode) {
- case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
- return MODE_PASSWORD;
- case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
- return MODE_PIN;
- case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
- return MODE_PATTERN;
- }
- return MODE_PATTERN;
+ public int getRequestedPasswordQuality() {
+ return getDevicePolicyManager().getPasswordQuality(null);
}
/**
@@ -233,7 +211,7 @@
try {
// Check if we can read a byte from the file
RandomAccessFile raf = new RandomAccessFile(filename, "r");
- byte first = raf.readByte();
+ raf.readByte();
raf.close();
return true;
} catch (FileNotFoundException fnfe) {
@@ -270,13 +248,46 @@
}
/**
+ * Used by device policy manager to validate the current password
+ * information it has.
+ */
+ public int getActivePasswordQuality() {
+ int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ switch (getKeyguardStoredPasswordQuality()) {
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ if (isLockPatternEnabled()) {
+ activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+ }
+ break;
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ if (isLockPasswordEnabled()) {
+ activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ }
+ break;
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ if (isLockPasswordEnabled()) {
+ activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+ }
+ break;
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ if (isLockPasswordEnabled()) {
+ activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+ }
+ break;
+ }
+ return activePasswordQuality;
+ }
+
+ /**
* Clear any lock pattern or password.
*/
public void clearLock() {
- saveLockPassword(null, LockPatternUtils.MODE_PATTERN);
+ getDevicePolicyManager().setActivePasswordState(
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
+ saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
setLockPatternEnabled(false);
saveLockPattern(null);
- setLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
}
/**
@@ -299,7 +310,7 @@
DevicePolicyManager dpm = getDevicePolicyManager();
if (pattern != null) {
setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
- setLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
+ setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size());
} else {
@@ -316,15 +327,9 @@
}
/**
- * Compare the given password and mode, ensuring that the password meets
- * the mode and returning the minimum mode needed for the given password.
- * @param password The password to be used.
- * @param reqMode The desired password mode.
- * @return Returns {@link #MODE_UNSPECIFIED} if the password is not
- * good enough for the given mode. Otherwise, returns either the original
- * reqMode or something better if that is needed for the given password.
+ * Compute the password quality from the given password string.
*/
- static public int adjustPasswordMode(String password, int reqMode) {
+ static public int computePasswordQuality(String password) {
boolean hasDigit = false;
boolean hasNonDigit = false;
final int len = password.length();
@@ -336,48 +341,26 @@
}
}
- // First check if it is sufficient.
- switch (reqMode) {
- case MODE_PASSWORD: {
- if (!hasDigit || !hasNonDigit) {
- return MODE_UNSPECIFIED;
- }
- } break;
-
- case MODE_PIN:
- case MODE_PATTERN: {
- // Whatever we have is acceptable; we may need to promote the
- // mode later.
- } break;
-
- default:
- // If it isn't a mode we specifically know, then fail fast.
- Log.w(TAG, "adjustPasswordMode: unknown mode " + reqMode);
- return MODE_UNSPECIFIED;
+ if (hasNonDigit && hasDigit) {
+ return DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
}
-
- // Do we need to promote?
if (hasNonDigit) {
- if (reqMode < MODE_PASSWORD) {
- reqMode = MODE_PASSWORD;
- }
+ return DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
}
if (hasDigit) {
- if (reqMode < MODE_PIN) {
- reqMode = MODE_PIN;
- }
+ return DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
}
-
- return reqMode;
+ return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
/**
- * Save a lock password. Does not ensure that the pattern is as good
+ * Save a lock password. Does not ensure that the password is as good
* as the requested mode, but will adjust the mode to be as good as the
* pattern.
* @param password The password to save
+ * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
*/
- public void saveLockPassword(String password, int mode) {
+ public void saveLockPassword(String password, int quality) {
// Compute the hash
final byte[] hash = passwordToHash(password);
try {
@@ -392,12 +375,15 @@
raf.close();
DevicePolicyManager dpm = getDevicePolicyManager();
if (password != null) {
- int finalMode = adjustPasswordMode(password, mode);
- if (mode < finalMode) {
- mode = finalMode;
+ int computedQuality = computePasswordQuality(password);
+ setLong(PASSWORD_TYPE_KEY, computedQuality);
+ if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ dpm.setActivePasswordState(computedQuality, password.length());
+ } else {
+ // The password is not anything.
+ dpm.setActivePasswordState(
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
}
- setLong(PASSWORD_TYPE_KEY, mode);
- dpm.setActivePasswordState(mode, password.length());
} else {
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
@@ -411,8 +397,14 @@
}
}
- public int getPasswordMode() {
- return (int) getLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
+ /**
+ * Retrieves the quality mode we're in.
+ * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+ *
+ * @return stored password quality
+ */
+ public int getKeyguardStoredPasswordQuality() {
+ return (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
}
/**
@@ -531,7 +523,10 @@
*/
public boolean isLockPasswordEnabled() {
long mode = getLong(PASSWORD_TYPE_KEY, 0);
- return savedPasswordExists() && (mode == MODE_PASSWORD || mode == MODE_PIN);
+ return savedPasswordExists() &&
+ (mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
}
/**
@@ -539,7 +534,8 @@
*/
public boolean isLockPatternEnabled() {
return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED)
- && getLong(PASSWORD_TYPE_KEY, MODE_PATTERN) == MODE_PATTERN;
+ && getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
+ == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
}
/**
@@ -655,9 +651,13 @@
}
public boolean isSecure() {
- long mode = getPasswordMode();
- boolean secure = mode == MODE_PATTERN && isLockPatternEnabled() && savedPatternExists()
- || (mode == MODE_PIN || mode == MODE_PASSWORD) && savedPasswordExists();
+ long mode = getKeyguardStoredPasswordQuality();
+ final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+ final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+ final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
+ || isPassword && savedPasswordExists();
return secure;
}
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 28bf6bb..53720e4 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -106,12 +106,16 @@
mKeyboardMode = mode;
}
- private void sendKeyEventsToTarget(int keyEventCode) {
+ private void sendKeyEventsToTarget(int character) {
Handler handler = mTargetView.getHandler();
KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.ALPHA).getEvents(
- new char[] { (char) keyEventCode });
+ new char[] { (char) character });
if (events != null) {
- for (KeyEvent event : events) {
+ final int N = events.length;
+ for (int i=0; i<N; i++) {
+ KeyEvent event = events[i];
+ event = KeyEvent.changeFlags(event, event.getFlags()
+ | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY, event));
}
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index ee2fc12..f487a16 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -60,6 +60,7 @@
public PointerLocationView(Context c) {
super(c);
+ setFocusable(true);
mVC = ViewConfiguration.get(c);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
@@ -350,4 +351,11 @@
addTouchEvent(event);
return true;
}
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ Log.i("Pointer", "Trackball: " + event);
+ return super.onTrackballEvent(event);
+ }
+
}
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 2a1f23a..d4ac24a 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -424,7 +424,8 @@
// faster.
if ("text/plain".equals(type) || "application/smil".equals(type)) {
String text = c.getString(PART_COLUMN_TEXT);
- byte [] blob = new EncodedStringValue(text).getTextString();
+ byte [] blob = new EncodedStringValue(text != null ? text : "")
+ .getTextString();
baos.write(blob, 0, blob.length);
} else {
@@ -858,7 +859,7 @@
} else {
values.put(Mms.SUBJECT, "");
}
-
+
long messageSize = sendReq.getMessageSize();
if (messageSize > 0) {
values.put(Mms.MESSAGE_SIZE, messageSize);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 85d1a6f..6d1a414 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -121,7 +121,6 @@
android_server_BluetoothA2dpService.cpp \
android_message_digest_sha1.cpp \
android_ddm_DdmHandleNativeHeap.cpp \
- android_location_GpsLocationProvider.cpp \
com_android_internal_os_ZygoteInit.cpp \
com_android_internal_graphics_NativeUtils.cpp \
android_backup_BackupDataInput.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5546666..c9e5bdc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -154,7 +154,6 @@
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
-extern int register_android_location_GpsLocationProvider(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
@@ -704,10 +703,10 @@
#if defined(WITH_JIT)
/* Minimal profile threshold to trigger JIT compilation */
- char jitThresholdBuf[sizeof("-Xthreshold:") + PROPERTY_VALUE_MAX];
+ char jitThresholdBuf[sizeof("-Xjitthreshold:") + PROPERTY_VALUE_MAX];
property_get("dalvik.vm.jit.threshold", propBuf, "");
if (strlen(propBuf) > 0) {
- strcpy(jitThresholdBuf, "-Xthreshold:");
+ strcpy(jitThresholdBuf, "-Xjitthreshold:");
strcat(jitThresholdBuf, propBuf);
opt.optionString = jitThresholdBuf;
mOptions.add(opt);
@@ -1267,7 +1266,6 @@
REG_JNI(register_android_server_BluetoothA2dpService),
REG_JNI(register_android_message_digest_sha1),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
- REG_JNI(register_android_location_GpsLocationProvider),
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelperBase),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b6f3997..b41bad0 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -164,8 +164,11 @@
using namespace android;
class NinePatchPeeker : public SkImageDecoder::Peeker {
+ SkImageDecoder* fHost;
public:
- NinePatchPeeker() {
+ NinePatchPeeker(SkImageDecoder* host) {
+ // the host lives longer than we do, so a raw ptr is safe
+ fHost = host;
fPatchIsValid = false;
}
@@ -197,6 +200,19 @@
// fPatch.sizeLeft, fPatch.sizeTop,
// fPatch.sizeRight, fPatch.sizeBottom);
fPatchIsValid = true;
+
+ // now update our host to force index or 32bit config
+ // 'cause we don't want 565 predithered, since as a 9patch, we know
+ // we will be stretched, and therefore we want to dither afterwards.
+ static const SkBitmap::Config gNo565Pref[] = {
+ SkBitmap::kIndex8_Config,
+ SkBitmap::kIndex8_Config,
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kARGB_8888_Config,
+ };
+ fHost->setPrefConfigTable(gNo565Pref);
} else {
fPatch = NULL;
}
@@ -364,7 +380,7 @@
decoder->setSampleSize(sampleSize);
decoder->setDitherImage(doDither);
- NinePatchPeeker peeker;
+ NinePatchPeeker peeker(decoder);
JavaPixelAllocator javaAllocator(env, reportSizeToVM);
SkBitmap* bitmap = new SkBitmap;
Res_png_9patch dummy9Patch;
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 11c608c..b8b2a10 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -75,7 +75,7 @@
return result;
}
- static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, int boundstype) {
+ static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds) {
const SkRect& bounds_ = obj->getBounds();
GraphicsJNI::rect_to_jrectf(bounds_, env, bounds);
}
@@ -267,7 +267,7 @@
{"native_setFillType","(II)V", (void*) SkPathGlue::setFillType},
{"native_isEmpty","(I)Z", (void*) SkPathGlue::isEmpty},
{"native_isRect","(ILandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
- {"native_computeBounds","(ILandroid/graphics/RectF;I)V", (void*) SkPathGlue::computeBounds},
+ {"native_computeBounds","(ILandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
{"native_incReserve","(II)V", (void*) SkPathGlue::incReserve},
{"native_moveTo","(IFF)V", (void*) SkPathGlue::moveTo__FF},
{"native_rMoveTo","(IFF)V", (void*) SkPathGlue::rMoveTo},
diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp
index 71279b2..b0b0cb8 100644
--- a/core/jni/android_bluetooth_HeadsetBase.cpp
+++ b/core/jni/android_bluetooth_HeadsetBase.cpp
@@ -96,6 +96,13 @@
return 0;
}
+static int is_ascii(char *line) {
+ for (;;line++) {
+ if (*line == 0) return 1;
+ if (*line >> 7) return 0;
+ }
+}
+
static const char* get_line(int fd, char *buf, int len, int timeout_ms,
int *err) {
char *bufit=buf;
@@ -125,7 +132,7 @@
return NULL;
}
- while ((int)(bufit - buf) < len)
+ while ((int)(bufit - buf) < (len - 1))
{
errno = 0;
int rc = read(fd, bufit, 1);
@@ -155,8 +162,18 @@
bufit++;
}
- *bufit = '\x0';
- LOG(LOG_INFO, "Bluetooth AT recv", buf);
+ *bufit = NULL;
+
+ // Simple validation. Must be all ASCII.
+ // (we sometimes send non-ASCII UTF-8 in address book, but should
+ // never receive non-ASCII UTF-8).
+ // This was added because of the BMW 2005 E46 which sends binary junk.
+ if (is_ascii(buf)) {
+ LOG(LOG_INFO, "Bluetooth AT recv", buf);
+ } else {
+ LOGW("Ignoring invalid AT command: %s", buf);
+ buf[0] = NULL;
+ }
return buf;
}
@@ -501,7 +518,7 @@
{
native_data_t *nat = get_native_data(env, obj);
if (nat->rfcomm_connected) {
- char buf[128];
+ char buf[256];
const char *ret = get_line(nat->rfcomm_sock,
buf, sizeof(buf),
timeout_ms,
diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp
index 3afe5f5..94e4409 100644
--- a/core/jni/android_bluetooth_ScoSocket.cpp
+++ b/core/jni/android_bluetooth_ScoSocket.cpp
@@ -37,6 +37,23 @@
#ifdef HAVE_BLUETOOTH
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+
+#define MAX_LINE 255
+
+/*
+ * Defines the module strings used in the blacklist file.
+ * These are used by consumers of the blacklist file to see if the line is
+ * used by that module.
+ */
+#define SCO_BLACKLIST_MODULE_NAME "scoSocket"
+
+
+/* Define the type strings used in the blacklist file. */
+#define BLACKLIST_BY_NAME "name"
+#define BLACKLIST_BY_PARTIAL_NAME "partial_name"
+#define BLACKLIST_BY_OUI "vendor_oui"
+
#endif
/* Ideally, blocking I/O on a SCO socket would return when another thread
@@ -67,11 +84,28 @@
struct thread_data_t;
static void *work_thread(void *arg);
-static int connect_work(const char *address);
+static int connect_work(const char *address, uint16_t sco_pkt_type);
static int accept_work(int signal_sk);
static void wait_for_close(int sk, int signal_sk);
static void closeNative(JNIEnv *env, jobject object);
+static void parseBlacklist(void);
+static uint16_t getScoType(char *address, const char *name);
+
+#define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
+
+/* Blacklist data */
+typedef struct scoBlacklist {
+ int fieldType;
+ char *value;
+ uint16_t scoType;
+ struct scoBlacklist *next;
+} scoBlacklist_t;
+
+#define BL_TYPE_NAME 1 // Field type is name string
+
+static scoBlacklist_t *blacklist = NULL;
+
/* shared native data - protected by mutex */
typedef struct {
pthread_mutex_t mutex;
@@ -87,11 +121,144 @@
bool is_accept; // accept (listening) or connect (outgoing) thread
int signal_sk; // socket for thread to listen for unblock signal
char address[BTADDR_SIZE]; // BT addres as string
+ uint16_t sco_pkt_type; // SCO packet types supported
};
static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
return (native_data_t *)(env->GetIntField(object, field_mNativeData));
}
+
+static uint16_t str2scoType (char *key) {
+ LOGV("%s: key = %s", __FUNCTION__, key);
+ if (COMPARE_STRING(key, "ESCO_HV1"))
+ return ESCO_HV1;
+ if (COMPARE_STRING(key, "ESCO_HV2"))
+ return ESCO_HV2;
+ if (COMPARE_STRING(key, "ESCO_HV3"))
+ return ESCO_HV3;
+ if (COMPARE_STRING(key, "ESCO_EV3"))
+ return ESCO_EV3;
+ if (COMPARE_STRING(key, "ESCO_EV4"))
+ return ESCO_EV4;
+ if (COMPARE_STRING(key, "ESCO_EV5"))
+ return ESCO_EV5;
+ if (COMPARE_STRING(key, "ESCO_2EV3"))
+ return ESCO_2EV3;
+ if (COMPARE_STRING(key, "ESCO_3EV3"))
+ return ESCO_3EV3;
+ if (COMPARE_STRING(key, "ESCO_2EV5"))
+ return ESCO_2EV5;
+ if (COMPARE_STRING(key, "ESCO_3EV5"))
+ return ESCO_3EV5;
+ if (COMPARE_STRING(key, "SCO_ESCO_MASK"))
+ return SCO_ESCO_MASK;
+ if (COMPARE_STRING(key, "EDR_ESCO_MASK"))
+ return EDR_ESCO_MASK;
+ if (COMPARE_STRING(key, "ALL_ESCO_MASK"))
+ return ALL_ESCO_MASK;
+ LOGE("Unknown SCO Type (%s) skipping",key);
+ return 0;
+}
+
+static void parseBlacklist(void) {
+ const char *filename = "/etc/bluetooth/blacklist.conf";
+ char line[MAX_LINE];
+ scoBlacklist_t *list = NULL;
+ scoBlacklist_t *newelem;
+
+ LOGV(__FUNCTION__);
+
+ /* Open file */
+ FILE *fp = fopen(filename, "r");
+ if(!fp) {
+ LOGE("Error(%s)opening blacklist file", strerror(errno));
+ return;
+ }
+
+ while (fgets(line, MAX_LINE, fp) != NULL) {
+ if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
+ continue;
+ char *module = strtok(line,":");
+ if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) {
+ newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t));
+ if (newelem == NULL) {
+ LOGE("%s: out of memory!", __FUNCTION__);
+ return;
+ }
+ // parse line
+ char *type = strtok(NULL, ",");
+ char *valueList = strtok(NULL, ",");
+ char *paramList = strtok(NULL, ",");
+ if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) {
+ // Extract Name from Value list
+ newelem->fieldType = BL_TYPE_NAME;
+ newelem->value = (char *)calloc(1, strlen(valueList));
+ if (newelem->value == NULL) {
+ LOGE("%s: out of memory!", __FUNCTION__);
+ continue;
+ }
+ valueList++; // Skip open quote
+ strncpy(newelem->value, valueList, strlen(valueList) - 1);
+
+ // Get Sco Settings from Parameters
+ char *param = strtok(paramList, ";");
+ uint16_t scoTypes = 0;
+ while (param != NULL) {
+ uint16_t sco;
+ if (param[0] == '-') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes &= ~sco;
+ } else if (param[0] == '+') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes |= sco;
+ } else if (param[0] == '=') {
+ param++;
+ sco = str2scoType(param);
+ if (sco != 0)
+ scoTypes = sco;
+ } else {
+ LOGE("Invalid SCO type must be =, + or -");
+ }
+ param = strtok(NULL, ";");
+ }
+ newelem->scoType = scoTypes;
+ } else {
+ LOGE("Unknown SCO type entry in Blacklist file");
+ continue;
+ }
+ if (list) {
+ list->next = newelem;
+ list = newelem;
+ } else {
+ blacklist = list = newelem;
+ }
+ LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
+ newelem->scoType);
+ }
+ }
+ fclose(fp);
+ return;
+}
+static uint16_t getScoType(char *address, const char *name) {
+ uint16_t ret = 0;
+ scoBlacklist_t *list = blacklist;
+
+ while (list != NULL) {
+ if (list->fieldType == BL_TYPE_NAME) {
+ if (COMPARE_STRING(name, list->value)) {
+ ret = list->scoType;
+ break;
+ }
+ }
+ list = list->next;
+ }
+ LOGI("%s %s - 0x%x", __FUNCTION__, name, ret);
+ return ret;
+}
#endif
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -104,6 +271,9 @@
method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
+
+ /* Read the blacklist file in here */
+ parseBlacklist();
#endif
}
@@ -192,7 +362,9 @@
return JNI_FALSE;
}
-static jboolean connectNative(JNIEnv *env, jobject object, jstring address) {
+static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
+ jstring name) {
+
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
native_data_t *nat = get_native_data(env, object);
@@ -200,6 +372,7 @@
pthread_t thread;
struct thread_data_t *data;
const char *c_address;
+ const char *c_name;
pthread_mutex_lock(&nat->mutex);
if (nat->signal_sk != -1) {
@@ -231,6 +404,15 @@
env->ReleaseStringUTFChars(address, c_address);
data->is_accept = false;
+ if (name == NULL) {
+ LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
+ data->sco_pkt_type = 0;
+ } else {
+ c_name = env->GetStringUTFChars(name, NULL);
+ /* See if this device is in the black list */
+ data->sco_pkt_type = getScoType(data->address, c_name);
+ env->ReleaseStringUTFChars(name, c_name);
+ }
if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
return JNI_FALSE;
@@ -282,7 +464,7 @@
sk = accept_work(data->signal_sk);
LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
} else {
- sk = connect_work(data->address);
+ sk = connect_work(data->address, data->sco_pkt_type);
}
/* callback with connection result */
@@ -426,7 +608,7 @@
return -1;
}
-static int connect_work(const char *address) {
+static int connect_work(const char *address, uint16_t sco_pkt_type) {
LOGV(__FUNCTION__);
struct sockaddr_sco addr;
int sk = -1;
@@ -449,6 +631,7 @@
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
get_bdaddr(address, &addr.sco_bdaddr);
+ addr.sco_pkt_type = sco_pkt_type;
LOGI("Connecting to socket");
while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errno != EINTR) {
@@ -493,7 +676,7 @@
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void *)initNative},
{"destroyNative", "()V", (void *)destroyNative},
- {"connectNative", "(Ljava/lang/String;)Z", (void *)connectNative},
+ {"connectNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectNative},
{"acceptNative", "()Z", (void *)acceptNative},
{"closeNative", "()V", (void *)closeNative},
};
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index fb93014..36234a9 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -72,8 +72,9 @@
}
static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
- LOGI("sqlite returned: database = %s, error code = %d, msg = %s\n",
- (char *)databaseName, iErrCode, zMsg);
+ // skip printing this message if it is due to certain types of errors
+ if (iErrCode == SQLITE_CONSTRAINT) return;
+ LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
}
// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
@@ -349,7 +350,7 @@
goto done;
}
- dbLocale = (rowCount >= 1) ? meta[1 * colCount + 0] : NULL;
+ dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
// database locale is the same as the desired locale; set up the collators and go
@@ -360,7 +361,8 @@
if ((flags & OPEN_READONLY)) {
// read-only database, so we're going to have to put up with whatever we got
- err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE);
+ // For registering new index. Not for modifing the read-only database.
+ err = register_localized_collators(handle, locale8, UTF16_STORAGE);
if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
goto done;
}
@@ -373,7 +375,7 @@
goto done;
}
- err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE);
+ err = register_localized_collators(handle, locale8, UTF16_STORAGE);
if (err != SQLITE_OK) {
LOGE("register_localized_collators() failed setting locale\n");
throw_sqlite3_exception(env, handle);
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 5432efb..0e7fd66 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -564,7 +564,10 @@
NULL, NULL, NULL, NULL, NULL);
tearDownEventLoop(nat);
nat->vm->DetachCurrentThread();
- shutdown(nat->controlFdR,SHUT_RDWR);
+
+ int fd = nat->controlFdR;
+ nat->controlFdR = 0;
+ close(fd);
return NULL;
}
case EVENT_LOOP_ADD:
@@ -653,9 +656,12 @@
done:
if (JNI_FALSE == result) {
- if (nat->controlFdW || nat->controlFdR) {
- shutdown(nat->controlFdW, SHUT_RDWR);
+ if (nat->controlFdW) {
+ close(nat->controlFdW);
nat->controlFdW = 0;
+ }
+ if (nat->controlFdR) {
+ close(nat->controlFdR);
nat->controlFdR = 0;
}
if (nat->me) env->DeleteGlobalRef(nat->me);
@@ -692,9 +698,10 @@
nat->watchData = NULL;
nat->pollDataSize = 0;
nat->pollMemberCount = 0;
- shutdown(nat->controlFdW, SHUT_RDWR);
+
+ int fd = nat->controlFdW;
nat->controlFdW = 0;
- nat->controlFdR = 0;
+ close(fd);
}
pthread_mutex_unlock(&(nat->thread_mutex));
#endif // HAVE_BLUETOOTH
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 627fcbf..d26cd28 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -21,16 +21,21 @@
#include "JNIHelp.h"
#include <fcntl.h>
-#include <sys/stat.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/Atomic.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
#include <utils/Log.h>
+#include <utils/SystemClock.h>
+#include <cutils/logger.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <utils/threads.h>
#include <android_runtime/AndroidRuntime.h>
@@ -590,9 +595,19 @@
{
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetIntField(clazz, gBinderOffsets.mObject);
- env->SetIntField(clazz, gBinderOffsets.mObject, 0);
- LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh);
- jbh->decStrong(clazz);
+ if (jbh != NULL) {
+ env->SetIntField(clazz, gBinderOffsets.mObject, 0);
+ LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh);
+ jbh->decStrong(clazz);
+ } else {
+ // Encountering an uninitialized binder is harmless. All it means is that
+ // the Binder was only partially initialized when its finalizer ran and called
+ // destroy(). The Binder could be partially initialized for several reasons.
+ // For example, a Binder subclass constructor might have thrown an exception before
+ // it could delegate to its superclass's constructor. Consequently init() would
+ // not have been called and the holder pointer would remain NULL.
+ LOGV("Java Binder %p: ignoring uninitialized binder", clazz);
+ }
}
// ----------------------------------------------------------------------------
@@ -730,7 +745,7 @@
{
IBinder* target = (IBinder*) env->GetIntField(obj, gBinderProxyOffsets.mObject);
if (target != NULL) {
- String16 desc = target->getInterfaceDescriptor();
+ const String16& desc = target->getInterfaceDescriptor();
return env->NewString(desc.string(), desc.size());
}
jniThrowException(env, "java/lang/RuntimeException",
@@ -749,6 +764,100 @@
return alive ? JNI_TRUE : JNI_FALSE;
}
+static int getprocname(pid_t pid, char *buf, size_t len) {
+ char filename[20];
+ FILE *f;
+
+ sprintf(filename, "/proc/%d/cmdline", pid);
+ f = fopen(filename, "r");
+ if (!f) { *buf = '\0'; return 1; }
+ if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
+ fclose(f);
+ return 0;
+}
+
+static bool push_eventlog_string(char** pos, const char* end, const char* str) {
+ jint len = strlen(str);
+ int space_needed = 1 + sizeof(len) + len;
+ if (end - *pos < space_needed) {
+ LOGW("not enough space for string. remain=%d; needed=%d",
+ (end - *pos), space_needed);
+ return false;
+ }
+ **pos = EVENT_TYPE_STRING;
+ (*pos)++;
+ memcpy(*pos, &len, sizeof(len));
+ *pos += sizeof(len);
+ memcpy(*pos, str, len);
+ *pos += len;
+ return true;
+}
+
+static bool push_eventlog_int(char** pos, const char* end, jint val) {
+ int space_needed = 1 + sizeof(val);
+ if (end - *pos < space_needed) {
+ LOGW("not enough space for int. remain=%d; needed=%d",
+ (end - *pos), space_needed);
+ return false;
+ }
+ **pos = EVENT_TYPE_INT;
+ (*pos)++;
+ memcpy(*pos, &val, sizeof(val));
+ *pos += sizeof(val);
+ return true;
+}
+
+// From frameworks/base/core/java/android/content/EventLogTags.logtags:
+#define LOGTAG_BINDER_OPERATION 52004
+
+static void conditionally_log_binder_call(int64_t start_millis,
+ IBinder* target, jint code) {
+ int duration_ms = static_cast<int>(uptimeMillis() - start_millis);
+
+ int sample_percent;
+ if (duration_ms >= 500) {
+ sample_percent = 100;
+ } else {
+ sample_percent = 100 * duration_ms / 500;
+ if (sample_percent == 0) {
+ return;
+ }
+ if (sample_percent < (random() % 100 + 1)) {
+ return;
+ }
+ }
+
+ char process_name[40];
+ getprocname(getpid(), process_name, sizeof(process_name));
+ String8 desc(target->getInterfaceDescriptor());
+
+ char buf[LOGGER_ENTRY_MAX_PAYLOAD];
+ buf[0] = EVENT_TYPE_LIST;
+ buf[1] = 5;
+ char* pos = &buf[2];
+ char* end = &buf[LOGGER_ENTRY_MAX_PAYLOAD - 1]; // leave room for final \n
+ if (!push_eventlog_string(&pos, end, desc.string())) return;
+ if (!push_eventlog_int(&pos, end, code)) return;
+ if (!push_eventlog_int(&pos, end, duration_ms)) return;
+ if (!push_eventlog_string(&pos, end, process_name)) return;
+ if (!push_eventlog_int(&pos, end, sample_percent)) return;
+ *(pos++) = '\n'; // conventional with EVENT_TYPE_LIST apparently.
+ android_bWriteLog(LOGTAG_BINDER_OPERATION, buf, pos - buf);
+}
+
+// We only measure binder call durations to potentially log them if
+// we're on the main thread. Unfortunately sim-eng doesn't seem to
+// have gettid, so we just ignore this and don't log if we can't
+// get the thread id.
+static bool should_time_binder_calls() {
+#ifdef HAVE_GETTID
+ return (getpid() == androidGetTid());
+#else
+#warning no gettid(), so not logging Binder calls...
+ return false;
+#endif
+}
+
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
@@ -776,9 +885,22 @@
LOGV("Java code calling transact on %p in Java object %p with code %d\n",
target, obj, code);
+
+ // Only log the binder call duration for things on the Java-level main thread.
+ // But if we don't
+ const bool time_binder_calls = should_time_binder_calls();
+
+ int64_t start_millis;
+ if (time_binder_calls) {
+ start_millis = uptimeMillis();
+ }
//printf("Transact from Java code to %p sending: ", target); data->print();
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+ if (time_binder_calls) {
+ conditionally_log_binder_call(start_millis, target, code);
+ }
+
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 060ca50..ed26cbe 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -35,6 +35,11 @@
namespace android {
+enum {
+ // should match Parcelable.java
+ PARCELABLE_WRITE_RETURN_VALUE = 0x0001
+};
+
// ----------------------------------------------------------------------------
static const char* const OutOfResourcesException =
@@ -612,6 +617,9 @@
const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
SurfaceControl::writeSurfaceToParcel(control, parcel);
+ if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
+ setSurfaceControl(env, clazz, 0);
+ }
}
// ----------------------------------------------------------------------------
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e2caa0..8eb5e96 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -250,14 +250,6 @@
android:description="@string/permdesc_writeHistoryBookmarks"
android:protectionLevel="dangerous" />
- <!-- Allows an application to write to (but not read) the user's
- geolocation permissions.. -->
- <permission android:name="com.android.browser.permission.WRITE_GEOLOCATION_PERMISSIONS"
- android:permissionGroup="android.permission-group.LOCATION"
- android:label="@string/permlab_writeGeolocationPermissions"
- android:description="@string/permdesc_writeGeolocationPermissions"
- android:protectionLevel="signatureOrSystem" />
-
<!-- ======================================= -->
<!-- Permissions for accessing location info -->
<!-- ======================================= -->
@@ -732,7 +724,7 @@
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_access"
android:description="@string/permdesc_asec_access" />
@@ -740,7 +732,7 @@
@hide -->
<permission android:name="android.permission.ASEC_CREATE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_create"
android:description="@string/permdesc_asec_create" />
@@ -748,7 +740,7 @@
@hide -->
<permission android:name="android.permission.ASEC_DESTROY"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_destroy"
android:description="@string/permdesc_asec_destroy" />
@@ -756,7 +748,7 @@
@hide -->
<permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_mount_unmount"
android:description="@string/permdesc_asec_mount_unmount" />
@@ -764,7 +756,7 @@
@hide -->
<permission android:name="android.permission.ASEC_RENAME"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signature"
android:label="@string/permlab_asec_rename"
android:description="@string/permdesc_asec_rename" />
diff --git a/core/res/assets/webkit/togglePlugin.png b/core/res/assets/webkit/togglePlugin.png
new file mode 100644
index 0000000..008333c
--- /dev/null
+++ b/core/res/assets/webkit/togglePlugin.png
Binary files differ
diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html
index 45d9c5e..289f8cf 100644
--- a/core/res/assets/webkit/youtube.html
+++ b/core/res/assets/webkit/youtube.html
@@ -5,18 +5,6 @@
a:hover { text-decoration: none; }
a:link { color: black; }
a:visited { color: black; }
- #bg {
- position: fixed;
- margin: 0px;
- border: 0px;
- padding: 0px;
- left: 0px;
- top: 0px;
- width: 100%;
- height: 100%;
- overflow: hidden;
- z-index: 0;
- }
#main {
position: absolute;
left: 0%;
@@ -28,26 +16,81 @@
}
</style>
</head>
- <body>
- <div id="bg">
- <img src="http://img.youtube.com/vi/VIDEO_ID/0.jpg"
- style="width:100%; height:100%"/>
- </div>
+ <body id="body">
+ <script type="text/javascript">
+ // Nominal original size. If the embed is smaller than this, the play and logo
+ // images get scaled appropriately. These are actually 3/4 of the sizes suggested
+ // by youtube, so the images don't get too tiny.
+ defHeight = 258;
+ defWidth = 318;
+
+ function setup() {
+ var width = document.body.clientWidth;
+ var height = document.body.clientHeight;
+ var canvas = document.getElementById("canvas");
+ // Resize the canvas to the right size
+ canvas.width = width;
+ canvas.height = height;
+ var ctx = canvas.getContext('2d');
+ var loadcount = 0;
+ function doload() {
+ if (++loadcount == 3) {
+ // All images are loaded, so display them.
+ // (Note that the images are loaded from javascript, so might load
+ // after document.onload fires)
+ ctx.drawImage(background, 0, 0, width, height);
+ playWidth = play.width;
+ playHeight = play.height;
+ logoWidth = logo.width;
+ logoHeight = logo.height;
+ var ratio = 1;
+ // If the page is smaller than it 'should' be in either dimension
+ // we scale the play button and logo according to the dimension that
+ // has been shrunk the most.
+ if (width / height > defWidth / defHeight && height < defHeight) {
+ ratio = height / defHeight;
+ } else if (width / height < defWidth / defHeight && width < defWidth) {
+ ratio = width / defWidth;
+ }
+ playWidth *= ratio;
+ playHeight *= ratio;
+ logoWidth *= ratio;
+ logoHeight *= ratio;
+ playLeft = (width - playWidth) / 2;
+ playTop = (height - playHeight) / 2;
+ ctx.drawImage(play, playLeft, playTop, playWidth, playHeight);
+ ctx.globalAlpha = 0.7
+ ctx.drawImage(logo, width - logoWidth, height - logoHeight, logoWidth, logoHeight);
+ // To make it slightly easier to hit, the click target is twice the width/height of the unscaled play button
+ targetLeft = width / 2 - play.width;
+ targetRight = width / 2 + play.width;
+ targetTop = height / 2 - play.height;
+ targetBottom = height / 2 + play.height;
+
+ canvas.addEventListener("click", function(e) {
+ var posx = e.clientX-canvas.offsetLeft;
+ var posy = e.clientY-canvas.offsetTop;
+ if (posx >= targetLeft && posx <= targetRight &&
+ posy >= targetTop && posy <= targetBottom) {
+ top.location.href = "vnd.youtube:VIDEO_ID";
+ }
+ }, false);
+ }
+ }
+ var background = new Image();
+ background.onload = doload;
+ background.src = "http://img.youtube.com/vi/VIDEO_ID/0.jpg";
+ play = new Image();
+ play.onload = doload;
+ play.src = "play.png";
+ logo = new Image();
+ logo.onload = doload;
+ logo.src = "youtube.png";
+ }
+ window.onload = setup;
+ </script>
<div id="main">
- <table height="100%" width="100%">
- <tr>
- <td align="center" valign="middle" height="100%">
- <a id="url" href="vnd.youtube:VIDEO_ID" target="_top">
- <img src="play.png" style="border:0px"/>
- </a>
- </td>
- </tr>
- <tr>
- <td valign="bottom" align="right">
- <img src="youtube.png" style="opacity:.7"/>
- </td>
- </tr>
- </table>
+ <canvas id="canvas"></canvas>
</div>
</body>
</html>
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index 2329abb..6eb2a89 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -20,14 +20,15 @@
<!-- This version zooms the new non-wallpaper up out of the
wallpaper, without zooming the wallpaper itself. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
android:zAdjustment="top">
<scale android:fromXScale=".5" android:toXScale="1.0"
android:fromYScale=".5" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/decelerate_interpolator"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:interpolator="@anim/accelerate_decelerate_interpolator"
+ android:duration="@android:integer/config_mediumAnimTime"/>
</set>
<!-- This version zooms the new non-wallpaper down on top of the
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index 3a6fb05..a32c39e 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -17,7 +17,7 @@
*/
-->
-<!-- This version zooms the new non-wallpaper up out of the
+<!-- This version zooms the exiting non-wallpaper down in to the
wallpaper, without zooming the wallpaper itself. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@anim/decelerate_interpolator"
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index e5b7007..8f0e012 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -16,16 +16,17 @@
** limitations under the License.
*/
-->
-<!-- This version zooms the new non-wallpaper up out of the
+<!-- This version zooms the exiting non-wallpaper down in to the
wallpaper, without zooming the wallpaper itself. -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
android:zAdjustment="top">
<scale android:fromXScale="1.0" android:toXScale=".5"
android:fromYScale="1.0" android:toYScale=".5"
android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@anim/decelerate_interpolator"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:interpolator="@anim/accelerate_decelerate_interpolator"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
diff --git a/core/res/res/color/primary_text_dark.xml b/core/res/res/color/primary_text_dark.xml
index 2ec46b9..39c9e22 100644
--- a/core/res/res/color/primary_text_dark.xml
+++ b/core/res/res/color/primary_text_dark.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@android:color/bright_foreground_dark_disabled"/>
- <item android:state_window_focused="false" android:color="@android:color/bright_foreground_dark"/>
+ <item android:state_window_focused="false" android:color="@android:color/bright_foreground_dark"/>
<item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
diff --git a/core/res/res/color/primary_text_light.xml b/core/res/res/color/primary_text_light.xml
index bd9ac8b..e112034 100644
--- a/core/res/res/color/primary_text_light.xml
+++ b/core/res/res/color/primary_text_light.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@android:color/bright_foreground_light_disabled"/>
- <item android:state_window_focused="false" android:color="@android:color/bright_foreground_light"/>
+ <item android:state_window_focused="false" android:color="@android:color/bright_foreground_light"/>
<item android:state_pressed="true" android:color="@android:color/bright_foreground_light"/>
<item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
<item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
diff --git a/core/res/res/color/secondary_text_dark.xml b/core/res/res/color/secondary_text_dark.xml
index 0d96221..c195ef0 100644
--- a/core/res/res/color/secondary_text_dark.xml
+++ b/core/res/res/color/secondary_text_dark.xml
@@ -15,7 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
+ <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
<item android:state_window_focused="false" android:color="@android:color/dim_foreground_dark"/>
<item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
<item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
diff --git a/core/res/res/color/secondary_text_light.xml b/core/res/res/color/secondary_text_light.xml
index 0846b5e..99249a5 100644
--- a/core/res/res/color/secondary_text_light.xml
+++ b/core/res/res/color/secondary_text_light.xml
@@ -15,7 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
+ <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
<item android:state_window_focused="false" android:color="@android:color/dim_foreground_light"/>
<!-- Since there is only one selector (for both light and dark), the light's selected state shouldn't be inversed like the dark's. -->
<item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
diff --git a/core/res/res/color/tertiary_text_dark.xml b/core/res/res/color/tertiary_text_dark.xml
index 7ce3580..66227bc 100644
--- a/core/res/res/color/tertiary_text_dark.xml
+++ b/core/res/res/color/tertiary_text_dark.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#808080"/>
- <item android:state_window_focused="false" android:color="#808080"/>
+ <item android:state_window_focused="false" android:color="#808080"/>
<item android:state_pressed="true" android:color="#808080"/>
<item android:state_selected="true" android:color="@android:color/dim_foreground_light"/>
<item android:color="#808080"/> <!-- not selected -->
diff --git a/core/res/res/color/tertiary_text_light.xml b/core/res/res/color/tertiary_text_light.xml
index 7e61fc89..ea65756 100644
--- a/core/res/res/color/tertiary_text_light.xml
+++ b/core/res/res/color/tertiary_text_light.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#808080"/>
- <item android:state_window_focused="false" android:color="#808080"/>
+ <item android:state_window_focused="false" android:color="#808080"/>
<item android:state_pressed="true" android:color="#808080"/>
<item android:state_selected="true" android:color="#808080"/>
<item android:color="#808080"/> <!-- not selected -->
diff --git a/core/res/res/drawable-hdpi/ic_btn_search_go.png b/core/res/res/drawable-hdpi/ic_btn_search_go.png
new file mode 100644
index 0000000..8a3a402
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_search_go.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_search_play.png b/core/res/res/drawable-hdpi/ic_btn_search_play.png
deleted file mode 100644
index fb1b974..0000000
--- a/core/res/res/drawable-hdpi/ic_btn_search_play.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png b/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png
index dc3e1f9..6ba42db 100644
--- a/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png
+++ b/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png b/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
index c67ed53..4d0b6010 100644
--- a/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
+++ b/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
new file mode 100644
index 0000000..aef8650
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_search_go.png b/core/res/res/drawable-mdpi/ic_btn_search_go.png
new file mode 100644
index 0000000..0ed9e8e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_search_go.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_search_play.png b/core/res/res/drawable-mdpi/ic_btn_search_play.png
deleted file mode 100644
index dc25dae..0000000
--- a/core/res/res/drawable-mdpi/ic_btn_search_play.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
new file mode 100644
index 0000000..12abeda
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default.xml b/core/res/res/drawable/btn_default.xml
index b8ce2bf..4765084 100644
--- a/core/res/res/drawable/btn_default.xml
+++ b/core/res/res/drawable/btn_default.xml
@@ -15,9 +15,9 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_default_normal" />
- <item android:state_window_focused="false" android:state_enabled="false"
+ <item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_default_normal_disable" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_default_pressed" />
diff --git a/core/res/res/drawable/btn_default_small.xml b/core/res/res/drawable/btn_default_small.xml
index 247e9e2..5485ea0 100644
--- a/core/res/res/drawable/btn_default_small.xml
+++ b/core/res/res/drawable/btn_default_small.xml
@@ -15,9 +15,9 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_default_small_normal" />
- <item android:state_window_focused="false" android:state_enabled="false"
+ <item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_default_small_normal_disable" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_default_small_pressed" />
diff --git a/core/res/res/drawable/btn_dialog.xml b/core/res/res/drawable/btn_dialog.xml
index ed4c28a..d1d7e29 100644
--- a/core/res/res/drawable/btn_dialog.xml
+++ b/core/res/res/drawable/btn_dialog.xml
@@ -15,9 +15,9 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_dialog_normal" />
- <item android:state_window_focused="false" android:state_enabled="false"
+ <item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_dialog_disable" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_dialog_pressed" />
diff --git a/core/res/res/drawable/btn_dropdown.xml b/core/res/res/drawable/btn_dropdown.xml
index 34a0504..d9905c6 100644
--- a/core/res/res/drawable/btn_dropdown.xml
+++ b/core/res/res/drawable/btn_dropdown.xml
@@ -18,7 +18,7 @@
<item
android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_dropdown_normal" />
- <item
+ <item
android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_dropdown_disabled" />
<item
diff --git a/core/res/res/drawable/btn_global_search.xml b/core/res/res/drawable/btn_global_search.xml
index 531f07e..2807a01 100644
--- a/core/res/res/drawable/btn_global_search.xml
+++ b/core/res/res/drawable/btn_global_search.xml
@@ -16,9 +16,9 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- TODO Need different assets for some of these button states. -->
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_global_search_normal" />
- <item android:state_window_focused="false" android:state_enabled="false"
+ <item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_global_search_normal" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_default_pressed" />
diff --git a/core/res/res/drawable/btn_search_dialog.xml b/core/res/res/drawable/btn_search_dialog.xml
index b7f5187..082633e 100644
--- a/core/res/res/drawable/btn_search_dialog.xml
+++ b/core/res/res/drawable/btn_search_dialog.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_search_dialog_default" />
<item android:state_pressed="true"
diff --git a/core/res/res/drawable/btn_search_dialog_voice.xml b/core/res/res/drawable/btn_search_dialog_voice.xml
index 748aaf5..1a76efa 100644
--- a/core/res/res/drawable/btn_search_dialog_voice.xml
+++ b/core/res/res/drawable/btn_search_dialog_voice.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_search_dialog_voice_default" />
<item android:state_pressed="true"
diff --git a/core/res/res/drawable/edit_text.xml b/core/res/res/drawable/edit_text.xml
index 23a97e9..315278d 100644
--- a/core/res/res/drawable/edit_text.xml
+++ b/core/res/res/drawable/edit_text.xml
@@ -15,9 +15,9 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/textfield_default" />
- <item android:state_window_focused="false" android:state_enabled="false"
+ <item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/textfield_disabled" />
<item android:state_pressed="true" android:drawable="@drawable/textfield_pressed" />
<item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected" />
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
deleted file mode 100644
index 651212f..0000000
--- a/core/res/res/drawable/pattern_underwear.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/textfield_search.xml b/core/res/res/drawable/textfield_search.xml
index 2923368..78c9486 100644
--- a/core/res/res/drawable/textfield_search.xml
+++ b/core/res/res/drawable/textfield_search.xml
@@ -16,7 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
+ <item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/textfield_search_default" />
<item android:state_pressed="true"
diff --git a/core/res/res/layout-land/usb_storage_activity.xml b/core/res/res/layout-land/usb_storage_activity.xml
index d714479..50ca569 100644
--- a/core/res/res/layout-land/usb_storage_activity.xml
+++ b/core/res/res/layout-land/usb_storage_activity.xml
@@ -24,7 +24,7 @@
<TextView android:id="@+id/banner"
android:layout_centerHorizontal="true"
- android:layout_below="@id/icon"
+ android:layout_alignParentTop="true"
android:layout_marginTop="10dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -45,8 +45,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
- android:layout_below="@id/message"
- android:layout_marginTop="20dip"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="20dip"
>
<Button android:id="@+id/mount_button"
@@ -64,6 +64,13 @@
android:paddingRight="18dip"
android:text="@string/usb_storage_stop_button_mount"
/>
+ <ProgressBar android:id="@+id/progress"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyle"
+ />
</RelativeLayout>
</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index b404955..a4b2357 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -41,20 +41,21 @@
android:ellipsize="marquee"
android:gravity="right|bottom"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="22sp"
/>
- <!-- emergency call button shown when sim is missing or PUKd -->
- <Button
- android:id="@+id/emergencyCallButton"
+ <!-- "emergency calls only" shown when sim is missing or PUKd -->
+ <TextView
+ android:id="@+id/emergencyCallText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
+ android:layout_below="@id/carrier"
android:layout_alignParentRight="true"
- android:layout_marginTop="10dip"
+ android:layout_marginTop="0dip"
android:layout_marginRight="8dip"
- android:drawableLeft="@drawable/ic_emergency"
- style="@style/Widget.Button.Transparent"
- android:drawablePadding="8dip"
+ android:text="@string/emergency_calls_only"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/white"
/>
<!-- time and date -->
@@ -64,6 +65,7 @@
android:layout_below="@id/carrier"
android:layout_marginTop="52dip"
android:layout_marginLeft="20dip"
+ android:layout_marginBottom="8dip"
>
<TextView android:id="@+id/timeDisplay"
@@ -71,7 +73,6 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="bottom"
android:textSize="72sp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:shadowColor="#C0000000"
@@ -84,8 +85,9 @@
<TextView android:id="@+id/am_pm"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="bottom"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
android:singleLine="true"
android:ellipsize="none"
android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 6ee659c..e1c9772 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -46,18 +46,19 @@
android:ellipsize="marquee"
android:gravity="right|bottom"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="22sp"
/>
- <!-- emergency call button shown when sim is missing or PUKd -->
- <Button
- android:id="@+id/emergencyCallButton"
+ <!-- "emergency calls only" shown when sim is missing or PUKd -->
+ <TextView
+ android:id="@+id/emergencyCallText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="20dip"
- android:drawableLeft="@drawable/ic_emergency"
- style="@style/Widget.Button.Transparent"
- android:drawablePadding="8dip"
+ android:text="@string/emergency_calls_only"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/white"
/>
<com.android.internal.widget.DigitalClock android:id="@+id/time"
@@ -65,12 +66,12 @@
android:layout_height="wrap_content"
android:layout_below="@id/carrier"
android:layout_marginTop="56dip"
+ android:layout_marginBottom="8dip"
>
<TextView android:id="@+id/timeDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="bottom"
android:singleLine="true"
android:ellipsize="none"
android:textSize="72sp"
@@ -85,8 +86,9 @@
<TextView android:id="@+id/am_pm"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="bottom"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
android:singleLine="true"
android:ellipsize="none"
android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index c1b406f..83381a1 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -58,18 +58,19 @@
android:ellipsize="marquee"
android:gravity="right|bottom"
/>
+
<com.android.internal.widget.DigitalClock android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
>
<TextView android:id="@+id/timeDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="bottom"
android:singleLine="true"
android:ellipsize="none"
android:textSize="72sp"
@@ -84,8 +85,9 @@
<TextView android:id="@+id/am_pm"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="bottom"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
android:singleLine="true"
android:ellipsize="none"
android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 16cd48c..97c4ae9 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -55,12 +55,12 @@
android:layout_alignParentTop="true"
android:layout_marginTop="15dip"
android:layout_marginLeft="20dip"
+ android:layout_marginBottom="8dip"
>
<TextView android:id="@+id/timeDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="bottom"
android:singleLine="true"
android:ellipsize="none"
android:textSize="56sp"
@@ -74,8 +74,9 @@
<TextView android:id="@+id/am_pm"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="bottom"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
android:singleLine="true"
android:ellipsize="none"
android:textSize="18sp"
diff --git a/core/res/res/layout/usb_storage_activity.xml b/core/res/res/layout/usb_storage_activity.xml
index 86bfadb..76c30fd 100644
--- a/core/res/res/layout/usb_storage_activity.xml
+++ b/core/res/res/layout/usb_storage_activity.xml
@@ -37,8 +37,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
- android:layout_below="@id/message"
- android:layout_marginTop="20dip"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="20dip"
>
<Button android:id="@+id/mount_button"
@@ -56,6 +56,13 @@
android:paddingRight="18dip"
android:text="@string/usb_storage_stop_button_mount"
/>
+ <ProgressBar android:id="@+id/progress"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyle"
+ />
</RelativeLayout>
</RelativeLayout>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 16facee..e02c48b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Omezený přístup byl změněn."</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Datová služba je zablokována."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Tísňová linka je zablokována."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Hlasová služba a služba SMS jsou zablokovány."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Veškeré hlasové služby a služby SMS jsou zablokovány."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Hlasová služba je zablokována."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Veškeré hlasové služby jsou zablokovány."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Služby SMS jsou zablokovány."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Hlasové a datové služby jsou zablokovány."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Hlasové služby a služby SMS jsou zablokovány."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Veškeré hlasové a datové služby a služby SMS jsou zablokovány."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Hlas"</string>
<string name="serviceClassData" msgid="872456782077937893">"Data"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Umožňuje aplikaci nainstalovat nové či aktualizované balíčky systému Android. Škodlivé aplikace mohou pomocí tohoto nastavení přidat nové aplikace s libovolnými oprávněními."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"smazání všech dat v mezipaměti aplikace"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Umožňuje aplikaci uvolnit paměť telefonu smazáním souborů v adresáři mezipaměti aplikace. Přístup je velmi omezený, většinou pouze pro systémové procesy."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Přesun zdrojů aplikace"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Umožňuje aplikaci přesunout své zdroje z interní paměti na externí médium a opačně."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"čtení systémových souborů protokolu"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"čtení nebo zápis do prostředků funkce diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Umožňuje aplikaci změnit informace o vlastníkovi telefonu uložené v telefonu. Škodlivé aplikace mohou pomocí tohoto nastavení vymazat či pozměnit informace o vlastníkovi."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"čtení informací o vlastníkovi"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Umožňuje aplikaci číst informace o vlastníkovi telefonu uložená v telefonu. Škodlivé aplikace mohou pomocí tohoto nastavení načíst informace o vlastníkovi."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"Čtení událostí v kalendáři"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Umožňuje aplikaci načíst všechny události kalendáře uložené ve vašem telefonu. Škodlivé aplikace poté mohou dalším lidem odeslat události z vašeho kalendáře."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"Přidávání nebo úprava událostí v kalendáři a odesílání e-mailů hostům"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Umožňuje aplikaci přidávat nebo měnit události v kalendáři, které budou odesílat e-maily hostům. Škodlivé aplikace mohou pomocí tohoto oprávnění mazat nebo upravovat události v kalendáři a odesílat e-maily hostům."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"simulace zdrojů polohy pro účely testování"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Vytváří simulované zdroje polohy pro účely testování. Škodlivé aplikace mohou pomocí tohoto nastavení změnit polohu či stav vrácený zdroji skutečné polohy, jako je např. jednotka GPS či poskytovatelé sítě."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"přístup k dalším příkazům poskytovatele polohy"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Umožňuje aplikaci změnit nastavení APN, jako je například proxy či port APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"změna připojení k síti"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Umožňuje aplikaci změnit stav připojení k síti."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"změnit sdílené datové připojení"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Změna sdíleného datového připojení"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Umožňuje aplikaci změnit stav sdíleného datového připojení k síti."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"změnit nastavení použití dat na pozadí"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Umožňuje aplikaci změnit nastavení použití dat na pozadí."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefon odemknete stisknutím tlačítka Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Odblokujte pomocí gesta"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Tísňové volání"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zavolat zpět"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Správně!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Zkuste to prosím znovu"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Nabíjení (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"před 1 hodinou"</item>
<item quantity="other" msgid="2467273239587587569">"před <xliff:g id="COUNT">%d</xliff:g> hod."</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Posledních <xliff:g id="COUNT">%d</xliff:g> dnů"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Poslední měsíc"</string>
+ <string name="older" msgid="5211975022815554840">"Starší"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"včera"</item>
<item quantity="other" msgid="2479586466153314633">"před <xliff:g id="COUNT">%d</xliff:g> dny"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"týd."</string>
<string name="year" msgid="4001118221013892076">"rokem"</string>
<string name="years" msgid="6881577717993213522">"lety"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Každý pracovní den (Po – Pá)"</string>
- <string name="daily" msgid="5738949095624133403">"Denně"</string>
- <string name="weekly" msgid="983428358394268344">"Každý týden v <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Měsíčně"</string>
- <string name="yearly" msgid="1519577999407493836">"Ročně"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Video nelze přehrát"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Omlouváme se, ale toto video nelze přenášet datovým proudem do tohoto zařízení."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Toto video bohužel nelze přehrát."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Před vypnutím úložiště USB zkontrolujte, zda jste odpojili (vyjmuli) kartu SD zařízení Android z počítače."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Vypnout úložiště USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Při vypínání úložiště USB došlo k problémům. Zkontrolujte, zda byl hostitel USB odpojen, a zkuste to znovu."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formátovat kartu SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Opravdu chcete kartu SD naformátovat? Všechna data na kartě budou ztracena."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formátovat"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Resetovat"</string>
<string name="submit" msgid="1602335572089911941">"Odeslat"</string>
<string name="description_star" msgid="2654319874908576133">"oblíbené"</string>
- <string name="tether_title" msgid="6970447107301643248">"Sdílené datové připojení prostřednictvím portu USB je k dispozici"</string>
- <string name="tether_message" msgid="554549994538298101">"Chcete-li sdílet datové připojení telefonu s počítačem, vyberte možnost Sdílet datové připojení."</string>
- <string name="tether_button" msgid="7409514810151603641">"Sdílet datové připojení"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Zrušit"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Při sdílení datového připojení došlo k chybě."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Sdílené datové připojení prostřednictvím portu USB je k dispozici"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Tuto možnost vyberte, chcete-li sdílet datové připojení telefonu s počítačem."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Zrušit sdílení datového připojení"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Výběrem této možnost zrušíte sdílení datového připojení s počítačem."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Odpojit sdílené datové připojení"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Mobilní datové připojení telefonu je sdíleno s počítačem. Chcete-li sdílení prostřednictvím portu USB zrušit, vyberte možnost Odpojit."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Odpojit"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Zrušit"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Při ukončování sdíleného datového připojení došlo k chybě. Zkuste to prosím znovu."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Aktivován režim V autě"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Vyberte, chcete-li ukončit režim V autě."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Sdílení je aktivní"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Dotykem zahájíte konfiguraci"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 35ab012..f3a4126 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Begrænset adgang ændret"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Datatjenesten er blokeret."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Nødtjenesten er blokeret."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Stemme-/sms-tjenesten er blokeret."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Alle stemme-/sms-tjenester er blokerede."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Stemmetjenesten er blokeret."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Alle stemmetjenester er blokerede."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Sms-tjenesten er blokeret."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Stemme-/datatjenester er blokerede."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Stemme-/sms-tjenester er blokerede."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Alle stemme-/data/sms-tjenester er blokerede."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Stemme"</string>
<string name="serviceClassData" msgid="872456782077937893">"Data"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Tillader, at et program installerer nye eller opdaterede Android-pakker. Ondsindede programmer kan bruge dette til at tilføje nye programmer med vilkårlige, effektive tilladelser."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"slet alle cachedata for programmet"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillader, at et program frigør plads på telefonen ved at slette filer i programmets cachemappe. Adgang er normalt meget begrænset til systemprocesser."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Flyt programressourcer"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillader, at et program flytter programressourcer fra interne til eksterne medier og omvendt."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"læs systemlogfiler"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"læs/skriv til ressourcer ejet af diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Tillader, at et program ændrer rtelefonens ejerdata, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at slette eller ændre ejerdata."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"læs ejerdata"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Tillader, at et program læser telefonens ejerdata, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at læse ejerdata."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"læs kalenderbegivenheder"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Tillader, at et program læser alle kalenderbegivenheder, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at sende dine kalenderbegivenheder til andre mennesker."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"tilføj eller rediger kalenderbegivenheder, og send e-mail til gæster"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Tillader, at et program tilføjer eller ændrer begivenhederne i din kalender, hvilket kan sende e-mail til gæster. Ondsindede programmer kan bruge dette til at slette eller ændre dine kalenderbegivenheder eller til at sende e-mail til gæster."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"imiterede placeringskilder til test"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Opret imiterede placeringskilder til testning. Ondsindede programmer kan bruge dette til at tilsidesætte den returnerede placering og/eller status fra rigtige placeringskilder som f.eks. GPS eller netværksudbydere."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få adgang til ekstra kommandoer for placeringsudbyder"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Tillader, at et program ændrer APN-indstillingerne, f.eks. enhver APNs Proxy og Port."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"skift netværksforbindelse"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Tillader, at et program ændrer netværksforbindelsens tilstand."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"skifte tethering-forbindelse"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Skift tethering-forbindelse"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Tillader, at et program ændrer tilstand for et bundet netværk."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"skift brugerindstilling for baggrundsdata"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Tillader, at et program ændrer brugerindstillingerne for baggrundsdata."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryk på Menu for at låse op."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Tegn oplåsningsmønster"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Nødopkald"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tilbage til opkald"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager! Prøv igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Oplader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"For 1 time siden"</item>
<item quantity="other" msgid="2467273239587587569">"For <xliff:g id="COUNT">%d</xliff:g> timer siden"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Seneste <xliff:g id="COUNT">%d</xliff:g> dage"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Seneste måned"</string>
+ <string name="older" msgid="5211975022815554840">"Ældre"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"i går"</item>
<item quantity="other" msgid="2479586466153314633">"For <xliff:g id="COUNT">%d</xliff:g> dage siden"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"uger"</string>
<string name="year" msgid="4001118221013892076">"år"</string>
<string name="years" msgid="6881577717993213522">"år"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Hverdage (man.-fre.)"</string>
- <string name="daily" msgid="5738949095624133403">"Dagligt"</string>
- <string name="weekly" msgid="983428358394268344">"Ugentlig hver <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Månedligt"</string>
- <string name="yearly" msgid="1519577999407493836">"Årligt"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Videoen kan ikke afspilles"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Beklager! Denne video er ikke gyldig til streaming på denne enhed."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Beklager! Denne video kan ikke afspilles."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Sørg for, at du har demonteret (\"udskubbet\") din Androids SD-kort fra computeren, før du slår USB-lagring fra."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Slå USB-lagring fra"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Der opstod et problem med at slå USB-lagringen fra. Sørg for, at du har demonteret USB-værten, og prøv så igen."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formater SD-kort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Er du sikker på, du ønsker at formatere SD-kortet? Alle data på kortet mistes."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formater"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Nulstil"</string>
<string name="submit" msgid="1602335572089911941">"Send"</string>
<string name="description_star" msgid="2654319874908576133">"favorit"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-binding tilgængelig"</string>
- <string name="tether_message" msgid="554549994538298101">"Vælg \"Tether\" hvis du vil dele telefonens dataforbindelse med computeren."</string>
- <string name="tether_button" msgid="7409514810151603641">"Bind"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Annuller"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Der er opstået et problem med tethering."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-binding tilgængelig"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Vælg tehtering af computeren og telefonen."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Fjern tethering"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Vælg at fjerne tethering fra computeren."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Afbryd binding"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Du har delt telefonens mobildataforbindelse med computeren. Vælg \"Afbryd\" for at afbryde USB-tethering."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Afbryd"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Annuller"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Der opstod et problem med at slå bindingen fra. Prøv igen."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Biltilstand er aktiveret"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Vælg for at afslutte biltilstand."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Tethering aktiveret"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Tryk for at konfigurere"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index f332937..f38dee9 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Eingeschränkter Zugriff geändert"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Daten-Dienst ist gesperrt."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Notruf ist gesperrt."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Sprach-/SMS-Dienst ist gesperrt."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Alle Sprach-/SMS-Dienste sind gesperrt."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Sprachdienst ist gesperrt."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Alle Sprachdienste sind gesperrt."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMS-Dienst ist gesperrt."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Sprach-/Datendienste sind gesperrt."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Sprach-/SMS-Dienste sind gesperrt."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Alle Sprach-/Daten-/SMS-Dienste sind gesperrt."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Sprachnotiz"</string>
<string name="serviceClassData" msgid="872456782077937893">"Daten"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Ermöglicht einer Anwendung, neue oder aktualisierte Android-Pakete zu installieren. Schädliche Anwendungen können so neue Anwendungen mit beliebig umfangreichen Berechtigungen hinzufügen."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"Alle Cache-Daten der Anwendung löschen"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Ermöglicht einer Anwendung, Telefonspeicher durch das Löschen von Dateien im Cache-Verzeichnis der Anwendung freizugeben. Der Zugriff beschränkt sich in der Regel auf Systemprozesse."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Anwendungsressourcen verschieben"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Ermöglicht einer Anwendung, Anwendungsressourcen von interne auf externe Medien zu verschieben und umgekehrt."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"System-Protokolldateien lesen"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"Lese-/Schreibberechtigung für zu Diagnosegruppe gehörige Elemente"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Ermöglicht einer Anwendung, die auf Ihrem Telefon gespeicherten Eigentümerdaten zu ändern. Schädliche Anwendungen können so Eigentümerdaten löschen oder verändern."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"Eigentümerdaten lesen"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Ermöglicht einer Anwendung, die auf Ihrem Telefon gespeicherten Eigentümerdaten zu lesen. Schädliche Anwendungen können so Eigentümerdaten lesen."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"Kalendereinträge lesen"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Ermöglicht einer Anwendung, alle auf Ihrem Telefon gespeicherten Kalenderereignisse zu lesen. Schädliche Anwendungen können so Ihre Kalenderereignisse an andere Personen senden."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"Kalendereinträge hinzufügen oder ändern und E-Mails an Gäste senden"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Ermöglicht einer Anwendung, Einträge auf Ihrem Kalender hinzuzufügen oder zu ändern, die E-Mails an Gäste senden können. Schädliche Anwendungen können so Ihre Kalenderdaten löschen oder verändern oder E-Mails versenden."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"Falsche Standortquellen für Testzwecke"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Erstellt falsche Standortquellen für Testzwecke. Schädliche Anwendungen können so den von den echten Standortquellen wie GPS oder Netzwerkanbieter zurückgegebenen Standort und/oder Status überschreiben."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Auf zusätzliche Dienstanbieterbefehle für Standort zugreifen"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Ermöglicht einer Anwendung, die APN-Einstellungen wie Proxy und Port eines Zugriffspunkts zu ändern."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"Netzwerkkonnektivität ändern"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Ermöglicht einer Anwendung, den Status der Netzwerkkonnektivität zu ändern."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"Tethering-Konnektivität ändern"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Tethering-Konnektivität ändern"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Ermöglicht einer Anwendung, den Status der Tethering-Konnektivität zu ändern."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"Einstellung zur Verwendung von Hintergrunddaten ändern"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Ermöglicht einer Anwendung, die Einstellung der Verwendung von Hintergrunddaten zu ändern."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Schema für Entsperrung zeichnen"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Notruf"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zurück zum Anruf"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tut uns leid. Versuchen Sie es noch einmal."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Wird geladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"Vor 1 Stunde"</item>
<item quantity="other" msgid="2467273239587587569">"Vor <xliff:g id="COUNT">%d</xliff:g> Stunden"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Letzte <xliff:g id="COUNT">%d</xliff:g> Tage"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Letzter Monat"</string>
+ <string name="older" msgid="5211975022815554840">"Älter"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"Gestern"</item>
<item quantity="other" msgid="2479586466153314633">"Vor <xliff:g id="COUNT">%d</xliff:g> Tagen"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"Wochen"</string>
<string name="year" msgid="4001118221013892076">"Jahr"</string>
<string name="years" msgid="6881577717993213522">"Jahre"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Jeden Wochentag (Mo-Fr)"</string>
- <string name="daily" msgid="5738949095624133403">"Täglich"</string>
- <string name="weekly" msgid="983428358394268344">"Jede Woche am <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Monatlich"</string>
- <string name="yearly" msgid="1519577999407493836">"Jährlich"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Video kann nicht wiedergegeben werden."</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Leider ist dieses Video nicht für Streaming auf diesem Gerät gültig."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Dieses Video kann leider nicht abgespielt werden."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Stellen Sie vor dem Deaktivieren des USB-Speichers sicher, dass Sie Ihre Android-SD-Karte von Ihrem Computer getrennt (\"ausgeworfen\") haben."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB-Speicher deaktivieren"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Beim Deaktivieren des USB-Speichers ist ein Problem aufgetreten. Überprüfen Sie, ob Sie den USB-Host getrennt haben, und versuchen Sie es erneut."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"SD-Karte formatieren"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Möchten Sie die SD-Karte wirklich formatieren? Alle Daten auf Ihrer Karte gehen dann verloren."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Zurücksetzen"</string>
<string name="submit" msgid="1602335572089911941">"Senden"</string>
<string name="description_star" msgid="2654319874908576133">"Favorit"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-Tethering verfügbar"</string>
- <string name="tether_message" msgid="554549994538298101">"Wählen Sie \"Tethering\" aus, wenn Sie die Datenverbindung Ihres Telefons für Ihren Computer freigeben möchten."</string>
- <string name="tether_button" msgid="7409514810151603641">"Tethering"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Abbrechen"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Beim Tethering ist ein Problem aufgetreten."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-Tethering verfügbar"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Auswählen, um Ihren Computer per Tethering an Ihr Telefon anzubinden"</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Tethering beenden"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Auswählen, um das Tethering mit Ihrem Computer zu beenden"</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Tethering-Verbindung trennen"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Sie haben die mobile Datenverbindung Ihres Telefons für Ihren Computer freigegeben. Wählen Sie \"Verbindung trennen\", um das USB-Tethering zu beenden."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Verbindung trennen"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Abbrechen"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Beim Beenden des Tetherings ist ein Problem aufgetreten. Bitte versuchen Sie es erneut."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Automodus aktiviert"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Zum Beenden des Automodus auswählen"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Tethering aktiv"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Zum Konfigurieren berühren"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8413224..22c597c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Η περιορισμένη πρόσβαση άλλαξε"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Η υπηρεσία δεδομένων είναι αποκλεισμένη."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Η υπηρεσία έκτακτης ανάγκης είναι αποκλεισμένη."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Η φωνητική υπηρεσία/υπηρεσία SMS είναι αποκλεισμένη."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Όλες οι φωνητικές υπηρεσίες/υπηρεσίες SMS έχουν αποκλειστεί."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Η υπηρεσία φωνής έχει αποκλειστεί."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Όλες οι υπηρεσίες φωνής έχουν αποκλειστεί."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Η υπηρεσία SMS έχει αποκλειστεί."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Οι υπηρεσίες φωνής/δεδομένων έχουν αποκλειστεί."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Όλες οι υπηρεσίες φωνής/SMS έχουν αποκλειστεί."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Όλες οι υπηρεσίες φωνής/δεδομένων/SMS έχουν αποκλειστεί."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Φωνή"</string>
<string name="serviceClassData" msgid="872456782077937893">"Δεδομένα"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"ΦΑΞ"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Επιτρέπει σε μια εφαρμογή την εγκατάσταση νέων ή ενημερωμένων πακέτων Android. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να προσθέσουν νέες εφαρμογές με πολλές αυθαίρετες άδειες."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"διαγραφή όλων των δεδομένων προσωρινής μνήμης εφαρμογής"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Επιτρέπει σε μια εφαρμογή να αυξήσει τον ελεύθερο χώρο αποθήκευσης του τηλεφώνου διαγράφοντας αρχεία από τον κατάλογο προσωρινής μνήμης της εφαρμογής. Η πρόσβαση είναι συνήθως πολύ περιορισμένη στη διαδικασία συστήματος."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Μετακίνηση πόρων εφαρμογής"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση πόρων εφαρμογής από ένα εσωτερικό σε ένα εξωτερικό μέσο και αντίστροφα."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"ανάγνωση αρχείων καταγραφής συστήματος"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"ανάγνωση/εγγραφή σε πόρους που ανήκουν στο διαγνωστικό"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Επιτρέπει σε μια εφαρμογή να τροποποιήσει τα δεδομένα κατόχου τηλεφώνου στο τηλέφωνό σας. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να τροποποιήσουν τα δεδομένα κατόχου."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"ανάγνωση δεδομένων κατόχου"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των δεδομένων κατόχου τηλεφώνου που είναι αποθηκευμένα στο τηλέφωνό σας. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για την ανάγνωση δεδομένων κατόχου τηλεφώνου."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"ανάγνωση συμβάντων ημερολογίου"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Επιτρέπει σε μια εφαρμογή να αναγνώσει όλα τα συμβάντα ημερολογίου που είναι αποθηκευμένα στο τηλέφωνό σας. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να αποστείλουν συμβάντα ημερολογίου σε άλλους χρήστες."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"προσθήκη ή τροποποίηση συμβάντων του ημερολογίου και αποστολή μηνυμάτων ηλεκτρονικού ταχυδρομείου στους προσκεκλημένους"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Επιτρέπει σε μια εφαρμογή την προσθήκη ή την αλλαγή συμβάντων στο ημερολόγιο σας, και την ενδεχόμενη αποστολή μηνυμάτων ηλεκτρονικού ταχυδρομείου στους προσκεκλημένους. Οι κακόβουλες εφαρμογές μπορούν να χρησιμοποιήσουν αυτή τη λειτουργία για να διαγράψουν ή να τροποποιήσουν τα συμβάντα του ημερολογίου σας ή για να στείλουν μηνύματα ηλεκτρονικού ταχυδρομείου στους προσκεκλημένους."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"δημιουργία ψευδών πηγών τοποθεσίας για δοκιμή"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Δημιουργία εικονικών πηγών τοποθεσίας για δοκιμή. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να παρακάμψουν την τοποθεσία και/ή την κατάσταση που βρίσκουν πραγματικές πηγές τοποθεσίας, όπως πάροχοι GPS ή πάροχοι δικτύου."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Επιτρέπει σε μια εφαρμογή να τροποποιήσει τις ρυθμίσεις APN, όπως Διακομιστής μεσολάβησης και Θύρα για ένα APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"αλλαγή συνδεσιμότητας δικτύου"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Επιτρέπει σε μια εφαρμογή την αλλαγή της κατάστασης συνδεσιμότητας δικτύου."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"αλλαγή συνδεσιμότητας μέσω σύνδεσης με κινητή συσκευή"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"αλλαγή συνδεσιμότητας της σύνδεσης μέσω κινητής συσκευής"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Επιτρέπει σε μια εφαρμογή την αλλαγή της κατάστασης συνδεσιμότητας δικτύου μέσω σύνδεσης με κινητή συσκευή."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"αλλαγή ρύθμισης της χρήσης δεδομένων στο παρασκήνιο"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Επιτρέπει σε μια εφαρμογή την αλλαγή της ρύθμισης χρήσης δεδομένων φόντου."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Σχεδιασμός μοτίβου για ξεκλείδωμα"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Κλήση έκτακτης ανάγκης"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Επιστροφή στην κλήση"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Προσπαθήστε αργότερα"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Φόρτιση (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"πριν από 1 ώρα"</item>
<item quantity="other" msgid="2467273239587587569">"πριν από <xliff:g id="COUNT">%d</xliff:g> ώρες"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Τελευταίες <xliff:g id="COUNT">%d</xliff:g> ημέρες"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Τελευταίος μήνας"</string>
+ <string name="older" msgid="5211975022815554840">"Παλαιότερα"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"χθες"</item>
<item quantity="other" msgid="2479586466153314633">"πριν από <xliff:g id="COUNT">%d</xliff:g> ημέρες"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"εβδομάδες"</string>
<string name="year" msgid="4001118221013892076">"έτος"</string>
<string name="years" msgid="6881577717993213522">"έτη"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Καθημερινές (Δευ-Παρ)"</string>
- <string name="daily" msgid="5738949095624133403">"Καθημερινά"</string>
- <string name="weekly" msgid="983428358394268344">"Κάθε εβδομάδα στο <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Μηνιαία"</string>
- <string name="yearly" msgid="1519577999407493836">"Ετήσια"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Δεν είναι δυνατή η αναπαραγωγή βίντεο"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Αυτό το βίντεο δεν είναι έγκυρο για ροή σε αυτή τη συσκευή."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Δεν είναι δυνατή η προβολή αυτού του βίντεο."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Προτού απενεργοποιήσετε το χώρο αποθήκευσης USB, βεβαιωθείτε ότι έχετε αποσυνδέσει (“αφαιρέσει”) την κάρτα SD του Android από τον υπολογιστή σας."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Απενεργοποίηση χώρου αποθήκευσης USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Παρουσιάστηκε πρόβλημα κατά την απενεργοποίηση του αποθηκευτικού χώρου USB. Βεβαιωθείτε ότι έχετε αφαιρέσει την υποδοχή USB και προσπαθήστε ξανά."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"ΟΚ"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Διαμόρφωση κάρτας SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Είστε βέβαιοι ότι θέλετε να διαμορφώσετε την κάρτα SD; Όλα τα δεδομένα στην κάρτα σας θα χαθούν."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Διαμόρφωση"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Επαναφορά"</string>
<string name="submit" msgid="1602335572089911941">"Υποβολή"</string>
<string name="description_star" msgid="2654319874908576133">"αγαπημένο"</string>
- <string name="tether_title" msgid="6970447107301643248">"Διαθέσιμη σύνδεση μέσω κινητής συσκευής με USB"</string>
- <string name="tether_message" msgid="554549994538298101">"Επιλέξτε \"Σύνδεση μέσω κινητής συσκευής\" αν θέλετε να κάνετε κοινή χρήση της σύνδεσης δεδομένων του τηλεφώνου σας με τον υπολογιστή σας."</string>
- <string name="tether_button" msgid="7409514810151603641">"Σύνδεση μέσω κινητής συσκευής"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Ακύρωση"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Υπάρχει πρόβλημα σύνδεσης μέσω κινητής συσκευής."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Διαθέσιμη σύνδεση μέσω κινητής συσκευής με USB"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Συνδέστε τον υπολογιστή με το τηλέφωνό σας."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Κατάργηση σύνδεσης μέσω κινητής συσκευής"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Καταργήστε τη σύνδεση μέσω κινητής συσκευής του υπολογιστή σας."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Κατάργηση σύνδεσης μέσω κινητής συσκευής"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Κάνετε κοινή χρήση της σύνδεσης των δεδομένων του κινητού σας τηλεφώνου με τον υπολογιστή σας. Επιλέξτε \"Αποσύνδεση\" για να καταργήσετε τη σύνδεση μέσω κινητής συσκευής με USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Αποσύνδεση"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Ακύρωση"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Παρουσιάστηκε ένα πρόβλημα κατά την απενεργοποίηση της σύνδεσης μέσω κινητής συσκευής. Προσπαθήστε ξανά."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Η λειτουργία αυτοκινήτου είναι ενεργοποιημένη"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Επιλέξτε για έξοδο από τη λειτουργία αυτοκινήτου."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Η σύνδεση μέσω κινητής συσκευής είναι ενεργή"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Αγγίξτε για να γίνει διαμόρφωση"</string>
</resources>
diff --git a/core/res/res/values-en-rUS/donottranslate-names.xml b/core/res/res/values-en-rUS/donottranslate-names.xml
index f8ec765..aa0abe3 100644
--- a/core/res/res/values-en-rUS/donottranslate-names.xml
+++ b/core/res/res/values-en-rUS/donottranslate-names.xml
@@ -4,35 +4,51 @@
<!-- various string resources for Contacts -->
<string-array name="common_nicknames">
- <item>Abigail, Abbie, Gail, Gayle</item>
+ <item>Abigail, Abbie</item>
+ <item>Abigail, Gail, Gayle</item>
<item>Abe, Abraham</item>
- <item>Aggie, Agatha, Agnes</item>
- <item>Albert, Al, Bert, Bertie</item>
- <item>Alexander, Al, Alec, Alex, Lex, Sasha</item>
- <item>Alexandra, Al, Allie, Ally, Lex, Lexie, Sandra, Sandy, Sasha</item>
+ <item>Agatha, Aggie</item>
+ <item>Agatha, Agnes</item>
+ <item>Albert, Al</item>
+ <item>Albert, Bert, Bertie</item>
+ <item>Alexander, Al, Alex</item>
+ <item>Alexander, Al, Alec</item>
+ <item>Alexander, Alex, Lex</item>
+ <item>Alexander, Alex, Sasha</item>
+ <item>Alexandra, Al, Allie, Ally</item>
+ <item>Alexandra, Lex, Lexie</item>
+ <item>Alexandra, Sandra, Sandy</item>
+ <item>Alexandra, Sasha</item>
<item>Alf, Alfred, Alfredo, Alfie</item>
<item>Alice, Allie, Ally</item>
<item>Alison, Allie, Ally</item>
<item>Allison, Allie, Ally</item>
<item>Amanda, Mandi, Mandy</item>
<item>Andrea, Andie</item>
- <item>Andrew, Andy, Drew</item>
+ <item>Andrew, Andy</item>
+ <item>Andrew, Drew</item>
<item>Anne, Annie, Annette</item>
<item>Anthony, Tony, Toni, Tone</item>
<item>Arthur, Art, Arty</item>
- <item>Barbara, Babs, Barb, Barbie</item>
- <item>Benjamin, Ben, Benji, Benny</item>
- <item>Bernard, Bern, Bernie, Barnie</item>
+ <item>Barbara, Barb, Barbie</item>
+ <item>Benjamin, Ben, Benny</item>
+ <item>Benjamin, Benji</item>
+ <item>Bernard, Barnie</item>
+ <item>Bernard, Bern, Bernie</item>
<item>Bertram, Bert, Bertie</item>
<item>Bradly, Brad</item>
<item>Calvin, Cal</item>
<item>Catherine, Cat, Cate, Cath, Catie, Cathy, Kat, Kate, Katie, Kathy</item>
<item>Carrie, Caroline, Carolyn</item>
- <item>Charles, Chuck, Chaz, Charlie, Buck</item>
+ <item>Charles, Charlie</item>
+ <item>Charles, Chuck</item>
+ <item>Charles, Chaz</item>
+ <item>Charles, Buck</item>
<item>Christine, Chrissy, Chrissie</item>
<item>Christopher, Chris</item>
<item>Clinton, Clint</item>
- <item>Cynthia, Cindy, Cynth</item>
+ <item>Cynthia, Cindy</item>
+ <item>Cynthia, Cynth</item>
<item>Daniel, Dan, Danny</item>
<item>David, Dave</item>
<item>Deborah, Deb, Debbie</item>
@@ -43,30 +59,46 @@
<item>Dorothea, Dot, Dotty</item>
<item>Dorothy, Dot, Dotty</item>
<item>Douglas, Doug</item>
- <item>Edward, Ed, Eddie, Ned, Neddie, Neddy, Ted, Teddy, Teddie</item>
- <item>Eleanor, Ella, Ellie, Elle</item>
+ <item>Edward, Ed, Eddie</item>
+ <item>Edward, Ned, Neddie, Neddy</item>
+ <item>Edward, Ted, Teddy, Teddie</item>
+ <item>Eleanor, Ella</item>
+ <item>Eleanor, Ellie, Elle</item>
<item>Elisabetta, Betta</item>
- <item>Elizabeth, Beth, Bess, Bessie, Betsy, Betty, Bette, Eliza, Lisa, Liza, Liz</item>
+ <item>Elizabeth, Beth</item>
+ <item>Elizabeth, Bess, Bessie</item>
+ <item>Elizabeth, Betsy</item>
+ <item>Elizabeth, Betty, Bette</item>
+ <item>Elizabeth, Eliza</item>
+ <item>Elizabeth, Lisa, Liza, Liz</item>
<item>Emily, Em, Ems, Emmy</item>
<item>Emma, Em, Ems, Emmy</item>
<item>Eugene, Gene</item>
<item>Fannie, Fanny</item>
<item>Florence, Flo</item>
- <item>Frances, Fran, Francie</item>
- <item>Francis, Fran, Frank, Frankie</item>
+ <item>Frances, Fran</item>
+ <item>Frances, Francie</item>
+ <item>Francis, Fran</item>
+ <item>Francis, Frank, Frankie</item>
<item>Frederick, Fred, Freddy</item>
<item>Gabriel, Gabe</item>
<item>Gerald, Gerry</item>
<item>Gerard, Gerry</item>
<item>Gregory, Greg, Gregg</item>
- <item>Harold, Hal, Harry</item>
- <item>Henry, Hal, Hank, Harry</item>
+ <item>Harold, Hal</item>
+ <item>Harold, Harry</item>
+ <item>Henry, Hal</item>
+ <item>Henry, Hank</item>
+ <item>Henry, Harry</item>
<item>Herbert, Bert, Bertie</item>
<item>Irving, Irv</item>
- <item>Isabella, Isa, Izzy, Bella</item>
+ <item>Isabella, Isa</item>
+ <item>Isabella, Izzy</item>
+ <item>Isabella, Bella</item>
<item>Jacob, Jake</item>
<item>Jacqueline, Jackie</item>
- <item>James, Jim, Jimmy, Jamie, Jock</item>
+ <item>James, Jim, Jimmy</item>
+ <item>James, Jamie</item>
<item>Janet, Jan</item>
<item>Janice, Jan</item>
<item>Jason, Jay</item>
@@ -90,10 +122,15 @@
<item>Laura, Lauri, Laurie</item>
<item>Lauren, Lauri, Laurie</item>
<item>Lawrence, Larry</item>
- <item>Leonard, Leo, Len, Lenny</item>
- <item>Leopold, Leo, Len, Lenny</item>
+ <item>Leonard, Leo</item>
+ <item>Leonard, Len, Lenny</item>
+ <item>Leopold, Leo</item>
<item>Madeline, Maddie, Maddy</item>
- <item>Margaret, Marge, Marg, Maggie, Mags, Meg, Peggy, Greta, Gretchen</item>
+ <item>Margaret, Marge, Marg</item>
+ <item>Margaret, Maggie, Mags</item>
+ <item>Margaret, Meg</item>
+ <item>Margaret, Peggy</item>
+ <item>Margaret, Greta, Gretchen</item>
<item>Martin, Martie, Marty</item>
<item>Matthew, Matt, Mattie</item>
<item>Maureen, Mo</item>
@@ -101,27 +138,36 @@
<item>Maxwell, Max</item>
<item>Maximilian, Maxim, Max</item>
<item>Megan, Meg</item>
- <item>Michael, Mickey, Mick, Mike, Mikey</item>
+ <item>Michael, Mickey, Mick</item>
+ <item>Michael, Mike, Mikey</item>
<item>Morris, Mo</item>
<item>Nancy, Nan</item>
- <item>Nathan, Nat, Nate</item>
- <item>Nathaniel, Nat, Nate</item>
+ <item>Nathan, Nat</item>
+ <item>Nathan, Nate</item>
+ <item>Nathaniel, Nat</item>
+ <item>Nathaniel, Nate</item>
<item>Nicholas, Nick</item>
<item>Nicole, Nicky, Nickie, Nikky</item>
<item>Pamela, Pam</item>
- <item>Patricia, Pat, Patsy, Patty, Trish, Tricia</item>
- <item>Patrick, Pat, Patter</item>
+ <item>Patricia, Pat, Patty</item>
+ <item>Patricia, Patsy</item>
+ <item>Patricia, Trish, Tricia</item>
+ <item>Patrick, Pat</item>
<item>Penelope, Penny</item>
<item>Peter, Pete</item>
<item>Raymond, Ray</item>
<item>Philip, Phil</item>
<item>Rebecca, Becca, Becky</item>
- <item>Richard, Rick, Rich, Dick</item>
- <item>Robert, Bob, Rob, Robbie, Bobby, Rab</item>
+ <item>Richard, Rick</item>
+ <item>Richard, Rich</item>
+ <item>Richard, Dick</item>
+ <item>Robert, Bob, Bobby</item>
+ <item>Robert, Rob, Robbie</item>
<item>Rodney. Rod</item>
<item>Ronald, Ron, Ronnie</item>
<item>Rosemary, Rosie, Rose</item>
- <item>Russell, Russ, Rusty</item>
+ <item>Russell, Russ</item>
+ <item>Russell, Rusty</item>
<item>Ryan, Ry</item>
<item>Samantha, Sam</item>
<item>Samuel, Sam, Sammy</item>
@@ -130,23 +176,34 @@
<item>Stephen, Steve</item>
<item>Steven, Steve</item>
<item>Stuart, Stu</item>
- <item>Susan, Sue, Susie, Suzie</item>
- <item>Suzanne, Sue, Susie, Suzie</item>
+ <item>Susan, Sue</item>
+ <item>Susan, Susie, Suzie</item>
+ <item>Suzanne, Sue</item>
+ <item>Suzanne, Susie, Suzie</item>
<item>Tamara, Tammy</item>
<item>Theresa, Teresa</item>
- <item>Theodora, Teddie, Thea, Theo</item>
- <item>Theodore, Ted, Teddy, Theo</item>
+ <item>Theodora, Teddie</item>
+ <item>Theodora, Thea</item>
+ <item>Theodore, Ted, Teddy</item>
+ <item>Theodore, Theo</item>
<item>Thomas, Tom, Thom, Tommy</item>
<item>Timothy, Tim, Timmy</item>
<item>Valerie, Val</item>
- <item>Veronica, Ronnie, Roni, Nica, Nikki, Nikka</item>
+ <item>Veronica, Ronnie, Roni</item>
+ <item>Veronica, Nica, Nikki, Nikka</item>
<item>Victor, Vic</item>
- <item>Victoria, Vicky, Vicki, Vickie, Tori</item>
- <item>Vincent, Vince, Vin, Vinnie</item>
+ <item>Victoria, Vicky, Vicki, Vickie</item>
+ <item>Victoria, Tori</item>
+ <item>Vincent, Vince</item>
+ <item>Vincent, Vin, Vinnie</item>
<item>Vivian, Vivi</item>
- <item>Walter, Walt, Wally</item>
- <item>Wendy, Wen, Wendel</item>
- <item>William, Bill, Billy, Will, Willy, Liam</item>
+ <item>Walter, Wally</item>
+ <item>Walter, Walt</item>
+ <item>Wendy, Wen</item>
+ <item>Wendy, Wendel</item>
+ <item>William, Bill, Billy</item>
+ <item>William, Will, Willy</item>
+ <item>William, Liam</item>
<item>Yvonna, Vonna</item>
<item>Zachary, Zach, Zack, Zac</item>
</string-array>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8a87fa1..8a36283 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Se ha cambiado el acceso restringido"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"El servicio de datos está bloqueado."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"El servicio de emergencias está bloqueado."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"El servicio de voz/SMS está bloqueado."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Todos los servicios de voz/SMS están bloqueados."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"El servicio de voz está bloqueado."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Todos los servicios de voz están bloqueados."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"El servicio de SMS está bloqueado."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Los servicios de voz/datos están bloqueados."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Los servicios de voz/SMS están bloqueados."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Todos los servicios de voz/datos/SMS están bloqueados."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voz"</string>
<string name="serviceClassData" msgid="872456782077937893">"Datos"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Admite una aplicación que instala paquetes de Android nuevos o actualizados. Las aplicaciones maliciosas pueden utilizarlo para agregar aplicaciones nuevas con permisos arbitrariamente potentes."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"eliminar todos los datos de memoria caché de la aplicación"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Admite una aplicación que libera espacio de almacenamiento en el teléfono al eliminar archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de la aplicación"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite a una aplicación mover recursos de aplicación de medios internos a externos y viceversa."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"leer y escribir a recursos dentro del grupo de diagnóstico"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Admite una aplicación que modifica los datos del propietario del teléfono guardados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar los datos del propietario."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"leer datos del propietario"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Admite una aplicación que lee los datos del propietario del teléfono guardados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para leer los datos del propietario del teléfono."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"Leer eventos del calendario"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Admite que una aplicación lea todos los eventos de calendario almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para enviar tus eventos de calendario a otras personas."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"Agregar o cambiar eventos del calendario y enviar un correo electrónico a los invitados"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Permite a una aplicación agregar o cambiar eventos en tu calendario, los cuales pueden enviar un correo electrónico a los invitados. Las aplicaciones malintencionadas pueden usar esto para borrar o modificar tus eventos del calendario o para enviar un correo electrónico a los invitados."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"crear fuentes de ubicación de prueba"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Crea fuentes de ubicación de prueba. Las aplicaciones maliciosas pueden utilizarlo para invalidar la ubicación o el estado que arrojen las fuentes de ubicación real, como GPS o proveedores de red."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionales del proveedor del lugar"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Admite una aplicación que modifica la configuración de APN, como el proxy y el puerto de cualquier APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"cambiar la conectividad de la red"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Permite que una aplicación cambie el estado de la conectividad de red."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"cambiar la conectividad de anclaje a red"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Cambiar la conectividad de anclaje a red"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Permite que una aplicación cambie el estado de la conectividad de red del anclaje."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar la configuración del uso de datos del fondo"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Admite una aplicación que cambia la configuración del uso de datos del fondo."</string>
@@ -427,7 +431,7 @@
<string name="policylab_wipeData" msgid="3910545446758639713">"Borrar todos los datos"</string>
<string name="policydesc_wipeData" msgid="2314060933796396205">"Realizar un reestablecimiento de fábrica y borrar todos tus datos sin ninguna confirmación."</string>
<string-array name="phoneTypes">
- <item msgid="8901098336658710359">"Página principal"</item>
+ <item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Celular"</item>
<item msgid="7897544654242874543">"Trabajo"</item>
<item msgid="1103601433382158155">"Fax laboral"</item>
@@ -437,19 +441,19 @@
<item msgid="9192514806975898961">"Personalización"</item>
</string-array>
<string-array name="emailAddressTypes">
- <item msgid="8073994352956129127">"Página principal"</item>
+ <item msgid="8073994352956129127">"Casa"</item>
<item msgid="7084237356602625604">"Trabajo"</item>
<item msgid="1112044410659011023">"Otros"</item>
<item msgid="2374913952870110618">"Personalización"</item>
</string-array>
<string-array name="postalAddressTypes">
- <item msgid="6880257626740047286">"Página principal"</item>
+ <item msgid="6880257626740047286">"Casa"</item>
<item msgid="5629153956045109251">"Trabajo"</item>
<item msgid="4966604264500343469">"Otros"</item>
<item msgid="4932682847595299369">"Personalización"</item>
</string-array>
<string-array name="imAddressTypes">
- <item msgid="1738585194601476694">"Pág. ppal."</item>
+ <item msgid="1738585194601476694">"Casa"</item>
<item msgid="1359644565647383708">"Trabajo"</item>
<item msgid="7868549401053615677">"Otros"</item>
<item msgid="3145118944639869809">"Personalización"</item>
@@ -470,7 +474,7 @@
<item msgid="1648797903785279353">"Jabber"</item>
</string-array>
<string name="phoneTypeCustom" msgid="1644738059053355820">"Personalizado"</string>
- <string name="phoneTypeHome" msgid="2570923463033985887">"Página principal"</string>
+ <string name="phoneTypeHome" msgid="2570923463033985887">"Casa"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"Celular"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"Trabajo"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"Fax laboral"</string>
@@ -494,16 +498,16 @@
<string name="eventTypeAnniversary" msgid="3876779744518284000">"Aniversario"</string>
<string name="eventTypeOther" msgid="5834288791948564594">"Evento"</string>
<string name="emailTypeCustom" msgid="8525960257804213846">"Personalizado"</string>
- <string name="emailTypeHome" msgid="449227236140433919">"Página principal"</string>
+ <string name="emailTypeHome" msgid="449227236140433919">"Casa"</string>
<string name="emailTypeWork" msgid="3548058059601149973">"Trabajo"</string>
<string name="emailTypeOther" msgid="2923008695272639549">"Otro"</string>
<string name="emailTypeMobile" msgid="119919005321166205">"Celular"</string>
<string name="postalTypeCustom" msgid="8903206903060479902">"Personalizado"</string>
- <string name="postalTypeHome" msgid="8165756977184483097">"Página principal"</string>
+ <string name="postalTypeHome" msgid="8165756977184483097">"Casa"</string>
<string name="postalTypeWork" msgid="5268172772387694495">"Trabajo"</string>
<string name="postalTypeOther" msgid="2726111966623584341">"Otro"</string>
<string name="imTypeCustom" msgid="2074028755527826046">"Personalizado"</string>
- <string name="imTypeHome" msgid="6241181032954263892">"Página principal"</string>
+ <string name="imTypeHome" msgid="6241181032954263892">"Casa"</string>
<string name="imTypeWork" msgid="1371489290242433090">"Trabajo"</string>
<string name="imTypeOther" msgid="5377007495735915478">"Otro"</string>
<string name="imProtocolCustom" msgid="6919453836618749992">"Personalizado"</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Presionar Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Extraer el patrón para desbloquear"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Llamada de emergencia"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Regresar a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lo sentimos, vuelve a intentarlo"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"hace 1 hora"</item>
<item quantity="other" msgid="2467273239587587569">"hace <xliff:g id="COUNT">%d</xliff:g> horas"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> días"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Último mes"</string>
+ <string name="older" msgid="5211975022815554840">"Antiguos"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"ayer"</item>
<item quantity="other" msgid="2479586466153314633">"hace <xliff:g id="COUNT">%d</xliff:g> días"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"semanas"</string>
<string name="year" msgid="4001118221013892076">"año"</string>
<string name="years" msgid="6881577717993213522">"años"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Los días de semana (lunes a viernes)"</string>
- <string name="daily" msgid="5738949095624133403">"Diariamente"</string>
- <string name="weekly" msgid="983428358394268344">"Semanalmente el día <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Mensual"</string>
- <string name="yearly" msgid="1519577999407493836">"Anual"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"No se puede reproducir el video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Lo sentimos, este video no es válido para las transmisiones a este dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Lo sentimos, no se puede reproducir este video."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Antes de desactivar el almacenamiento USB, asegúrate de haber desmontado (\"expulsado\") la tarjeta SD de Android de tu computadora."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Desactivar el almacenamiento USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Se ha producido un problema al desactivar el almacenamiento USB. Asegúrate de haber desmontado el host USB, luego vuelve a intentarlo."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatear tarjeta SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"¿Estás seguro de que quieres formatear la tarjeta SD? Se perderán todos los datos de tu tarjeta."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formato"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="description_star" msgid="2654319874908576133">"favorito"</string>
- <string name="tether_title" msgid="6970447107301643248">"Anclaje a red USB disponible"</string>
- <string name="tether_message" msgid="554549994538298101">"Selecciona \"Anclar a red\" si deseas compartir la conexión de datos de tu teléfono con tu computadora."</string>
- <string name="tether_button" msgid="7409514810151603641">"Anclaje de red"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Cancelar"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Existe un problema en el anclaje a red."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Anclaje a red USB disponible"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Seleccionar el anclaje a red de tu computadora a tu teléfono"</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Desconectar el anclaje a red"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Seleccionar para desconectar de tu computadora del anclaje a red"</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Desconectar el anclaje a red"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Has estado compartiendo con tu computadora la conexión de datos del celular. Selecciona \"Desconectar\" para desconectar el anclaje a red USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Desconectar"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Cancelar"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Hemos encontrado un problema al desactivar el anclaje a red. Vuelve a intentarlo."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo auto habilitado"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleccionar para salir del modo auto"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Conexión de anclaje a red activa"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 441fd38..2606795 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"El acceso restringido se ha modificado."</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"El servicio de datos está bloqueado."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"El servicio de emergencia está bloqueado."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"El servicio de voz y SMS está bloqueado."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Todos los servicios de voz y SMS están bloqueados."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"El servicio de voz está bloqueado."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Todos los servicios de voz están bloqueados."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"El servicio de SMS está bloqueado."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Los servicios de voz y de datos están bloqueados."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Todos los servicios de voz y de SMS están bloqueados."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Todos los servicios de voz, de datos y de SMS están bloqueados."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voz"</string>
<string name="serviceClassData" msgid="872456782077937893">"Datos"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Permite que una aplicación instale paquetes Android nuevos o actualizados. Las aplicaciones malintencionadas pueden utilizar este permiso para añadir aplicaciones nuevas con permisos arbitrariamente potentes."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"eliminar todos los datos de caché de la aplicación"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que una aplicación libere espacio de almacenamiento en el teléfono mediante la eliminación de archivos en el directorio de caché de la aplicación. El acceso al proceso del sistema suele estar muy restringido."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicaciones"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que una aplicación mueva los recursos de aplicaciones de un medio interno a otro externo y viceversa."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"leer/escribir en los recursos propiedad del grupo de diagnóstico"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Permite que una aplicación modifique los datos del propietario del teléfono almacenados en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar los datos del propietario."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"leer datos del propietario"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Permite que una aplicación lea los datos del propietario del teléfono almacenados en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para leer los datos del propietario del teléfono."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"leer eventos de calendario"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Permite que una aplicación lea todos los eventos de calendario almacenados en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para enviar tus eventos de calendario a otras personas."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"añadir o modificar eventos de calendario y enviar mensajes de correo electrónico a los invitados"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Permite que una aplicación añada o modifique los eventos de tu calendario, que puede enviar mensajes de correo electrónico a los invitados. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar tus eventos de calendario o para enviar mensajes a los invitados."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"simular fuentes de ubicación para prueba"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Crear fuentes de origen simuladas para realizar pruebas. Las aplicaciones malintencionadas pueden utilizar este permiso para sobrescribir la ubicación o el estado devueltos por orígenes de ubicación reales, tales como los proveedores de red o GPS."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos de proveedor de ubicación adicional"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Permite que una aplicación modifique los valores de configuración de un APN como, por ejemplo, el proxy y el puerto de cualquier APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"cambiar la conectividad de red"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Permite que una aplicación cambie el estado de la conectividad de red."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"cambiar conectividad de anclaje a red"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"cambiar conectividad de anclaje a red"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Permite que una aplicación cambie el estado de la conectividad de red de anclaje."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiar configuración de uso de datos de referencia"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Permite a una aplicación cambiar la configuración de uso de los datos de referencia."</string>
@@ -443,7 +447,7 @@
<item msgid="2374913952870110618">"Personalizar"</item>
</string-array>
<string-array name="postalAddressTypes">
- <item msgid="6880257626740047286">"Página principal"</item>
+ <item msgid="6880257626740047286">"Casa"</item>
<item msgid="5629153956045109251">"Trabajo"</item>
<item msgid="4966604264500343469">"Otra"</item>
<item msgid="4932682847595299369">"Personalizar"</item>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Dibujar patrón de desbloqueo"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Llamada de emergencia"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Volver a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Inténtalo de nuevo"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"Hace 1 hora"</item>
<item quantity="other" msgid="2467273239587587569">"Hace <xliff:g id="COUNT">%d</xliff:g> horas"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> días"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"El mes pasado"</string>
+ <string name="older" msgid="5211975022815554840">"Anterior"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"ayer"</item>
<item quantity="other" msgid="2479586466153314633">"Hace <xliff:g id="COUNT">%d</xliff:g> días"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"semanas"</string>
<string name="year" msgid="4001118221013892076">"año"</string>
<string name="years" msgid="6881577717993213522">"años"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Todos los días laborables (Lun-Vie)"</string>
- <string name="daily" msgid="5738949095624133403">"Diariamente"</string>
- <string name="weekly" msgid="983428358394268344">"Semanalmente, el <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Mensualmente"</string>
- <string name="yearly" msgid="1519577999407493836">"Anualmente"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"No se puede reproducir el vídeo."</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Este vídeo no se puede transmitir al dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Este vídeo no se puede reproducir."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Antes de desactivar el almacenamiento USB, asegúrate de haber desmontado (\"retirado\") la tarjeta SD del teléfono con Android del equipo."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Desactivar almacenamiento USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Se ha producido un problema al desactivar el almacenamiento USB. Asegúrate de haber desactivado el host USB y, a continuación, vuelve a intentarlo."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatear tarjeta SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"¿Estás seguro de que quieres formatear la tarjeta SD? Se perderán todos los datos de la tarjeta."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formato"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="description_star" msgid="2654319874908576133">"favoritos"</string>
- <string name="tether_title" msgid="6970447107301643248">"El anclaje de USB está disponible."</string>
- <string name="tether_message" msgid="554549994538298101">"Selecciona \"Activar anclaje a red\" si quieres compartir la conexión de datos del teléfono con el equipo."</string>
- <string name="tether_button" msgid="7409514810151603641">"Activar anclaje a red"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Cancelar"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Se ha producido un problema al activar el anclaje a red."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"El anclaje de USB está disponible."</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Selecciona esta opción para activar el anclaje del equipo al teléfono."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Desactivar anclaje a red"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Selecciona esta opción para desactivar el anclaje del equipo."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Desactivar anclaje a red"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Has compartido la conexión de datos móviles con el equipo. Selecciona \"Desconectar\" para desactivar el anclaje de USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Desconectar"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Cancelar"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Se ha producido un problema al desactivar el anclaje a red. Vuelve a intentarlo."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Se ha habilitado el modo coche."</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecciona esta opción para salir del modo coche."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Anclaje a red activo"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Toca para iniciar la configuración."</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 99c7ad8..d19c365 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"O"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"L\'accès limité a été modifié."</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Le service de données est bloqué."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Le service d\'appel d\'urgence est bloqué."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Le service vocal/SMS est bloqué."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Tous les services vocaux/SMS sont bloqués."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Le service vocal est bloqué."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Tous les services vocaux sont bloqués."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Le service SMS est bloqué."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Les services vocaux/de données sont bloqués."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Les services vocaux/SMS sont bloqués."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Tous les services vocaux/de données/SMS sont bloqués."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voix"</string>
<string name="serviceClassData" msgid="872456782077937893">"Données"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"Télécopie"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Permet à une application d\'installer des nouveaux paquets de données ou des mises à jour Android. Des applications malveillantes peuvent utiliser cette fonctionnalité pour ajouter de nouvelles applications disposant d\'autorisations anormalement élevées."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"Suppression des données du cache de toutes les applications"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permet à une application de libérer de l\'espace dans la mémoire du téléphone en supprimant des fichiers du répertoire du cache des applications. Cet accès est en général limité aux processus système."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Déplacer des ressources d\'application"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Autorise l\'application à déplacer des ressources d\'application d\'un support interne à un support externe et inversement."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"Lecture des fichiers journaux du système"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone, sans pour autant récupérer des informations d\'ordre personnel ou privé."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"Lecture/écriture dans les ressources appartenant aux diagnostics"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Permet à une application de modifier les données du propriétaire du téléphone enregistrées sur votre appareil. Des applications malveillantes peuvent utiliser cette fonctionnalité pour effacer ou modifier ces données."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"Lecture des données du propriétaire"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Permet à une application de lire les données du propriétaire du téléphone enregistrées sur votre appareil. Des applications malveillantes peuvent utiliser cette fonctionnalité pour lire ces données."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"lire des événements de l\'agenda"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Permet à une application de lire tous les événements de l\'agenda enregistrés sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonctionnalité pour envoyer les événements de votre agenda à d\'autres personnes."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"ajouter ou modifier des événements d\'agenda et envoyer des e-mails aux invités"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Autorise les applications à ajouter ou à modifier des événements dans votre agenda, qui pourra envoyer des e-mails aux invités. Des logiciels malveillants peuvent utiliser cette fonctionnalité pour supprimer ou modifier des événements de l\'agenda ou envoyer des e-mails aux invités."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"Création de sources de localisation fictives à des fins de test"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Permet de créer des sources de localisation fictives à des fins de test. Des applications malveillantes peuvent utiliser cette fonctionnalité pour remplacer la position géographique et/ou l\'état fournis par des sources réelles comme le GPS ou les fournisseurs d\'accès."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Accès aux commandes de fournisseur de position géographique supplémentaires"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Permet à une application de modifier les paramètres APN (Nom des points d\'accès), comme le proxy ou le port de tout APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"Modification de la connectivité du réseau"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Permet à une application de modifier l\'état de la connectivité réseau."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"changer la connectivité du partage de connexion"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Changer la connectivité du partage de connexion"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Permet à une application de modifier l\'état de la connectivité du partage de connexion."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"modifier le paramètre d\'utilisation des données en arrière-plan"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Permet à une application de modifier le paramètre d\'utilisation des données en arrière-plan."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Dessinez un schéma pour déverrouiller le téléphone"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Appel d\'urgence"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Combinaison correcte !"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Incorrect. Merci de réessayer."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Chargement (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"il y a 1 heure"</item>
<item quantity="other" msgid="2467273239587587569">"Il y a <xliff:g id="COUNT">%d</xliff:g> heures"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Les <xliff:g id="COUNT">%d</xliff:g> derniers jours"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Le mois dernier"</string>
+ <string name="older" msgid="5211975022815554840">"Préc."</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"hier"</item>
<item quantity="other" msgid="2479586466153314633">"Il y a <xliff:g id="COUNT">%d</xliff:g> jours"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"semaines"</string>
<string name="year" msgid="4001118221013892076">"année"</string>
<string name="years" msgid="6881577717993213522">"années"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Tous les jours ouvrés (lun.- ven.)"</string>
- <string name="daily" msgid="5738949095624133403">"Tous les jours"</string>
- <string name="weekly" msgid="983428358394268344">"Toutes les semaines le <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Tous les mois"</string>
- <string name="yearly" msgid="1519577999407493836">"Tous les ans"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Échec de la lecture de la vidéo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Désolé, cette vidéo ne peut être lue sur cet appareil."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Désolé, impossible de lire cette vidéo."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Avant de mettre hors tension le stockage USB, assurez-vous d\'avoir désactivé (\"éjecté\") la carte SD de votre téléphone Android à partir de votre ordinateur."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Désactiver le périphérique de stockage USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Un problème est survenu lors de la mise hors tension du périphérique de stockage USB. Assurez-vous que l\'hôte USB a bien été désactivé, puis essayez à nouveau."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formater la carte SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Voulez-vous vraiment formater la carte SD ? Toutes les données de cette carte seront perdues."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Réinitialiser"</string>
<string name="submit" msgid="1602335572089911941">"Envoyer"</string>
<string name="description_star" msgid="2654319874908576133">"favori"</string>
- <string name="tether_title" msgid="6970447107301643248">"Partage de connexion par USB disponible"</string>
- <string name="tether_message" msgid="554549994538298101">"Pour que votre ordinateur puisse profiter de la connexion de données cellulaires de votre téléphone, sélectionnez \"Partager la connexion\"."</string>
- <string name="tether_button" msgid="7409514810151603641">"Partager la connexion"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Annuler"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Un problème est survenu lors du partage de connexion."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Partage de connexion par USB disponible"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Sélectionnez cette option pour que votre ordinateur partage la connexion de données cellulaires de votre téléphone."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Désactiver le partage de connexion"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Sélectionnez cette option pour désactiver le partage de connexion avec votre ordinateur."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Déconnecter le partage de connexion"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Vous partagez la connexion de données cellulaires de votre téléphone avec votre ordinateur. Sélectionnez \"Déconnecter\" pour mettre fin au partage de connexion par USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Déconnecter"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Annuler"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Un problème est survenu lors de la désactivation du partage de connexion. Veuillez réessayer."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mode Voiture activé"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Sélectionnez cette option pour quitter le mode Voiture."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Partage de connexion activé"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Toucher pour configurer"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b77e8fd..97e7c65 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Accesso limitato modificato"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Il servizio dati è bloccato."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Il servizio di emergenza è bloccato."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Il servizio vocale/SMS è bloccato."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Tutti i servizi vocali/SMS sono bloccati."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Il servizio vocale è bloccato."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Tutti i servizi vocali sono bloccati."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Il servizio SMS è bloccato."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"I servizi vocali/dati sono bloccati."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"I servizi vocali/SMS sono bloccati."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Tutti i servizi vocali/dati/SMS sono bloccati."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voce"</string>
<string name="serviceClassData" msgid="872456782077937893">"Dati"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Consente a un\'applicazione di installare nuovi pacchetti Android o aggiornamenti. Le applicazioni dannose possono sfruttare questa possibilità per aggiungere nuove applicazioni con potenti autorizzazioni arbitrarie."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"eliminazione dati della cache applicazioni"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Consente a un\'applicazione di liberare spazio sul telefono eliminando file nella directory della cache dell\'applicazione. L\'accesso è generalmente limitato a processi di sistema."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Spostare risorse dell\'applicazione"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Consente a un\'applicazione di spostare risorse applicative da supporti interni a esterni e viceversa."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"lettura file di registro sistema"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"lettura/scrittura risorse di proprietà di diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Consente a un\'applicazione di modificare i dati del proprietario del telefono memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare tali dati."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"lettura dati proprietario"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Consente a un\'applicazione di leggere i dati del proprietario del telefono memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per leggere tali dati."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"leggere eventi di calendario"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Consente la lettura da parte di un\'applicazione di tutti gli eventi di calendario memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per inviare i tuoi eventi di calendario ad altre persone."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"aggiungere o modificare eventi di calendario e inviare email agli invitati"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Consente a un\'applicazione di aggiungere o modificare gli eventi nel tuo calendario, eventualmente inviando email agli invitati. Le applicazioni dannose possono utilizzare questa autorizzazione per cancellare o modificare i tuoi eventi di calendario per inviare email agli invitati."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"fonti di localizzazione fittizie per test"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Creare fonti di localizzazione fittizie per test. Le applicazioni dannose possono sfruttare questa possibilità per sostituire la posizione e/o lo stato restituito da reali fonti di localizzazione come GPS o provider di rete."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesso a comandi aggiuntivi del provider di localizz."</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Consente a un\'applicazione di modificare le impostazioni APN, come proxy e porta di qualsiasi APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"modifica connettività di rete"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Consente a un\'applicazione di modificare lo stato di connettività di rete."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"modifica della connettività tethering"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"modificare la connettività tethering"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Consente a un\'applicazione di modificare lo stato di connettività di rete tethering."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"cambiare l\'impostazione di utilizzo dei dati in background"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Consente a un\'applicazione di cambiare l\'impostazione di utilizzo dei dati in background."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premi Menu per sbloccare."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Traccia la sequenza di sblocco"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Chiamata di emergenza"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Torna a chiamata"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Corretta."</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Riprova"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 ora fa"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ore fa"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Ultimi <xliff:g id="COUNT">%d</xliff:g> giorni"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Ultimo mese"</string>
+ <string name="older" msgid="5211975022815554840">"Precedente"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"ieri"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> giorni fa"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"settimane"</string>
<string name="year" msgid="4001118221013892076">"anno"</string>
<string name="years" msgid="6881577717993213522">"anni"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Ogni giorno feriale (lun-ven)"</string>
- <string name="daily" msgid="5738949095624133403">"Quotidianamente"</string>
- <string name="weekly" msgid="983428358394268344">"Ogni settimana il <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Mensilmente"</string>
- <string name="yearly" msgid="1519577999407493836">"Annualmente"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Impossibile riprodurre il video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Spiacenti, questo video non è valido per lo streaming su questo dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Spiacenti. Impossibile riprodurre il video."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Prima di disattivare l\'archiviazione USB, assicurati di avere smontato (\"espulso\") la scheda SD di Android dal computer."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Disattiva archiviazione USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Si è verificato un problema durante la disattivazione dell\'archiviazione USB. Verifica di avere smontato l\'host USB e riprova."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatta scheda SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Formattare la scheda SD? Tutti i dati sulla scheda verranno persi."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatta"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Reimposta"</string>
<string name="submit" msgid="1602335572089911941">"Invia"</string>
<string name="description_star" msgid="2654319874908576133">"preferiti"</string>
- <string name="tether_title" msgid="6970447107301643248">"Tethering USB disponibile"</string>
- <string name="tether_message" msgid="554549994538298101">"Seleziona \"Tethering\" per condividere la connessione dati del telefono con il computer."</string>
- <string name="tether_button" msgid="7409514810151603641">"Tethering"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Annulla"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Si è verificato un problema con il tethering."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Tethering USB disponibile"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Seleziona per impostare il tethering del computer con il telefono."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Interrompi tethering"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Seleziona per interrompere il tethering del computer."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Disconnetti tethering"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Stai condividendo la connessione dati del telefono con il computer. Seleziona \"Disconnetti\" per disconnettere il tethering USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Disconnetti"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Annulla"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Si è verificato un problema durante la disattivazione del tethering. Riprova."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modalità automobile attivata"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleziona per uscire dalla modalità automobile."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Tethering attivo"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Tocca per configurare"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3e53107..4813f26 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"アクセス制限が変更されました"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"データサービスがブロックされています。"</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"緊急サービスがブロックされています。"</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"音声/SMSサービスがブロックされています。"</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"すべての音声/SMSサービスがブロックされています。"</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"音声サービスがブロックされています。"</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"すべての音声サービスがブロックされています。"</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMSサービスがブロックされています。"</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"音声/データサービスがブロックされています。"</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"音声/SMSサービスがブロックされています。"</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"すべての音声/データ/SMSサービスがブロックされています。"</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"音声"</string>
<string name="serviceClassData" msgid="872456782077937893">"データ"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Androidパッケージのインストール/更新をアプリケーションに許可します。悪意のあるアプリケーションが、勝手に強力な権限を持つ新しいアプリケーションを追加する恐れがあります。"</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"アプリケーションキャッシュデータの削除"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"アプリケーションのキャッシュディレクトリからファイルを削除して携帯電話のメモリを解放することをアプリケーションに許可します。通常、アクセスはシステムプロセスのみに制限されます。"</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"アプリケーションリソースの移動"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"内部と外部のメディア間でのアプリケーションリソースの移動をアプリケーションに許可します。"</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"システムログファイルの読み取り"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"diagが所有するリソースの読み書き"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"端末に保存した所有者のデータの変更をアプリケーションに許可します。悪意のあるアプリケーションが所有者のデータを消去/変更する恐れがあります。"</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"所有者データの読み取り"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"携帯電話に保存した所有者データの読み取りをアプリケーションに許可します。悪意のあるアプリケーションが所有者データを読み取る恐れがあります。"</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"カレンダーの予定の読み取り"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"端末に保存したカレンダーの予定の読み取りをアプリケーションに許可します。悪意のあるアプリケーションがカレンダーの予定を他人に送信する恐れがあります。"</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"カレンダーの予定の追加や変更を行い、ゲストにメールを送信する"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"カレンダーの予定の追加や変更をアプリケーションに許可します。ゲストにメールが送信される場合もあります。悪意のあるアプリケーションがこの機能を利用し、イベントを削除または変更したりゲストにメールを送信したりする可能性があります。"</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"仮の位置情報でテスト"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"テスト用に仮の位置情報源を作成します。これにより悪意のあるアプリケーションが、GPS、ネットワークプロバイダなどから返される本当の位置情報や状況を改ざんする恐れがあります。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"位置情報提供者の追加コマンドアクセス"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"APNのプロキシやポートなどのAPN設定の変更をアプリケーションに許可します。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ネットワーク接続の変更"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"ネットワークの接続状態の変更をアプリケーションに許可します。"</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"テザリング接続の変更"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"テザリング接続の変更"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"ネットワークのテザリング接続状態の変更をアプリケーションに許可します。"</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"バックグラウンドデータ使用設定の変更"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"バックグラウンドデータ使用の設定の変更をアプリケーションに許可します。"</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"MENUキーでロック解除"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"ロックを解除するパターンを入力"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"緊急通報"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"通話に戻る"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"一致しました"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"やり直してください"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"充電中(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1時間前"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>時間前"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"過去<xliff:g id="COUNT">%d</xliff:g>日間"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"先月"</string>
+ <string name="older" msgid="5211975022815554840">"もっと前"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"昨日"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g>日前"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"週間"</string>
<string name="year" msgid="4001118221013892076">"年"</string>
<string name="years" msgid="6881577717993213522">"年"</string>
- <string name="every_weekday" msgid="8777593878457748503">"平日(月~金)"</string>
- <string name="daily" msgid="5738949095624133403">"毎日"</string>
- <string name="weekly" msgid="983428358394268344">"毎週<xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"毎月"</string>
- <string name="yearly" msgid="1519577999407493836">"毎年"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"動画を再生できません"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"この動画はご使用の端末でストリーミングできません。"</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"この動画は再生できません。"</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"USBストレージをOFFにする前に、パソコンで必ずAndroidのSDカードのマウントを解除して(カードを取り出して)ください。"</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USBストレージをOFFにする"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"USBストレージをOFFにする際に問題が発生しました。USBホストのマウントが解除されていることを確認してからもう一度お試しください。"</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"SDカードをフォーマット"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SDカードをフォーマットしてもよろしいですか?カード内のすべてのデータが失われます。"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"フォーマット"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"リセット"</string>
<string name="submit" msgid="1602335572089911941">"送信"</string>
<string name="description_star" msgid="2654319874908576133">"お気に入り"</string>
- <string name="tether_title" msgid="6970447107301643248">"USBテザリングが可能"</string>
- <string name="tether_message" msgid="554549994538298101">"携帯電話のデータ接続をパソコンと共有するには[テザリング]を選択します。"</string>
- <string name="tether_button" msgid="7409514810151603641">"テザリング"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"キャンセル"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"テザリング中に問題が発生しました。"</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USBテザリングが可能"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"パソコンを携帯電話にテザリングする場合に選択します。"</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"テザリングの解除"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"パソコンのテザリングを解除する場合に選択します。"</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"テザリングの切断"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"携帯電話のデータ接続をパソコンと共有しています。USBテザリングを切断するには[接続を解除]を選択します。"</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"接続を解除"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"キャンセル"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"テザリングをOFFにする際に問題が発生しました。もう一度お試しください。"</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"運転モードを有効にする"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"運転モードを終了するには選択してください。"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"テザリングが有効です"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"タップして設定する"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c17bf4f..57bf12e 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"제한된 액세스가 변경되었습니다."</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"데이터 서비스가 차단되었습니다."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"긴급 서비스가 차단되었습니다."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"음성/SMS 서비스가 차단되었습니다."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"모든 음성/SMS 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"음성 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"모든 음성 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMS 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"음성/데이터 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"음성/SMS 서비스가 차단되었습니다."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"모든 음성/데이터/SMS 서비스가 차단되었습니다."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"음성"</string>
<string name="serviceClassData" msgid="872456782077937893">"데이터"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"팩스"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"응용프로그램이 새로운 또는 업데이트된 Android 패키지를 설치할 수 있도록 합니다. 이 경우 악성 응용프로그램이 임의의 강력한 권한으로 새 응용프로그램을 추가할 수 있습니다."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"모든 응용프로그램 캐시 데이터 삭제"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"응용프로그램이 응용프로그램 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"응용프로그램 리소스 이동"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"응용프로그램이 응용프로그램 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"시스템 로그 파일 읽기"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"응용프로그램이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 응용프로그램은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있지만 여기에 개인정보는 포함되어서는 안 됩니다."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"진단 그룹 소유의 리소스 읽기/작성"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"응용프로그램이 휴대전화에 저장된 소유자 데이터를 수정할 수 있도록 합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 소유자 데이터를 지우거나 수정할 수 있습니다."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"소유자 데이터 읽기"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"응용프로그램이 휴대전화에 저장된 휴대전화 소유자 데이터를 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 휴대전화 소유자 데이터를 읽을 수 있습니다."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"캘린더 일정 읽기"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"응용프로그램이 휴대전화에 저장된 모든 캘린더 일정을 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 캘린더 일정을 다른 사람에게 보낼 수 있습니다."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"캘린더 일정 추가/수정 및 참석자에게 이메일 전송"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"응용프로그램이 캘린더에 일정을 추가하거나 변경할 수 있도록 합니다. 이렇게 하면 참석자에게 이메일을 보낼 수 있습니다. 악성 응용프로그램이 이를 사용하여 캘린더 일정을 삭제, 수정하거나 참석자에게 이메일을 보낼 수 있습니다."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"테스트를 위해 위치 소스로 가장"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"테스트용 가짜 위치 소스를 만듭니다. 단, 악성 응용프로그램이 이 기능을 이용하여 GPS, 네트워크 제공업체 같은 실제 위치 소스에서 반환한 위치 및/또는 상태를 덮어쓸 수 있습니다."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령 액세스"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"응용프로그램이 APN의 프록시 및 포트 같은 APN 설정을 수정할 수 있도록 합니다."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"네트워크 연결 변경"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"응용프로그램이 네트워크 연결 상태를 변경할 수 있도록 합니다."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"연결 변경"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"연결 변경"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"응용프로그램이 연결된 네트워크의 연결 상태를 변경할 수 있도록 합니다."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"백그라운드 데이터 사용 설정 변경"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"응용프로그램이 백그라운드 데이터 사용 설정을 변경할 수 있도록 합니다."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"잠금해제를 위해 패턴 그리기"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"비상 전화"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"통화로 돌아가기"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"죄송합니다. 다시 시도하세요."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1시간 전"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"지난 <xliff:g id="COUNT">%d</xliff:g>일"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"지난 달"</string>
+ <string name="older" msgid="5211975022815554840">"이전"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"어제"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g>일 전"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"주"</string>
<string name="year" msgid="4001118221013892076">"년"</string>
<string name="years" msgid="6881577717993213522">"년"</string>
- <string name="every_weekday" msgid="8777593878457748503">"주중 매일(월-금)"</string>
- <string name="daily" msgid="5738949095624133403">"매일"</string>
- <string name="weekly" msgid="983428358394268344">"매주 <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"매월"</string>
- <string name="yearly" msgid="1519577999407493836">"매년"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"동영상 재생 안됨"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"죄송합니다. 이 기기로의 스트리밍에 적합하지 않은 동영상입니다."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"죄송합니다. 동영상을 재생할 수 없습니다."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"USB 저장소를 사용하지 않도록 설정하기 전에 컴퓨터에서 Android의 SD 카드를 마운트 해제(\'방출\')했는지 확인하시기 바랍니다."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB 저장소 사용 안함"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"USB 저장소를 사용하지 않도록 설정하는 동안 문제가 발생했습니다. USB 호스트와 연결을 해제했는지 확인한 다음 다시 시도하세요."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"확인"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"SD 카드 포맷"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SD 카드를 포맷하시겠습니까? 포맷하면 카드의 모든 데이터를 잃게 됩니다."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"포맷"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"재설정"</string>
<string name="submit" msgid="1602335572089911941">"제출"</string>
<string name="description_star" msgid="2654319874908576133">"즐겨찾기"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB 연결 사용 가능"</string>
- <string name="tether_message" msgid="554549994538298101">"휴대전화의 데이터 연결을 컴퓨터와 공유하려면 \'연결\'을 선택합니다."</string>
- <string name="tether_button" msgid="7409514810151603641">"연결"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"취소"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"연결하는 중 문제가 발생했습니다."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB 연결 사용 가능"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"컴퓨터를 휴대전화에 연결하려면 선택합니다."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"연결 해제"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"컴퓨터 연결을 해제하려면 선택하세요."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"연결 끊기"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"휴대전화의 휴대전화 데이터 연결이 컴퓨터와 공유되고 있습니다. USB 연결을 해제하려면 \'연결 끊기\'를 선택하세요."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"연결 끊기"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"취소"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"연결을 사용하지 않도록 설정하는 동안 문제가 발생했습니다. 다시 시도해 주세요."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"차량 모드 사용"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"차량 모드를 종료하려면 선택하세요."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"연결 사용중"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"구성하려면 터치하세요."</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 359e954..10152dc 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Tilgangsbegrensning endret"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Datatjenesten er blokkert."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Nødtjenesten er blokkert."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Tale-/SMS-tjenester er blokkert."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Alle tale-/SMS-tjenester er blokkert."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Taletjenesten er blokkert."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Alle taletjenester er blokkert."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Tekstmeldingstjenesten er blokkert."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Alle tjenester for tale og data er blokkert."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Tjenester for tale og tekstmeldinger er blokkert."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Alle tjenester for tale, data og tekstmeldinger er blokkert."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Tale"</string>
<string name="serviceClassData" msgid="872456782077937893">"Data"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"Fax"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Lar applikasjonen installere nye eller oppdaterte Android-pakker. Ondsinnede applikasjoner kan bruke dette til å legge til nye applikasjoner med vilkårlig kraftige rettigheter."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"slette hurtigbufferdata for alle applikasjoner"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Flytter programressurser"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Gir et program tillatelse til å flytte programressurser fra interne til eksterne medier og omvendt."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"lese systemets loggfiler"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"lese/skrive ressurser eid av diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Lar applikasjonen endre dataene om telefoneieren. Ondsinnede applikasjoner kan bruke dette til å slette eller redigere telefonens eierdata."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"lese eierinformasjon"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Lar applikasjonen lese dataene om telefoneieren. Ondsinnede applikasjoner kan bruke dette til å lese telefonens eierdata."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"les kalenderaktiviteter"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Lar applikasjonen lese alle kalenderhendelser lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å sende kalenderhendelser til andre."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"legg til eller endre kalenderaktiviteter og send e-postmelding til gjestene"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Gir et program tillatelse til å legge til eller endre aktiviteter i kalenderen. Dette kan medføre at det sendes e-postmelding til deltakerne. Skadelige programmer kan bruke dette til å slette eller endre kalenderaktiviteter eller sende e-postmeldinger til deltakerne."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"lage simulerte plasseringskilder for testing"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Lage simulerte plassingskilder for testing. Ondsinnede applikasjoner kan bruke dette til å overstyre plasseringen og/eller statusen rapportert av ekte plasseringskilder slik som GPS eller nettverksoperatører."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få tilgang til ekstra plasseringskommandoer"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Lar applikasjonen to endre APN-innstillinger slik som mellomtjener eller port for hvilket som helst aksesspunkt."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"endre nettverkskonnektivitet"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Tillater et program å endre innstillingene for nettverkstilkoblingen."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"endre tilknytningsoppsett"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Endre tilknytningsoppsett"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Tillater et program å endre innstillingene for nettverkstilknytningen."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"endre innstilling for bakgrunnsdata"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Lar applikasjonen endre innstillingen for bakgrunnsdata."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Trykk på menyknappen for å låse opp."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Tegn mønster for å låse opp"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Nødanrop"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tilbake til samtale"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager, prøv igjen:"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Lader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"for en time siden"</item>
<item quantity="other" msgid="2467273239587587569">"for <xliff:g id="COUNT">%d</xliff:g> timer siden"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Siste <xliff:g id="COUNT">%d</xliff:g> dager"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Forrige måned"</string>
+ <string name="older" msgid="5211975022815554840">"Eldre"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"i går"</item>
<item quantity="other" msgid="2479586466153314633">"for <xliff:g id="COUNT">%d</xliff:g> dager siden"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"uker"</string>
<string name="year" msgid="4001118221013892076">"år"</string>
<string name="years" msgid="6881577717993213522">"år"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Hverdager (man–fre)"</string>
- <string name="daily" msgid="5738949095624133403">"Hver dag"</string>
- <string name="weekly" msgid="983428358394268344">"Hver <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"En gang i måneden"</string>
- <string name="yearly" msgid="1519577999407493836">"En gang i året"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Kan ikke spille video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Beklager, denne videoen er ikke gyldig for streaming til denne enheten."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Beklager, kan ikke spille denne videoen."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Før du slår av USB-lagring, må du kontrollere at du har løst ut SD-kortet fra Android-telefonen fra datamaskinen."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Slå av USB-lagring"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Det oppstod et problem ved deaktivering av USB-lagring. Kontroller at du har demontert USB-verten, og prøv på nytt."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatere minnekort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Er du sikker på at du ønsker å formatere minnekortet? Alle data på kortet vil gå tapt."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatér"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Tilbakestill"</string>
<string name="submit" msgid="1602335572089911941">"Send inn"</string>
<string name="description_star" msgid="2654319874908576133">"favoritt"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-tilknytning er tilgjengelig"</string>
- <string name="tether_message" msgid="554549994538298101">"Velg Knytt til hvis du vil dele telefonens datatilkobling med datamaskinen."</string>
- <string name="tether_button" msgid="7409514810151603641">"Knytt til"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Avbryt"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Det oppstod et problem under tilknytning."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-tilknytning er tilgjengelig"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Velg å knytte datamaskinen til telefonen."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Fjern tilknytning"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Velg å fjerne tilknytningen til datamaskinen."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Deaktiver tilknytning"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Du deler for øyeblikket telefonens mobildatatilkobling med datamaskinen. Velg Koble fra for å oppheve USB-tilknytningen."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Koble fra"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Avbryt"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Det oppstod et problem under deaktivering av tilknytningen. Prøv på nytt."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Bilmodus er aktivert"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Velg for å avslutte bilmodus"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Tilknytning er aktiv"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Trykk for å konfigurere"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 23d90cb..2d14757 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Beperkte toegang gewijzigd"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Gegevensservice is geblokkeerd."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Alarmservice is geblokkeerd."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Spraak-/SMS-service is geblokkeerd."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Alle spraak-/SMS-services zijn geblokkeerd."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Spraakservice is geblokkeerd."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Alle spraakservices zijn geblokkeerd."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMS-service is geblokkeerd."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Spraak-/gegevensservices zijn geblokkeerd."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Spraak-/SMS-services zijn geblokkeerd."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Alle spraak-/gegevens-/SMS-services zijn geblokkeerd."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Spraak"</string>
<string name="serviceClassData" msgid="872456782077937893">"Gegevens"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Hiermee kan een toepassing nieuwe of bijgewerkte Android-pakketten installeren. Schadelijke toepassingen kunnen hiervan gebruik maken om nieuwe toepassingen met willekeurig krachtige machtigingen toe te voegen."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"alle cachegegevens van toepassing verwijderen"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Hiermee kan een toepassing opslagruimte op de telefoon vrij maken door bestanden te verwijderen uit de cachemap van de toepassing. De toegang is doorgaans beperkt tot het systeemproces."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Toepassingsbronnen verplaatsen"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Een toepassing toestaan toepassingsbronnen te verplaatsen van interne naar externe media en omgekeerd."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"systeemlogbestanden lezen"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"lezen/schrijven naar bronnen van diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Hiermee kan een toepassing de op uw telefoon opgeslagen gegevens van de eigenaar wijzigen. Schadelijke toepassingen kunnen hiermee gegevens van de eigenaar verwijderen of wijzigen."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"gegevens eigenaar lezen"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Hiermee kan een toepassing de op uw telefoon opgeslagen gegevens van de eigenaar lezen. Schadelijke toepassingen kunnen hiermee gegevens van de eigenaar lezen."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"agendagebeurtenissen lezen"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Hiermee kan een toepassing alle agendagebeurtenissen lezen die zijn opgeslagen op uw telefoon. Schadelijke toepassingen kunnen hiervan gebruik maken om uw agendagebeurtenissen te verzenden naar andere personen."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"agendagebeurtenissen toevoegen of aanpassen en e-mail verzenden naar gasten"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Een toepassing toestaan gebeurtenissen aan uw agenda toe te voegen of te wijzigen, wat inhoudt dat er e-mails kunnen worden verzonden naar gasten. Schadelijke toepassingen kunnen dit gebruiken om uw agendagebeurtenissen te wissen of aan te passen of om e-mail naar gasten te verzenden."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"neplocatiebronnen voor test"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Neplocatiebronnen voor testdoeleinden maken. Schadelijke toepassingen kunnen dit gebruiken om de locatie en/of status te overschrijven die door de echte locatiebronnen wordt aangegeven, zoals GPS of netwerkaanbieders."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"toegang opdrachten aanbieder extra locaties"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Hiermee kan een toepassing de APN-instellingen, zoals proxy en poort, van elke APN wijzigen."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"netwerkverbinding wijzigen"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Staat een toepassing toe de status van de netwerkverbinding te wijzigen."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"getetherde verbinding wijzigen"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Getetherde verbinding wijzigen"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Staat een toepassing toe de status van de getetherde netwerkverbinding te wijzigen."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"instelling voor gebruik van achtergrondgegevens van gegevens wijzigen"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Hiermee kan een toepassing de instelling voor gebruik van achtergrondgegevens wijzigen."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk op \'Menu\' om te ontgrendelen."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Patroon tekenen om te ontgrendelen"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Noodoproep"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Terug naar gesprek"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Juist!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Probeer het opnieuw"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Opladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 uur geleden"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> uur geleden"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Afgelopen <xliff:g id="COUNT">%d</xliff:g> dagen"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Afgelopen maand"</string>
+ <string name="older" msgid="5211975022815554840">"Ouder"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"gisteren"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> dagen geleden"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"weken"</string>
<string name="year" msgid="4001118221013892076">"jaar"</string>
<string name="years" msgid="6881577717993213522">"jaren"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Elke weekdag (ma-vr)"</string>
- <string name="daily" msgid="5738949095624133403">"Dagelijks"</string>
- <string name="weekly" msgid="983428358394268344">"Wekelijks op <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Maandelijks"</string>
- <string name="yearly" msgid="1519577999407493836">"Jaarlijks"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Video kan niet worden afgespeeld"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Deze video kan helaas niet worden gestreamd naar dit apparaat."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Deze video kan niet worden afgespeeld."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Voordat u USB-opslag uitschakelt, moet u de SD-kaart van uw Android hebben ontkoppeld (\'uitgeworpen\') van uw computer."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB-opslag uitschakelen"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Er is een probleem opgetreden tijdens het uitschakelen van de USB-opslag. Controleer of u de USB-host heeft losgekoppeld en probeer het opnieuw."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"SD-kaart formatteren"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Weet u zeker dat u de SD-kaart wilt formatteren? Alle gegevens op uw kaart gaan dan verloren."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatteren"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Opnieuw instellen"</string>
<string name="submit" msgid="1602335572089911941">"Verzenden"</string>
<string name="description_star" msgid="2654319874908576133">"favoriet"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-tethering beschikbaar"</string>
- <string name="tether_message" msgid="554549994538298101">"Selecteer \'Tetheren\' als u de gegevensverbinding van uw telefoon wilt delen met uw computer."</string>
- <string name="tether_button" msgid="7409514810151603641">"Tetheren"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Annuleren"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Er is een probleem opgetreden bij het tetheren."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-tethering beschikbaar"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Selecteer dit om uw computer aan uw telefoon te tetheren."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Tethering opheffen"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Selecteer dit om de tethering van uw computer op te heffen."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Getetherde verbinding verbreken"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"U deelt de gegevensverbinding van uw telefoon met uw computer. Selecteer \'Verbinding verbreken\' om USB-tethering op te heffen."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Verbinding verbreken"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Annuleren"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Er is een probleem opgetreden bij het uitschakelen van tethering. Probeer het opnieuw."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Automodus ingeschakeld"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecteer dit om de automodus af te sluiten."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Tethering actief"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Aanraken om te configureren"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index e9b11fc..4de7fbc 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Zmieniono ograniczenie dostępu"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Usługa transmisji danych jest zablokowana."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Usługa połączeń alarmowych jest zablokowana."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Usługa głosowa/SMS jest zablokowana."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Wszystkie usługi głosowe/SMS są zablokowane."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Usługa głosowa jest zablokowana."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Wszystkie usługi głosowe są zablokowane."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Usługa SMS jest zablokowana."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Usługi głosowe/danych są zablokowane."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Usługi głosowe/SMS są zablokowane."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Wszystkie usługi głosowe/danych/SMS są zablokowane."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Głos"</string>
<string name="serviceClassData" msgid="872456782077937893">"Dane"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAKS"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Pozwala aplikacji na instalowanie nowych lub zaktualizowanych pakietów systemu Android. Szkodliwe aplikacje mogą to wykorzystać, aby dodać nowe aplikacje z arbitralnie nadanymi rozległymi uprawnieniami."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"usuwanie wszystkich danych aplikacji z pamięci podręcznej"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Pozwala aplikacji na zwalnianie pamięci telefonu przez usuwanie plików z katalogu pamięci podręcznej aplikacji. Dostęp jest bardzo ograniczony, z reguły do procesów systemu."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Przenoszenie zasobów aplikacji"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Zezwala aplikacji na przeniesienie zasobów aplikacji z nośnika wewnętrznego na zewnętrzny i odwrotnie."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"czytanie plików dziennika systemu"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"czytanie/zapisywanie w zasobach należących do diagnostyki"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Pozwala aplikacji na zmianę danych właściciela zapisanych w telefonie. Szkodliwe aplikacje mogą to wykorzystać, aby wymazać lub zmienić dane właściciela."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"czytanie danych właściciela"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Pozwala aplikacji na czytanie danych właściciela zapisanych w telefonie. Szkodliwe aplikacje mogą to wykorzystać do odczytania danych właściciela."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"odczytywanie wydarzeń w kalendarzu"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Pozwala aplikacji na odczytywanie wszystkich wydarzeń z kalendarza, zapisanych w telefonie. Szkodliwe aplikacje mogą to wykorzystać do rozsyłania wydarzeń z kalendarza do innych ludzi."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"dodawanie i modyfikowanie wydarzeń w kalendarzu oraz wysyłanie wiadomości e-mail do gości"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Zezwala aplikacji na dodawanie i zmianę wydarzeń w kalendarzu, co może powodować wysyłanie wiadomości e-mail do gości. Złośliwe aplikacje mogą używać tego uprawnienia do usuwania i modyfikowania wydarzeń w kalendarzu oraz do wysyłania wiadomości e-mail do gości."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"udawanie źródeł położenia dla testów"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Tworzenie pozorowanych źródeł ustalania położenia dla testów. Szkodliwe aplikacje mogą to wykorzystać, aby zastąpić prawdziwe położenie i/lub stan zwracany przez prawdziwe źródła, takie jak GPS lub dostawcy usługi sieciowej."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Pozwala aplikacji na zmianę ustawień APN, takich jak serwer proxy oraz port dowolnego APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"zmienianie połączeń sieci"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Zezwala aplikacji na zmianę stanu łączności sieciowej."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"zmiana łączności powiązanej"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Zmiana łączności powiązanej"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Zezwala aplikacji na zmianę stanu powiązanej łączności sieciowej."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"zmienianie ustawienia używania danych w tle"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Zezwala aplikacji na zmianę ustawień użycia danych w tle."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Naciśnij Menu, aby odblokować."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Narysuj wzór, aby odblokować"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Połączenie alarmowe"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Powrót do połączenia"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Niestety, spróbuj ponownie"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"godzinę temu"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> godzin temu"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Ostatnie dni (<xliff:g id="COUNT">%d</xliff:g>)"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Ostatni miesiąc"</string>
+ <string name="older" msgid="5211975022815554840">"Starsze"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"wczoraj"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> dni temu"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"tygodni"</string>
<string name="year" msgid="4001118221013892076">"rok"</string>
<string name="years" msgid="6881577717993213522">"lat"</string>
- <string name="every_weekday" msgid="8777593878457748503">"W każdy dzień roboczy (pon–pt)"</string>
- <string name="daily" msgid="5738949095624133403">"Codziennie"</string>
- <string name="weekly" msgid="983428358394268344">"Co tydzień w <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Miesięcznie"</string>
- <string name="yearly" msgid="1519577999407493836">"Co roku"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Nie można odtworzyć filmu wideo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Przepraszamy, ten film wideo nie nadaje się do przesyłania strumieniowego do tego urządzenia."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Niestety, nie można odtworzyć tego filmu wideo."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Przed wyłączeniem nośnika USB upewnij się, że karta SD systemu Android została odłączona („wyjęta”) od komputera."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Wyłącz nośnik USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Wystąpił problem podczas wyłączania nośnika USB. Upewnij się, że host USB został odłączony, a następnie spróbuj ponownie."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatuj kartę SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Czy na pewno sformatować kartę SD? Wszystkie dane na karcie zostaną utracone."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatuj"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Resetuj"</string>
<string name="submit" msgid="1602335572089911941">"Prześlij"</string>
<string name="description_star" msgid="2654319874908576133">"ulubione"</string>
- <string name="tether_title" msgid="6970447107301643248">"Dostępne powiązanie USB"</string>
- <string name="tether_message" msgid="554549994538298101">"Wybierz opcję „Powiąż”, aby udostępnić na komputerze połączenie transmisji danych telefonu."</string>
- <string name="tether_button" msgid="7409514810151603641">"Powiąż"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Anuluj"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Wystąpił problem podczas tworzenia powiązania."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Dostępne powiązanie USB"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Wybierz, aby powiązać komputer z telefonem."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Anuluj powiązanie"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Wybierz, aby anulować powiązanie z komputerem."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Rozłącz powiązanie"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Komórkowe połączenie transmisji danych telefonu zostało udostępnione na komputerze. Wybierz opcję „Rozłącz”, aby rozłączyć powiązanie USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Rozłącz"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Anuluj"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Wystąpił problem podczas wyłączania powiązania. Spróbuj ponownie."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Tryb samochodowy włączony"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Wybierz, aby zakończyć tryb samochodowy."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Powiązanie aktywne"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Dotknij, aby skonfigurować"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 2a3dde3..c60561c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Acesso restrito modificado"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"O serviço de dados está bloqueado."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"O serviço de emergência está bloqueado."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"O serviço de voz/SMS está bloqueado."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Todos os serviços de voz/SMS estão bloqueados."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"O serviço de voz está bloqueado."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Todos os serviços de Voz estão bloqueados."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"O serviço de SMS está bloqueado."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Os serviços de Voz/Dados estão bloqueados."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Os serviços de Voz/SMS estão bloqueados."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Todos os serviços de Voz/Dados/SMS estão bloqueados."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voz"</string>
<string name="serviceClassData" msgid="872456782077937893">"Dados"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Permite a uma aplicação instalar pacotes novos ou actualizados do Android. Algumas aplicações maliciosas podem utilizar este item para adicionar novas aplicações com autorizações arbitrariamente fortes."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"eliminar todos os dados da aplicações"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite a uma aplicação libertar espaço de armazenamento no telefone eliminando ficheiros no directório da cache da aplicação. Geralmente, o acesso é muito limitado para processamento do sistema."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicações"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que uma aplicação mova recursos de aplicações de meios internos para meios externos e vice-versa."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"ler ficheiros de registo do sistema"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"ler/escrever em recursos propriedade de diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Permite a uma aplicação modificar os dados do proprietário do telefone armazenados no seu telefone. Algumas aplicações maliciosas podem utilizar este item para apagar ou modificar dados do proprietário."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"ler dados do proprietário"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Permite a uma aplicação modificar os dados do proprietário do telefone armazenados no seu telefone. Algumas aplicações maliciosas podem utilizar este item para ler dados do proprietário do telefone."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"ler eventos da agenda"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Permite a uma aplicação ler todos os eventos do calendário armazenados no seu telefone. Algumas aplicações maliciosas podem utilizar este item para enviar os eventos do seu calendário a outras pessoas."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"adicionar ou alterar eventos da agenda e enviar e-mails para os convidados"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Permite que uma aplicação adicione ou altere os eventos na sua agenda, a qual pode enviar e-mails para os convidados. As aplicações maliciosas podem utilizar esta função para apagar ou alterar os eventos da sua agenda ou para enviar e-mails para os convidados."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"fontes de localização fictícias para teste"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Crie fontes de localização fictícias para fins de teste. Algumas aplicações maliciosas podem utilizar este item para substituir a localização e/ou o estado devolvido por fontes de localização reais, tais como fornecedores de GPS ou de Rede."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aceder a comandos adicionais do fornecedor de localização"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Permite a uma aplicaçaõ modificar as definições de APN, tais como Proxy e Porta de qualquer APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"mudar conectividade de rede"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Permite a uma aplicação alterar o estado da conectividade de rede."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"alterar conectividade associada"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Alterar conectividade associada"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Permite a uma aplicação alterar o estado da conectividade de rede associada."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"mudar definição de utilização de dados de segundo plano"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Permite a uma aplicação mudar a definição de utilização de dados de segundo plano."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Prima Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Desenhar padrão para desbloquear"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Chamada de emergência"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Regressar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lamentamos, tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"A carregar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"Há 1 hora"</item>
<item quantity="other" msgid="2467273239587587569">"Há <xliff:g id="COUNT">%d</xliff:g> horas"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> dias"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Último mês"</string>
+ <string name="older" msgid="5211975022815554840">"Mais antiga"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"ontem"</item>
<item quantity="other" msgid="2479586466153314633">"Há <xliff:g id="COUNT">%d</xliff:g> dias"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"semanas"</string>
<string name="year" msgid="4001118221013892076">"ano"</string>
<string name="years" msgid="6881577717993213522">"anos"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Todos os dias úteis (Seg-Sex)"</string>
- <string name="daily" msgid="5738949095624133403">"Diariamente"</string>
- <string name="weekly" msgid="983428358394268344">"Semanalmente à/ao <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Mensalmente"</string>
- <string name="yearly" msgid="1519577999407493836">"Anualmente"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Impossível reproduzir vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Lamentamos, este vídeo não é válido para transmissão em sequência neste dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Lamentamos, não é possível reproduzir este vídeo."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Antes de desactivar o armazenamento USB, certifique-se de que desinstalou (\"ejectou\") o cartão SD do Android do computador."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Desactivar armazenamento USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Ocorreu um problema ao desactivar o armazenamento USB. Confirme se desinstalou o anfitrião USB e, em seguida, tente novamente."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatar cartão SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Tem a certeza de que pretende formatar o cartão SD? Perder-se-ão todos os dados no cartão."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatar"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Repor"</string>
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="description_star" msgid="2654319874908576133">"favorito"</string>
- <string name="tether_title" msgid="6970447107301643248">"Associação USB disponível"</string>
- <string name="tether_message" msgid="554549994538298101">"Seleccione \"Associar\" se pretender partilhar a ligação de dados do telefone com o computador."</string>
- <string name="tether_button" msgid="7409514810151603641">"Associar"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Cancelar"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Existe um problema na associação."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Associação USB disponível"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Seleccione para associar o computador ao telefone."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Desassociar"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Seleccione para desassociar o computador."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Desligar associação"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Tem estado a partilhar a ligação de dados móvel do telefone com o computador. Seleccione \"Desligar\" para desligar a associação USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Desligar"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Cancelar"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Foi detectado um problema ao desactivar a Associação. Tente novamente."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo automóvel activado"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleccionar para sair do modo automóvel."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Associação activa"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b71df63..f080109 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Acesso restrito alterado"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"O serviço de dados está bloqueado."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"O serviço de emergência está bloqueado."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"O serviço de voz/SMS está bloqueado."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Todos os serviços de voz/SMS estão bloqueados."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"O serviço de voz está bloqueado."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Todos os serviços de voz estão bloqueados."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"O serviço de SMS está bloqueado."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Os serviços de voz/dados estão bloqueados."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Os serviços de voz/SMS estão bloqueados."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Todos os serviços de voz/dados/SMS estão bloqueados."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Voz"</string>
<string name="serviceClassData" msgid="872456782077937893">"Dados"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Permite que um aplicativo instale pacotes novos ou atualizados do Android. Aplicativos maliciosos podem usar isso para adicionar novos aplicativos com permissões arbitrariamente avançadas."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"excluir todos os dados de cache do aplicativo"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que um aplicativo libere o espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente muito restrito para o processo do sistema."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos do aplicativo"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que um aplicativo mova os recursos do aplicativo da mídia interna para a externa e vice-versa."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"ler arquivos de registro do sistema"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"ler/gravar em recursos pertencentes ao diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Permite que um aplicativo modifique os dados de proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar dados do proprietário."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"ler dados do proprietário"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Permite que um aplicativo leia os dados de proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para ler os dados de proprietário do telefone."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"ler eventos da agenda"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Permite que um aplicativo leia todos os eventos da agenda armazenados no seu telefone. Aplicativos maliciosos podem usar isso para enviar eventos da sua agenda para outras pessoas."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"adicionar ou modificar eventos da agenda e enviar e-mail aos convidados"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Permite que um aplicativo adicione ou altere os eventos na sua agenda, que pode enviar e-mail aos convidados. Aplicativos maliciosos podem usar isso para apagar ou modificar os eventos da sua agenda ou para enviar e-mail aos convidados."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"fontes de locais fictícios para teste"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Cria fontes de locais fictícios para teste. Aplicativos maliciosos podem usar isso para substituir o local e/ou o status retornado pelas fontes de locais reais como GPS ou provedores de rede."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Permite que um aplicativo modifique as configurações de APN, como Proxy e Porta de qualquer APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"alterar conectividade da rede"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Permite que um aplicativo altere o estado da conectividade de rede."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"alterar conectividade vinculada"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Alterar conectividade vinculada"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Permite que um aplicativo altere o estado da conectividade de rede vinculada."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"alterar configuração de uso dos dados de segundo plano"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Permite que um aplicativo altere a configuração de uso dos dados de segundo plano."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Desenhe o padrão para desbloquear"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Chamada de emergência"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Carregando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 hora atrás"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> horas atrás"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> dias"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Mês passado"</string>
+ <string name="older" msgid="5211975022815554840">"Mais antigos"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"ontem"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> dias atrás"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"semanas"</string>
<string name="year" msgid="4001118221013892076">"ano"</string>
<string name="years" msgid="6881577717993213522">"anos"</string>
- <string name="every_weekday" msgid="8777593878457748503">"De segunda a sexta-feira"</string>
- <string name="daily" msgid="5738949095624133403">"Diariamente"</string>
- <string name="weekly" msgid="983428358394268344">"Semanalmente na <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Mensalmente"</string>
- <string name="yearly" msgid="1519577999407493836">"Anualmente"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Não é possível reproduzir o vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Este vídeo não é válido para transmissão com este dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Este vídeo não pode ser reproduzido."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Antes de desativar o armazenamento USB, verifique se desconectou (“ejetou”) o cartão SD do Android do computador."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Desativar o armazenamento USB"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Houve um problema ao desativar o armazenamento USB. Verifique se desconectou o host USB e tente novamente."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatar cartão SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Tem certeza de que deseja formatar o cartão SD? Todos os dados no seu cartão serão perdidos."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatar"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Redefinir"</string>
<string name="submit" msgid="1602335572089911941">"Enviar"</string>
<string name="description_star" msgid="2654319874908576133">"favorito"</string>
- <string name="tether_title" msgid="6970447107301643248">"Vínculo USB disponível"</string>
- <string name="tether_message" msgid="554549994538298101">"Selecione \"Vincular\" se quiser compartilhar a conexão de dados do seu telefone com o seu computador."</string>
- <string name="tether_button" msgid="7409514810151603641">"Vincular"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Cancelar"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Há um problema com o vínculo."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"Vínculo USB disponível"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Selecione para vincular o seu computador ao seu telefone."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Desvincular"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Selecione para desvincular o seu computador."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Desconectar vínculo"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Você está compartilhando a conexão de dados de celular do seu telefone com o seu computador. Selecione \"Desconectar\" para desconectar o vínculo USB."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Desconectar"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Cancelar"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Encontramos um problema ao desativar o Vínculo. Tente novamente."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo de carro ativado"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecione para sair do modo de carro."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Vínculo ativo"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Toque para configurar"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 93fff41..09b9f1b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"Б"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Ограничения доступа изменены"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Служба данных заблокирована."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Служба экстренной помощи заблокирована."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Служба передачи SMS/голосовых сообщений заблокирована."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Все службы передачи SMS/голосовых сообщений заблокированы."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Служба передачи голосовых сообщений заблокирована."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Все службы передачи голосовых сообщений заблокированы."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"Служба передачи SMS заблокирована."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Службы передачи голосовых сообщений/данных заблокированы."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Службы передачи голосовых сообщений/SMS заблокированы."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Все службы передачи голосовых сообщений/данных/SMS заблокированы."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Голосовая связь"</string>
<string name="serviceClassData" msgid="872456782077937893">"Данные"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"ФАКС"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Позволяет приложению устанавливать новые или обновленные пакеты Android. Вредоносные приложения могут использовать эту возможность для добавления новых приложений со сколь угодно высоким уровнем разрешения."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"удалять все данные из кэша приложений"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Позволяет приложению освобождать память телефона с помощью удаления файлов из каталога кэша приложений. Обычно это разрешается только системным процессам."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Перемещать ресурсы приложения"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Позволяет приложению перемещать ресурсы приложения с внутренних на внешние носители и наоборот."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"считывать системные файлы журналов"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"считывать/записывать данные в ресурсы, принадлежащие группе диагностики"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Позволяет приложению изменять сведения о владельце, сохраненные на телефоне. Вредоносные приложения могут использовать эту возможность для удаления или изменения данных владельца."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"считывать данные о владельце"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Позволяет приложению считывать сведения о владельце, сохраненные в памяти телефона. Вредоносные приложения могут использовать эту возможность для считывания данных владельца."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"считывать мероприятия в календаре"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Позволяет приложению считывать все события календаря, сохраненные на телефоне. Вредоносные приложения могут использовать эту возможность для передачи ваших событий календаря посторонним лицам."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"добавлять и изменять мероприятия в календаре и отправлять письма гостям"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Позволяет приложению добавлять и изменять мероприятия в вашем календаре, в котором предусмотрена функция отправления писем гостям. Вредоносные приложения могут воспользоваться этим для удаления или изменения мероприятий в календаре или отправки писем гостям."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"копировать источники мест для проверки"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Создавать копии источников данных о местоположении для проверки. Вредоносные приложения могут использовать эту возможность для перезаписи места и/или состояния, возвращаемого действительными источниками данных о местоположении, такими как GPS или операторы связи."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"получать доступ к дополнительным командам источника данных о местоположении"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Позволяет приложению изменять настройки APN, такие как прокси-сервер и порт любого APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"изменять настройки подключения к сети"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Позволяет программе изменять состояние сетевого канала."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"изменить подключение к компьютеру"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Изменять подключение к компьютеру"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Позволяет программе изменять состояние подключенного сетевого канала."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"изменять настройки использования данных в фоновом режиме"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Позволяет приложению изменять настройку использования данных в фоновом режиме."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Для разблокировки нажмите \"Меню\"."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Для разблокировки введите графический ключ"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Вызов службы экстренной помощи"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Вернуться к вызову"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Повторите попытку"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Идет зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 час назад"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Последние <xliff:g id="COUNT">%d</xliff:g> дн."</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Прошлый месяц"</string>
+ <string name="older" msgid="5211975022815554840">"Пред."</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"вчера"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> дн. назад"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"нед."</string>
<string name="year" msgid="4001118221013892076">"г."</string>
<string name="years" msgid="6881577717993213522">"г."</string>
- <string name="every_weekday" msgid="8777593878457748503">"Каждый рабочий день (пн-пт)"</string>
- <string name="daily" msgid="5738949095624133403">"Ежедневно"</string>
- <string name="weekly" msgid="983428358394268344">"Еженедельно, <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Ежемесячно"</string>
- <string name="yearly" msgid="1519577999407493836">"Ежегодно"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Не удалось воспроизвести видео"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Это видео не подходит для потокового воспроизведения на данном устройстве."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Невозможно воспроизвести видео."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Перед отключением USB-накопителя убедитесь, что SD-карта устройства Android была отключена от компьютера."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Выключить USB-накопитель"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"При выключении USB-накопителя произошла неполадка. Убедитесь, что USB-хост отключен, и повторите попытку."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"ОК"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Форматировать карту SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Отформатировать карту SD? Все данные, находящиеся на карте, будут уничтожены."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Формат"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Сбросить"</string>
<string name="submit" msgid="1602335572089911941">"Отправить"</string>
<string name="description_star" msgid="2654319874908576133">"избранное"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-подключение доступно"</string>
- <string name="tether_message" msgid="554549994538298101">"Выберите \"Подключить\", если вы хотите разрешить использовать на компьютере подключение для передачи данных, установленное на телефоне."</string>
- <string name="tether_button" msgid="7409514810151603641">"Подключение"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Отмена"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Возникла ошибка при подключении."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-подключение доступно"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Выберите, чтобы подключить телефон к компьютеру."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Отключить"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Выберите, чтобы разорвать подключение к компьютеру."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Разорвать подключение"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Подключение для передачи данных на телефоне используется на компьютере. Выберите \"Отключить\", чтобы разорвать USB-подключение."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Отключить"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Отмена"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"При разрыве подключения возникла неполадка. Пожалуйста, повторите попытку."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Режим громкой связи включен"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Выберите для выхода из режима громкой связи."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Подключение активно"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Нажмите для настройки"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index aa4bcb4..8ed41d8 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Begränsad åtkomst har ändrats"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Datatjänsten är blockerad."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Räddningstjänsten är blockerad."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Tjänsten röst/SMS är blockerad."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Alla röst-/SMS-tjänster har blockerats."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Rösttjänsten är blockerad."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Alla rösttjänster är blockerade."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMS-tjänsten är blockerad."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Röst- och datatjänster är blockerade."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Röst- och SMS-tjänster är blockerade."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Alla röst-, data- och SMS-tjänster är blockerade."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Röst"</string>
<string name="serviceClassData" msgid="872456782077937893">"Data"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAX"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Tillåter att ett program installerar nya eller uppdaterade Android-paket. Skadliga program kan använda detta för att lägga till nya program med godtyckliga och starka behörigheter."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"ta bort cacheinformation för alla program"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillåter att ett program frigör lagringsutrymme i telefonen genom att ta bort filer i programmets katalog för cachelagring. Åtkomst är mycket begränsad, vanligtvis till systemprocesser."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Flytta programresurser"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillåter att ett program flyttar programresurser från interna till externa medier och tvärt om."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"läsa systemets loggfiler"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"läsa/skriva till resurser som ägs av diag"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Tillåter att ett program ändrar information om telefonens ägare som har lagrats på din telefon. Skadliga program kan använda detta för att radera eller ändra ägaruppgifter."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"läsa information om ägare"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Tillåter att ett program läser information om telefonens ägare som har lagrats på telefonen. Skadliga program kan använda detta för att läsa telefonens ägaruppgifter."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"läsa kalenderhändelser"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Tillåter att ett program läser alla händelser i kalendern som har lagrats på din telefon. Skadliga program kan använda detta för att skicka din kalender till andra personer."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"lägg till och ändra kalenderhändelser och skicka e-post till gäster"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Tillåter att ett program lägger till och ändrar händelser i din kalender, t.ex. genom att skicka e-post till gäster. Skadliga program kan använda detta för att radera eller ändra dina kalenderhändelser och för att skicka e-post till gäster."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"skenplatser för att testa"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Skapa skenplatser för att testa. Skadliga program kan använda detta för att åsidosätta platsen och/eller statusen som returneras av riktiga platser, till exempel GPS- eller nätverksleverantörer."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Tillåter att ett program ändrar APN-inställningarna, till exempel Proxy och Port för alla APN."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ändra nätverksanslutning"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Tillåter att ett program ändrar statusens för en kopplad nätverksanslutning."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"ändra sammanlänkad anslutning"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"ändra sammanlänkad anslutning"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Tillåter att ett program ändrar statusens för en sammanlänkad nätverksanslutning."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"ändra inställningar för användning av bakgrundsdata"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Tillåter att ett program ändrar inställningen för användning av bakgrundsdata."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu om du vill låsa upp."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Rita grafiskt lösenord för att låsa upp"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Nödsamtal"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Återgå till samtalet"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Försök igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"för 1 timme sedan"</item>
<item quantity="other" msgid="2467273239587587569">"för <xliff:g id="COUNT">%d</xliff:g> timmar sedan"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"De senaste <xliff:g id="COUNT">%d</xliff:g> dagarna"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Föregående månad"</string>
+ <string name="older" msgid="5211975022815554840">"Äldre"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"igår"</item>
<item quantity="other" msgid="2479586466153314633">"för <xliff:g id="COUNT">%d</xliff:g> dagar sedan"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"veckor"</string>
<string name="year" msgid="4001118221013892076">"år"</string>
<string name="years" msgid="6881577717993213522">"år"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Alla vardagar (mån–fre)"</string>
- <string name="daily" msgid="5738949095624133403">"Varje dag"</string>
- <string name="weekly" msgid="983428358394268344">"Varje vecka på <xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"Varje månad"</string>
- <string name="yearly" msgid="1519577999407493836">"Varje år"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Det går inte att spela upp videon"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Videon kan tyvärr inte spelas upp i den här enheten."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Det går tyvärr inte att spela upp den här videon."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"Kontrollera att du har demonterat (\"matat ut\") Android-telefonens SD-kort från datorn, innan du inaktiverar USB-lagring."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"Inaktivera USB-lagring"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"Ett problem uppstod när USB-lagringsplatsen skulle inaktiveras. Kontrollera att USB-värden har demonterats och försök igen."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatera SD-kort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Vill du formatera SD-kortet? Alla data på ditt kort kommer att gå förlorade."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Återställ"</string>
<string name="submit" msgid="1602335572089911941">"Skicka"</string>
<string name="description_star" msgid="2654319874908576133">"favorit"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB-sammanlänkning tillgänglig"</string>
- <string name="tether_message" msgid="554549994538298101">"Välj \"Sammanlänka\" om du vill dela telefonens dataanslutning med en dator."</string>
- <string name="tether_button" msgid="7409514810151603641">"Sammanlänka"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"Avbryt"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Ett problem uppstod vid sammanlänkning."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB-sammanlänkning tillgänglig"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Sammanlänka datorn med din telefon."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Ta bort sammanlänkning"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Ta bort sammanlänkning med datorn."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Koppla ifrån sammanlänkad anslutning"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Du har delat telefonens dataanslutning med din dator. Välj \"Koppla ifrån\" om du vill koppla från USB-sammanlänkningen."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Koppla ifrån"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"Avbryt"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Ett problem uppstod när sammanlänkningen inaktiverades. Försök igen."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Billäge aktiverat"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Välj om du vill avsluta billäge."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Aktiv sammanlänkning"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Tryck om du vill konfigurera"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 927edd1..8ea59df 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Kısıtlanmış erişim değiştirildi"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Veri hizmeti engellendi."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Acil durum hizmeti engellendi."</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"Ses/SMS hizmeti engellendi."</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"Tüm ses/SMS hizmetleri engellendi."</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"Ses hizmeti engellendi."</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"Tüm Ses hizmetleri engellendi."</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"SMS hizmeti engellendi."</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"Ses/Veri hizmetleri engellendi."</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"Ses/SMS hizmetleri engellendi."</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"Tüm Ses/Veri/SMS hizmetleri engellendi."</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"Ses"</string>
<string name="serviceClassData" msgid="872456782077937893">"Veri"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"FAKS"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"Uygulamaların yeni veya güncellenmiş Android paketleri yüklemesine izin verir. Kötü amaçlı uygulamalar bunu, kendilerine verilen izin derecesi keyfi olarak değişen yeni uygulamalar eklemek için kullanabilir."</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"tüm uygulama önbelleği verilerini sil"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Uygulamaların uygulama önbelleği dizinindeki dosyaları silerek telefonda yer açmasına izin verir. Erişim genellikle sistem işlemlerine ve yüksek düzeyde kısıtlı olarak verilir."</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"Uygulama kaynaklarını taşı"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"Bir uygulamanın, uygulama kaynaklarını dahili ve harici ortamlar arasında taşımasına olanak tanır."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"sistem günlük dosyalarını oku"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"sahibi tanılama olan kaynakları oku/bunlara yaz"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"Uygulamaların telefonunuzda depolanan telefon sahibi verilerini değiştirmesine izin verir. Kötü amaçlı uygulamalar bu işlevi kullanıcı verilerini silmek veya değiştirmek için kullanabilir."</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"sahip verilerini oku"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"Uygulamaların telefonunuzda depolanan telefon sahibi verilerini okumasına izin verir. Kötü amaçlı uygulamalar bunu telefon sahibi verilerini okumak için kullanabilir."</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"takvim etkinliklerini oku"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Uygulamaların telefonunuzda depolanan takvim etkinliklerinin tümünü okumasına izin verir. Kötü amaçlı uygulamalar bunu, takvim etkinliklerinizi başkalarına göndermek için kullanabilir."</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"takvim etkinlikleri ekle veya değiştir ve konuklara e-posta gönder"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"Bir uygulamanın takviminize etkinlik ekleyebilmesini veya takviminizdeki etkinlikleri değiştirebilmesini sağlar ve konuklara e-posta gönderebilir. Kötü amaçlı uygulamalar, bunu takvim etkinliklerinizi silmek veya değiştirmek veya konuklara e-posta göndermek için kullanabilir."</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"test için sahte konum kaynakları"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Test amacıyla sahte konum kaynakları oluşturur. Kötü amaçlı uygulamalar bu işlevi GPS veya Ağ Hizmeti sağlayıcılar gibi gerçek kaynaklardan gelen konum ve/veya durum bilgilerini geçersiz kılmak için kullanabilir."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"Uygulamaların herhangi bir APN\'nin Proxy ve Bağlantı Noktası gibi APN ayarlarını değiştirmesine izin verir."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"ağ bağlantısını değiştir"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"Bir uygulamanın ağ bağlantı durumunu değiştirmesine izin verir."</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"kullanılan bağlantıyı değiştir"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"Kullanılan bağlantıyı değiştir"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"Bir uygulamanın bağlanılan ağ bağlantısı durumunu değiştirmesine izin verir."</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"arka plan veri kullanımı ayarını değiştir"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"Uygulamaların arka plan veri kullanımı ayarını değiştirmesine izin verir."</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmak için Menü\'ye basın."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Kilit açmak için deseni çizin"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Acil durum çağrısı"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Çağrıya dön"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Doğru!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Üzgünüz, lütfen yeniden deneyin"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Şarj oluyor (<xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 saat önce"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> saat önce"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"Son <xliff:g id="COUNT">%d</xliff:g> gün"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"Son ay"</string>
+ <string name="older" msgid="5211975022815554840">"Daha eski"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"dün"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> gün önce"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"hafta"</string>
<string name="year" msgid="4001118221013892076">"yıl"</string>
<string name="years" msgid="6881577717993213522">"yıl"</string>
- <string name="every_weekday" msgid="8777593878457748503">"Hafta içi her gün (Pzt-Cum)"</string>
- <string name="daily" msgid="5738949095624133403">"Her gün"</string>
- <string name="weekly" msgid="983428358394268344">"Her hafta <xliff:g id="DAY">%s</xliff:g> günü"</string>
- <string name="monthly" msgid="2667202947170988834">"Aylık"</string>
- <string name="yearly" msgid="1519577999407493836">"Yılda bir"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"Video oynatılamıyor"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"Maalesef, bu video cihaza akışla göndermek için uygun değil."</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"Maalesef bu video oynatılamıyor."</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"USB depolama birimini kapatmadan önce Android SD kartını bilgisayarınızdan kaldırdığınızdan (\"çıkardığınızdan\") emin olun."</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB depolama birimini kapat"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"USB depolama birimini kapatırken bir sorun oluştu. USB ana makinesini kaldırdığınızdan emin olun ve daha sonra tekrar deneyin."</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"Tamam"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"SD kartı biçimlendir"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SD kartı biçimlendirmek istediğinizden emin misiniz? Kartınızdaki tüm veriler yok olacak."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Biçimlendir"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
<string name="submit" msgid="1602335572089911941">"Gönder"</string>
<string name="description_star" msgid="2654319874908576133">"favori"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB bağlantısı kullanılabilir"</string>
- <string name="tether_message" msgid="554549994538298101">"Telefonunuzun veri bağlantısını bilgisayarınızla paylaşmak istiyorsanız, \"Bağlan\" seçeneğini belirleyin."</string>
- <string name="tether_button" msgid="7409514810151603641">"Bağlan"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"İptal"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"Bağlantıyla ilgili bir sorun var."</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB bağlantısı kullanılabilir"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"Bilgisayarınızı telefonunuza bağlamak için seçin."</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"Bağlantıyı kaldır"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"Bilgisayarınızın bağlantısını kesmek için seçin."</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"Bağlantıyı kes"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"Telefonunuzun cep telefonu veri bağlantısını bilgisayarınızla paylaşıyordunuz. USB bağlantısını kesmek için \"Bağlantıyı Kes\" seçeneğini belirleyin."</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"Bağlantıyı kes"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"İptal"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"Bağlantı kapatılırken bir sorunla karşılaşıldı. Lütfen tekrar deneyin."</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Araba modu etkin"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Araba modundan çıkmak için seçin."</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"Bağlantı etkin"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"Yapılandırmak için dokunun"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ad081d6..dbdcaf96 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"B"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"访问受限情况已发生变化"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"数据服务已禁用。"</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"紧急服务已禁用。"</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"语音/短信服务已禁用。"</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"所有语音/短信服务均已禁用。"</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"已禁用语音服务。"</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"已禁用所有语音服务。"</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"已禁用短信服务。"</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"已禁用语音/数据服务。"</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"已禁用语音/短信服务。"</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"已禁用所有语音/数据/短信服务。"</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"语音"</string>
<string name="serviceClassData" msgid="872456782077937893">"数据"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"传真"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"允许应用程序安装全新的或更新的 Android 包。恶意应用程序可能会借此添加其具有任意权限的新应用程序。"</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"删除所有应用程序缓存数据"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。通常此权限只适用于系统进程。"</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"移动应用程序资源"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"允许应用程序在内部介质和外部介质之间移动应用程序资源。"</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"读取系统日志文件"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"允许应用程序修改您手机上存储的手机所有者数据。恶意应用程序可借此清除或修改所有者数据。"</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"读取所有者数据"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"允许应用程序读取您手机上存储的手机所有者数据。恶意应用程序可借此读取手机所有者数据。"</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"读取日历活动"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"允许应用程序读取您手机上存储的所有日历活动。恶意应用程序可借此将您的日历活动发送给其他人。"</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"添加或修改日历活动以及向邀请对象发送电子邮件"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"允许应用程序添加或更改日历中的活动,这可能会向邀请对象发送电子邮件。恶意应用程序可能会借此清除或修改您的日历活动,或者向邀请对象发送电子邮件。"</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"使用模拟地点来源进行测试"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"创建模拟地点来源进行测试。恶意应用程序可能利用此选项覆盖由真实地点来源(如 GPS 或网络提供商)传回的地点和/或状态。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"访问额外的位置信息提供程序命令"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"允许应用程序修改 APN 设置,例如任何 APN 的代理和端口。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"更改网络连接性"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"允许应用程序更改网络连接的状态。"</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"更改绑定的连接"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"更改绑定的连接"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"允许应用程序更改绑定网络连接的状态。"</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"更改背景数据使用设置"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"允许应用程序更改背景数据使用设置。"</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按 MENU 解锁。"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"绘制解锁图案"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"紧急呼救"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"返回通话"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,请重试"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 小时前"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小时前"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"过去 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"上月"</string>
+ <string name="older" msgid="5211975022815554840">"往前"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"昨天"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天前"</item>
@@ -692,11 +695,6 @@
<string name="weeks" msgid="6509623834583944518">"周"</string>
<string name="year" msgid="4001118221013892076">"年"</string>
<string name="years" msgid="6881577717993213522">"年"</string>
- <string name="every_weekday" msgid="8777593878457748503">"每个工作日(周一至周五)"</string>
- <string name="daily" msgid="5738949095624133403">"每天"</string>
- <string name="weekly" msgid="983428358394268344">"每周的<xliff:g id="DAY">%s</xliff:g>"</string>
- <string name="monthly" msgid="2667202947170988834">"每月"</string>
- <string name="yearly" msgid="1519577999407493836">"每年"</string>
<string name="VideoView_error_title" msgid="3359437293118172396">"无法播放视频"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="897920883624437033">"抱歉,该视频不适合在此设备上播放。"</string>
<string name="VideoView_error_text_unknown" msgid="710301040038083944">"很抱歉,无法播放此视频。"</string>
@@ -792,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"在关闭 USB 存储设备前,请确保您已从计算机中卸载(“弹出”)Android 手机的 SD 卡。"</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"关闭 USB 存储设备"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"关闭 USB 存储设备时遇到问题。请检查并确保已卸载了 USB 主设备,然后重试。"</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"确定"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"格式化 SD 卡"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"确定要将 SD 卡格式化吗?该卡上的所有数据都将丢失。"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"格式化"</string>
@@ -858,22 +855,8 @@
<string name="reset" msgid="2448168080964209908">"重置"</string>
<string name="submit" msgid="1602335572089911941">"提交"</string>
<string name="description_star" msgid="2654319874908576133">"收藏"</string>
- <string name="tether_title" msgid="6970447107301643248">"可进行 USB 绑定"</string>
- <string name="tether_message" msgid="554549994538298101">"如果您希望自己的手机与计算机共享数据连接,请选择“绑定”。"</string>
- <string name="tether_button" msgid="7409514810151603641">"绑定"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"取消"</string>
- <string name="tether_error_message" msgid="6672110337349077628">"绑定时出现问题。"</string>
- <string name="tether_available_notification_title" msgid="367754042700082080">"可进行 USB 绑定"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"选择将您的计算机与手机进行绑定。"</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"解除绑定"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"选择解除计算机的绑定。"</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"解除绑定"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"您的手机与计算机已共享蜂窝数据连接。选择“断开绑定”可解除 USB 绑定。"</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"断开绑定"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"取消"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"在解除绑定时遇到了问题。请重试。"</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (3262812780382240778) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已启用车载模式"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"选择退出车载模式"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"绑定生效"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"触摸可进行配置"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 72fd0ad..fb9fb77 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1,18 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"位元組"</string>
@@ -64,8 +69,12 @@
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"受限存取已變更"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"已封鎖資料傳輸服務。"</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"已封鎖緊急服務。"</string>
- <string name="RestrictedOnNormal" msgid="2045364908281990708">"已封鎖語音/SMS 服務。"</string>
- <string name="RestrictedOnAll" msgid="4923139582141626159">"已封鎖所有語音/SMS 服務。"</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"已封鎖語音服務。"</string>
+ <string name="RestrictedOnAllVoice" msgid="1459318899842232234">"已封鎖所有語音服務。"</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"已封鎖 SMS 服務。"</string>
+ <string name="RestrictedOnVoiceData" msgid="8244438624660371717">"已封鎖語音/資料服務。"</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"已封鎖語音/SMS 服務。"</string>
+ <string name="RestrictedOnAll" msgid="2714924667937117304">"已封鎖所有語音/資料/SMS 服務。"</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"語音"</string>
<string name="serviceClassData" msgid="872456782077937893">"資料"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"傳真"</string>
@@ -251,10 +260,8 @@
<string name="permdesc_installPackages" msgid="526669220850066132">"允許應用程式安裝新的 Android 程式或更新。請注意:惡意程式可能利用此功能新增具有極高權限的程式。"</string>
<string name="permlab_clearAppCache" msgid="4747698311163766540">"刪除所有應用程式快取資料。"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"允許應用程式刪除快取目錄裡的檔案,釋放儲存空間。此操作通常受到系統程序嚴格限制。"</string>
- <!-- no translation found for permlab_movePackage (728454979946503926) -->
- <skip />
- <!-- no translation found for permdesc_movePackage (6323049291923925277) -->
- <skip />
+ <string name="permlab_movePackage" msgid="728454979946503926">"移動應用程式資源"</string>
+ <string name="permdesc_movePackage" msgid="6323049291923925277">"允許應用程式將應用程式資源從內部媒體移到外部媒體,反之亦可。"</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"讀取系統記錄檔"</string>
<string name="permdesc_readLogs" msgid="2257937955580475902">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string>
@@ -281,13 +288,10 @@
<string name="permdesc_writeOwnerData" msgid="2344055317969787124">"允許應用程式更改手機持有者的資料。請注意:惡意程式可能利用此功能,清除或修改持有者的資料。"</string>
<string name="permlab_readOwnerData" msgid="6668525984731523563">"讀取持有者的資料"</string>
<string name="permdesc_readOwnerData" msgid="3088486383128434507">"允許應用程式讀取手機持有者資料。請注意:惡意程式可能利用此功能讀取持有者的資料。"</string>
- <!-- no translation found for permlab_readCalendar (6898987798303840534) -->
- <skip />
+ <string name="permlab_readCalendar" msgid="6898987798303840534">"讀取日曆活動"</string>
<string name="permdesc_readCalendar" msgid="5533029139652095734">"允許應用程式讀取手機上所有日曆資料。請注意:惡意程式可能利用此功能將您的日曆資料傳送給其他人。"</string>
- <!-- no translation found for permlab_writeCalendar (3894879352594904361) -->
- <skip />
- <!-- no translation found for permdesc_writeCalendar (2988871373544154221) -->
- <skip />
+ <string name="permlab_writeCalendar" msgid="3894879352594904361">"新增或修改日曆活動,並傳送電子郵件給他人"</string>
+ <string name="permdesc_writeCalendar" msgid="2988871373544154221">"允許應用程式在您的日曆上新增或變更活動,此時,應用程式可能會傳送電子郵件給他人。不過,若允許的是惡意應用程式,日曆活動可能會因此遭到刪除或竄改,惡意應用程式也可能傳送電子郵件騷擾他人。"</string>
<string name="permlab_accessMockLocation" msgid="8688334974036823330">"模擬位置來源以供測試"</string>
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"建立模擬位置來源以供測試。請注意:惡意程式可能利用此功能覆寫 GPS 或電信業者傳回的位置及/或狀態。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
@@ -382,7 +386,7 @@
<string name="permdesc_writeApnSettings" msgid="7443433457842966680">"允許應用程式修改 APN 設定,例如:Proxy 及 APN 的連接埠。"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"變更網路連線"</string>
<string name="permdesc_changeNetworkState" msgid="4199958910396387075">"允許應用程式變更網路連線狀態。"</string>
- <string name="permlab_changeTetherState" msgid="5952584964373017960">"變更數據連線"</string>
+ <string name="permlab_changeTetherState" msgid="2702121155761140799">"變更數據連線"</string>
<string name="permdesc_changeTetherState" msgid="8905815579146349568">"允許應用程式變更數據網路連線狀態。"</string>
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"變更背景資料使用設定"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"允許應用程式變更背景資料使用設定。"</string>
@@ -532,8 +536,7 @@
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按下 Menu 鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"畫出解鎖圖形"</string>
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"緊急電話"</string>
- <!-- no translation found for lockscreen_return_to_call (5244259785500040021) -->
- <skip />
+ <string name="lockscreen_return_to_call" msgid="5244259785500040021">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,請再試一次"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充電 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
@@ -620,11 +623,11 @@
<item quantity="one" msgid="9150797944610821849">"1 小時以前"</item>
<item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時以前"</item>
</plurals>
- <!-- no translation found for last_num_days:other (3069992808164318268) -->
- <!-- no translation found for last_month (3959346739979055432) -->
- <skip />
- <!-- no translation found for older (5211975022815554840) -->
- <skip />
+ <plurals name="last_num_days">
+ <item quantity="other" msgid="3069992808164318268">"最近 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
+ </plurals>
+ <string name="last_month" msgid="3959346739979055432">"上個月"</string>
+ <string name="older" msgid="5211975022815554840">"較舊"</string>
<plurals name="num_days_ago">
<item quantity="one" msgid="861358534398115820">"昨天"</item>
<item quantity="other" msgid="2479586466153314633">"<xliff:g id="COUNT">%d</xliff:g> 天以前"</item>
@@ -787,14 +790,13 @@
<string name="usb_storage_stop_message" msgid="3613713396426604104">"關閉 USB 儲存裝置前,請務必先將 Android 系統的 SD 卡從電腦上卸下 (退出)。"</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"關閉 USB 儲存裝置"</string>
<string name="usb_storage_stop_error_message" msgid="143881914840412108">"關閉 USB 儲存裝置時發生問題。請檢查您是否已卸載 USB Host,然後再試一次。"</string>
- <!-- no translation found for dlg_confirm_kill_storage_users_title (203356557714612214) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_title (963039033470478697) -->
<skip />
- <!-- no translation found for dlg_confirm_kill_storage_users_text (3902998926994232358) -->
+ <!-- no translation found for dlg_confirm_kill_storage_users_text (3202838234780505886) -->
<skip />
- <!-- no translation found for dlg_error_title (9179426738781357928) -->
+ <!-- no translation found for dlg_error_title (8048999973837339174) -->
<skip />
- <!-- no translation found for dlg_ok (7376953167039865701) -->
- <skip />
+ <string name="dlg_ok" msgid="7376953167039865701">"確定"</string>
<string name="extmedia_format_title" msgid="8663247929551095854">"將 SD 卡格式化"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"確定要將 SD 卡格式化嗎?該 SD 卡中的所有資料將會遺失。"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"格式化"</string>
@@ -853,23 +855,8 @@
<string name="reset" msgid="2448168080964209908">"重設"</string>
<string name="submit" msgid="1602335572089911941">"提交"</string>
<string name="description_star" msgid="2654319874908576133">"我的最愛"</string>
- <string name="tether_title" msgid="6970447107301643248">"USB 數據連線運作中"</string>
- <string name="tether_message" msgid="554549994538298101">"如要與電腦分享手機的資料連線,請選取 [數據連線]。"</string>
- <string name="tether_button" msgid="7409514810151603641">"數據連線"</string>
- <string name="tether_button_cancel" msgid="6744369928952219677">"取消"</string>
- <!-- no translation found for tether_error_message (950843122853838817) -->
- <skip />
- <string name="tether_available_notification_title" msgid="367754042700082080">"USB 數據連線運作中"</string>
- <string name="tether_available_notification_message" msgid="8439443306503453793">"選取此選項可讓您的電腦與手機進行數據連線。"</string>
- <string name="tether_stop_notification_title" msgid="1902246071807668101">"中斷數據連線"</string>
- <string name="tether_stop_notification_message" msgid="6920086906891516128">"選取即可中斷與電腦的數據連線。"</string>
- <string name="tether_stop_title" msgid="3118332507220912235">"中斷數據連線"</string>
- <string name="tether_stop_message" msgid="7741840433788363174">"您已與電腦分享手機的行動資料連線,如要中斷 USB 數據連線,請選取 [中斷連線]。"</string>
- <string name="tether_stop_button" msgid="8061941592702063029">"中斷連線"</string>
- <string name="tether_stop_button_cancel" msgid="3694669546501300230">"取消"</string>
- <string name="tether_stop_error_message" msgid="4244697367270211648">"關閉數據連線時發生問題,請再試一次。"</string>
- <!-- no translation found for car_mode_disable_notification_title (3164768212003864316) -->
- <skip />
- <!-- no translation found for car_mode_disable_notification_message (668663626721675614) -->
- <skip />
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已啟用車用模式"</string>
+ <string name="car_mode_disable_notification_message" msgid="668663626721675614">"選取結束車用模式。"</string>
+ <string name="tethered_notification_title" msgid="8146103971290167718">"啟用數據連線"</string>
+ <string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f00572d..4e2f9c3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -531,8 +531,8 @@
<!-- The screen orientation has changed, that is the user has
rotated the device. -->
<flag name="orientation" value="0x0080" />
- <!-- The screen orientation has changed, that is the user has
- rotated the device. -->
+ <!-- The screen layout has changed. This might be caused by a
+ different display being activated. -->
<flag name="screenLayout" value="0x0100" />
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 84cd4c3..29a815f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -282,4 +282,8 @@
<!-- Component name of the service providing geocoder API support. -->
<string name="config_geocodeProvider">@null</string>
+
+ <!-- Boolean indicating if current platform supports bluetooth SCO for off call
+ use cases -->
+ <bool name="config_bluetooth_sco_off_call">true</bool>
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index cac26b9..8b6af71 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -67,4 +67,5 @@
<item type="id" name="addToDictionary" />
<item type="id" name="accountPreferences" />
<item type="id" name="smallIcon" />
+ <item type="id" name="custom" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8c00884..a7336fc 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1238,6 +1238,8 @@
<public type="attr" name="overscrollHeader" id="0x010102bf" />
<public type="attr" name="overscrollFooter" id="0x010102c0" />
- <public type="anim" name="cycle_interpolator" id="0x010a000c" />
+ <public type="id" name="custom" id="0x0102002b" />
+ <public type="anim" name="cycle_interpolator" id="0x010a000c" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 584fe25..bf7425f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2013,6 +2013,8 @@
<item quantity="one">Open Wi-Fi network available</item>
<item quantity="other">Open Wi-Fi networks available</item>
</plurals>
+ <!-- Do not translate. Default access point SSID used for tethering -->
+ <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
<!-- Name of the dialog that lets the user choose an accented character to insert -->
<string name="select_character">Insert character</string>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9e19c57..6dae8b8 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -471,7 +471,6 @@
<!-- Theme for the search input bar. -->
<style name="Theme.SearchBar" parent="Theme.Panel">
- <item name="android:backgroundDimEnabled">true</item>
<item name="windowContentOverlay">@null</item>
</style>
diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk
new file mode 100644
index 0000000..a1546fa
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2010, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ConnectivityManagerTest
+
+#LOCAL_INSTRUMENTATION_FOR := connectivitymanagertest
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
new file mode 100644
index 0000000..76b58e1
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.connectivitymanagertest">
+
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="ConnectivityManagerTestActivity"
+ android:label="CMTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
+ </activity>
+ </application>
+ <!--
+ This declares that this app uses the instrumentation test runner targeting
+ the package of browserpowertest. To run the tests use the command:
+ "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner"
+ -->
+ <instrumentation android:name=".ConnectivityManagerTestRunner"
+ android:targetPackage="com.android.connectivitymanagertest"
+ android:label="Test runner for Connectivity Manager Tests"
+ />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+</manifest>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
new file mode 100644
index 0000000..badcc1b
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import java.util.List;
+import android.widget.LinearLayout;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+
+
+/**
+ * An activity registered with connectivity manager broadcast
+ * provides network connectivity information and
+ * can be used to set device states: Cellular, Wifi, Airplane mode.
+ */
+public class ConnectivityManagerTestActivity extends Activity {
+
+ public static final String LOG_TAG = "ConnectivityManagerTestActivity";
+ public static final int WAIT_FOR_SCAN_RESULT = 5 * 1000; //5 seconds
+ public static final int WIFI_SCAN_TIMEOUT = 20 * 1000;
+ public ConnectivityReceiver mConnectivityReceiver = null;
+ public WifiReceiver mWifiReceiver = null;
+ /*
+ * Track network connectivity information
+ */
+ public State mState;
+ public NetworkInfo mNetworkInfo;
+ public NetworkInfo mOtherNetworkInfo;
+ public boolean mIsFailOver;
+ public String mReason;
+ public boolean mScanResultIsAvailable = false;
+ public ConnectivityManager mCM;
+
+ /*
+ * Control Wifi States
+ */
+ public WifiManager mWifiManager;
+
+ /*
+ * Verify connectivity state
+ */
+ public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE;
+ NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES];
+
+ /**
+ * A wrapper of a broadcast receiver which provides network connectivity information
+ * for all kinds of network: wifi, mobile, etc.
+ */
+ private class ConnectivityReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
+ return;
+ }
+
+ boolean noConnectivity =
+ intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+
+ if (noConnectivity) {
+ mState = State.DISCONNECTED;
+ } else {
+ mState = State.CONNECTED;
+ }
+
+ mNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+
+ mOtherNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+
+ mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
+ mIsFailOver = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
+ recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
+ if (mOtherNetworkInfo != null) {
+ recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
+ }
+ }
+ }
+
+ private class WifiReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ Log.v(LOG_TAG, "onReceive() is calleld with " + intent);
+ return;
+ }
+ notifyScanResult();
+ }
+ }
+
+ public ConnectivityManagerTestActivity() {
+ mState = State.UNKNOWN;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v(LOG_TAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+
+ // Create a simple layout
+ LinearLayout contentView = new LinearLayout(this);
+ contentView.setOrientation(LinearLayout.VERTICAL);
+ setContentView(contentView);
+ setTitle("ConnectivityManagerTestActivity");
+
+ mConnectivityReceiver = new ConnectivityReceiver();
+ // register a connectivity receiver for CONNECTIVITY_ACTION;
+ registerReceiver(mConnectivityReceiver,
+ new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+ mWifiReceiver = new WifiReceiver();
+ registerReceiver(mWifiReceiver,
+ new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ // Get an instance of ConnectivityManager
+ mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Get an instance of WifiManager
+ mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
+ initializeNetworkStates();
+
+ if (mWifiManager.isWifiEnabled()) {
+ Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+ clearWifi();
+ }
+ }
+
+ // for each network type, initialize network states to UNKNOWN, and no verification flag is set
+ public void initializeNetworkStates() {
+ for (int networkType = NUM_NETWORK_TYPES - 1; networkType >=0; networkType--) {
+ connectivityState[networkType] = new NetworkState();
+ Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+ connectivityState[networkType].toString());
+ }
+ }
+
+ // deposit a network state
+ public void recordNetworkState(int networkType, State networkState) {
+ Log.v(LOG_TAG, "record network state for network " + networkType +
+ " state is " + networkState);
+ connectivityState[networkType].recordState(networkState);
+ }
+
+ // set the state transition criteria
+ public void setStateTransitionCriteria(int networkType, State initState,
+ int transitionDir, State targetState) {
+ connectivityState[networkType].setStateTransitionCriteria(
+ initState, transitionDir, targetState);
+ }
+
+ // Validate the states recorded
+ public boolean validateNetworkStates(int networkType) {
+ Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+ return connectivityState[networkType].validateStateTransition();
+ }
+
+ // return result from network state validation
+ public String getTransitionFailureReason(int networkType) {
+ Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+ connectivityState[networkType].toString());
+ return connectivityState[networkType].getReason();
+ }
+
+ private void notifyScanResult() {
+ synchronized (this) {
+ Log.v(LOG_TAG, "notify that scan results are available");
+ this.notify();
+ }
+ }
+
+ // Return true if device is currently connected to mobile network
+ public boolean isConnectedToMobile() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+ }
+
+ // Return true if device is currently connected to Wifi
+ public boolean isConnectedToWifi() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ }
+
+ public boolean enableWifi() {
+ return mWifiManager.setWifiEnabled(true);
+ }
+
+ /**
+ * Associate the device to given SSID
+ * If the device is already associated with a WiFi, disconnect and forget it,
+ * We don't verify whether the connection is successful or not, leave this to the test
+ */
+ public boolean connectToWifi(String knownSSID) {
+ //If Wifi is not enabled, enable it
+ if (!mWifiManager.isWifiEnabled()) {
+ Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ List<ScanResult> netList = mWifiManager.getScanResults();
+ if (netList == null) {
+ // if no scan results are available, start active scan
+ mWifiManager.startScanActive();
+ mScanResultIsAvailable = false;
+ long startTime = System.currentTimeMillis();
+ while (!mScanResultIsAvailable) {
+ if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+ return false;
+ }
+ // wait for the scan results to be available
+ synchronized (this) {
+ // wait for the scan result to be available
+ try {
+ this.wait(WAIT_FOR_SCAN_RESULT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mWifiManager.getScanResults() == null) ||
+ (mWifiManager.getScanResults().size() <= 0)) {
+ continue;
+ }
+ mScanResultIsAvailable = true;
+ }
+ }
+ }
+
+ netList = mWifiManager.getScanResults();
+ for (int i = 0; i < netList.size(); i++) {
+ ScanResult sr= netList.get(i);
+ if (sr.SSID.equals(knownSSID)) {
+ Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = sr.SSID;
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ int networkId = mWifiManager.addNetwork(config);
+ mWifiManager.saveConfiguration();
+ // Connect to network by disabling others.
+ mWifiManager.enableNetwork(networkId, true);
+ mWifiManager.reconnect();
+ break;
+ }
+ }
+
+ List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+ if (netConfList.size() <= 0) {
+ Log.v(LOG_TAG, knownSSID + " is not available");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Disable Wifi
+ * @return true if Wifi is disabled successfully
+ */
+ public boolean disableWiFi() {
+ return mWifiManager.setWifiEnabled(false);
+ }
+
+ /**
+ * Disconnect from the current Wifi and clear the configuration list
+ */
+ public boolean clearWifi() {
+ if (mWifiManager.isWifiEnabled()) {
+ //remove the current network Id
+ int curNetworkId = mWifiManager.getConnectionInfo().getNetworkId();
+ mWifiManager.removeNetwork(curNetworkId);
+ mWifiManager.saveConfiguration();
+
+ // remove other saved networks
+ List<WifiConfiguration> netConfList = mWifiManager.getConfiguredNetworks();
+ if (netConfList != null) {
+ Log.v(LOG_TAG, "remove configured network ids");
+ for (int i = 0; i < netConfList.size(); i++) {
+ WifiConfiguration conf = new WifiConfiguration();
+ conf = netConfList.get(i);
+ mWifiManager.removeNetwork(conf.networkId);
+ }
+ }
+ mWifiManager.saveConfiguration();
+ // disable Wifi
+ if (!mWifiManager.setWifiEnabled(false)) {
+ return false;
+ }
+ // wait for the actions to be completed
+ try {
+ Thread.sleep(5*1000);
+ } catch (InterruptedException e) {}
+ }
+ return true;
+ }
+
+ /**
+ * Set airplane mode
+ */
+ public void setAirplaneMode(Context context, boolean enableAM) {
+ //set the airplane mode
+ Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+ enableAM ? 1 : 0);
+ // Post the intent
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", enableAM);
+ context.sendBroadcast(intent);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ //Unregister receiver
+ if (mConnectivityReceiver != null) {
+ unregisterReceiver(mConnectivityReceiver);
+ }
+ if (mWifiReceiver != null) {
+ unregisterReceiver(mWifiReceiver);
+ }
+ Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+ }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
new file mode 100644
index 0000000..592be92
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import android.util.Log;
+import com.android.connectivitymanagertest.functional.ConnectivityManagerMobileTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Instrumentation Test Runner for all connectivity manager tests.
+ *
+ * To run the connectivity manager tests:
+ *
+ * adb shell am instrument -e ssid <ssid> \
+ * -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner
+ */
+
+public class ConnectivityManagerTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(ConnectivityManagerMobileTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return ConnectivityManagerTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ String testSSID = (String) icicle.get("ssid");
+ if (testSSID != null) {
+ TEST_SSID = testSSID;
+ }
+ }
+
+ public String TEST_SSID = null;
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
new file mode 100644
index 0000000..2e5a0f8
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest;
+
+import android.net.NetworkInfo.State;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class NetworkState {
+ public static final int TO_DISCONNECTION = 0; // transition to disconnection
+ public static final int TO_CONNECTION = 1; // transition to connection
+ public static final int DO_NOTHING = -1; // no state change
+ private final String LOG_TAG = "NetworkState";
+ private List<State> mStateDepository;
+ private State mTransitionTarget;
+ private int mTransitionDirection;
+ private String mReason = null; // record mReason of state transition failure
+
+ public NetworkState() {
+ mStateDepository = new ArrayList<State>();
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ public NetworkState(State currentState) {
+ mStateDepository = new ArrayList<State>();
+ mStateDepository.add(currentState);
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ // Reinitialize the network state
+ public void resetNetworkState() {
+ mStateDepository.clear();
+ mTransitionDirection = DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ // set the transition criteria, transitionDir could be:
+ // DO_NOTHING, TO_CONNECTION, TO_DISCONNECTION
+ public void setStateTransitionCriteria(State initState, int transitionDir, State targetState) {
+ if (!mStateDepository.isEmpty()) {
+ mStateDepository.clear();
+ }
+ mStateDepository.add(initState);
+ mTransitionDirection = transitionDir;
+ mTransitionTarget = targetState;
+ Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
+ }
+
+ public void recordState(State currentState) {
+ mStateDepository.add(currentState);
+ }
+
+ // Verify state transition
+ public boolean validateStateTransition() {
+ Log.v(LOG_TAG, "print state depository: " + printStates());
+ if (mTransitionDirection == DO_NOTHING) {
+ if (mStateDepository.isEmpty()) {
+ Log.v(LOG_TAG, "no state is recorded");
+ mReason = "no state is recorded.";
+ return false;
+ } else if (mStateDepository.size() > 1) {
+ Log.v(LOG_TAG, "no broadcast is expected, " +
+ "instead broadcast is probably received");
+ mReason = "no broadcast is expected, instead broadcast is probably received";
+ return false;
+ } else if (mStateDepository.get(0) != mTransitionTarget) {
+ Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
+ mStateDepository.get(0));
+ mReason = mTransitionTarget + " is expected, but it is " + mStateDepository.get(0);
+ return false;
+ }
+ return true;
+ } else if (mTransitionDirection == TO_CONNECTION) {
+ Log.v(LOG_TAG, "transition to CONNECTED");
+ return transitToConnection();
+ } else {
+ Log.v(LOG_TAG, "transition to DISCONNECTED");
+ return transitToDisconnection();
+ }
+ }
+
+ /*
+ * Transition from CONNECTED -> DISCONNECTED:
+ * CONNECTED->DISCONNECTING->DISCONNECTED
+ * return false if any state transition is not valid and save a message in mReason
+ */
+ public boolean transitToDisconnection () {
+ mReason = "states: " + printStates();
+ if (mStateDepository.get(0) != State.CONNECTED) {
+ mReason += " initial state should be CONNECTED, but it is " +
+ mStateDepository.get(0) + ".";
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ mReason += " the last state should be DISCONNECTED, but it is " + lastState;
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size() - 1; i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+ (curState == State.DISCONNECTED))) {
+ continue;
+ } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else {
+ mReason += " Transition state from " + preState.toString() + " to " +
+ curState.toString() + " is not valid.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // DISCONNECTED->CONNECTING->CONNECTED
+ public boolean transitToConnection() {
+ mReason = "states: " + printStates();
+ if (mStateDepository.get(0) != State.DISCONNECTED) {
+ mReason += " initial state should be DISCONNECTED, but it is " +
+ mStateDepository.get(0) + ".";
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ mReason += " the last state should be CONNECTED, but it is " + lastState;
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size(); i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
+ (curState == State.CONNECTED))) {
+ continue;
+ } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+ continue;
+ } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
+ continue;
+ } else {
+ mReason += " Transition state from " + preState.toString() + " to " +
+ curState.toString() + " is not valid.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public List<State> getTransitionStates() {
+ return mStateDepository;
+ }
+
+ // return state failure mReason
+ public String getReason() {
+ return mReason;
+ }
+
+ public String printStates() {
+ StringBuilder stateBuilder = new StringBuilder("");
+ for (int i = 0; i < mStateDepository.size(); i++) {
+ stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
+ }
+ return stateBuilder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(" ");
+ builder.append("mTransitionDirection: ").append(Integer.toString(mTransitionDirection)).
+ append("; ").append("states:").
+ append(printStates()).append("; ");
+ return builder.toString();
+ }
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
new file mode 100644
index 0000000..ad564ae
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package com.android.connectivitymanagertest.functional;
+
+import com.android.connectivitymanagertest.ConnectivityManagerTestActivity;
+
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkInfo.DetailedState;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.ActivityInstrumentationTestCase2;
+import com.android.connectivitymanagertest.ConnectivityManagerTestRunner;
+import com.android.connectivitymanagertest.NetworkState;
+import android.util.Log;
+import junit.framework.*;
+
+public class ConnectivityManagerMobileTest
+ extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> {
+
+ private static final String LOG_TAG = "ConnectivityManagerMobileTest";
+ private static final String PKG_NAME = "com.android.connectivitymanagertest";
+ private static final long WIFI_CONNECTION_TIMEOUT = 30 * 1000;
+ private static final long WIFI_NOTIFICATION_TIMEOUT = 10 * 1000;
+ private String TEST_ACCESS_POINT;
+ private ConnectivityManagerTestActivity cmActivity;
+
+ public ConnectivityManagerMobileTest() {
+ super(PKG_NAME, ConnectivityManagerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ cmActivity = getActivity();
+ ConnectivityManagerTestRunner mRunner =
+ (ConnectivityManagerTestRunner)getInstrumentation();
+ TEST_ACCESS_POINT = mRunner.TEST_SSID;
+ // Each test case will start with cellular connection
+ verifyCellularConnection();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ // clear Wifi after each test case
+ cmActivity.clearWifi();
+ cmActivity.finish();
+ Log.v(LOG_TAG, "tear down ConnectivityManager test activity");
+ super.tearDown();
+ }
+
+ // help function to verify 3G connection
+ public void verifyCellularConnection() {
+ NetworkInfo extraNetInfo = cmActivity.mNetworkInfo;
+ assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE,
+ extraNetInfo.getType());
+ assertTrue("not connected to cellular network", extraNetInfo.isConnected());
+ assertTrue("no data connection", cmActivity.mState.equals(State.CONNECTED));
+ }
+
+ // Test case 1: Test enabling Wifi without associating with any AP
+ @LargeTest
+ public void test3GToWifiNotification() {
+ // As Wifi stays in DISCONNECTED, the connectivity manager will not broadcast
+ // any network connectivity event for Wifi
+ NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+ NetworkState.DO_NOTHING, State.CONNECTED);
+ networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+ NetworkState.DO_NOTHING, State.DISCONNECTED);
+ // Eanble Wifi
+ cmActivity.enableWifi();
+ try {
+ Thread.sleep(WIFI_NOTIFICATION_TIMEOUT);
+ } catch (Exception e) {
+ Log.v(LOG_TAG, "exception: " + e.toString());
+ }
+
+ // validate state and broadcast
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+ Log.v(LOG_TAG, "the state for WIFI is changed");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+ assertTrue("state validation fail", false);
+ }
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+ Log.v(LOG_TAG, "the state for MOBILE is changed");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+ assertTrue(false);
+ }
+ // Verify that the device is still connected to MOBILE
+ verifyCellularConnection();
+ }
+
+ // Test case 2: test connection to a given AP
+ @LargeTest
+ public void testConnectToWifi() {
+ assertNotNull("SSID is null", TEST_ACCESS_POINT);
+ //Prepare for connectivity verification
+ NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(),
+ NetworkState.TO_DISCONNECTION, State.DISCONNECTED);
+ networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
+ NetworkState.TO_CONNECTION, State.CONNECTED);
+
+ // Enable Wifi and connect to a test access point
+ assertTrue("failed to connect to " + TEST_ACCESS_POINT,
+ cmActivity.connectToWifi(TEST_ACCESS_POINT));
+ try {
+ Thread.sleep(WIFI_CONNECTION_TIMEOUT);
+ } catch (Exception e) {
+ Log.v(LOG_TAG, "exception: " + e.toString());
+ }
+
+ // validate states
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
+ Log.v(LOG_TAG, "Wifi state transition validation failed.");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
+ assertTrue(false);
+ }
+ if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
+ Log.v(LOG_TAG, "Mobile state transition validation failed.");
+ Log.v(LOG_TAG, "reason: " +
+ cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
+ assertTrue(false);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
index 7028d1a..48fe765 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -214,7 +214,6 @@
MockContentResolver mockResolver = new MockContentResolver();
final TestContext testContext = new TestContext(mockResolver, getContext());
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<accounts>\n"
@@ -230,7 +229,7 @@
fos.write(accountsFileData);
accountInfoFile.finishWrite(fos);
- engine.clearAndReadState();
+ SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority1);
assertEquals(1, syncs.size());
@@ -245,7 +244,7 @@
assertEquals(sync3, syncs.get(0));
accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"1\">\n"
+ + "<accounts version=\"2\">\n"
+ "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
+ "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
+ "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
@@ -268,7 +267,7 @@
assertEquals(0, syncs.size());
accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"1\">\n"
+ + "<accounts version=\"2\">\n"
+ "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
+ "<periodicSync period=\"1000\" />\n"
+ "</authority>"
@@ -299,6 +298,89 @@
assertEquals(1, syncs.size());
assertEquals(sync3s, syncs.get(0));
}
+
+ @SmallTest
+ public void testAuthorityRenaming() throws Exception {
+ final Account account1 = new Account("acc1", "type1");
+ final Account account2 = new Account("acc2", "type2");
+ final String authorityContacts = "contacts";
+ final String authorityCalendar = "calendar";
+ final String authorityOther = "other";
+ final String authorityContactsNew = "com.android.contacts";
+ final String authorityCalendarNew = "com.android.calendar";
+
+ MockContentResolver mockResolver = new MockContentResolver();
+
+ final TestContext testContext = new TestContext(mockResolver, getContext());
+
+ byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<accounts>\n"
+ + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n"
+ + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n"
+ + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n"
+ + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n"
+ + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n"
+ + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n"
+ + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\""
+ + " authority=\"com.android.calendar\" />\n"
+ + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\""
+ + " authority=\"com.android.contacts\" />\n"
+ + "</accounts>\n").getBytes();
+
+ File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
+ syncDir.mkdirs();
+ AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+ FileOutputStream fos = accountInfoFile.startWrite();
+ fos.write(accountsFileData);
+ accountInfoFile.finishWrite(fos);
+
+ SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
+
+ assertEquals(false, engine.getSyncAutomatically(account1, authorityContacts));
+ assertEquals(false, engine.getSyncAutomatically(account1, authorityCalendar));
+ assertEquals(true, engine.getSyncAutomatically(account1, authorityOther));
+ assertEquals(true, engine.getSyncAutomatically(account1, authorityContactsNew));
+ assertEquals(true, engine.getSyncAutomatically(account1, authorityCalendarNew));
+
+ assertEquals(false, engine.getSyncAutomatically(account2, authorityContacts));
+ assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendar));
+ assertEquals(true, engine.getSyncAutomatically(account2, authorityOther));
+ assertEquals(false, engine.getSyncAutomatically(account2, authorityContactsNew));
+ assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendarNew));
+ }
+
+ @SmallTest
+ public void testSyncableMigration() throws Exception {
+ final Account account = new Account("acc", "type");
+
+ MockContentResolver mockResolver = new MockContentResolver();
+
+ final TestContext testContext = new TestContext(mockResolver, getContext());
+
+ byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<accounts>\n"
+ + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n"
+ + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n"
+ + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\""
+ + " authority=\"other3\" />\n"
+ + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\""
+ + " authority=\"other4\" />\n"
+ + "</accounts>\n").getBytes();
+
+ File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
+ syncDir.mkdirs();
+ AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+ FileOutputStream fos = accountInfoFile.startWrite();
+ fos.write(accountsFileData);
+ accountInfoFile.finishWrite(fos);
+
+ SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
+
+ assertEquals(-1, engine.getIsSyncable(account, "other1"));
+ assertEquals(1, engine.getIsSyncable(account, "other2"));
+ assertEquals(0, engine.getIsSyncable(account, "other3"));
+ assertEquals(1, engine.getIsSyncable(account, "other4"));
+ }
}
class TestContext extends ContextWrapper {
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index ab142c9..656029d 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -16,15 +16,12 @@
package android.database;
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX;
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX;
import android.content.ContentValues;
import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.CharArrayBuffer;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
-import android.os.Environment;
import android.os.Handler;
import android.os.Parcel;
import android.test.AndroidTestCase;
@@ -33,18 +30,14 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.text.Collator;
-import java.util.Arrays;
-
import junit.framework.Assert;
-import junit.framework.TestCase;
-import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX;
-import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Locale;
public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase {
+ private static final String TAG = "DatabaseGeneralTest";
private static final String sString1 = "this is a test";
private static final String sString2 = "and yet another test";
@@ -1029,4 +1022,91 @@
fail("unexpected, of course");
}
}
+
+ /**
+ * This test is available only when the platform has a locale with the language "ja".
+ * It finishes without failure when it is not available.
+ */
+ @MediumTest
+ public void testCollateLocalizedForJapanese() throws Exception {
+ final String testName = "DatabaseGeneralTest#testCollateLocalizedForJapanese()";
+ final Locale[] localeArray = Locale.getAvailableLocales();
+ final String japanese = Locale.JAPANESE.getLanguage();
+ final String english = Locale.ENGLISH.getLanguage();
+ Locale japaneseLocale = null;
+ Locale englishLocale = null;
+ for (Locale locale : localeArray) {
+ if (locale != null) {
+ final String language = locale.getLanguage();
+ if (language == null) {
+ continue;
+ } else if (language.equals(japanese)) {
+ japaneseLocale = locale;
+ } else if (language.equals(english)) {
+ englishLocale = locale;
+ }
+ }
+
+ if (japaneseLocale != null && englishLocale != null) {
+ break;
+ }
+ }
+
+ if (japaneseLocale == null || englishLocale == null) {
+ Log.d(TAG, testName + "n is silently skipped since " +
+ (englishLocale == null ?
+ (japaneseLocale == null ?
+ "Both English and Japanese locales do not exist." :
+ "English locale does not exist.") :
+ (japaneseLocale == null ?
+ "Japanese locale does not exist." :
+ "...why?")));
+ return;
+ }
+
+ Locale originalLocale = Locale.getDefault();
+ try {
+
+ final String dbName = "collate_localized_test";
+ mDatabase.execSQL("CREATE TABLE " + dbName + " (" +
+ "_id INTEGER PRIMARY KEY, " +
+ "s TEXT COLLATE LOCALIZED) ");
+ DatabaseUtils.InsertHelper ih =
+ new DatabaseUtils.InsertHelper(mDatabase, dbName);
+ ContentValues cv = new ContentValues();
+
+ cv = new ContentValues(); //
+ cv.put("s", "\uFF75\uFF77\uFF85\uFF9C"); // O-ki-na-wa in half-width Katakana
+ ih.insert(cv);
+
+ cv = new ContentValues(); //
+ cv.put("s", "\u306B\u307B\u3093"); // Ni-ho-n in Hiragana
+ ih.insert(cv);
+
+ cv = new ContentValues(); //
+ cv.put("s", "\u30A2\u30E1\u30EA\u30AB"); // A-me-ri-ca in hull-width Katakana
+ ih.insert(cv);
+
+ // Assume setLocale() does REINDEX and an English locale does not consider
+ // Japanese-specific LOCALIZED order.
+ Locale.setDefault(englishLocale);
+ Locale.setDefault(japaneseLocale);
+
+ Cursor cur = mDatabase.rawQuery(
+ "SELECT * FROM " + dbName + " ORDER BY s", null);
+ assertTrue(cur.moveToFirst());
+ assertEquals("\u30A2\u30E1\u30EA\u30AB", cur.getString(1));
+ assertTrue(cur.moveToNext());
+ assertEquals("\uFF75\uFF77\uFF85\uFF9C", cur.getString(1));
+ assertTrue(cur.moveToNext());
+ assertEquals("\u306B\u307B\u3093", cur.getString(1));
+ } finally {
+ if (originalLocale != null) {
+ try {
+ Locale.setDefault(originalLocale);
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index ad71fcb..095ee13 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -574,5 +574,13 @@
.appendQueryParameter("key", "y z")
.build();
assertEquals("y z", uri.getQueryParameter("key"));
+
+ // key is a substring of parameters, but not present
+ uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("akeyb", "a b")
+ .appendQueryParameter("keya", "c d")
+ .appendQueryParameter("bkey", "e f")
+ .build();
+ assertNull(uri.getQueryParameter("key"));
}
}
diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
index 20c55c2..89b3fb6 100644
--- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
+++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
@@ -48,7 +48,7 @@
private static final int TEST_CMD_6 = 6;
private static final boolean DBG = true;
- private static final boolean WAIT_FOR_DEBUGGER = false;
+ private static final boolean WAIT_FOR_DEBUGGER = true;
private static final String TAG = "HierarchicalStateMachineTest";
/**
@@ -109,7 +109,7 @@
synchronized (smQuitTest) {
// Send 6 messages
for (int i = 1; i <= 6; i++) {
- smQuitTest.sendMessage(smQuitTest.obtainMessage(i));
+ smQuitTest.sendMessage(i);
}
// First two are ignored
@@ -152,6 +152,154 @@
}
/**
+ * Test enter/exit can use transitionTo
+ */
+ class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
+ StateMachineEnterExitTransitionToTest(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+ addState(mS2);
+ addState(mS3);
+ addState(mS4);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected void enter() {
+ // Test that a transition in enter and the initial state works
+ mS1EnterCount += 1;
+ transitionTo(mS2);
+ Log.d(TAG, "S1.enter");
+ }
+ @Override protected void exit() {
+ mS1ExitCount += 1;
+ Log.d(TAG, "S1.exit");
+ }
+ }
+
+ class S2 extends HierarchicalState {
+ @Override protected void enter() {
+ mS2EnterCount += 1;
+ Log.d(TAG, "S2.enter");
+ }
+ @Override protected void exit() {
+ // Test transition in exit work
+ mS2ExitCount += 1;
+ transitionTo(mS4);
+ Log.d(TAG, "S2.exit");
+ }
+ @Override protected boolean processMessage(Message message) {
+ // Start a transition to S3 but it will be
+ // changed to a transition to S4
+ transitionTo(mS3);
+ Log.d(TAG, "S2.processMessage");
+ return true;
+ }
+ }
+
+ class S3 extends HierarchicalState {
+ @Override protected void enter() {
+ // Test that we can do halting in an enter/exit
+ transitionToHaltingState();
+ mS3EnterCount += 1;
+ Log.d(TAG, "S3.enter");
+ }
+ @Override protected void exit() {
+ mS3ExitCount += 1;
+ Log.d(TAG, "S3.exit");
+ }
+ }
+
+
+ class S4 extends HierarchicalState {
+ @Override protected void enter() {
+ // Test that we can do halting in an enter/exit
+ transitionToHaltingState();
+ mS4EnterCount += 1;
+ Log.d(TAG, "S4.enter");
+ }
+ @Override protected void exit() {
+ mS4ExitCount += 1;
+ Log.d(TAG, "S4.exit");
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachineEnterExitTransitionToTest mThisSm;
+ private S1 mS1 = new S1();
+ private S2 mS2 = new S2();
+ private S3 mS3 = new S3();
+ private S4 mS4 = new S4();
+ private int mS1EnterCount = 0;
+ private int mS1ExitCount = 0;
+ private int mS2EnterCount = 0;
+ private int mS2ExitCount = 0;
+ private int mS3EnterCount = 0;
+ private int mS3ExitCount = 0;
+ private int mS4EnterCount = 0;
+ private int mS4ExitCount = 0;
+ }
+
+ @SmallTest
+ public void testStateMachineEnterExitTransitionToTest() throws Exception {
+ //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
+ new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
+ smEnterExitTranstionToTest.start();
+ if (smEnterExitTranstionToTest.isDbg()) {
+ Log.d(TAG, "testStateMachineEnterExitTransitionToTest E");
+ }
+
+ synchronized (smEnterExitTranstionToTest) {
+ smEnterExitTranstionToTest.sendMessage(1);
+
+ try {
+ // wait for the messages to be handled
+ smEnterExitTranstionToTest.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting "
+ + e.getMessage());
+ }
+ }
+
+ assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
+
+ ProcessedMessages.Info pmi;
+
+ // Message should be handled by mS2.
+ pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
+ assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
+
+ assertEquals(smEnterExitTranstionToTest.mS1EnterCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS1ExitCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS2EnterCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS2ExitCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
+ assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
+
+ if (smEnterExitTranstionToTest.isDbg()) {
+ Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
+ }
+ }
+
+ /**
* Tests that ProcessedMessage works as a circular buffer.
*/
class StateMachine0 extends HierarchicalStateMachine {
@@ -297,8 +445,8 @@
synchronized (sm1) {
// Send two messages
- sm1.sendMessage(sm1.obtainMessage(TEST_CMD_1));
- sm1.sendMessage(sm1.obtainMessage(TEST_CMD_2));
+ sm1.sendMessage(TEST_CMD_1);
+ sm1.sendMessage(TEST_CMD_2);
try {
// wait for the messages to be handled
@@ -402,8 +550,8 @@
synchronized (sm2) {
// Send two messages
- sm2.sendMessage(sm2.obtainMessage(TEST_CMD_1));
- sm2.sendMessage(sm2.obtainMessage(TEST_CMD_2));
+ sm2.sendMessage(TEST_CMD_1);
+ sm2.sendMessage(TEST_CMD_2);
try {
// wait for the messages to be handled
@@ -494,8 +642,8 @@
synchronized (sm3) {
// Send two messages
- sm3.sendMessage(sm3.obtainMessage(TEST_CMD_1));
- sm3.sendMessage(sm3.obtainMessage(TEST_CMD_2));
+ sm3.sendMessage(TEST_CMD_1);
+ sm3.sendMessage(TEST_CMD_2);
try {
// wait for the messages to be handled
@@ -587,8 +735,8 @@
synchronized (sm4) {
// Send two messages
- sm4.sendMessage(sm4.obtainMessage(TEST_CMD_1));
- sm4.sendMessage(sm4.obtainMessage(TEST_CMD_2));
+ sm4.sendMessage(TEST_CMD_1);
+ sm4.sendMessage(TEST_CMD_2);
try {
// wait for the messages to be handled
@@ -861,12 +1009,12 @@
synchronized (sm5) {
// Send 6 messages
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_1));
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_2));
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_3));
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_4));
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_5));
- sm5.sendMessage(sm5.obtainMessage(TEST_CMD_6));
+ sm5.sendMessage(TEST_CMD_1);
+ sm5.sendMessage(TEST_CMD_2);
+ sm5.sendMessage(TEST_CMD_3);
+ sm5.sendMessage(TEST_CMD_4);
+ sm5.sendMessage(TEST_CMD_5);
+ sm5.sendMessage(TEST_CMD_6);
try {
// wait for the messages to be handled
@@ -950,7 +1098,7 @@
class S1 extends HierarchicalState {
@Override protected void enter() {
- sendMessage(obtainMessage(TEST_CMD_1));
+ sendMessage(TEST_CMD_1);
}
@Override protected boolean processMessage(Message message) {
@@ -994,7 +1142,7 @@
synchronized (sm6) {
// Send a message
sentTimeMsg2 = SystemClock.elapsedRealtime();
- sm6.sendMessageDelayed(sm6.obtainMessage(TEST_CMD_2), DELAY_TIME);
+ sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME);
try {
// wait for the messages to be handled
@@ -1045,7 +1193,7 @@
return true;
}
@Override protected void exit() {
- sendMessage(obtainMessage(TEST_CMD_2));
+ sendMessage(TEST_CMD_2);
}
}
@@ -1053,7 +1201,7 @@
@Override protected void enter() {
// Send a delayed message as a watch dog
- sendMessageDelayed(obtainMessage(TEST_CMD_3), SM7_DELAY_TIME);
+ sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME);
}
@Override protected boolean processMessage(Message message) {
@@ -1103,7 +1251,7 @@
synchronized (sm7) {
// Send a message
sentTimeMsg2 = SystemClock.elapsedRealtime();
- sm7.sendMessage(sm7.obtainMessage(TEST_CMD_1));
+ sm7.sendMessage(TEST_CMD_1);
try {
// wait for the messages to be handled
@@ -1178,7 +1326,7 @@
synchronized (sm) {
// Send 2 messages
for (int i = 1; i <= 2; i++) {
- sm.sendMessage(sm.obtainMessage(i));
+ sm.sendMessage(i);
}
try {
@@ -1262,7 +1410,7 @@
// Send messages to each of the state machines
for (StateMachineSharedThread sm : sms) {
for (int i = 1; i <= 4; i++) {
- sm.sendMessage(sm.obtainMessage(i));
+ sm.sendMessage(i);
}
}
@@ -1295,8 +1443,8 @@
Hsm1 sm = Hsm1.makeHsm1();
// Send messages
- sm.sendMessage(sm.obtainMessage(Hsm1.CMD_1));
- sm.sendMessage(sm.obtainMessage(Hsm1.CMD_2));
+ sm.sendMessage(Hsm1.CMD_1);
+ sm.sendMessage(Hsm1.CMD_2);
synchronized (sm) {
// Wait for the last state machine to notify its done
@@ -1389,7 +1537,7 @@
switch(message.what) {
case CMD_2:
// CMD_2 will arrive in mS2 before CMD_3
- sendMessage(obtainMessage(CMD_3));
+ sendMessage(CMD_3);
deferMessage(message);
transitionTo(mS2);
retVal = true;
@@ -1435,7 +1583,7 @@
Log.d(TAG, "S2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_2):
- sendMessage(obtainMessage(CMD_4));
+ sendMessage(CMD_4);
retVal = true;
break;
case(CMD_3):
@@ -1457,7 +1605,7 @@
class P2 extends HierarchicalState {
@Override protected void enter() {
Log.d(TAG, "P2.enter");
- sendMessage(obtainMessage(CMD_5));
+ sendMessage(CMD_5);
}
@Override protected boolean processMessage(Message message) {
Log.d(TAG, "P2.processMessage what=" + message.what);
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 1f7ee26..009a0e2 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -42,7 +42,8 @@
/**
* Keep allocating new files till the system purges them.
*/
- @LargeTest
+ // Flaky test - temporarily suppress from large suite for now
+ // @LargeTest
public void testPurge() throws Exception {
List<MemoryFile> files = new ArrayList<MemoryFile>();
try {
diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
index 019b9e3..c3f6f79 100644
--- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
@@ -215,7 +215,7 @@
mTestCase.fail("Unexpected Uri has come: " + uri);
}
} // for (int i = 0; i < size; i++) {
- return null;
+ return fakeResultArray;
}
public void verify() {
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java
index ad99ee8..dfb10fb 100644
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java
+++ b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java
@@ -28,11 +28,11 @@
import junit.framework.Assert;
public class ExpandableListTester {
- private ExpandableListView mExpandableListView;
- private ExpandableListAdapter mAdapter;
- private ListUtil mListUtil;
+ private final ExpandableListView mExpandableListView;
+ private final ExpandableListAdapter mAdapter;
+ private final ListUtil mListUtil;
- private ActivityInstrumentationTestCase2<? extends ExpandableListScenario>
+ private final ActivityInstrumentationTestCase2<? extends ExpandableListScenario>
mActivityInstrumentation;
Instrumentation mInstrumentation;
@@ -76,6 +76,8 @@
View headerChild = mExpandableListView.getChildAt(index
- mExpandableListView.getFirstVisiblePosition());
mExpandableListView.showContextMenuForChild(headerChild);
+ mInstrumentation.waitForIdleSync();
+ Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
index++;
}
@@ -92,6 +94,8 @@
View groupChild = mExpandableListView.getChildAt(index
- mExpandableListView.getFirstVisiblePosition());
mExpandableListView.showContextMenuForChild(groupChild);
+ mInstrumentation.waitForIdleSync();
+ Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
index++;
final int childrenCount = mAdapter.getChildrenCount(groupIndex);
@@ -102,6 +106,8 @@
View child = mExpandableListView.getChildAt(index
- mExpandableListView.getFirstVisiblePosition());
mExpandableListView.showContextMenuForChild(child);
+ mInstrumentation.waitForIdleSync();
+ Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
index++;
}
}
@@ -115,6 +121,8 @@
View footerChild = mExpandableListView.getChildAt(index
- mExpandableListView.getFirstVisiblePosition());
mExpandableListView.showContextMenuForChild(footerChild);
+ mInstrumentation.waitForIdleSync();
+ Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
index++;
}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java b/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java
index b482005..2dbdff8 100644
--- a/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java
+++ b/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java
@@ -23,8 +23,6 @@
import android.widget.ExpandableListView;
import android.widget.AdapterView.AdapterContextMenuInfo;
-import junit.framework.Assert;
-
public class PositionTesterContextMenuListener implements OnCreateContextMenuListener {
private int groupPosition, childPosition;
@@ -33,6 +31,9 @@
private static final int ADAPTER_TYPE = -1;
private int testType; // as returned by getPackedPositionType
+ // Will be set to null by each call to onCreateContextMenu, unless an error occurred.
+ private String errorMessage;
+
public void expectGroupContextMenu(int groupPosition) {
this.groupPosition = groupPosition;
testType = ExpandableListView.PACKED_POSITION_TYPE_GROUP;
@@ -50,30 +51,61 @@
}
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ errorMessage = null;
if (testType == ADAPTER_TYPE) {
- Assert.assertTrue("MenuInfo is not an AdapterContextMenuInfo",
- menuInfo instanceof AdapterContextMenuInfo);
+ if (!isTrue("MenuInfo is not an AdapterContextMenuInfo",
+ menuInfo instanceof AdapterContextMenuInfo)) {
+ return;
+ }
AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo;
- Assert.assertEquals("Wrong flat position",
- groupPosition,
- adapterContextMenuInfo.position);
+ if (!areEqual("Wrong flat position", groupPosition, adapterContextMenuInfo.position)) {
+ return;
+ }
} else {
- Assert.assertTrue("MenuInfo is not an ExpandableListContextMenuInfo",
- menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo);
+ if (!isTrue("MenuInfo is not an ExpandableListContextMenuInfo",
+ menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo)) {
+ return;
+ }
ExpandableListView.ExpandableListContextMenuInfo elvMenuInfo =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
long packedPosition = elvMenuInfo.packedPosition;
int packedPositionType = ExpandableListView.getPackedPositionType(packedPosition);
- Assert.assertEquals("Wrong packed position type", testType, packedPositionType);
+ if (!areEqual("Wrong packed position type", testType, packedPositionType)) {
+ return;
+ }
int packedPositionGroup = ExpandableListView.getPackedPositionGroup(packedPosition);
- Assert.assertEquals("Wrong group position", groupPosition, packedPositionGroup);
+ if (!areEqual("Wrong group position", groupPosition, packedPositionGroup)) {
+ return;
+ }
if (testType == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
- int packedPosChild = ExpandableListView.getPackedPositionChild(packedPosition);
- Assert.assertEquals("Wrong child position", childPosition, packedPosChild);
+ int packedPositionChild = ExpandableListView.getPackedPositionChild(packedPosition);
+ if (!areEqual("Wrong child position", childPosition, packedPositionChild)) {
+ return;
+ }
}
}
}
+
+ private boolean areEqual(String message, int expected, int actual) {
+ if (expected != actual) {
+ errorMessage = String.format(message + " (%d vs %d", expected, actual);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isTrue(String message, boolean value) {
+ if (!value) {
+ errorMessage = message;
+ return false;
+ }
+ return true;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
}
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
new file mode 100644
index 0000000..8dfa850
--- /dev/null
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ddmlib.SyncService.SyncResult;
+import com.android.hosttest.DeviceTestCase;
+import com.android.hosttest.DeviceTestSuite;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.Assert;
+import com.android.hosttest.DeviceTestCase;
+
+/**
+ * Set of tests that verify host side install cases
+ */
+public class PackageManagerHostTestUtils extends Assert {
+
+ private static final String LOG_TAG = "PackageManagerHostTests";
+ private IDevice mDevice = null;
+
+ // TODO: get this value from Android Environment instead of hardcoding
+ private static final String APP_PRIVATE_PATH = "/data/app-private/";
+ private static final String DEVICE_APP_PATH = "/data/app/";
+ private static final String SDCARD_APP_PATH = "/mnt/secure/asec/";
+
+ private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000;
+ private static final int WAIT_FOR_DEVICE_POLL_TIME = 10 * 1000;
+
+ /**
+ * Constructor takes the device to use
+ * @param the device to use when performing operations
+ */
+ public PackageManagerHostTestUtils(IDevice device)
+ {
+ mDevice = device;
+ }
+
+ /**
+ * Disable default constructor
+ */
+ private PackageManagerHostTestUtils() {}
+
+ /**
+ * Returns the path on the device of forward-locked apps.
+ *
+ * @return path of forward-locked apps on the device
+ */
+ public static String getAppPrivatePath() {
+ return APP_PRIVATE_PATH;
+ }
+
+ /**
+ * Returns the path on the device of normal apps.
+ *
+ * @return path of forward-locked apps on the device
+ */
+ public static String getDeviceAppPath() {
+ return DEVICE_APP_PATH;
+ }
+
+ /**
+ * Returns the path of apps installed on the SD card.
+ *
+ * @return path of forward-locked apps on the device
+ */
+ public static String getSDCardAppPath() {
+ return SDCARD_APP_PATH;
+ }
+
+ /**
+ * Helper method to push a file to device
+ * @param apkAppPrivatePath
+ * @throws IOException
+ */
+ public void pushFile(final String localFilePath, final String destFilePath)
+ throws IOException {
+ SyncResult result = mDevice.getSyncService().pushFile(
+ localFilePath, destFilePath, new NullSyncProgressMonitor());
+ assertEquals(SyncService.RESULT_OK, result.getCode());
+ }
+
+ /**
+ * Helper method to install a file to device
+ * @param localFilePath the absolute file system path to file on local host to install
+ * @param reinstall set to <code>true</code> if re-install of app should be performed
+ * @throws IOException
+ */
+ public void installFile(final String localFilePath, final boolean replace)
+ throws IOException {
+ String result = mDevice.installPackage(localFilePath, replace);
+ assertEquals(null, result);
+ }
+
+ /**
+ * Helper method to determine if file on device exists.
+ *
+ * @param destPath the absolute path of file on device to check
+ * @return <code>true</code> if file exists, <code>false</code> otherwise.
+ * @throws IOException if adb shell command failed
+ */
+ public boolean doesRemoteFileExist(String destPath) throws IOException {
+ String lsGrep = executeShellCommand(String.format("ls %s", destPath));
+ return !lsGrep.contains("No such file or directory");
+ }
+
+ /**
+ * Helper method to determine if file exists on the device containing a given string.
+ *
+ * @param destPath the
+ * @return <code>true</code> if file exists containing given string,
+ * <code>false</code> otherwise.
+ * @throws IOException if adb shell command failed
+ */
+ public boolean doesRemoteFileExistContainingString(String destPath, String searchString)
+ throws IOException {
+ String lsResult = executeShellCommand(String.format("ls %s", destPath));
+ return lsResult.contains(searchString);
+ }
+
+ /**
+ * Helper method to determine if package on device exists.
+ *
+ * @param packageName the Android manifest package to check.
+ * @return <code>true</code> if package exists, <code>false</code> otherwise
+ * @throws IOException if adb shell command failed
+ */
+ public boolean doesPackageExist(String packageName) throws IOException {
+ String pkgGrep = executeShellCommand(String.format("pm path %s", packageName));
+ return pkgGrep.contains("package:");
+ }
+
+ /**
+ * Helper method to determine if app was installed on device.
+ *
+ * @param packageName package name to check for
+ * @return <code>true</code> if file exists, <code>false</code> otherwise.
+ * @throws IOException if adb shell command failed
+ */
+ private boolean doesAppExistOnDevice(String packageName) throws IOException {
+ return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName);
+ }
+
+ /**
+ * Helper method to determine if app was installed on SD card.
+ *
+ * @param packageName package name to check for
+ * @return <code>true</code> if file exists, <code>false</code> otherwise.
+ * @throws IOException if adb shell command failed
+ */
+ public boolean doesAppExistOnSDCard(String packageName) throws IOException {
+ return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName);
+ }
+
+ /**
+ * Waits for device's package manager to respond.
+ *
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ public void waitForDevice() throws InterruptedException, IOException {
+ Log.i(LOG_TAG, "waiting for device");
+ int currentWaitTime = 0;
+ // poll the package manager until it returns something for android
+ while (!doesPackageExist("android")) {
+ Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
+ currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
+ if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
+ Log.e(LOG_TAG, "time out waiting for device");
+ throw new InterruptedException();
+ }
+ }
+ }
+
+ /**
+ * Helper method which executes a adb shell command and returns output as a {@link String}
+ * @return the output of the command
+ * @throws IOException
+ */
+ public String executeShellCommand(String command) throws IOException {
+ Log.d(LOG_TAG, String.format("adb shell %s", command));
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver();
+ mDevice.executeShellCommand(command, receiver);
+ String output = receiver.getOutput();
+ Log.d(LOG_TAG, String.format("Result: %s", output));
+ return output;
+ }
+
+ /**
+ * A {@link IShellOutputReceiver} which collects the whole shell output into one {@link String}
+ */
+ private class CollectingOutputReceiver extends MultiLineReceiver {
+
+ private StringBuffer mOutputBuffer = new StringBuffer();
+
+ public String getOutput() {
+ return mOutputBuffer.toString();
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line: lines) {
+ mOutputBuffer.append(line);
+ mOutputBuffer.append("\n");
+ }
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+ }
+
+ private class NullSyncProgressMonitor implements ISyncProgressMonitor {
+ public void advance(int work) {
+ // ignore
+ }
+
+ public boolean isCanceled() {
+ // ignore
+ return false;
+ }
+
+ public void start(int totalWork) {
+ // ignore
+
+ }
+
+ public void startSubTask(String name) {
+ // ignore
+ }
+
+ public void stop() {
+ // ignore
+ }
+ }
+
+ /**
+ * Helper method for installing an app to wherever is specified in its manifest, and
+ * then verifying the app was installed onto SD Card.
+ *
+ * @param the path of the apk to install
+ * @param the name of the package
+ * @param <code>true</code> if the app should be overwritten, <code>false</code> otherwise
+ * @throws IOException if adb shell command failed
+ * @throws InterruptedException if the thread was interrupted
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void installAppAndVerifyExistsOnSDCard(String apkPath, String pkgName, boolean overwrite)
+ throws IOException, InterruptedException {
+ // Start with a clean slate if we're not overwriting
+ if (!overwrite) {
+ // cleanup test app just in case it already exists
+ mDevice.uninstallPackage(pkgName);
+ // grep for package to make sure its not installed
+ assertFalse(doesPackageExist(pkgName));
+ }
+
+ installFile(apkPath, overwrite);
+ assertTrue(doesAppExistOnSDCard(pkgName));
+ assertFalse(doesAppExistOnDevice(pkgName));
+ waitForDevice();
+
+ // grep for package to make sure it is installed
+ assertTrue(doesPackageExist(pkgName));
+ }
+
+ /**
+ * Helper method for installing an app to wherever is specified in its manifest, and
+ * then verifying the app was installed onto device.
+ *
+ * @param the path of the apk to install
+ * @param the name of the package
+ * @param <code>true</code> if the app should be overwritten, <code>false</code> otherwise
+ * @throws IOException if adb shell command failed
+ * @throws InterruptedException if the thread was interrupted
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void installAppAndVerifyExistsOnDevice(String apkPath, String pkgName, boolean overwrite)
+ throws IOException, InterruptedException {
+ // Start with a clean slate if we're not overwriting
+ if (!overwrite) {
+ // cleanup test app just in case it already exists
+ mDevice.uninstallPackage(pkgName);
+ // grep for package to make sure its not installed
+ assertFalse(doesPackageExist(pkgName));
+ }
+
+ installFile(apkPath, overwrite);
+ assertFalse(doesAppExistOnSDCard(pkgName));
+ assertTrue(doesAppExistOnDevice(pkgName));
+ waitForDevice();
+
+ // grep for package to make sure it is installed
+ assertTrue(doesPackageExist(pkgName));
+ }
+
+ /**
+ * Helper method for uninstalling an app.
+ *
+ * @param pkgName package name to uninstall
+ * @throws IOException if adb shell command failed
+ * @throws InterruptedException if the thread was interrupted
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void uninstallApp(String pkgName) throws IOException, InterruptedException {
+ mDevice.uninstallPackage(pkgName);
+ // make sure its not installed anymore
+ assertFalse(doesPackageExist(pkgName));
+ }
+
+}
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
index 9c9d777..90ddc3a 100644
--- a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.Log;
import com.android.ddmlib.MultiLineReceiver;
@@ -35,23 +36,78 @@
*/
public class PackageManagerHostTests extends DeviceTestCase {
+ private static final String LOG_TAG = "PackageManagerHostTests";
+ private PackageManagerHostTestUtils mPMHostUtils = null;
+
+ private String appPrivatePath = null;
+ private String deviceAppPath = null;
+ private String sdcardAppPath = null;
+
+ // Various test files and their corresponding package names...
+
// testPushAppPrivate constants
// these constants must match values defined in test-apps/SimpleTestApp
private static final String SIMPLE_APK = "SimpleTestApp.apk";
private static final String SIMPLE_PKG = "com.android.framework.simpletestapp";
- // TODO: get this value from Android Environment instead of hardcoding
- private static final String APP_PRIVATE_PATH = "/data/app-private/";
- private static final String LOG_TAG = "PackageManagerHostTests";
-
- private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000;
- private static final int WAIT_FOR_DEVICE_POLL_TIME = 10 * 1000;
+ // Apk with install location set to auto
+ private static final String AUTO_LOC_APK = "AutoLocTestApp.apk";
+ private static final String AUTO_LOC_PKG = "com.android.framework.autoloctestapp";
+ // Apk with install location set to internalOnly
+ private static final String INTERNAL_LOC_APK = "InternalLocTestApp.apk";
+ private static final String INTERNAL_LOC_PKG = "com.android.framework.internalloctestapp";
+ // Apk with install location set to preferExternal
+ private static final String EXTERNAL_LOC_APK = "ExternalLocTestApp.apk";
+ private static final String EXTERNAL_LOC_PKG = "com.android.framework.externalloctestapp";
+ // Apk with no install location set
+ private static final String NO_LOC_APK = "NoLocTestApp.apk";
+ private static final String NO_LOC_PKG = "com.android.framework.noloctestapp";
+ // Apk with 2 different versions - v1 is set to external, v2 has no location setting
+ private static final String UPDATE_EXTERNAL_LOC_V1_EXT_APK
+ = "UpdateExternalLocTestApp_v1_ext.apk";
+ private static final String UPDATE_EXTERNAL_LOC_V2_NONE_APK
+ = "UpdateExternalLocTestApp_v2_none.apk";
+ private static final String UPDATE_EXTERNAL_LOC_PKG
+ = "com.android.framework.updateexternalloctestapp";
+ // Apk with 2 different versions - v1 is set to external, v2 is set to internalOnly
+ private static final String UPDATE_EXT_TO_INT_LOC_V1_EXT_APK
+ = "UpdateExtToIntLocTestApp_v1_ext.apk";
+ private static final String UPDATE_EXT_TO_INT_LOC_V2_INT_APK
+ = "UpdateExtToIntLocTestApp_v2_int.apk";
+ private static final String UPDATE_EXT_TO_INT_LOC_PKG
+ = "com.android.framework.updateexttointloctestapp";
+ // Apks with the same package name, but install location set to
+ // one of: Internal, External, Auto, or None
+ private static final String VERSATILE_LOC_PKG = "com.android.framework.versatiletestapp";
+ private static final String VERSATILE_LOC_INTERNAL_APK = "VersatileTestApp_Internal.apk";
+ private static final String VERSATILE_LOC_EXTERNAL_APK = "VersatileTestApp_External.apk";
+ private static final String VERSATILE_LOC_AUTO_APK = "VersatileTestApp_Auto.apk";
+ private static final String VERSATILE_LOC_NONE_APK = "VersatileTestApp_None.apk";
@Override
protected void setUp() throws Exception {
super.setUp();
// ensure apk path has been set before test is run
assertNotNull(getTestAppPath());
+
+ // setup the PackageManager host tests utilities class, and get various paths we'll need...
+ mPMHostUtils = new PackageManagerHostTestUtils(getDevice());
+ appPrivatePath = mPMHostUtils.getAppPrivatePath();
+ deviceAppPath = mPMHostUtils.getDeviceAppPath();
+ sdcardAppPath = mPMHostUtils.getSDCardAppPath();
+ }
+
+ /**
+ * Get the absolute file system location of test app with given filename
+ * @param fileName the file name of the test app apk
+ * @return {@link String} of absolute file path
+ */
+ public String getTestAppFilePath(String fileName) {
+ return String.format("%s%s%s", getTestAppPath(), File.separator, fileName);
+ }
+
+ public static Test suite() {
+ return new DeviceTestSuite(PackageManagerHostTests.class);
}
/**
@@ -62,155 +118,181 @@
*/
public void testPushAppPrivate() throws IOException, InterruptedException {
Log.i(LOG_TAG, "testing pushing an apk to /data/app-private");
- final String apkAppPrivatePath = APP_PRIVATE_PATH + SIMPLE_APK;
+ final String apkAppPrivatePath = appPrivatePath + SIMPLE_APK;
// cleanup test app just in case it was accidently installed
getDevice().uninstallPackage(SIMPLE_PKG);
- executeShellCommand("stop");
- pushFile(getTestAppFilePath(SIMPLE_APK), apkAppPrivatePath);
- // sanity check to make sure file is there
- assertTrue(doesRemoteFileExist(apkAppPrivatePath));
- executeShellCommand("start");
+ mPMHostUtils.executeShellCommand("stop");
+ mPMHostUtils.pushFile(getTestAppFilePath(SIMPLE_APK), apkAppPrivatePath);
- waitForDevice();
+ // sanity check to make sure file is there
+ assertTrue(mPMHostUtils.doesRemoteFileExist(apkAppPrivatePath));
+ mPMHostUtils.executeShellCommand("start");
+
+ mPMHostUtils.waitForDevice();
// grep for package to make sure its not installed
- assertFalse(doesPackageExist(SIMPLE_PKG));
+ assertFalse(mPMHostUtils.doesPackageExist(SIMPLE_PKG));
// ensure it has been deleted from app-private
- assertFalse(doesRemoteFileExist(apkAppPrivatePath));
+ assertFalse(mPMHostUtils.doesRemoteFileExist(apkAppPrivatePath));
}
/**
- * Helper method to push a file to device
- * @param apkAppPrivatePath
- * @throws IOException
+ * Regression test to verify that an app with its manifest set to installLocation=auto
+ * will install the app to the device.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*/
- private void pushFile(final String localFilePath, final String destFilePath)
- throws IOException {
- SyncResult result = getDevice().getSyncService().pushFile(
- localFilePath, destFilePath,
- new NullSyncProgressMonitor());
- assertEquals(SyncService.RESULT_OK, result.getCode());
- }
+ public void testInstallAppAutoLoc() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test an app with installLocation=auto gets installed on device");
- /**
- * Helper method to determine if file on device exists.
- *
- * @param destPath the absolute path of file on device to check
- * @return <code>true</code> if file exists, <code>false</code> otherwise.
- * @throws IOException if adb shell command failed
- */
- private boolean doesRemoteFileExist(String destPath) throws IOException {
- String lsGrep = executeShellCommand(String.format("ls %s",
- destPath));
- return !lsGrep.contains("No such file or directory");
- }
-
- /**
- * Helper method to determine if package on device exists.
- *
- * @param packageName the Android manifest package to check.
- * @return <code>true</code> if package exists, <code>false</code> otherwise
- * @throws IOException if adb shell command failed
- */
- private boolean doesPackageExist(String packageName) throws IOException {
- String pkgGrep = executeShellCommand(String.format("pm path %s",
- packageName));
- return pkgGrep.contains("package:");
- }
-
- /**
- * Waits for device's package manager to respond.
- *
- * @throws InterruptedException
- * @throws IOException
- */
- private void waitForDevice() throws InterruptedException, IOException {
- Log.i(LOG_TAG, "waiting for device");
- int currentWaitTime = 0;
- // poll the package manager until it returns something for android
- while (!doesPackageExist("android")) {
- Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
- currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
- if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
- Log.e(LOG_TAG, "time out waiting for device");
- throw new InterruptedException();
- }
+ try {
+ mPMHostUtils.installAppAndVerifyExistsOnDevice(
+ getTestAppFilePath(AUTO_LOC_APK), AUTO_LOC_PKG, false);
+ }
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(AUTO_LOC_PKG);
}
}
/**
- * Helper method which executes a adb shell command and returns output as a {@link String}
- * @return
- * @throws IOException
+ * Regression test to verify that an app with its manifest set to installLocation=internalOnly
+ * will install the app to the device.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*/
- private String executeShellCommand(String command) throws IOException {
- Log.d(LOG_TAG, String.format("adb shell %s", command));
- CollectingOutputReceiver receiver = new CollectingOutputReceiver();
- getDevice().executeShellCommand(command, receiver);
- String output = receiver.getOutput();
- Log.d(LOG_TAG, String.format("Result: %s", output));
- return output;
+ public void testInstallAppInternalLoc() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test an app with installLocation=internalOnly gets installed on device");
+
+ try {
+ mPMHostUtils.installAppAndVerifyExistsOnDevice(
+ getTestAppFilePath(INTERNAL_LOC_APK), INTERNAL_LOC_PKG, false);
+ }
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(INTERNAL_LOC_PKG);
+ }
}
/**
- * Get the absolute file system location of test app with given filename
- * @param fileName the file name of the test app apk
- * @return {@link String} of absolute file path
+ * Regression test to verify that an app with its manifest set to installLocation=preferExternal
+ * will install the app to the SD card.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*/
- private String getTestAppFilePath(String fileName) {
- return String.format("%s%s%s", getTestAppPath(), File.separator, fileName);
- }
+ public void testInstallAppExternalLoc() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test an app with installLocation=preferExternal gets installed on SD Card");
- public static Test suite() {
- return new DeviceTestSuite(PackageManagerHostTests.class);
+ try {
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(
+ getTestAppFilePath(EXTERNAL_LOC_APK), EXTERNAL_LOC_PKG, false);
+ }
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(EXTERNAL_LOC_PKG);
+ }
}
/**
- * A {@link IShellOutputReceiver} which collects the whole shell output into one {@link String}
+ * Regression test to verify that we can install an app onto the device,
+ * uninstall it, and reinstall it onto the SD card.
+ * <p/>
+ * Assumes adb is running as root in device under test.
*/
- private static class CollectingOutputReceiver extends MultiLineReceiver {
+ // TODO: This currently relies on the app's manifest to switch from device to
+ // SD card install locations. We might want to make Device's installPackage()
+ // accept a installLocation flag so we can install a package to the
+ // destination of our choosing.
+ public void testReinstallInternalToExternal() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test installing an app first to the device, then to the SD Card");
- private StringBuffer mOutputBuffer = new StringBuffer();
-
- public String getOutput() {
- return mOutputBuffer.toString();
+ try {
+ mPMHostUtils.installAppAndVerifyExistsOnDevice(
+ getTestAppFilePath(VERSATILE_LOC_INTERNAL_APK), VERSATILE_LOC_PKG, false);
+ mPMHostUtils.uninstallApp(VERSATILE_LOC_PKG);
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(
+ getTestAppFilePath(VERSATILE_LOC_EXTERNAL_APK), VERSATILE_LOC_PKG, false);
}
-
- @Override
- public void processNewLines(String[] lines) {
- for (String line: lines) {
- mOutputBuffer.append(line);
- mOutputBuffer.append("\n");
- }
- }
-
- public boolean isCancelled() {
- return false;
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(VERSATILE_LOC_PKG);
}
}
- private static class NullSyncProgressMonitor implements ISyncProgressMonitor {
- public void advance(int work) {
- // ignore
+ /**
+ * Regression test to verify that we can install an app onto the SD Card,
+ * uninstall it, and reinstall it onto the device.
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ // TODO: This currently relies on the app's manifest to switch from device to
+ // SD card install locations. We might want to make Device's installPackage()
+ // accept a installLocation flag so we can install a package to the
+ // destination of our choosing.
+ public void testReinstallExternalToInternal() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test installing an app first to the SD Care, then to the device");
+
+ try {
+ // install the app externally
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(
+ getTestAppFilePath(VERSATILE_LOC_EXTERNAL_APK), VERSATILE_LOC_PKG, false);
+ mPMHostUtils.uninstallApp(VERSATILE_LOC_PKG);
+ // then replace the app with one marked for internalOnly
+ mPMHostUtils.installAppAndVerifyExistsOnDevice(
+ getTestAppFilePath(VERSATILE_LOC_INTERNAL_APK), VERSATILE_LOC_PKG, false);
}
-
- public boolean isCanceled() {
- // ignore
- return false;
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(VERSATILE_LOC_PKG);
}
+ }
- public void start(int totalWork) {
- // ignore
+ /**
+ * Regression test to verify that updating an app on the SD card will install
+ * the update onto the SD card as well when location is not explicitly set in the
+ * updated apps' manifest file.
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void testUpdateToSDCard() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test updating an app on the SD card stays on the SD card");
+ try {
+ // install the app externally
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(getTestAppFilePath(
+ UPDATE_EXTERNAL_LOC_V1_EXT_APK), UPDATE_EXTERNAL_LOC_PKG, false);
+ // now replace the app with one where the location is blank (app should stay external)
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(getTestAppFilePath(
+ UPDATE_EXTERNAL_LOC_V2_NONE_APK), UPDATE_EXTERNAL_LOC_PKG, true);
}
-
- public void startSubTask(String name) {
- // ignore
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(UPDATE_EXTERNAL_LOC_PKG);
}
+ }
- public void stop() {
- // ignore
+ /**
+ * Regression test to verify that updating an app on the SD card will install
+ * the update onto the SD card as well when location is not explicitly set in the
+ * updated apps' manifest file.
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void testUpdateSDCardToDevice() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "Test updating an app on the SD card to the Device through manifest change");
+
+ try {
+ // install the app externally
+ mPMHostUtils.installAppAndVerifyExistsOnSDCard(getTestAppFilePath(
+ UPDATE_EXT_TO_INT_LOC_V1_EXT_APK), UPDATE_EXT_TO_INT_LOC_PKG, false);
+ // now replace the app with an update marked for internalOnly...
+ mPMHostUtils.installAppAndVerifyExistsOnDevice(getTestAppFilePath(
+ UPDATE_EXT_TO_INT_LOC_V2_INT_APK), UPDATE_EXT_TO_INT_LOC_PKG, true);
+ }
+ // cleanup test app
+ finally {
+ mPMHostUtils.uninstallApp(UPDATE_EXT_TO_INT_LOC_PKG);
}
}
}
diff --git a/core/tests/hosttests/test-apps/AutoLocTestApp/Android.mk b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.mk
new file mode 100644
index 0000000..b3cab481
--- /dev/null
+++ b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := AutoLocTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/AutoLocTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/AutoLocTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..d8c806a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/AutoLocTestApp/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.autoloctestapp"
+ android:installLocation="auto">
+
+ <application android:label="AutoLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/AutoLocTestApp/src/com/android/framework/autoloctestapp/AutoLocTestAppActivity.java b/core/tests/hosttests/test-apps/AutoLocTestApp/src/com/android/framework/autoloctestapp/AutoLocTestAppActivity.java
new file mode 100644
index 0000000..fba5691
--- /dev/null
+++ b/core/tests/hosttests/test-apps/AutoLocTestApp/src/com/android/framework/autoloctestapp/AutoLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.autoloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class AutoLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.mk b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.mk
new file mode 100644
index 0000000..9a05fa6
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := ExternalLocPermFLTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..f45c627
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.externallocpermsfltestapp"
+ android:installLocation="preferExternal">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <application android:label="ExternalLocPermsFLTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/src/com/android/framework/externallocpermsfltestapp/ExternalLocPermsFLTestAppActivity.java b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/src/com/android/framework/externallocpermsfltestapp/ExternalLocPermsFLTestAppActivity.java
new file mode 100644
index 0000000..48fd744
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/src/com/android/framework/externallocpermsfltestapp/ExternalLocPermsFLTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.externallocpermsfltestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class ExternalLocPermsFLTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.mk b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.mk
new file mode 100644
index 0000000..5aec78a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := ExternalLocTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/ExternalLocTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/ExternalLocTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..a0c7b02
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocTestApp/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.externalloctestapp"
+ android:installLocation="preferExternal">
+
+ <application android:label="ExternalLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/ExternalLocTestApp/src/com/android/framework/externalloctestapp/ExternalLocTestAppActivity.java b/core/tests/hosttests/test-apps/ExternalLocTestApp/src/com/android/framework/externalloctestapp/ExternalLocTestAppActivity.java
new file mode 100644
index 0000000..0a61628
--- /dev/null
+++ b/core/tests/hosttests/test-apps/ExternalLocTestApp/src/com/android/framework/externalloctestapp/ExternalLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.externalloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class ExternalLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/InternalLocTestApp/Android.mk b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.mk
new file mode 100644
index 0000000..5b58e72
--- /dev/null
+++ b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := InternalLocTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/InternalLocTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/InternalLocTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..fae3ae0
--- /dev/null
+++ b/core/tests/hosttests/test-apps/InternalLocTestApp/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.internalloctestapp"
+ android:installLocation="internalOnly">
+
+ <application android:label="InternalLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/InternalLocTestApp/src/com/android/framework/internalloctestapp/InternalLocTestAppActivity.java b/core/tests/hosttests/test-apps/InternalLocTestApp/src/com/android/framework/internalloctestapp/InternalLocTestAppActivity.java
new file mode 100644
index 0000000..6934641
--- /dev/null
+++ b/core/tests/hosttests/test-apps/InternalLocTestApp/src/com/android/framework/internalloctestapp/InternalLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.internalloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class InternalLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/NoLocTestApp/Android.mk b/core/tests/hosttests/test-apps/NoLocTestApp/Android.mk
new file mode 100644
index 0000000..11916b0
--- /dev/null
+++ b/core/tests/hosttests/test-apps/NoLocTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := NoLocTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/NoLocTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/NoLocTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..bfdad37
--- /dev/null
+++ b/core/tests/hosttests/test-apps/NoLocTestApp/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.noloctestapp">
+
+ <application android:label="NoLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/NoLocTestApp/src/com/android/framework/noloctestapp/NoLocTestAppActivity.java b/core/tests/hosttests/test-apps/NoLocTestApp/src/com/android/framework/noloctestapp/NoLocTestAppActivity.java
new file mode 100644
index 0000000..72c8d2b
--- /dev/null
+++ b/core/tests/hosttests/test-apps/NoLocTestApp/src/com/android/framework/noloctestapp/NoLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.noloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class NoLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.mk b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.mk
new file mode 100644
index 0000000..f2baefe
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := UpdateExtToIntLocTestApp_v1_ext
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/AndroidManifest.xml b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/AndroidManifest.xml
new file mode 100644
index 0000000..b572e86
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.updateexttointloctestapp"
+ android:installLocation="preferExternal"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application android:label="UpdateExtToIntLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java
new file mode 100644
index 0000000..5c51fb7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.updateexttointloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class UpdateExtToIntLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.mk b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.mk
new file mode 100644
index 0000000..492c326
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := UpdateExtToIntLocTestApp_v2_int
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/AndroidManifest.xml b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/AndroidManifest.xml
new file mode 100644
index 0000000..c9437ae
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.updateexttointloctestapp"
+ android:installLocation="internalOnly"
+ android:versionCode="2"
+ android:versionName="2.0">
+
+ <application android:label="UpdateExtToIntLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java
new file mode 100644
index 0000000..5c51fb7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/src/com/android/framework/updateexttointloctestapp/UpdateExtToIntLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.updateexttointloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class UpdateExtToIntLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.mk b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.mk
new file mode 100644
index 0000000..45867f7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := UpdateExternalLocTestApp_v1_ext
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/AndroidManifest.xml b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/AndroidManifest.xml
new file mode 100644
index 0000000..ec622ce
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.updateexternalloctestapp"
+ android:installLocation="preferExternal"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application android:label="UpdateExternalLocTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java
new file mode 100644
index 0000000..02ecb75
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.updateexternalloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class UpdateExternalLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.mk b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.mk
new file mode 100644
index 0000000..780a9d72
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := UpdateExternalLocTestApp_v2_none
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/AndroidManifest.xml b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/AndroidManifest.xml
new file mode 100644
index 0000000..50fb104
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.updateexternalloctestapp"
+ android:versionCode="2"
+ android:versionName="2.0">
+
+ <application android:label="UpdateExternalLocTestApp"/>
+</manifest>
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java
new file mode 100644
index 0000000..02ecb75
--- /dev/null
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/src/com/android/framework/updateexternalloctestapp/UpdateExternalLocTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.updateexternalloctestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class UpdateExternalLocTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.mk b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.mk
new file mode 100644
index 0000000..fc42bc4a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := VersatileTestApp_Auto
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/AndroidManifest.xml b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/AndroidManifest.xml
new file mode 100644
index 0000000..f249250
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.versatiletestapp"
+ android:installLocation="auto">
+
+ <application android:label="VersatileTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
new file mode 100644
index 0000000..c201c7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.versatiletestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class VersatileTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.mk b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.mk
new file mode 100644
index 0000000..c72a92c
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := VersatileTestApp_External
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_External/AndroidManifest.xml b/core/tests/hosttests/test-apps/VersatileTestApp_External/AndroidManifest.xml
new file mode 100644
index 0000000..0d17ac2
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_External/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.versatiletestapp"
+ android:installLocation="preferExternal">
+
+ <application android:label="VersatileTestApp"/>
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_External/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java b/core/tests/hosttests/test-apps/VersatileTestApp_External/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
new file mode 100644
index 0000000..c201c7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_External/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.versatiletestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class VersatileTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.mk b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.mk
new file mode 100644
index 0000000..e477825e
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := VersatileTestApp_Internal
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/AndroidManifest.xml b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/AndroidManifest.xml
new file mode 100644
index 0000000..e8cae6e
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.versatiletestapp"
+ android:installLocation="internalOnly">
+
+ <application android:label="VersatileTestApp"/>
+</manifest>
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
new file mode 100644
index 0000000..c201c7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.versatiletestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class VersatileTestAppActivity extends Activity {
+
+}
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.mk b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.mk
new file mode 100644
index 0000000..1fc516c4
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := VersatileTestApp_None
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_None/AndroidManifest.xml b/core/tests/hosttests/test-apps/VersatileTestApp_None/AndroidManifest.xml
new file mode 100644
index 0000000..1df40d4
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_None/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.versatiletestapp">
+
+ <application android:label="VersatileTestApp"/>
+</manifest>
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_None/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java b/core/tests/hosttests/test-apps/VersatileTestApp_None/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
new file mode 100644
index 0000000..c201c7a
--- /dev/null
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_None/src/com/android/framework/versatiletestapp/VersatileTestAppActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.versatiletestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class VersatileTestAppActivity extends Activity {
+
+}
diff --git a/data/fonts/DroidSansHebrew.ttf b/data/fonts/DroidSansHebrew.ttf
index 8cc670d..0b48628 100644
--- a/data/fonts/DroidSansHebrew.ttf
+++ b/data/fonts/DroidSansHebrew.ttf
Binary files differ
diff --git a/data/sounds/effects/Lock.aiff b/data/sounds/effects/Lock.aiff
deleted file mode 100644
index 90870f2..0000000
--- a/data/sounds/effects/Lock.aiff
+++ /dev/null
Binary files differ
diff --git a/data/sounds/effects/Lock.ogg b/data/sounds/effects/Lock.ogg
index 841ee2e..e7928e4 100644
--- a/data/sounds/effects/Lock.ogg
+++ b/data/sounds/effects/Lock.ogg
Binary files differ
diff --git a/data/sounds/effects/Lock.wav b/data/sounds/effects/Lock.wav
new file mode 100644
index 0000000..b8076369
--- /dev/null
+++ b/data/sounds/effects/Lock.wav
Binary files differ
diff --git a/data/sounds/effects/Unlock.aiff b/data/sounds/effects/Unlock.aiff
deleted file mode 100644
index 546ea39..0000000
--- a/data/sounds/effects/Unlock.aiff
+++ /dev/null
Binary files differ
diff --git a/data/sounds/effects/Unlock.ogg b/data/sounds/effects/Unlock.ogg
index 26d5ea6..cca9594 100644
--- a/data/sounds/effects/Unlock.ogg
+++ b/data/sounds/effects/Unlock.ogg
Binary files differ
diff --git a/data/sounds/effects/Unlock.wav b/data/sounds/effects/Unlock.wav
new file mode 100644
index 0000000..e1d475e
--- /dev/null
+++ b/data/sounds/effects/Unlock.wav
Binary files differ
diff --git a/docs/html/guide/appendix/g-app-intents.jd b/docs/html/guide/appendix/g-app-intents.jd
index 2846859..df9d29b 100644
--- a/docs/html/guide/appendix/g-app-intents.jd
+++ b/docs/html/guide/appendix/g-app-intents.jd
@@ -1,8 +1,11 @@
page.title=Intents List: Invoking Google Applications on Android Devices
@jd:body
+<div class="sidebox-wrapper">
<div class="sidebox">
-For more information about intents, see the <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.
+For more information about intents, see the <a
+href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.
+</div>
</div>
<p>The table below lists the intents that your application can send, to invoke Google applications on Android devices in certain ways. For each action/uri pair, the table describes how the receiving Google application handles the intent. </p>
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 8fa4cec..22c099f 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -55,7 +55,7 @@
<h3 id="dev-phone-1">Android Dev Phone 1</h3>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<p>Selected specs for Android Dev Phone 1:</p>
<ul>
<li>Touch screen</li>
diff --git a/docs/html/guide/developing/tools/avd.jd b/docs/html/guide/developing/tools/avd.jd
index 06b1245..acebb7e 100644
--- a/docs/html/guide/developing/tools/avd.jd
+++ b/docs/html/guide/developing/tools/avd.jd
@@ -93,7 +93,7 @@
<h2 id="creating">Creating an AVD</h2>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<p>The Android SDK does not include any preconfigured AVDs, so
you need to create an AVD before you can run any application in the emulator
(even the Hello World application).</p>
diff --git a/docs/html/guide/developing/tools/draw9patch.jd b/docs/html/guide/developing/tools/draw9patch.jd
index 9607ae7..61da1e0 100644
--- a/docs/html/guide/developing/tools/draw9patch.jd
+++ b/docs/html/guide/developing/tools/draw9patch.jd
@@ -7,9 +7,8 @@
the section on Nine-patch in the
<a href="{@docRoot}guide/topics/resources/available-resources.html#ninepatch">Nine-patch Images</a> topic.</p>
-<div class="sidebox" style="width:auto"><br/>
-<img src="{@docRoot}images/draw9patch-norm.png" alt="" height="300" width="341" />
-</div>
+<img src="{@docRoot}images/draw9patch-norm.png" style="float:right" alt="" height="300" width="341"
+/>
<p>Here's a quick guide to create a Nine-patch graphic using the Draw 9-patch tool.
You'll need the PNG image with which you'd like to create a NinePatch.</p>
@@ -39,9 +38,8 @@
A previously saved 9-patch file (<code>*.9.png</code>) will be loaded as-is,
with no drawing area added, because it already exists.</p>
-<div class="sidebox" style="width:auto"><br/>
-<img src="{@docRoot}images/draw9patch-bad.png" alt="" height="300" width="341" />
-</div>
+<img src="{@docRoot}images/draw9patch-bad.png" style="float:right" alt="" height="300" width="341"
+/>
<p>Optional controls include:</p>
<ul>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 13d752a..dce78c4 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -143,6 +143,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/manifest/intent-filter-element.html"><intent-filter></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/manifest-element.html"><manifest></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/meta-data-element.html"><meta-data></a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/manifest/path-permission-element.html"><path-permission></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-element.html"><permission></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-group-element.html"><permission-group></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/manifest/permission-tree-element.html"><permission-tree></a></li>
@@ -198,7 +199,18 @@
</a></li>
<li><a href="<?cs var:toroot?>guide/topics/wireless/bluetooth.html">
<span class="en">Bluetooth</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot?>guide/topics/search/index.html">
+ <span class="en">Search</span>
+ </a> <span class="new">new!</span></div>
+ <ul>
+ <li><a href="<?cs var:toroot?>guide/topics/search/search-dialog.html">Using the Android Search Dialog</a></li>
+ <li><a href="<?cs var:toroot?>guide/topics/search/adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
+ <li><a href="<?cs var:toroot?>guide/topics/search/adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
+ <li><a href="<?cs var:toroot?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
+ </ul>
+ </li>
</ul>
</li>
diff --git a/docs/html/guide/practices/design/responsiveness.jd b/docs/html/guide/practices/design/responsiveness.jd
index 1d5a235..8a4e7cf 100644
--- a/docs/html/guide/practices/design/responsiveness.jd
+++ b/docs/html/guide/practices/design/responsiveness.jd
@@ -23,8 +23,8 @@
this with basic performance, which was described above as a <em>method</em>-level
concern.)</p>
-<div class="sidebox" style="margin-top:1em;border:0;">
-<div style="border:0;background-color:#fff;padding:15px;padding-right:2em;margin:0;">
+<div class="sidebox-wrapper">
+<div class="sidebox">
<img src="{@docRoot}images/anr.png" width="240" height="320" alt="Screenshot of ANR dialog box">
<p style="margin-top:.5em;padding:.5em;">An ANR dialog displayed to the user.</p>
</div>
diff --git a/docs/html/guide/publishing/preparing.jd b/docs/html/guide/publishing/preparing.jd
index 5357166..c1c6351 100644
--- a/docs/html/guide/publishing/preparing.jd
+++ b/docs/html/guide/publishing/preparing.jd
@@ -176,10 +176,13 @@
<h3 id="mapsApiKey">7. Register for a Maps API Key, if your application is using
MapView elements</h3>
-<div class="sidebox" style="margin-bottom:.5em;padding:1em;"><p>
+<div class="sidebox-wrapper">
+<div class="sidebox"><p>
For complete information about getting a Maps API Key, see <a
href="http://code.google.com/android/add-ons/google-apis/mapkey.html">
-Obtaining a Maps API Key</a>.</p></div>
+Obtaining a Maps API Key</a>.</p>
+</div>
+</div>
<p>If your application uses one or more Mapview elements, you will need to
register your application with the Google
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index f36b684..7a8dd59 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -220,7 +220,7 @@
<h2 id="AppWidgetProvider">Using the AppWidgetProvider Class</h2>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+<div class="sidebox">
<p>You must declare your AppWidgetProvider class implementation as a broadcast receiver
using the <code><receiver></code> element in the AndroidManifest (see
<a href="#Manifest">Declaring an App Widget in the Manifest</a> above).</p>
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index c10af8e..807c7ff 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -1180,7 +1180,7 @@
</p>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<h2>Calling into the superclass</h2>
<p>
An implementation of any activity lifecycle method should always first
diff --git a/docs/html/guide/topics/graphics/index.jd b/docs/html/guide/topics/graphics/index.jd
index e1158be..92771a8 100644
--- a/docs/html/guide/topics/graphics/index.jd
+++ b/docs/html/guide/topics/graphics/index.jd
@@ -56,7 +56,7 @@
draw your graphics into a View when you want to display a static graphic or predefined animation, within
an otherwise static application. Read <a href="#draw-to-view">Simple Graphics Inside a View</a>.</li>
-<p>Option "b," drawing to a Canvas, is better when you're application needs to regularly re-draw itself.
+<p>Option "b," drawing to a Canvas, is better when your application needs to regularly re-draw itself.
Basically, any video game should be drawing to the Canvas on its own. However, there's more than
one way to do this: </p>
<ul>
@@ -117,7 +117,7 @@
<h3 id="on-view">On a View</h3>
-<p>If you're application does not require a significant amount of processing or
+<p>If your application does not require a significant amount of processing or
frame-rate speed (perhaps for a chess game, a snake game,
or another slowly-animated application), then you should consider creating a custom View component
and drawing with a Canvas in <code>{@link android.view.View#onDraw(Canvas) View.onDraw()}</code>.
diff --git a/docs/html/guide/topics/intents/intents-filters.jd b/docs/html/guide/topics/intents/intents-filters.jd
index 110721d..bd1d694 100644
--- a/docs/html/guide/topics/intents/intents-filters.jd
+++ b/docs/html/guide/topics/intents/intents-filters.jd
@@ -392,7 +392,7 @@
</p>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<h2>Filters and security</h2>
<p>An intent filter cannot be relied on for security. While it opens a
component to receiving only certain kinds of implicit intents, it does
diff --git a/docs/html/guide/topics/search/adding-custom-suggestions.jd b/docs/html/guide/topics/search/adding-custom-suggestions.jd
new file mode 100644
index 0000000..9ea4c8b
--- /dev/null
+++ b/docs/html/guide/topics/search/adding-custom-suggestions.jd
@@ -0,0 +1,699 @@
+page.title=Adding Custom Suggestions
+parent.title=Search
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.app.SearchManager}</li>
+<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
+<li>{@link android.content.ContentProvider}</li>
+</ol>
+<h2>In this document</h2>
+<ol>
+<li><a href="#TheBasics">The Basics</a></li>
+<li><a href="#CustomSearchableConfiguration">Modifying the searchable configuration</a></li>
+<li><a href="#CustomContentProvider">Creating a Content Provider</a>
+ <ol>
+ <li><a href="#HandlingSuggestionQuery">Handling a suggestion query</a></li>
+ <li><a href="#SuggestionTable">Building a suggestion table</a></li>
+ </ol>
+</li>
+<li><a href="#IntentForSuggestions">Declaring an Intent for suggestions</a>
+ <ol>
+ <li><a href="#IntentAction">Declaring the Intent action</a></li>
+ <li><a href="#IntentData">Declaring the Intent data</a></li>
+ </ol>
+</li>
+<li><a href="#HandlingIntent">Handling the Intent</a></li>
+<li><a href="#RewritingQueryText">Rewriting the query text</a></li>
+<li><a href="#QSB">Exposing search suggestions to Quick Search Box</a></li>
+</ol>
+<h2>See also</h2>
+<ol>
+<li><a href="searchable-config.html">Searchable Configuration</a></li>
+<li><a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></li>
+<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary sample app</a></li>
+</ol>
+</div>
+</div>
+
+<p>The Android search framework provides the ability for your application to
+provide suggestions while the user types into the Android search dialog. In this guide, you'll learn
+how to create custom suggestions. These are suggestions based on custom data provided by your
+application. For example, if your application is a word dictionary, you can suggest words from the
+dictionary that match the text entered so far. These are the most valuable suggestions because you
+can effectively predict what the user wants and provide instant access to it. Once you provide
+custom suggestions, you then make them available to the system-wide Quick Search Box, providing
+access to your content from outside your application.</p>
+
+<p>Before you begin, you need to have implemented the Android search dialog for searches in your
+application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search
+Dialog</a>.</p>
+
+
+<h2 id="TheBasics">The Basics</h2>
+
+<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417"
+style="float:right;clear:right;" />
+
+<p>When the user selects a custom suggestions, the Search Manager will send a customized Intent to
+your searchable Activity. Whereas a normal search query will send an Intent with the {@link
+android.content.Intent#ACTION_SEARCH} action, you can instead define your custom suggestions to use
+{@link android.content.Intent#ACTION_VIEW} (or any other action), and also include additional data
+that's relevant to the selected suggestion. Continuing
+the dictionary example, when the user selects a suggestion, your application can immediately
+open the definition for that word, instead of searching the dictionary for matches.</p>
+
+<p>To provide custom suggestions, you need to do the following:</p>
+
+<ul>
+ <li>Implement a basic searchable Activity, as described in <a
+href="search-dialog.html">Using the Android Search Dialog</a>.</li>
+ <li>Build a table (such as in an {@link android.database.sqlite.SQLiteDatabase}) for your
+suggestions and format the table with required columns.</li>
+ <li>Create a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
+Provider</a> that has access to your suggestions table and declare the provider
+in your manifest.</li>
+ <li>Declare the type of {@link android.content.Intent} to be sent when the user selects a
+suggestion (including a custom action and custom data). </li>
+ <li>Modify the searchable configuration with information about the content provider.</li>
+</ul>
+
+<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work
+to display all search suggestions below the search dialog. All you need to do is provide a source
+from which the suggestions can be retrieved.</p>
+
+<p class="note"><strong>Note:</strong> If you're not familiar with creating Content
+Providers, please read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
+Providers</a> developer guide before you continue.</p>
+
+<p>When the Search Manager identifies that your Activity is searchable and also provides search
+suggestions, the following procedure will take place as soon as the user types into the Android
+search box:</p>
+
+<ul>
+ <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a
+query to the content provider that manages your suggestions.</li>
+ <li>Your content provider then returns a {@link android.database.Cursor} that points to all
+suggestions that are relevant to the search query text.</li>
+ <li>The Search Manager then displays the list of suggestions provided by the Cursor (as
+demonstrated in the screenshot to the right).</li>
+</ul>
+
+<p>At this point, the following may happen:</p>
+
+<ul>
+ <li>If the user types another key, or changes the query in any way, the above steps are repeated
+and the suggestion list is updated as appropriate. </li>
+ <li>If the user executes the search, the suggestions are ignored and the search is delivered
+to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH}
+Intent.</li>
+ <li>If the user selects a suggestion, an Intent is sent to your searchable Activity, carrying a
+custom action and custom data so that your application can open the suggested content.</li>
+</ul>
+
+
+
+<h2 id="CustomSearchableConfiguration">Modifying the searchable configuration</h2>
+
+<p>To add support for custom suggestions, add the {@code android:searchSuggestAuthority} attribute
+to the {@code <searchable>} element in your searchable configuration file. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider" >
+</searchable>
+</pre>
+
+<p>You may require some additional attributes, depending on the type of Intent you attach
+to each suggestion and how you want to format queries to your content provider. The other optional
+attributes are discussed in the relevant sections below.</p>
+
+
+<h2 id="CustomContentProvider">Creating a Content Provider</h2>
+
+<p>Creating a content provider for custom suggestions requires previous knowledge about Content
+Providers that's covered in the <a
+href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> developer
+guide. For the most part, a content provider for custom suggestions is the
+same as any other content provider. However, for each suggestion you provide, the respective row in
+the {@link android.database.Cursor} must include specific columns that the Search Manager
+understands.</p>
+
+<p>When the user starts typing into the search dialog, the Search Manager will query your Content
+Provider for suggestions by calling {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} each time
+a letter is typed. In your implementation of {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()}, your
+content provider must search your suggestion data and return a {@link
+android.database.Cursor} that points to the rows you determine to be good suggestions.</p>
+
+<p>The following two sections describe how the Search Manager will send requests to your Content
+Provider and how you can handle them, and define the columns that the Search Manager understands and
+expects to be provided in the {@link android.database.Cursor} returned with each query.</p>
+
+
+<h3 id="HandlingSuggestionQuery">Handling the suggestion query</h3>
+
+<p>When the Search Manager makes a request for suggestions from your content provider, it will call
+{@link android.content.ContentProvider#query(Uri,String[],String,String[],String)}. You must
+implement this method in your content provider so that it will search your suggestions and return a
+Cursor that contains the suggestions you deem relevant.</p>
+
+<p>Here's a summary of the parameters that the Search Manager will pass to your {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method
+(listed in order):</p>
+
+<dl>
+ <dt><code>uri</code></dt>
+ <dd>This will always be a content {@link android.net.Uri}, formatted as:
+<pre class="no-pretty-print">
+content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
+android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>
+</pre>
+<p>The default behavior is for Search Manager to pass this URI and append it with the query text.
+For example:</p>
+<pre class="no-pretty-print">
+content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
+android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>/puppies
+</pre>
+<p>The query text on the end will be encoded using URI encoding rules, so you may need to decode
+it.</p>
+<p>The <em>{@code optional.suggest.path}</em> portion is only included in the URI if you have set
+such a path in your searchable configuration file with the {@code android:searchSuggestPath}
+attribute. This is only needed if you use the same content provider for multiple searchable
+activities, in which case you need to disambiguate the source of the suggestion query.</p>
+<p>Note that {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY} is not the literal
+string provided in the URI, but a constant that you should use if you need to refer to this
+path.</p>
+ </dd>
+
+ <dt><code>projection</code></dt>
+ <dd>This is always null</dd>
+
+ <dt><code>selection</code></dt>
+ <dd>This is the value provided in the {@code android:searchSuggestSelection} attribute of
+your searchable configuration file, or null if you have not declared the {@code
+android:searchSuggestSelection} attribute. More about this below.</dd>
+
+ <dt><code>selectionArgs</code></dt>
+ <dd>This contains the search query as the first (and only) element of the array if you have
+declared the {@code android:searchSuggestSelection} attribute in your searchable configuration. If
+you have not declared {@code android:searchSuggestSelection}, then this parameter is null. More
+about this below.</dd>
+
+ <dt><code>sortOrder</code></dt>
+ <dd>This is always null</dd>
+</dl>
+
+<p>As you may have realized, there are two ways by which the Search Manager can send you the search
+query text. The default manner is for the query text to be included as the last path of the content
+URI that is passed in the {@code uri} parameter. However, if you include a selection value in your
+searchable configuration's {@code
+android:searchSuggestSelection} attribute, then the query text will instead be passed as the first
+element of the {@code selectionArgs} string array. Both options are summarized below.</p>
+
+
+<h4>Get the query in the Uri</h4>
+
+<p>By default, the query will be appended as the last segment of the {@code uri}
+parameter (a {@link android.net.Uri} object). To retrieve the query text in this case, simply use
+{@link android.net.Uri#getLastPathSegment()}. For example:</p>
+
+<pre>
+String query = uri.getLastPathSegment().toLowerCase();
+</pre>
+
+<p>This will return the last segment of the Uri, which is the query text entered in the search
+dialog.</p>
+
+
+
+<h4>Get the query in the selection arguments</h4>
+
+<p>Instead of using the URI, you may decide it makes more sense for your {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method to
+receive everything it needs to perform the look-up and you want the
+{@code selection} and {@code selectionArgs} parameters to carry values. In this case, you can
+add the {@code android:searchSuggestSelection} attribute to your searchable configuration with your
+SQLite selection string. In this selection string, you can include a question mark ("?") as
+a placeholder for the actual search query. This selection string will be delivered as the
+{@code selection} string parameter, and the query entered into the search dialog will be delivered
+as the first element in the {@code selectionArgs} string array parameter.</p>
+
+<p>For example, here's how you might form the {@code android:searchSuggestSelection} attribute to
+create a full-text search statement:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider"
+ android:searchSuggestIntentAction="android.Intent.action.VIEW"
+ android:searchSuggestSelection="word MATCH ?">
+</searchable>
+</pre>
+
+<p>When you then receive the {@code selection} and {@code selectionArgs} parameters in your {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) ContentProvider.query()}
+method, they will carry the selection ("word MATCH ?") and the query text, respectively. When
+these are passed to an SQLite {@link
+android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String,
+String) query} method, they will be synthesized together (replacing the question mark with the query
+text, wrapped in single-quotes). Note that if you chose this method and need to add any wildcards to
+your query text, you must do so by appending (and/or prefixing) them to the {@code selectionArgs}
+parameter, because this is the value that will be wrapped in quotes and inserted in place of the
+question mark.</p>
+
+<p class="note"><strong>Tip:</strong> If you don't want to define a selection clause in
+the {@code android:searchSuggestSelection} attribute, but would still like to receive the query
+text in the {@code selectionArgs} parameter, simply provide a non-null value for the {@code
+android:searchSuggestSelection} attribute. This will trigger the query to be passed in {@code
+selectionArgs} and you can ignore the {@code selection} parameter. In this way, you can instead
+define the actual selection clause at a lower level so that your content provider doesn't have to
+handle it.</p>
+
+
+
+<h3 id="SuggestionTable">Building a suggestion table</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Creating a Cursor on the fly</h2>
+<p>If your search suggestions are not stored in a table format using the columns required by the
+Search Manager, then you can search your suggestion data for matches and then format them
+into the necessary table on the fly. To do so, create a {@link android.database.MatrixCursor} using
+the required column names and then add a row for each suggestion using {@link
+android.database.MatrixCursor#addRow(Object[])}. Return the final product from your Content
+Provider's {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method.</p>
+</div>
+</div>
+
+<p>When you return suggestions to the Search Manager with a {@link android.database.Cursor}, the
+Search Manager expects there to be specific columns in each row. So, regardless of whether you
+decide to store
+your suggestion data in an SQLite database on the device, a database on a web server, or another
+format on the device or web, you must format the suggestions as rows in a table and
+present them with a {@link android.database.Cursor}. There are several columns that the Search
+Manager will understand, but only two are required:</p>
+
+<dl>
+ <dt>{@link android.provider.BaseColumns#_ID}</dt>
+ <dd>This is the unique row ID for each suggestion. The search dialog requires this in order
+to present the suggestions in a ListView.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</dt>
+ <dd>This is the line of text that will be presented to the user as a suggestion.</dd>
+</dl>
+
+<p>The following columns are all optional (and most will be discussed further in the following
+sections, so you may want to skip this list for now):</p>
+
+<dl>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_2}</dt>
+ <dd>If your Cursor includes this column, then all suggestions will be provided in a two-line
+format. The data in this column will be displayed as a second, smaller line of text below the
+primary suggestion text. It can be null or empty to indicate no secondary text.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_1}</dt>
+ <dd>If your Cursor includes this column, then all suggestions will be provided in an
+icon-plus-text format with the icon on the left side. This value should be a reference to the
+icon. It can be null or zero to indicate no icon in this row.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_2}</dt>
+ <dd>If your Cursor includes this column, then all suggestions will be provided in an
+icon-plus-text format with the icon on the right side. This value should be a reference to the
+icon. It can be null or zero to indicate no icon in this row.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}</dt>
+ <dd>If this column exists and this element exists at the given row, this is the action that will
+be used when forming the suggestion's Intent . If the element is not provided, the action will be
+taken from the {@code android:searchSuggestIntentAction} field in your searchable configuration. At
+least one of these
+must be present for the suggestion to generate an Intent. Note: If your action is the same for all
+suggestions, it is more efficient to specify the action using {@code
+android:searchSuggestIntentAction} and omit this column from the Cursor .</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}</dt>
+ <dd>If this column exists and this element exists at the given row, this is the data that will be
+used when forming the suggestion's Intent. If the element is not provided, the data will be taken
+from the {@code android:searchSuggestIntentData} field in your searchable configuration. If neither
+source is provided,
+the Intent's data field will be null. Note: If your data is the same for all suggestions, or can be
+described using a constant part and a specific ID, it is more efficient to specify it using {@code
+android:searchSuggestIntentData} and omit this column from the Cursor .
+</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}</dt>
+ <dd>If this column exists and this element exists at the given row, then "/" and this value will
+be appended to the data field in the Intent. This should only be used if the data field specified
+by the {@code android:searchSuggestIntentData} attribute in the searchable configuration has already
+been set to an appropriate base string.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}</dt>
+ <dd>If this column exists and this element exists at a given row, this is the <em>extra</em> data
+that will be used when forming the suggestion's Intent. If not provided, the Intent's extra data
+field will be
+null. This column allows suggestions to provide additional arbitrary data which will be
+included as an extra in the Intent's {@link android.app.SearchManager#EXTRA_DATA_KEY} key.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_QUERY}</dt>
+ <dd>If this column exists and this element exists at the given row, this is the data that will be
+used when forming the suggestion's query, included as an extra in the Intent's {@link
+android.app.SearchManager#QUERY} key. Required if suggestion's action is {@link
+android.content.Intent#ACTION_SEARCH}, optional otherwise.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID}</dt>
+ <dd>Only used when providing suggestions for Quick Search Box. This column is used to indicate
+whether a search suggestion should be stored as a
+shortcut, and whether it should be validated. Shortcuts are usually formed when the user clicks a
+suggestion from Quick Search Box. If missing, the result will be stored as a shortcut and never
+refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result will
+not be stored as a shortcut.
+Otherwise, the shortcut id will be used to check back for for an up to date suggestion using
+{@link android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}.</dd>
+ <dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</dt>
+ <dd>Only used when providing suggestions for Quick Search Box. This column is used to specify that
+a spinner should be shown instead of an icon from {@link
+android.app.SearchManager#SUGGEST_COLUMN_ICON_2}
+while the shortcut of this suggestion is being refreshed in Quick Search Box.</dd>
+</dl>
+
+<p>Again, most of these columns will be discussed in the relevant sections below, so don't worry if
+they don't make sense to you now.</p>
+
+
+
+<h2 id="IntentForSuggestions">Declaring an Intent for suggestions</h2>
+
+<p>When the user selects a suggestion from the list that appears below the search
+dialog (instead of performing a search), the Search Manager will send
+a custom {@link android.content.Intent} to your searchable Activity. You must define both the
+<em>action</em> and <em>data</em> for the Intent.</p>
+
+
+<h3 id="IntentAction">Declaring the Intent action</h3>
+
+<p>The most common Intent action for a custom suggestion is {@link
+android.content.Intent#ACTION_VIEW}, which is appropriate when
+you want to open something, like the definition for a word, a person's contact information, or a web
+page. However, the Intent action can be whatever you want and can even be different for each
+suggestion.</p>
+
+<p>To declare an Intent action that will be the same for all suggestions, define the action in
+the {@code android:searchSuggestIntentAction} attribute of your searchable configuration file. For
+example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestIntentAction="android.Intent.action.VIEW" >
+</searchable>
+</pre>
+
+<p>If you want to declare an Intent action that's unique for each suggestion, add the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to
+your suggestions table and, for each suggestion, place in it the action to use (such as
+{@code "android.Intent.action.VIEW"}). </p>
+
+<p>You can also combine these two techniques. For instance, you can include the {@code
+android:searchSuggestIntentAction} attribute with an action to be used with all suggestions by
+default, then override this action for some suggestions by declaring a different action in the
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. If you do not include
+a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column, then the
+Intent provided in the {@code android:searchSuggestIntentAction} attribute will be used.</p>
+
+<p class="note"><strong>Note</strong>: If you do not include the
+{@code android:searchSuggestIntentAction} attribute in your searchable configuration, then you
+<em>must</em> include a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}
+column for every suggestion, or the Intent will fail.</p>
+
+
+<h3 id="IntentData">Declaring Intent data</h3>
+
+<p>When the user selects a suggestion, your searchable Activity will receive the Intent with the
+action you've defined (as discussed in the previous section), but the Intent must also carry
+data in order for your Activity to identify which suggestions was selected. Specifically,
+the data should be something unique for each suggestion, such as the row ID for the suggestion in
+your suggestions table. When the Intent is received,
+you can retrieve the attached data with {@link android.content.Intent#getData()} or {@link
+android.content.Intent#getDataString()}.</p>
+
+<p>There are two ways to define the data that is included with the Intent:</p>
+
+<ol type="a">
+ <li>Define the data for each suggestion inside the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column of your suggestions table.</li>
+ <li>Fragment a data URI into two pieces: the portion common to all suggestions and the portion
+unique to each suggestion. Place these parts into the {@code android:searchSuggestIntentData}
+attribute of the searchable configuration and the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your
+suggestions table, respectively.</li>
+</ol>
+
+<p>The first option is straight-forward. Simply provide all necessary data information for each
+Intent in the suggestions table by including the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with unique
+data for each row. The data from this column will be attached to the Intent exactly as it
+is found in this column. You can then retrieve it with with {@link android.content.Intent#getData()}
+or {@link android.content.Intent#getDataString()}.</p>
+
+<p class="note"><strong>Tip</strong>: It's usually easiest to use the table's row ID as the
+Intent data because it's always unique. And the easiest way to do that is by using the
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column name as an alias for the row ID
+column. See the <a
+href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary sample
+app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} is used to
+create a projection map of column names to aliases.</p>
+
+<p>The second option is to fragment your data URI into the common piece and the unique piece.
+Declare the piece of the URI that is common to all suggestions in the {@code
+android:searchSuggestIntentData} attribute of your searchable configuration. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestIntentAction="android.Intent.action.VIEW"
+ android:searchSuggestIntentData="content://my.package/datatable" >
+</searchable>
+</pre>
+
+<p>Now include the final path for each suggestion (the unique part) in the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}
+column of your suggestions table. When the user selects a suggestion, the Search Manager will take
+the string from {@code android:searchSuggestIntentData}, append a slash ("/") and then add the
+respective value from the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column to
+form a complete content URI. You can then retrieve the {@link android.net.Uri} with with {@link
+android.content.Intent#getData()}.</p>
+
+<h4>Add more data</h4>
+
+<p>If you need to express even more information with your Intent, you can add another table column,
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}, which can store additional
+information about the suggestion. The data saved in this column will be placed in {@link
+android.app.SearchManager#EXTRA_DATA_KEY} of the Intent's extra Bundle.</p>
+
+
+<h2 id="HandlingIntent">Handling the Intent</h2>
+
+<p>Now that your search dialog provides custom search suggestions with custom formatted Intents, you
+need your searchable Activity to handle these Intents as they are delivered once the user selects a
+suggestion. (This is, of course, in addition to handling the {@link
+android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.)
+Accepting the new Intent is rather self-explanatory, so we'll skip straight to an example:</p>
+
+<pre>
+Intent intent = getIntent();
+if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ // Handle the normal search query case
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ doSearch(query);
+} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ // Handle a suggestions click (because my suggestions all use ACTION_VIEW)
+ Uri data = intent.getData());
+ showResult(rowId);
+}
+</pre>
+
+<p>In this example, the Intent action is {@link
+android.content.Intent#ACTION_VIEW} and the data carries a complete URI pointing to the suggested
+item, as synthesized by the {@code android:searchSuggestIntentData} string and {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to a local
+method that will query the content provider for the item specified by the URI and show it.</p>
+
+
+
+<h2 id="RewritingQueryText">Rewriting the query text</h2>
+
+<p>If the user navigates through the suggestions list using the device directional controls, the
+text in the search dialog won't change, by default. However, you can temporarily rewrite the
+user's query text as it appears in the text box with
+a query that matches the currently selected suggestion. This enables the user to see what query is
+being suggested (if appropriate) and then select the search box and edit the query before
+dispatching it as a search.</p>
+
+<p>You can rewrite the query text in the following ways:</p>
+
+<ol type="a">
+ <li>Add the {@code android:searchMode} attribute to your searchable configuration with the
+"queryRewriteFromText" value. In this case, the content from the suggestion's {@link
+android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}
+column will be used to rewrite the query text.</li>
+ <li>Add the {@code android:searchMode} attribute to your searchable configuration with the
+"queryRewriteFromData" value. In this case, the content from the suggestion's
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column will be used to rewrite the
+query text. Note that this should only
+be used with Uri's or other data formats that are intended to be user-visible, such as HTTP URLs.
+Internal Uri schemes should not be used to rewrite the query in this way.</li>
+ <li>Provide a unique query text string in the {@link
+android.app.SearchManager#SUGGEST_COLUMN_QUERY} column of your suggestions table. If this column is
+present and contains a value for the current suggestion, it will be used to rewrite the query text
+(and override either of the previous implementations).</li>
+</ol>
+
+
+<h2 id="QSB">Exposing search suggestions to Quick Search Box</h2>
+
+<p>Once your application is configured to provide custom search suggestions, making them available
+to the globally-accessible Quick Search Box is as easy as modifying your searchable configuration to
+include {@code android:includeInGlobalSearch} as "true".</p>
+
+<p>The only scenario in which additional work will be required is if your content provider for
+custom suggestions requires a permission for read access. In which case, you need to add a special
+{@code <path-permission>} element for the provider to grant Quick Search Box read access to your
+content provider. For example:</p>
+
+<pre>
+<provider android:name="MySuggestionProvider"
+ android:authorities="my.package.authority"
+ android:readPermission="com.example.provider.READ_MY_DATA"
+ android:writePermission="com.example.provider.WRITE_MY_DATA">
+ <path-permission android:pathPrefix="/search_suggest_query"
+ android:readPermission="android.permission.GLOBAL_SEARCH" />
+</provider>
+</pre>
+
+<p>In this example, the provider restricts read and write access to the content. The
+{@code <path-permission>} element amends the restriction by granting read access to content
+inside the {@code "/search_suggest_query"} path prefix when the {@code
+"android.permission.GLOBAL_SEARCH"} permission exists. This grants access to Quick Search Box
+so that it may query your content provider for suggestions.</p>
+
+<p>Content providers that enforce no permissions are already available to the search
+infrastructure.</p>
+
+
+<h3 id="EnablingSuggestions">Enabling suggestions on a device</h3>
+
+<p>When your application is configured to provide suggestions in Quick Search Box, it is not
+actually enabled to provide suggestions in Quick Search Box, by default. It is the user's choice
+whether to include suggestions from your application in the Quick Search Box. To enable search
+suggestions from your application, the user must open "Searchable items" (in Settings > Search) and
+enable your application as a searchable item.</p>
+
+<p>Each application that is available to Quick Search Box has an entry in the Searchable items
+settings page. The entry includes the name of the application and a short description of what
+content can be searched from the application and made available for suggestions in Quick Search Box.
+To define the description text for your searchable application, add the {@code
+android:searchSettingsDescription} attribute to your searchable configuration. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestIntentAction="android.Intent.action.VIEW"
+ android:includeInGlobalSearch="true"
+ android:searchSettingsDescription="@string/search_description" >
+</searchable>
+</pre>
+
+<p>The string for {@code android:searchSettingsDescription} should be as concise as possible and
+state the content that is searchable. For example, "Artists, albums, and tracks" for a music
+application, or "Saved notes" for a notepad application. Providing this description is important so
+the user knows what kind of suggestions will be provided. This attribute should always be included
+when {@code android:includeInGlobalSearch} is "true".</p>
+
+<p>Remember that the user must visit this settings menu to enable search suggestions for your
+application before your search suggestions will appear in Quick Search Box. As such, if search is an
+important aspect of your application, then you may want to consider a way to message this to your
+users — perhaps with a note the first time they launch the app about how to enable search
+suggestions for Quick Search Box.</p>
+
+
+<h3 id="ManagingShortcuts">Managing Quick Search Box suggestion shortcuts</h3>
+
+<p>Suggestions that the user selects from Quick Search Box may be automatically made into shortcuts.
+These are suggestions that the Search Manager has copied from your content provider so it can
+quickly access the suggestion without the need to re-query your content provider. </p>
+
+<p>By default, this is enabled for all suggestions retrieved by Quick Search Box, but if your
+suggestion data may change over time, then you can request that the shortcuts be refreshed. For
+instance, if your suggestions refer to dynamic data, such as a contact's presence status, then you
+should request that the suggestion shortcuts be refreshed when shown to the user. To do so,
+include the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} in your suggestions table.
+Using this column, you can
+configure the shortcut behavior for each suggestion in the following ways:</p>
+
+<ol type="a">
+ <li>Have Quick Search Box re-query your content provider for a fresh version of the shortcutted
+suggestion.
+ <p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column
+and the suggestion will be
+re-queried for a fresh version of the suggestion each time the shortcut is displayed. The shortcut
+will be quickly displayed with whatever data was most recently available until the refresh query
+returns, after which the suggestion will be dynamically refreshed with the new information. The
+refresh query will be sent to your content provider with a URI path of {@link
+android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}
+(instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}). The Cursor you return should
+contain one suggestion using the
+same columns as the original suggestion, or be empty, indicating that the shortcut is no
+longer valid (in which case, the suggestion will disappear and the shortcut will be removed).</p>
+ <p>If a suggestion refers to data that could take longer to refresh, such as a network based
+refresh, you may also add the {@link
+android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} column to your suggestions
+table with a value
+of "true" in order to show a progress spinner for the right hand icon until the refresh is complete.
+(Any value other than "true" will not show the progress spinner.)</p></li>
+ <li>Prevent the suggestion from being copied into a shortcut at all.
+ <p>Provide a value of {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT} in the
+{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column. In
+this case, the suggestion will never be copied into a shortcut. This should only be necessary if you
+absolutely do not want the previously copied suggestion to appear at all. (Recall that if you
+provide a normal value for the column then the suggestion shortcut will appear only until the
+refresh query returns.)</p></li>
+ <li>Allow the default shortcut behavior to apply.
+ <p>Simply leave the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} empty for each
+suggestion that will not change and can be saved as a shortcut.</p></li>
+</ol>
+
+<p>Of course, if none of your suggestions will ever change, then you do not need the
+{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column at all.</p>
+
+<p class="note"><strong>Note</strong>: Quick Search Box will ultimately decide whether to shortcut
+your app's suggestions, considering these values as a strong request from your application.</p>
+
+
+<h3 id="AboutRanking">About Quick Search Box suggestion ranking</h3>
+
+<p>Once your application's search results are made available to Quick Search Box, how they surface
+to the user for a particular query will be determined as appropriate by Quick Search Box ranking.
+This may depend on how many other apps have results for that query, and how often the user has
+selected on your results compared to those of the other apps. There is no guarantee about how
+ranking will occur, or whether your app's suggestions will show at all for a given query. In
+general, you can expect that providing quality results will increase the likelihood that your app's
+suggestions are provided in a prominent position, and apps that provide lower quality suggestions
+will be more likely to be ranked lower and/or not displayed.</p>
+
+<div class="special">
+<p>See the <a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary sample app</a> for a complete demonstration of custom search suggestions.</p>
+</div>
+
diff --git a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
new file mode 100644
index 0000000..37e0e82
--- /dev/null
+++ b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
@@ -0,0 +1,225 @@
+page.title=Adding Recent Query Suggestions
+parent.title=Search
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.provider.SearchRecentSuggestions}</li>
+<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
+</ol>
+<h2>In this document</h2>
+<ol>
+<li><a href="#TheBasics">The Basics</a></li>
+<li><a href="#RecentQuerySearchableConfiguration">Modifying the searchable
+configuration</a></li>
+<li><a href="#RecentQueryContentProvider">Creating a Content Provider</a></li>
+<li><a href="#SavingQueries">Saving queries</a></li>
+<li><a href="#ClearingSuggestionData">Clearing the suggestion data</a></li>
+</ol>
+<h2>See also</h2>
+<ol>
+<li><a href="searchable-config.html">Searchable Configuration</a></li>
+</ol>
+</div>
+</div>
+
+<p>The Android search framework provides the ability for your application to
+provide suggestions while the user types into the Android search dialog. In this guide, you'll learn
+how to create recent query suggestions. These are suggestions based
+on queries previously entered by the user. So, if the user previously searched for "puppies" then it
+will appear as a suggestion as they begin typing the same string of text. The screenshot below
+shows an example of recent query suggestions.</p>
+
+<p>Before you begin, you need to have implemented the Android search dialog for searches in your
+application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search
+Dialog</a>.</p>
+
+
+<h2 id="TheBasics">The Basics</h2>
+
+<img src="{@docRoot}images/search/search-suggest-recent-queries.png" alt="" height="417"
+style="float:right;clear:right;" />
+
+<p>Recent query suggestions are simply saved searches. When the user selects one of
+the suggestions, your searchable Activity will receive a normal {@link
+android.content.Intent#ACTION_SEARCH} Intent with the suggestion as the search query, which your
+searchable Activity will already handle.</p>
+
+<p>To provide recent queries suggestions, you need to:</p>
+
+<ul>
+ <li>Implement a basic searchable Activity, as documented in <a
+href="{@docRoot}guide/topics/search/search-dialog.html">Using the Android Search Dialog</a>.</li>
+ <li>Create a content provider that extends {@link
+android.content.SearchRecentSuggestionsProvider} and declare it in your application manifest.</li>
+ <li>Modify the searchable configuration with information about the content provider.</li>
+ <li>Save queries to your content provider each time a search is made.</li>
+</ul>
+
+<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work
+to display all search suggestions below the search dialog. All you need to do is provide a source
+from which the suggestions can be retrieved.</p>
+
+<p>When the Search Manager identifies that your Activity is searchable and also provides search
+suggestions, the following procedure will take place as soon as the user types into the Android
+search box:</p>
+
+<ul>
+ <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a
+query to the content provider that manages your suggestions.</li>
+ <li>Your content provider then returns a {@link android.database.Cursor} that points to all
+suggestions that are relevant to the search query text.</li>
+ <li>The Search Manager then displays the list of suggestions provided by the Cursor (as
+demonstrated in the screenshot to the right).</li>
+</ul>
+
+<p>At this point, the following may happen:</p>
+
+<ul>
+ <li>If the user types another key, or changes the query in any way, the above steps are repeated
+and the suggestion list is updated as appropriate.</li>
+ <li>If the user executes the search, the suggestions are ignored and the search is delivered
+to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH}
+Intent.</li>
+ <li>If the user selects a suggestion, a normal
+{@link android.content.Intent#ACTION_SEARCH} Intent is triggered, using the suggested text as the
+query.</li>
+</ul>
+
+<p>As you'll soon discover, the {@link android.content.SearchRecentSuggestionsProvider} class that
+you'll extend for your content provider will automatically do the work described above, so there's
+actually very little code to write.</p>
+
+
+<h2 id="RecentQuerySearchableConfiguration">Modifying the searchable configuration</h2>
+
+<p>First, you need to add the {@code android:searchSuggestAuthority} and
+{@code android:searchSuggestSelection} attributes to the {@code <searchable>} element in your
+searchable configuration file. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestSelection=" ?" >
+</searchable>
+</pre>
+
+<p>The value for {@code android:searchSuggestAuthority} should be a fully-qualified name for
+your content provider: your application package name followed by the name of your content provider.
+This string must match the authority used in the content provider (discussed in the next section).
+</p>
+
+<p>The value for {@code android:searchSuggestSelection} must be a single question-mark, preceded by
+a space (" ?"), which is simply a placeholder for the SQLite selection argument (which will be
+automatically replaced by the query text entered by the user).</p>
+
+
+<h2 id="RecentQueryContentProvider">Creating a Content Provider</h2>
+
+<p>The content provider that you need for recent query suggestions must be an implementation
+of {@link android.content.SearchRecentSuggestionsProvider}. This class does practically everything
+for you. All you have to do is write a class constructor that executes one line of code.</p>
+
+<p>For example, here's a complete implementation of a content provider for recent query
+suggestions:</p>
+
+<pre>
+public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
+ public final static String AUTHORITY = "my.package.MySuggestionProvider";
+ public final static int MODE = DATABASE_MODE_QUERIES;
+
+ public MySuggestionProvider() {
+ setupSuggestions(AUTHORITY, MODE);
+ }
+}
+</pre>
+
+<p>The call to {@link android.content.SearchRecentSuggestionsProvider#setupSuggestions(String,int)}
+passes the name of the search authority (matching the one in the searchable configuration) and a
+database mode. The database mode must include {@link
+android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_QUERIES} and can optionally include
+{@link
+android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, which will add another column
+to the suggestions table that allows you to provide a second line of text with each suggestion. For
+example:</p>
+<pre>
+public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;
+</pre>
+
+<p>In the following section, you'll see how to save both lines of text.</p>
+
+<p>Now simply declare the content provider in your application manifest with the same authority
+string used in the class (and in the searchable configuration). For example:</p>
+
+<pre>
+<application>
+ <provider android:name=".MySuggestionProvider"
+ android:authorities="my.package.authority" />
+ ...
+</application>
+</pre>
+
+
+<h2 id="SavingQueries">Saving queries</h2>
+
+<p>In order to populate your collection of recent queries, you need to add each query
+received by your searchable Activity to the content provider you've just built. To do this, create
+an instance of {@link
+android.provider.SearchRecentSuggestions} and call {@link
+android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} each time your searchable
+Activity receives a query. For example, here's how you can save the query during your
+Activity's {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
+
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Intent Intent = getIntent();
+
+ if (Intent.ACTION_SEARCH.equals(Intent .getAction())) {
+ String query = Intent .getStringExtra(SearchManager.QUERY);
+ SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
+ MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);
+ suggestions.saveRecentQuery(query, null);
+ }
+}
+</pre>
+
+<p>Notice that the {@link android.content.SearchRecentSuggestionsProvider} constructor requires the
+same authority and database mode declared by your content provider.</p>
+
+<p>The {@link android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} method takes
+the search query string as the first parameter and, optionally, a second string to include as the
+second line of the suggestion. The second parameter is only used if you've enabled two-line mode
+for the search suggestions with {@link
+android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}. If you have enabled
+two-line mode, then the query text will be matched against this second line as well.</p>
+
+<p>That's all that's needed to build a recent queries suggestion provider. However, there's one
+other important thing to do: provide the ability for the user to clear this search history.</p>
+
+
+<h2 id="ClearingSuggestionData">Clearing the suggestion data</h2>
+
+<p>To protect the user's privacy, you should always provide a way for the user to clear the recent
+query suggestions. To clear the recent queries, simply call {@link
+android.provider.SearchRecentSuggestions#clearHistory()}. For example:</p>
+
+<pre>
+SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
+ HelloSuggestionProvider.AUTHORITY, HelloSuggestionProvider.MODE);
+suggestions.clearHistory();
+</pre>
+
+<p>Simply execute this from your choice of a "Clear Search History" menu item,
+preference item, or button. You should also provide a confirmation dialog when this is pressed, to
+verify that the user wants to delete their search history.</p>
+
diff --git a/docs/html/guide/topics/search/index.jd b/docs/html/guide/topics/search/index.jd
new file mode 100644
index 0000000..b2252bb
--- /dev/null
+++ b/docs/html/guide/topics/search/index.jd
@@ -0,0 +1,111 @@
+page.title=Search
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Topics</h2>
+<ol>
+<li><a href="search-dialog.html">Using the Android Search Dialog</a></li>
+<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
+<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
+</ol>
+<h2>Reference</h2>
+<ol>
+<li><a href="searchable-config.html">Searchable Configuration</a></li>
+</ol>
+<h2>See also</h2>
+<ol>
+<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary sample app</a></li>
+</ol>
+</div>
+</div>
+
+
+<p>The ability to search is considered to be a core user feature on Android. The user should be able
+to search any data that is available to them, whether the content is located on the device or the
+Internet. This experience should be seamless and consistent across the entire
+system, which is why Android provides a simple search framework to help you provide users with
+a familiar search dialog and a great search experience.</p>
+
+<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417"
+style="float:right;clear:right;" />
+
+<p>Android's search framework provides a user interface in which the user can perform a search and
+an interaction layer that communicates with your application. This way, you don't have to build
+a search box that the user must find in order to begin a search. Instead,
+a custom search dialog will appear at the top of the screen at the user's command.
+The search framework will manage the search dialog and when the user executes their search, the
+search framework will pass the query text to your application so that your application can begin a
+search. The screenshot to the right shows an example of the search dialog (using
+search suggestions).</p>
+
+<p>Once your application is set up to use the search dialog, you can:</p>
+
+<ul>
+<li>Customize some of the search dialog characteristics</li>
+<li>Enable voice search</li>
+<li>Provide search suggestions based on recent user queries</li>
+<li>Provide search suggestions that match actual results in your application data</li>
+<li>Offer your application's search suggestions in the system-wide Quick Search Box</li>
+</ul>
+
+<p>The following documents will teach you how to use the search dialog in
+your application:</p>
+
+<dl>
+ <dt><strong><a href="search-dialog.html">Using the Android Search Dialog</a></strong></dt>
+ <dd>How to set up your application to use the search dialog for searches. </dd>
+ <dt><strong><a href="adding-recent-query-suggestions.html">Adding Recent Query
+Suggestions</a></strong></dt>
+ <dd>How to show suggestions based on queries previously used in the search dialog.</dd>
+ <dt><strong><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></strong></dt>
+ <dd>How to show suggestions based on custom data from your application and offer your suggestions
+in the system-wide Quick Search Box.</dd>
+</dl>
+
+<p>Also, the <strong><a href="searchable-config.html">Searchable Configuration</a></strong> document
+provides a reference for the searchable configuration file (though the above
+documents also discuss the configuration file in terms of specific behaviors).</p>
+
+<p class="note"><strong>Note</strong>: The search framework does <em>not</em> provide APIs to
+perform searches on your data. Performing actual searches is a task that you must accomplish
+using APIs appropriate for your data, such as those in {@link android.database.sqlite}
+if your data is in an SQLite database.</p>
+
+
+<h2>Protecting User Privacy</h2>
+
+<p>When you implement search in your application, you should take steps to protect the user's
+privacy whenever possible. Many users consider their activities on the phone, including searches, to
+be private information. To protect the user's privacy, you should abide by the following
+principles:</p>
+
+<ul>
+<li><strong>Don't send personal information to servers, and if you do, don't log it.</strong>
+<p>"Personal information" is information that can personally identify your users, such as their
+name, email address, billing information, or other data which can be reasonably linked to such
+information. If
+your application implements search with the assistance of a server, try to avoid sending personal
+information along with the search queries. For example, if you are searching for businesses near a
+zip code,
+you don't need to send the user ID as well — send only the zip code to the server. If you must
+send the personal information, you should take steps to avoid logging it. If you must log it, you
+should protect that data very carefully and erase it as soon as possible.</p>
+</li>
+<li><strong>Provide the user with a way to clear their search history.</strong>
+<p>The search framework helps your application provide context-specific suggestions while they type.
+Sometimes these
+suggestions are based on previous searches, or other actions taken by the user in an earlier
+session. A user may not wish for previous searches to be revealed to other users, for instance if
+they share their phone with a friend. If your application provides suggestions that can reveal
+previous activities, you should implement a "Clear History" menu item, preference, or button. If you
+are
+using {@link android.provider.SearchRecentSuggestions}, you can simply call its {@link
+android.provider.SearchRecentSuggestions#clearHistory()} method. If you are implementing custom
+suggestions, you'll need to provide a
+similar "clear history" method in your provider that can be invoked by the user.</p>
+</li>
+</ul>
+
+
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
new file mode 100644
index 0000000..718668c
--- /dev/null
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -0,0 +1,542 @@
+page.title=Using the Android Search Dialog
+parent.title=Search
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.app.SearchManager}</li>
+</ol>
+<h2>In this document</h2>
+<ol>
+<li><a href="#TheBasics">The Basics</a></li>
+<li><a href="#SearchableConfiguration">Creating a Searchable Configuration</a></li>
+<li><a href="#SearchableActivity">Creating a Searchable Activity</a>
+ <ol>
+ <li><a href="#DeclaringSearchableActivity">Declaring a searchable Activity</a></li>
+ <li><a href="#PerformingSearch">Performing a search</a></li>
+ </ol>
+</li>
+<li><a href="#InvokingTheSearchDialog">Invoking the Search Dialog</a>
+ <ol>
+ <li><a href="#LifeCycle">The impact of the search dialog on your Activity life-cycle</a></li>
+ </ol>
+</li>
+<li><a href="#SearchContextData">Passing Search Context Data</a></li>
+<li><a href="#VoiceSearch">Adding Voice Search</a></li>
+</ol>
+<h2>See also</h2>
+<ol>
+<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
+<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
+<li><a href="searchable-config.html">Searchable Configuration</a></li>
+<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary App</a></li>
+</ol>
+</div>
+</div>
+
+<p>When you want to provide search in your application, the last thing you should have to worry
+about is where to put your search box. By using the Android search framework, your application will
+reveal a custom search dialog whenever the user requests it. At the
+press of a dedicated search key or an API call from your application, the search dialog will
+appear at the top of the screen and will automatically show your application icon. An example is
+shown in the screenshot below.</p>
+
+<p>This guide will teach you how to set up your application to provide search in a custom search
+dialog. In doing so, you will provide a standardized search experience and be able to add
+features like voice search and search suggestions.</p>
+
+
+<h2 id="TheBasics">The Basics</h2>
+
+<img src="{@docRoot}images/search/search-ui.png" alt="" height="417"
+style="float:right;clear:right;" />
+
+<p>The Android search framework will manage the search dialog on your behalf; you never need
+to draw it or worry about where it is, and your current Activity will not be
+interrupted. The {@link android.app.SearchManager} is the component that does this work for
+you (hereafter, referred to as "the Search Manager"). It manages the life of the Android search
+dialog and will send your application the search query when executed by the user.</p>
+
+<p>When the user executes a search, the Search Manager will use a specially-formed Intent to pass
+the search query to the Activity that you've declared to handle searches. Essentially, all you
+need is an Activity that receives this Intent, performs the search, and presents the results.
+Specifically, what you need is the following:</p>
+
+<dl>
+ <dt>A searchable configuration</dt>
+ <dd>This is an XML file that configures the search dialog and includes settings for
+features such as the hint text shown in text box and settings voice search and search
+suggestion.</dd>
+ <dt>A searchable Activity</dt>
+ <dd>This is the {@link android.app.Activity} that receives the search query then
+searches your data and displays the search results.</dd>
+ <dt>A mechanism by which the user can invoke search</dt>
+ <dd>By default, the device search key (if available) will invoke the search dialog once
+you've configured a searchable Activity. However, you should always provide another means by
+which the user can invoke a search, such as with a search button in the Options Menu or elsewhere in
+the Activity UI, because not all devices provide a dedicated search key.</dd>
+</dl>
+
+
+<h2 id="SearchableConfiguration">Creating a Searchable Configuration</h2>
+
+<p>The searchable configuration is an XML file that defines several settings for the Android search
+dialog in your application. This file is traditionally named {@code searchable.xml} and must be
+saved in the {@code res/xml/} project directory.</p>
+
+<p>The file must consist of the {@code <searchable>} element as the root node and specify one
+or more attributes that configure your search dialog. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label" >
+</searchable>
+</pre>
+
+<p>This is the minimum configuration required in order to provide the search dialog. The {@code
+android:label} attribute is the only required attribute and points to a string resource, which
+should normally be the same as the application. (Although it's required, this
+label isn't actually shown to the user until you enable suggestions for Quick Search Box.)</p>
+
+<p>There are several other attributes accepted by the {@code <searchable>} element. Most of
+which apply only when configuring features such as search suggestions and voice
+search. However, we recommend that you always include the {@code android:hint} attribute, which
+specifies a string resource for the text to display in the search dialog's text box before the user
+enters their query—it provides important clues to the user about what they can search. </p>
+
+<p class="note"><strong>Tip:</strong> For consistency among other
+Android applications, you should format the string for {@code android:hint} as "Search
+<em><content-or-product></em>". For example, "Search songs and artists" or "Search
+YouTube".</p>
+
+<p>Next, you'll hook this configuration into your application.</p>
+
+
+<h2 id="SearchableActivity">Creating a Searchable Activity</h2>
+
+<p>When the user executes a search from the search dialog, the Search Manager will send
+your searchable {@link android.app.Activity} the search query with the {@link
+android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}. Your searchable Activity will
+then search your data and present the results.</p>
+
+
+<h3 id="DeclaringSearchableActivity">Declaring a searchable Activity</h3>
+
+<p>If you don't have one already, create an {@link android.app.Activity} that will be used to
+perform searches, then declare it to
+accept the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent} and apply the
+searchable configuration. To do so, you need to add an {@code
+<intent-filter>} element and a {@code <meta-data>} element to the
+appropriate {@code <activity>} element in your manifest file. For example:</p>
+
+<pre>
+<application ... >
+ <activity android:name=".MySearchableActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ </intent-filter>
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"/>
+ </activity>
+ ...
+</application>
+</pre>
+
+<p>The {@code android:name} attribute in the {@code <meta-data>} element must be defined with
+{@code "android.app.searchable"} and the {@code android:resource} attribute value must be a
+reference to the searchable configuration file saved in {@code res/xml} (in this example, it
+refers to the {@code res/xml/searchable.xml} file).</p>
+
+<p class="note">If you're wondering why the {@code
+<intent-filter>} does not include a {@code <category>} with the {@code DEFAULT}
+value, it's because the Intent that is delivered to this Activity when a search is executed will
+explicitly define this Activity as the component for the Intent (which the Search Manager knows
+from the searcahble meta-data declared for the Activity).</p>
+
+<p>Be aware that the search dialog will not be available from within every
+Activity of your application, by default. Rather, the search dialog will be presented to
+users only when they
+invoke search from a searchable context of your application. A searchable context is any Activity
+for which you have
+declared searchable meta-data in the manifest file. For example, the searchable Activity itself
+(declared in the manifest snippet above) is
+a searchable context because it contains searchable meta-data that defines the
+searchable configuration. Any other Activity in your application is not a searchable context, by
+default, and thus, will not reveal the search dialog. You probably do want the
+search dialog to be available from every Activity in your application, so this can be easily
+fixed.</p>
+
+<p>If you want all of your activities to provide the search dialog, add another {@code
+<meta-data>} element inside the {@code
+<application>} element. Use this element to declare the existing searchable Activity as the
+default searchable Activity. For example:</p>
+
+<pre>
+<application ... >
+ <activity android:name=".MySearchableActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ </intent-filter>
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"/>
+ </activity>
+ <activity android:name=".AnotherActivity" ... >
+ </activity>
+ <!-- this one declares the searchable Activity for the whole app -->
+ <meta-data android:name="android.app.default_searchable"
+ android:value=".MySearchableActivity" />
+ ...
+</application>
+</pre>
+
+<p>The {@code <meta-data>} element with the {@code android:name} attribute value of
+{@code "android.app.default_searchable"} specifies a default searchable Activity for the context in
+which it is placed (which, in this case, is the entire application). The searchable Activity to
+use is specified with the {@code android:value} attribute. All other activities in the
+application, such as {@code AnotherActivity}, are now considered a searchable context and can invoke
+the search dialog. When a search is executed, {@code MySearchableActivity} will
+be launched to handle the search query.</p>
+
+<p>Notice that this allows you to control which activities provide search at a more granular level.
+To specify only an individual Activity as a searchable context, simply place the {@code
+<meta-data>} with the {@code
+"android.app.default_searchable"} name inside the respective {@code <activity>}
+element (rather than inside the {@code <application>}). And, while it is uncommon, you can
+even create more than one searchable Activity and provide each one in different contexts of your
+application, either by declaring a different searchable Activity in each {@code <activity>}
+element, or declaring a default searchable Activity for the entire application and then overriding
+it with a different {@code <meta-data>} element inside certain activities.</p>
+
+
+<h3 id="PerformingSearch">Performing a search</h3>
+
+<p>Once your Activity is declared searchable, performing the actual search involves three steps:
+receiving the query, searching your data, and presenting the results.</p>
+
+<p>Traditionally, your search results should be presented in a {@link android.widget.ListView}
+(assuming that our results are text-based), so
+you may want your searchable Activity to extend {@link android.app.ListActivity}, which
+provides easy access to {@link android.widget.ListView} APIs. (See the <a
+href="{@docRoot}resources/tutorials/views/hello-listview.html">List View Tutorial</a> for a simple
+{@link android.app.ListActivity} sample.)</p>
+
+
+<h4 id="ReceivingTheQuery">Receiving the query</h4>
+
+<p>When a search is executed from the search dialog, your searchable Activity will be opened
+with the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}, which carries
+the search query in the
+{@link android.app.SearchManager#QUERY QUERY} extra. All you need to do is check for
+this Intent and extract the string. For example, here's how you can get the query when your
+Activity launches:</p>
+
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search);
+
+ Intent intent = getIntent();
+
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ doMySearch(query);
+ }
+}
+</pre>
+
+<p>The {@link android.app.SearchManager#QUERY QUERY} string is always included with
+the {@link android.content.Intent#ACTION_SEARCH} Intent. In this example, the query is
+retrieved and passed to a local {@code doMySearch()} method where the actual search operation
+is done.</p>
+
+
+<h4 id="SearchingYourData">Searching your data</h4>
+
+<p>The process of storing and searching your data is a process that's unique to your application.
+There are many ways that you might do this and discussing all options is beyond the scope of
+this document. This guide will not teach you how to store your data and search it; this
+is something you must carefully consider in terms of your needs and your data. However, here are
+some tips you may be able to apply:</p>
+
+ <ul>
+ <li>If your data is stored in a SQLite database on the device, performing a full-text search
+(using FTS3, rather than a LIKE query) can provide a more robust search across text data and can
+produce results many, many times faster. See <a href="http://sqlite.org/fts3.html">sqlite.org</a>
+for information about FTS3 and the {@link android.database.sqlite.SQLiteDatabase} class for
+information about SQLite on Android. Also look at the <a
+href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample
+application to see a complete SQLite implementation that performs searches with FTS3.</li>
+ <li>If your data is stored online, then the perceived search performance may be
+inhibited by the user's data connection. You may want to display a spinning progress wheel until
+your search returns. See {@link android.net} for a reference of network APIs and <a
+href="guide/topics/ui/dialogs.html#ProgressDialog">Creating a Progress Dialog</a> to see how
+you can display a progress wheel.</li>
+ </ul>
+
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>About Adapters</h2>
+<p>An Adapter will bind individual items from a set of data into individual {@link
+android.view.View} objects. When the Adapter
+is applied to a {@link android.widget.ListView}, the Views are injected as individual items of the
+list. {@link
+android.widget.Adapter} is simply an interface, so implementations such as {@link
+android.widget.CursorAdapter} (for binding data from a {@link android.database.Cursor}) are needed.
+If none of the existing implementations work for your data, then you should implement your own from
+{@link android.widget.BaseAdapter}. Install the SDK Samples package for API Level 4 to see a
+version of the Searchable Dictionary that creates a custom BaseAdapter.</p>
+</div>
+</div>
+
+<p>Regardless of where your data lives and how you search it, we recommend that you return search
+results to your searchable Activity with an {@link android.widget.Adapter}. This way, you can easily
+present all the search results in a {@link android.widget.ListView}. If your data comes from a
+SQLite database query, then you can easily apply your results to a {@link android.widget.ListView}
+using a {@link android.widget.CursorAdapter}. If your data comes in some other type of format, then
+you can create an extension of the {@link android.widget.BaseAdapter}.</p>
+
+<h4 id="PresentingTheResults">Presenting the results</h4>
+
+<p>Presenting your search results is mostly a UI detail and not something covered by the search
+framework APIs. However, a simple solution is to create your searchable Activity to extend {@link
+android.app.ListActivity} and then call {@link
+android.app.ListActivity#setListAdapter(ListAdapter)}, passing it an {@link
+android.widget.Adapter} that is bound to your data. This will automatically project all the
+results into the Activity {@link android.widget.ListView}.</p>
+
+<p>For more help presenting your results, see the {@link android.app.ListActivity}
+documentation.</p>
+
+<p>Also see the <a
+href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample
+application for an a complete demonstration of how to search an SQLite database and use an
+{@link android.widget.Adapter} to provide resuls in a {@link android.widget.ListView}.</p>
+
+
+<h2 id="InvokingTheSearchDialog">Invoking the Search Dialog</h2>
+
+<p>Once you have a searchable Activity in place, invoking the search dialog so the user can
+submit a
+query is easy. Many Android devices provide a dedicated search key and when it is pressed while the
+user is within a searchable context of your application, the search dialog will be revealed.
+However,
+you should never assume that a search key is available on the user's device and should always
+provide a search button in your UI that will invoke search.</p>
+
+<p>To invoke search from your Activity, simply call {@link
+android.app.Activity#onSearchRequested()}.</p>
+
+<p>For example, you should provide a menu item in your <a
+href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> or a button in your UI to
+invoke search with this method. For your convenience, this <a
+href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a> file includes icons for
+medium and high density screens, which you can use for your menu item or button (low density
+screens will automatically scale-down the hdpi image by one half). </p>
+
+<!-- ... maybe this should go into the Creating Menus document ....
+<p>If you chose to provide a shortcut key for the menu item, using {@link
+android.view.MenuItem#setAlphabeticShortcut(char)}, then SearchManager.MENU_KEY is the recommended
+key character, representing the default search key.</p>
+-->
+
+<p>You can also enable "type-to-search" functionality in your Activity by calling {@link
+android.app.Activity#setDefaultKeyMode(int) setDefaultKeyMode}({@link
+android.app.Activity#DEFAULT_KEYS_SEARCH_LOCAL}). When this is enabled and the user begins typing on
+the keyboard, search will automatically be
+invoked and the keystrokes will be inserted in the search dialog. Be sure to enable this mode
+during your Activity {@link android.app.Activity#onCreate(Bundle) onCreate()} method.</p>
+
+
+<h3 id="LifeCycle">The impact of the search dialog on your Activity life-cycle</h3>
+
+<p>The search dialog behaves like a {@link android.app.Dialog} that floats at the top of the
+screen. It
+does not cause any change in the Activity stack, so no life-cycle methods (such as {@link
+android.app.Activity#onPause()}) will
+be called. All that happens is your Activity loses input focus as it is given to the search dialog.
+</p>
+
+<p>If you want to be notified when search is invoked, simply override the {@link
+android.app.Activity#onSearchRequested()} method. When this is called, you can do any work you may
+want to do when your Activity looses input focus (such as pause animations). But unless you are
+<a href="#SearchContextData">Passing Search Context Data</a> (discussed above), you should always
+call the super class implementation. For example:</p>
+
+<pre>
+@Override
+public boolean onSearchRequested() {
+ pauseSomeStuff();
+ return super.onSearchRequested();
+}
+</pre>
+
+<p>If the user cancels search by pressing the device Back key, the Activity in which search was
+invoked will re-gain input focus. You can register to be notified when the search dialog is
+closed with {@link android.app.SearchManager#setOnDismissListener(SearchManager.OnDismissListener)}
+and/or {@link android.app.SearchManager#setOnCancelListener(SearchManager.OnCancelListener)}. You
+should normally only need to register the {@link android.app.SearchManager.OnDismissListener
+OnDismissListener}, because this is called every time that the search dialog is closed. The {@link
+android.app.SearchManager.OnCancelListener OnCancelListener} only pertains to events in which the
+user explicitly left the search dialog, so it is not called when a search is executed (in which
+case, the search dialog naturally disappears).</p>
+
+<p>If the current Activity is not the searchable Activity, then the normal Activity life-cycle
+events will be triggered once the user executes a search (the current Activity will receive {@link
+android.app.Activity#onPause()} and so forth, as
+described in <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application
+Fundamentals</a>). If, however, the current Activity is the searchable Activity, then one of two
+things will happen:</p>
+
+<ul>
+ <li>By default, the searchable Activity will receive the {@link
+android.content.Intent#ACTION_SEARCH} Intent with a call to {@link
+android.app.Activity#onCreate(Bundle) onCreate()} and a new instance of the
+Activity will be brought to the top of the stack. You'll now have two instances of your searchable
+Activity in the Activity stack (so pressing the Back key will go back to the previous instance of
+the searchable Activity, rather than exiting the searchable Activity).</li>
+ <li>On the other hand, if the Activity has set {@code android:launchMode} to "singleTop" then the
+searchable Activity will receive the {@link android.content.Intent#ACTION_SEARCH} Intent with a call
+to {@link android.app.Activity#onNewIntent(Intent)}, passing the new {@link
+android.content.Intent#ACTION_SEARCH} Intent here. For example, here's how you might want to handle
+this case:
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search);
+ handleIntent(getIntent());
+}
+
+@Override
+protected void onNewIntent(Intent intent) {
+ setIntent(intent);
+ handleIntent(intent);
+}
+
+private void handleIntent(Intent intent) {
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ doMySearch(query);
+ }
+}
+</pre>
+
+<p>Compared to the example code in the section about <a href="#PerfomingSearch">Performing a
+Search</a>, all the code to handle the
+search Intent has been moved outside the {@link android.app.Activity#onCreate(Bundle)
+onCreate()} method so it can also be executed from {@link android.app.Activity#onNewIntent(Intent)
+onNewIntent()}.
+It's important to note that when {@link android.app.Activity#onNewIntent(Intent)} is
+called, the Activity has not been restarted, so the {@link android.app.Activity#getIntent()} method
+will still return the Intent that was first received with {@link
+android.app.Activity#onCreate(Bundle) onCreate()}. This is why {@link
+android.app.Activity#setIntent(Intent)} is called inside {@link
+android.app.Activity#onNewIntent(Intent)} (just in case you call {@link
+android.app.Activity#getIntent()} at a later time).</p>
+
+</li>
+</ul>
+
+<p>This second scenario is normally ideal, because the chances are good that once a search is
+completed, the user will perform additional searches and it's a bad experience if your application
+piles multiple instances of the searchable Activity on the stack. So we recommend that you set your
+searchable Activity to "singleTop" launch mode in the application manifest. For example:</p>
+
+<pre>
+<activity android:name=".MySearchableActivity"
+ android:launchMode="singleTop" >
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ </intent-filter>
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"/>
+ </activity>
+</pre>
+
+
+<h2 id="SearchContextData">Passing Search Context Data</h2>
+
+<p>In order to refine your search criteria, you may want to provide some additional
+data to your searchable Activity when a search is executed. For instance, when you search your data,
+you may want to filter results based on more than just the search query text. In a simple
+case, you could just make your refinements inside the searchable Activity, for every search made.
+If, however, your
+search criteria may vary from one searchable context to another, then you can pass whatever data is
+necessary to refine your search in the {@link android.app.SearchManager#APP_DATA} Bundle, which is
+included in the {@link android.content.Intent#ACTION_SEARCH} Intent.</p>
+
+<p>To pass this kind of data to your searchable Activity, you need to override {@link
+android.app.Activity#onSearchRequested()} method for the Activity in which search will be invoked.
+For example:</p>
+
+<pre>
+@Override
+public boolean onSearchRequested() {
+ Bundle appData = new Bundle();
+ appData.putBoolean(MySearchableActivity.JARGON, true);
+ startSearch(null, false, appData, false);
+ return true;
+ }
+</pre>
+
+<p>Returning "true" indicates that you have successfully handled this callback event. Then in your
+searchable Activity, you can extract this data from the {@link
+android.app.SearchManager#APP_DATA} {@link android.os.Bundle} to refine the search. For example:</p>
+
+<pre>
+ Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
+ if (appData != null) {
+ boolean jargon = appData.getBoolean(MySearchableActivity.JARGON);
+ }
+</pre>
+
+<p class="caution"><strong>Note:</strong> You should never call the {@link
+android.app.Activity#startSearch(String,boolean,Bundle,boolean) startSearch()} method from outside
+the {@link android.app.Activity#onSearchRequested()} callback method. When you want to invoke the
+search dialog, always call {@link android.app.Activity#onSearchRequested()} so that custom
+implementations (such as the addition of {@code appData}, in the above example) can be accounted
+for.</p>
+
+
+<h2 id="VoiceSearch">Adding Voice Search</h2>
+
+<p>You can easily add voice search functionality to your search dialog by adding the {@code
+android:voiceSearchMode} attribute to your searchable configuration. This will add a voice search
+button in the search dialog that, when clicked, will launch a voice prompt. When the user
+has finished speaking, the transcribed search query will be sent to your searchable
+Activity.</p>
+
+<p>To enable voice search for your activity, add the {@code android:voiceSearchMode}
+attribute to your searchable configuration. For example:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/search_label"
+ android:hint="@string/search_hint"
+ android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" >
+</searchable>
+</pre>
+
+<p>The value {@code showVoiceSearchButton} is required to enable voice
+search, while the second value, {@code launchRecognizer}, specifies that the voice search button
+should launch a recognizer that returns the transcribed text to the searchable Activity. This is
+how most applications should declare this attribute.</p>
+
+<p>There are some additional attributes you can provide to specify the voice search behavior, such
+as the language to be expected and the maximum number of results to return. See the <a
+href="searchable-config.html">Searchable Configuration</a> for more information about the
+available attributes.</p>
+
+<p class="note"><strong>Note:</strong> Carefully consider whether voice search is appropriate for
+your application. All searches performed with the voice search button will be immediately sent to
+your searchable Activity without a chance for the user to review the transcribed query. Be sure to
+sufficiently test the voice recognition and ensure that it understands the types of queries that
+the user will submit inside your application.</p>
diff --git a/docs/html/guide/topics/search/searchable-config.jd b/docs/html/guide/topics/search/searchable-config.jd
new file mode 100644
index 0000000..71566de
--- /dev/null
+++ b/docs/html/guide/topics/search/searchable-config.jd
@@ -0,0 +1,361 @@
+page.title=Searchable Configuration
+parent.title=Search
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>See also</h2>
+<ol>
+ <li><a href="search-dialog.html">Using the Android Search Dialog</a></li>
+ <li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
+ <li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
+</ol>
+</div>
+</div>
+
+<p>In order to utilize the Android search framework and provide a custom search dialog, your
+application must provide a search
+configuration in the form of an XML resource. This document describes the search configuration XML
+in terms of its syntax and usage. For a more complete discussion about how to implement search
+features for your application, see the companion documents about <a
+href="index.html">Search</a>.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/xml/<em>filename</em>.xml</code><br/>
+The filename will be used as the resource ID.</dd>
+
+<dt>syntax:</dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#searchable-element">searchable</a> xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="<em>string resource</em>"
+ android:hint="<em>string resource</em>"
+ android:searchMode=["queryRewriteFromData" | "queryRewriteFromText"]
+ android:searchButtonText="<em>string resource</em>"
+ android:inputType="<em>{@link android.R.attr#inputType}</em>"
+ android:imeOptions="<em>{@link android.R.attr#imeOptions}</em>"
+ android:searchSuggestAuthority="<em>string</em>"
+ android:searchSuggestPath="<em>string</em>"
+ android:searchSuggestSelection="<em>string</em>"
+ android:searchSuggestIntentAction="<em>string</em>"
+ android:searchSuggestIntentData="<em>string</em>"
+ android:searchSuggestThreshold="<em>int</em>"
+ android:includeInGlobalSearch=["true" | "false"]
+ android:searchSettingsDescription="<em>string resource</em>"
+ android:queryAfterZeroResults=["true" | "false"]
+ android:voiceSearchMode=["showVoiceSearchButton" | "launchWebSearch" | "launchRecognizer"]
+ android:voiceLanguageModel=["free-form" | "web_search"]
+ android:voicePromptText="<em>string resource</em>"
+ android:voiceLanguage="<em>string</em>"
+ android:voiceMaxResults="<em>int</em>"
+ >
+ <<a href="#actionkey-element">actionkey</a>
+ android:keycode="<em>{@link android.view.KeyEvent KEYCODE}</em>"
+ android:queryActionMsg="<em>string</em>"
+ android:suggestActionMsg="<em>string</em>"
+ android:suggestActionMsgColumn="<em>string</em>" >
+</searchable>
+</pre>
+</dd>
+
+<dt>elements:</dt>
+<dd>
+<dl class="tag-list">
+ <dt id="searchable-element"><code><searchable></code></dt>
+ <dd>Defines all search configurations used with the search dialog.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:label</code></dt>
+ <dd><em>String resource</em>. <strong>Required</strong>. This is the name of your application.
+It should normally be the same as the name applied to the {@code android:label} attribute of your <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#label">{@code <activity>}</a> or
+<a href="{@docRoot}guide/topics/manifest/application-element.html#label">{@code
+<application>}</a> manifest element. This is only visible to the user when you set
+<code>android:includeInGlobalSearch</code> "true", in which case, this label is used to identify
+your application as a searchable item in the system's search settings.</dd>
+ <dt><code>android:hint</code></dt>
+ <dd><em>String resource</em>. The text to display in the search text field when no text has
+ been entered. This is recommended in order to provide a hint to the user about what
+content is searchable. For consistency among other Android applications, you should format the
+string for {@code android:hint} as "Search <em><content-or-product></em>". For example,
+"Search songs and artists" or "Search YouTube".</dd>
+ <dt><code>android:searchMode</code></dt>
+ <dd><em>Keyword</em>. Sets additional modes that control the search presentation.
+Specifically, the available modes define how the query text in the search dialog's text box
+should be rewritten when a suggestion is focused. The following mode values are accepted:
+ <table>
+ <tr><th>Value</th><th>Description</th></tr>
+ <tr>
+ <td><code>"queryRewriteFromData"</code></td>
+ <td>If set, this causes the suggestion column
+ {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} to be considered as the
+text for suggestion query
+ rewriting. This should only be used when the values in
+ {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} are suitable for user
+inspection and editing -
+ typically, HTTP/HTTPS Uri's.</td>
+ </tr>
+ <tr>
+ <td><code>"queryRewriteFromText"</code></td>
+ <td>If set, this causes the suggestion
+ column {@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1} to be considered as the
+text for suggestion query
+ rewriting. This should be used for suggestions in which no query
+ text is provided and the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}
+values are not suitable
+ for user inspection and editing.</td>
+ </tr>
+ </table>
+ <p>For more information, see the discussion about rewriting the query text in <a
+href="adding-custom-suggestions.html#RewritingQueryText">Adding Custom Suggestions</a>.</p>
+ </dd>
+ <dt><code>android:searchButtonText</code></dt>
+ <dd><em>String resource</em>. The text to display in the button that executes the search. By
+default, the button shows a search icon (a magnifying glass), which is ideal for
+internationalization.</dd>
+ <dt><code>android:inputType</code></dt>
+ <dd><em>Keyword</em>. Defines the type of input method (soft-keyboard) to use with the search
+dialog. For most searches, in which free form text is expected, this attribute is not needed and
+the default input method should be used. See {@link android.R.attr#inputType} for a list of suitable
+values for this attribute.</dd>
+ <dt><code>android:imeOptions</code></dt>
+ <dd><em>Keyword</em>. Supplies additional options for the input method.
+ For most searches, in which free form text is expected, this attribute is not needed,
+ and will default to "actionSearch" (provides the "search" button instead of a carriage
+return). See {@link android.R.attr#imeOptions} for a list of suitable values for this attribute.
+ </dd>
+ </dl>
+
+ <p>If you have defined a content provider to generate search suggestions, you need to
+ define additional attributes in order to configure communications with the Content
+ Provider. When providing search suggestions, you'll need some of the following
+ {@code <searchable>} attributes:</p><br/>
+
+ <dl class="atn-list">
+ <dt><code>android:searchSuggestAuthority</code></dt>
+ <dd><em>String</em>. <strong>Required to provide search suggestions</strong>.
+ This value must match the authority string provided in the {@code android:authorities}
+attribute of the {@code <provider>} element.</dd>
+ <dt><code>android:searchSuggestPath</code></dt>
+ <dd><em>String</em>. This path will be used as a portion of the suggestions
+ query {@link android.net.Uri}, after the prefix and authority, but before
+the standard suggestions path.
+ This is only required if you have a single content provider issuing different types
+ of suggestions (e.g. for different data types) and you need
+ a way to disambiguate the suggestions queries when they are received.</dd>
+ <dt><code>android:searchSuggestSelection</code></dt>
+ <dd><em>String</em>. This value will be passed into your
+ query function as the {@code selection} parameter. Typically this will be a WHERE clause
+for your database, and should contain a single question mark, which is a place-holder for the
+actual query string that has been typed by the user. However, you can also use any non-null
+value to simply trigger the delivery of the query text via the {@code
+selectionArgs} parameter (and then ignore the {@code selection} parameter).</dd>
+ <dt><code>android:searchSuggestIntentAction</code></dt>
+ <dd><em>String</em>. The default Intent action to be used when a user
+ clicks on a search suggestion (such as {@code "android.intent.action.VIEW"}).
+ If not overridden by the selected suggestion (via the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column), this value will
+ be placed in the action field of the {@link android.content.Intent} when the
+ user clicks a suggestion.</dd>
+ <dt><code>android:searchSuggestIntentData</code></dt>
+ <dd><em>String</em>. The default Intent data to be used when a user
+ clicks on a search suggestion.
+ If not overridden by the selected suggestion (via the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value will be
+ placed in the data field of the {@link android.content.Intent} when the user clicks
+ a suggestion.</dd>
+ <dt><code>android:searchSuggestThreshold</code></dt>
+ <dd><em>Integer</em>. The minimum number of characters needed to
+ trigger a suggestion look-up. Only guarantees that a source will not be
+ queried for anything shorter than the threshold. The default value is 0.</dd>
+ </dl>
+
+ <p>For more information about the above attributes for search suggestions, see the guides for
+ <a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a> and
+ <a href="adding-custom-suggestions.html">Adding Custom Suggestions</a>.</p>
+
+ <p>Beyond providing search suggestions while using your application's search dialog, you
+ can also configure your search suggestions to be made available to Quick Search Box,
+ which will allow users so receive search suggestions from your application content from outside
+ your application. When providing search suggestions to Quick Search Box, you'll need some of the
+ following {@code <searchable>} attributes:</p><br/>
+
+ <dl class="atn-list">
+ <dt><code>android:includeInGlobalSearch</code></dt>
+ <dd><em>Boolean</em>. <strong>Required to provide search suggestions in
+ Quick Search Box</strong>. "true" if you want your suggestions to be
+ included in the globally accessible Quick Search Box. Note that the user must
+ still enable your application as a searchable item in the system search settings in order
+ for your suggestions to appear in Quick Search Box.</dd>
+ <dt><code>android:searchSettingsDescription</code></dt>
+ <dd><em>String</em>. Provides a brief description of the search suggestions that you provide
+to Quick Search Box, which will be displayed in the searchable items entry for your application.
+Your description should concisely describe the content that is searchable. For example, "Artists,
+albums, and tracks" for a music application, or "Saved notes" for a notepad application.</dd>
+ <dt><code>android:queryAfterZeroResults</code></dt>
+ <dd><em>Boolean</em>. "true" if you want your content provider to be invoked for
+ supersets of queries that have returned zero results for in the past. For example, if a
+ source returned zero results for "bo", it would be ignored for "bob". If "false",
+ this source will only be ignored for a single session; the next time the search dialog
+ is invoked, all sources will be queried. The default value is false.</dd>
+ </dl>
+
+ <p>To enable voice search for your search dialog, you'll need some of the
+ following {@code <searchable>} attributes:</p><br/>
+
+ <dl class="atn-list">
+ <dt><code>android:voiceSearchMode</code></dt>
+ <dd><em>Keyword</em>. <strong>Required to provide voice search capabilities</strong>.
+ Enables voice search for the search dialog, with a specific mode for voice search.
+ (Voice search may not be provided by the device, in which case these flags will
+ have no effect.) The following mode values are accepted:
+ <table>
+ <tr><th>Value</th><th>Description</th></tr>
+ <tr>
+ <td><code>"showVoiceSearchButton"</code></td>
+ <td>Display a voice search button. This only
+ takes effect if voice search is available on the device. If set, then either
+ {@code "launchWebSearch"} or {@code "launchRecognizer"} must also be set
+ (separated by the pipe | character).</td>
+ </tr>
+ <tr>
+ <td><code>"launchWebSearch"</code></td>
+ <td>The voice search button will take the user directly
+ to a built-in voice web search activity. Most applications will not use this flag, as
+ it will take the user away from the Activity in which search was invoked.</td>
+ </tr>
+ <tr>
+ <td><code>"launchRecognizer"</code></td>
+ <td>The voice search button will take
+ the user directly to a built-in voice recording activity. This Activity
+ will prompt the user to speak, transcribe the spoken text, and forward the resulting
+ query text to the searchable Activity, just as if the user had typed it into the
+ search UI and clicked the search button.</td>
+ </tr>
+ </table>
+ </dd>
+ <dt><code>android:voiceLanguageModel</code></dt>
+ <dd><em>Keyword</em>. The language model that
+ should be used by the voice recognition system. The following values are accepted:
+ <table>
+ <tr><th>Value</th><th>Description</th></tr>
+ <tr>
+ <td><code>"free_form"</code></td>
+ <td>Use a language model based on free-form speech recognition. This is the
+default.</td>
+ </tr>
+ <tr>
+ <td><code>"web_search"</code></td>
+ <td>Use a language model based on web search terms.</td>
+ </tr>
+ </table>
+ <p>Also see
+ {@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL} for more
+ information.</p></dd>
+ <dt><code>android:voicePromptText</code></dt>
+ <dd><em>String</em>. An additional message to display in the voice input dialog.</dd>
+ <dt><code>android:voiceLanguage</code></dt>
+ <dd><em>String</em>. The spoken language to be expected, expressed as the string value of
+a constants in {@link java.util.Locale} (for example, {@code "de"} for German or {@code "fr"} for
+French). This is only needed if it is different from the current value of {@link
+java.util.Locale#getDefault() Locale.getDefault()}.</dd>
+ <dt><code>android:voiceMaxResults</code></dt>
+ <dd><em>Integer</em>. Forces the maximum number of results to return,
+ including the "best" result which will always be provided as the {@link
+android.content.Intent#ACTION_SEARCH} Intent's primary
+ query. Must be 1 or greater. Use {@link android.speech.RecognizerIntent#EXTRA_RESULTS} to
+get the results from the Intent.
+ If not provided, the recognizer will choose how many results to return.</dd>
+ </dl>
+ </dd> <!-- end searchable element -->
+
+
+ <dt id="actionkey-element"><code><actionkey></code></dt>
+ <dd>Defines a shortcut key for a search action, in order to provide special behaviors at the touch
+of a button, based on the current query or focused suggestion. For example, the Contacts
+application enables the device call key for suggestions. So, when
+the user focuses on a search suggestion using the directional controls and then presses the call
+key, the application will immediately initiate a phone call to the suggested contact.
+ <p>Not all action keys are available on every device, and not
+all keys are allowed to be overriden in this way. For example, the "Home" key cannot be used and
+must always return to the home screen. Also be sure not to define an action
+key for a key that's needed for typing a search query. This essentially limits the
+available and reasonable action keys to the call button and menu button. Also note that action
+keys are not generally discoverable, so you should not provide them as a core user feature.</p>
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:keycode</code></dt>
+ <dd><em>String</em>. <strong>Required</strong>. A key code from {@link
+android.view.KeyEvent} that represents the action key
+ you wish to respond to (for example {@code "KEYCODE_CALL"}). This will be added to the
+ {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is passed to your
+ searchable Activity. To examine the key code, use
+ {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.
+ In addition to the key code, you must also provide one or more of
+ the action specifier attributes below. Not all action keys
+are actually supported using this mechanism, as many of them are used for typing,
+ navigation, or system functions. Note that although each of the action message elements are
+optional, at least one must be present for the action key to have any effect.</dd>
+ <dt><code>android:queryActionMsg</code></dt>
+ <dd><em>String</em>. An action message to be sent if the action key is pressed while the
+user is simply entering query text. This will be added to the
+ {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is
+ passed to your searchable Activity. To examine the string, use
+ {@link android.content.Intent#getStringExtra
+ getStringExtra(SearchManager.ACTION_MSG)}.</dd>
+ <dt><code>android:suggestActionMsg</code></dt>
+ <dd><em>String</em>. An action message to be sent if the action key is pressed while a
+ suggestion is focused. This will be added to the
+ Intent that is passed to your searchable Activity (using the action you've defined for
+ the suggestion). To examine the string,
+ use {@link android.content.Intent#getStringExtra
+ getStringExtra(SearchManager.ACTION_MSG)}. Note that this should only be used if all your
+suggestions support this action key. If not all suggestions can handle the same action key, then
+you must instead use the following {@code android:suggestActionMsgColumn} attribute.</dd>
+ <dt><code>android:suggestActionMsgColumn</code></dt>
+ <dd><em>String</em>. The name of the column in your content provider that defines the
+action message for this action key, which is to be sent if the action key is pressed while a
+ suggestion is focused. This attribute lets you control the
+action key on a suggestion-by-suggestion basis, because, instead of using the {@code
+android:suggestActionMsg} attribute to define the action message for all suggestions, each entry in
+your content provider provides its own action message. First, you must define a column in your
+content provider for each suggestion to provide an action message, then provide the name of that
+column in this attribute. The search manager will look at your suggestion cursor,
+ using the string provided here in order to select your action message column, and
+ then select the action message string from the cursor. That string will be added to the
+ Intent that is passed to your searchable Activity (using the action you've defined for
+ suggestions). To examine the string, use {@link
+android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. If the data
+does not exist for the selected suggestion, the action key will be ignored.</dd>
+ </dl>
+ </dd><!-- end action key -->
+ </dl>
+</dd><!-- end elements -->
+
+
+<dt>example:</dt>
+<dd>XML file saved at <code>res/xml/searchable.xml</code>:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/search_label"
+ android:hint="@string/search_hint"
+ android:searchSuggestAuthority="dictionary"
+ android:searchSuggestIntentAction="android.intent.action.VIEW"
+ android:includeInGlobalSearch="true"
+ android:searchSettingsDescription="@string/settings_description" >
+</searchable>
+</pre>
+
+</dd> <!-- end example -->
+
+
+</dl>
+
+
+
+
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index 662efbf..f114895 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -41,6 +41,7 @@
<p>The Android framework gives you the flexibility to use either or both of these methods for declaring and managing your application's UI. For example, you could declare your application's default layouts in XML, including the screen elements that will appear in them and their properties. You could then add code in your application that would modify the state of the screen objects, including those declared in XML, at run time. </p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<ul>
<li>The <a href="{@docRoot}sdk/eclipse-adt.html">ADT
@@ -54,6 +55,7 @@
<li>The <a href="{@docRoot}guide/developing/tools/layoutopt.html">layoutopt</a> tool lets
you quickly analyze your layouts and hierarchies for inefficiencies or other problems.</li>
</div>
+</div>
<p>The advantage to declaring your UI in XML is that it enables you to better separate the presentation of your application from the code that controls its behavior. Your UI descriptions are external to your application code, which means that you can modify or adapt it without having to modify your source code and recompile. For example, you can create XML layouts for different screen orientations, different device screen sizes, and different languages. Additionally, declaring the layout in XML makes it easier to visualize the structure of your UI, so it's easier to debug problems. As such, this document focuses on teaching you how to declare your layout in XML. If you're
interested in instantiating View objects at runtime, refer to the {@link android.view.ViewGroup} and
@@ -69,10 +71,15 @@
<h2 id="write">Write the XML</h2>
-<div class="sidebox"><p>For your convenience, the API reference documentation for UI related classes lists the available XML attributes that correspond to the class methods, including inherited attributes.</p>
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<p>For your convenience, the API reference documentation for UI related classes
+lists the available XML attributes that correspond to the class methods, including inherited
+attributes.</p>
<p>To learn more about the available XML elements and attributes, as well as the format of the XML file, see <a
href="{@docRoot}guide/topics/resources/available-resources.html#layoutresources">Layout Resources</a>.</p>
- </div>
+</div>
+</div>
<p>Using Android's XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create web pages in HTML — with a series of nested elements. </p>
diff --git a/docs/html/guide/topics/ui/how-android-draws.jd b/docs/html/guide/topics/ui/how-android-draws.jd
index efb55a2..3a57afa 100644
--- a/docs/html/guide/topics/ui/how-android-draws.jd
+++ b/docs/html/guide/topics/ui/how-android-draws.jd
@@ -18,12 +18,14 @@
siblings drawn in the order they appear in the tree.
</p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<p>The framework will not draw Views that are not in the invalid region, and also
will take care of drawing the Views background for you.</p>
<p>You can force a View to draw, by calling <code>{@link android.view.View#invalidate()}</code>.
</p>
</div>
+</div>
<p>
Drawing the layout is a two pass process: a measure pass and a layout pass. The measuring
@@ -50,11 +52,13 @@
as to how much space they each get, the parent will intervene and set the rules on the second pass).
</p>
- <div class="sidebox"><p>
+<div class="sidebox-wrapper">
+<div class="sidebox"><p>
To initiate a layout, call <code>{@link android.view.View#requestLayout}</code>. This method is typically
called by a View on itself when it believes that is can no longer fit within
its current bounds.</p>
- </div>
+</div>
+</div>
<p>
The measure pass uses two classes to communicate dimensions. The
diff --git a/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd
index 494bda8..abcf6be 100644
--- a/docs/html/guide/topics/ui/index.jd
+++ b/docs/html/guide/topics/ui/index.jd
@@ -107,7 +107,7 @@
<p>For more on building a UI layout, read <a href="declaring-layout.html">Declaring Layout</a>.
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<p><b>Tip:</b> You can also draw View and ViewGroups objects in Java code,
using the <code>{@link android.view.ViewGroup#addView(View)}</code> methods
to dynamically insert new View and ViewGroup objects.</p>
diff --git a/docs/html/guide/topics/ui/layout-objects.jd b/docs/html/guide/topics/ui/layout-objects.jd
index 345e9b3..8b2792d 100644
--- a/docs/html/guide/topics/ui/layout-objects.jd
+++ b/docs/html/guide/topics/ui/layout-objects.jd
@@ -55,6 +55,7 @@
"more important" than both the others, so it gets half the total remaining space, while the first two
share the rest equally.</p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<p><strong>Tip</strong>: To create a proportionate size
layout on the screen, create a container view group object with the
@@ -64,6 +65,7 @@
to each child, depending on what proportion of the screen each should
have.</p>
</div>
+</div>
<p>The following two forms represent a {@link android.widget.LinearLayout LinearLayout} with a set of elements: a
button, some labels and text boxes. The text boxes have their width set to <var>fill_parent</var>; other
diff --git a/docs/html/guide/topics/views/ui-xml.jd b/docs/html/guide/topics/views/ui-xml.jd
index 8ae599c..bcfa562 100644
--- a/docs/html/guide/topics/views/ui-xml.jd
+++ b/docs/html/guide/topics/views/ui-xml.jd
@@ -21,11 +21,16 @@
example, the EditText element has a <code>text</code> attribute that corresponds to
EditText.setText. </p>
-<div class="sidebox"><p>For your convenience, the API reference documentation for UI related classes lists the available XML attributes that correspond to the class methods, including inherited attributes.</p>
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<p>For your convenience, the API reference documentation for UI related classes
+lists the available XML attributes that correspond to the class methods, including inherited
+attributes.</p>
<p>To learn more about the available XML elements and attributes, as well as the format of the XML file, see <a
href="{@docRoot}reference/available-resources.html#layoutresources">Layout Resources</a>.</p>
- </div>
+</div>
+</div>
<p>Using Android's XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create HTML files — as a series of nested tags. </p>
diff --git a/docs/html/guide/topics/wireless/bluetooth.jd b/docs/html/guide/topics/wireless/bluetooth.jd
index 80aa0d7..a8ff007 100644
--- a/docs/html/guide/topics/wireless/bluetooth.jd
+++ b/docs/html/guide/topics/wireless/bluetooth.jd
@@ -466,7 +466,8 @@
the {@link android.bluetooth.BluetoothServerSocket} can (and should) be
discarded, unless you want to accept more connections.</p>
-<div class="sidebox-wrapper"><div class="sidebox-inner">
+<div class="sidebox-wrapper">
+<div class="sidebox">
<h2>About UUID</h2>
<p>A Universally Unique Identifier (UUID) is a standardized 128-bit format for a string
@@ -476,7 +477,8 @@
use with your application, you can use one of the many random UUID generators on
the web, then initialize a {@link java.util.UUID} with {@link
java.util.UUID#fromString(String)}.</p>
-</div></div>
+</div>
+</div>
<p>Here's the basic procedure to set up a server socket and accept a
connection:</p>
diff --git a/docs/html/guide/tutorials/hello-world.jd b/docs/html/guide/tutorials/hello-world.jd
index f6b54ff..6e315b2 100644
--- a/docs/html/guide/tutorials/hello-world.jd
+++ b/docs/html/guide/tutorials/hello-world.jd
@@ -35,7 +35,7 @@
<h2 id="avd">Create an AVD</h2>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>To learn more about how to use AVDs and the options
available to you, refer to the
<a href="{@docRoot}guide/developing/tools/avd.html">Android
@@ -243,7 +243,7 @@
</ol>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>To learn more about creating and editing run configurations in Eclipse, refer to
<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">Developing In Eclipse,
with ADT</a>.</p>
@@ -357,12 +357,14 @@
your application requires. In addition to layout files, resources also include assets
such as images, sounds, and localized strings.</p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Landscape layout</h2>
<p>When you want a different design for landscape, put your layout XML file
inside /res/layout-land. Android will automatically look here when the layout changes.
Without this special landscape layout defined, Android will stretch the default layout.</p>
</div>
+</div>
<p>The Eclipse plugin automatically creates one of these layout files for you: main.xml.
In the "Hello World" application you just completed, this file was ignored and you created a
@@ -478,12 +480,12 @@
because it lets you quickly and interactively locate the specific reference
you're looking for.</p>
-<p>It's possible yours looks slightly different than this (perhaps the hexadecimal values are different).
+<p>It's possible yours looks slighly different than this (perhaps the hexadecimal values are different).
For now, notice the inner class named "layout", and its
member field "main". The Eclipse plugin noticed the XML
layout file named main.xml and generated a class for it here. As you add other
resources to your project (such as strings in the <code>res/values/string.xml</code> file or drawables inside
-the <code>res/drawable/</code> directory) you'll see <code>R.java</code> change to keep up.</p>
+the <code>res/drawable/</code> direcory) you'll see <code>R.java</code> change to keep up.</p>
<p>When not using Eclipse, this class file will be generated for you at build time (with the Ant tool).</p>
<p><em>You should never edit this file by hand.</em></p>
</div>
diff --git a/docs/html/guide/tutorials/notepad/index.jd b/docs/html/guide/tutorials/notepad/index.jd
index 6319fad..f569314 100644
--- a/docs/html/guide/tutorials/notepad/index.jd
+++ b/docs/html/guide/tutorials/notepad/index.jd
@@ -20,7 +20,7 @@
concepts in Android development. If you want to add more complex features to
your application, you can examine the code in an alternative implementation
of a Note Pad application, in the
-<a href="{@docRoot}guide/samples/index.html">Sample Code</a> section. </p>
+<a href="{@docRoot}resources/samples/index.html">Sample Code</a> section. </p>
<a name="who"></a>
@@ -46,7 +46,7 @@
Fundamentals</a> before continuing. </p>
<p>This tutorial also builds on the introductory information provided in the
-<a href="{@docRoot}guide/tutorials/hello-world.html">Hello World</a>
+<a href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a>
tutorial, which explains how to set up your Eclipse environment
for building Android applications. We recommend you complete the Hello World
tutorial before starting this one.</p>
@@ -81,7 +81,7 @@
table lists the
tutorial examples and describes what each covers. ">
<tr>
- <th width="120"><a href="{@docRoot}guide/tutorials/notepad/notepad-ex1.html">Exercise
+ <th width="120"><a href="{@docRoot}resources/tutorials/notepad/notepad-ex1.html">Exercise
1</a></th>
<td>Start here. Construct a simple notes list that lets the user add new notes but not
edit them. Demonstrates the basics of <code>ListActivity</code> and creating
@@ -89,7 +89,7 @@
menu options. Uses a SQLite database to store the notes.</td>
</tr>
<tr>
- <th><a href="{@docRoot}guide/tutorials/notepad/notepad-ex2.html">Exercise 2</a></th>
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex2.html">Exercise 2</a></th>
<td>Add a second Activity to the
application. Demonstrates constructing a
new Activity, adding it to the Android manifest, passing data between the
@@ -98,13 +98,13 @@
<code>startActivityForResult()</code>.</td>
</tr>
<tr>
- <th><a href="{@docRoot}guide/tutorials/notepad/notepad-ex3.html">Exercise 3</a></th>
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex3.html">Exercise 3</a></th>
<td>Add handling of life-cycle events to
the application, to let it
maintain application state across the life cycle. </td>
</tr>
<tr>
- <th><a href="{@docRoot}guide/tutorials/notepad/notepad-extra-credit.html">Extra
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-extra-credit.html">Extra
Credit</a></th>
<td>Demonstrates how to use the Eclipse
debugger and how you can use it to
@@ -119,11 +119,11 @@
<ul>
<li>For a lighter but broader introduction to concepts not covered in the
tutorial,
-take a look at <a href="{@docRoot}guide/appendix/faq/commontasks.html">Common Android Tasks</a>.</li>
+take a look at <a href="{@docRoot}resources/faq/commontasks.html">Common Android Tasks</a>.</li>
<li>The Android SDK includes a variety of fully functioning sample applications
that make excellent opportunities for further learning. You can find the sample
applications in the <code>samples/</code> directory of your downloaded SDK, or browser them
-here, in the <a href="{@docRoot}guide/samples/index.html">Sample Code</a> section.</li>
+here, in the <a href="{@docRoot}resources/samples/index.html">Sample Code</a> section.</li>
<li>This tutorial draws from the full Notepad application included in the
<code>samples/</code> directory of the SDK, though it does not match it exactly.
When you are done with the tutorial,
diff --git a/docs/html/guide/tutorials/notepad/notepad-ex1.jd b/docs/html/guide/tutorials/notepad/notepad-ex1.jd
index b5173b8..cf7765e 100644
--- a/docs/html/guide/tutorials/notepad/notepad-ex1.jd
+++ b/docs/html/guide/tutorials/notepad/notepad-ex1.jd
@@ -33,7 +33,7 @@
<p><code>Notepadv1</code> is a project that is provided as a starting point. It
takes care of some of the boilerplate work that you have already seen if you
- followed the <a href="{@docRoot}guide/tutorials/hello-world.html">Hello,
+ followed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
World</a> tutorial.</p>
<ol>
@@ -44,7 +44,7 @@
In the New Android Project dialog, select <strong>Create project from existing source</strong>.</li>
<li>
Click <strong>Browse</strong> and navigate to where you copied the <code>NotepadCodeLab</code>
- (downloaded during <a href="{@docRoot}guide/tutorials/notepad/index.html#preparing">setup</a>)
+ (downloaded during <a href="{@docRoot}resources/tutorials/notepad/index.html#preparing">setup</a>)
and select <code>Notepadv1</code>.</li>
<li>
The Project Name and other properties should be automatically filled for you.
@@ -64,23 +64,22 @@
<h2>Step 2</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Accessing and modifying data</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">For this
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Accessing and modifying data</h2>
+ <p>For this
exercise, we are using a SQLite database to store our data. This is useful
if only <em>your</em> application will need to access or modify the data. If you wish for
other activities to access or modify the data, you have to expose the data using a
{@link android.content.ContentProvider ContentProvider}.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">If you are interested, you can find out more about
- <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the whole
+ <p>If you are interested, you can find out more about
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the
+whole
subject of <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.
The NotePad sample in the <code>samples/</code> folder of the SDK also has an example of how
to create a ContentProvider.</p>
</div>
+ </div>
<p>Take a look at the <code>NotesDbAdapter</code> class — this class is provided to
encapsulate data access to a SQLite database that will hold our notes data
@@ -137,25 +136,22 @@
<h2 style="clear:right;">Step 3</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Layouts and activities</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Most Activity classes will have a layout associated with them. The layout
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Layouts and activities</h2>
+ <p>Most Activity classes will have a layout associated with them. The layout
will be the "face" of the Activity to the user. In this case our layout will
take over the whole screen and provide a list of notes.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Full screen layouts are not the only option for an Activity however. You
+ <p>Full screen layouts are not the only option for an Activity however. You
might also want to use a <a
-href="{@docRoot}guide/appendix/faq/commontasks.html#floatingorfull">floating
+href="{@docRoot}resources/faq/commontasks.html#floatingorfull">floating
layout</a> (for example, a <a
-href="{@docRoot}guide/appendix/faq/commontasks.html#dialogsandalerts">dialog
+href="{@docRoot}resources/faq/commontasks.html#dialogsandalerts">dialog
or alert</a>),
or perhaps you don't need a layout at all (the Activity will be invisible
to the user unless you specify some kind of layout for it to use).</p>
- </div>
+ </div>
+ </div>
<p>Open the <code>notepad_list.xml</code> file in <code>res/layout</code>
and
@@ -233,23 +229,22 @@
<h2 style="clear:right;">Step 5</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Resources and the R class</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The folders under res/ in the Eclipse project are for resources.
- There is a <a href="{@docRoot}guide/appendix/faq/commontasks.html#filelist">specific structure</a> to the
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Resources and the R class</h2>
+ <p>The folders under res/ in the Eclipse project are for resources.
+ There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a>
+to the
folders and files under res/.</p>
- <p style="padding-left:.5em;font-size:12px;
-margin:0; padding:.0em .5em .5em 1em;">Resources defined in these folders and files will have
+ <p>Resources defined in these folders and files will have
corresponding entries in the R class allowing them to be easily accessed
and used from your application. The R class is automatically generated using the contents
of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools).
Furthermore, they will be bundled and deployed for you as part of the application.</p>
</p>
</div>
+ </div>
+
<p>To make the list of notes in the ListView, we also need to define a View for each row:</p>
<ol>
<li>
@@ -373,20 +368,18 @@
<h2>Step 9</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">More on menus</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The notepad application we are constructing only scratches the
- surface with <a href="{@docRoot}guide/appendix/faq/commontasks.html#addmenuitems">menus</a>. </p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">You can also <a href="{@docRoot}guide/appendix/faq/commontasks.html#menukeyshortcuts">add
-shortcut keys for menu items</a>, <a href="{@docRoot}guide/appendix/faq/commontasks.html#menukeyshortcuts">create
-submenus</a> and even <a href="{@docRoot}guide/appendix/faq/commontasks.html#addingtoothermenus">add
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>More about menus</h2>
+ <p>The notepad application we are constructing only scratches the
+ surface with <a href="{@docRoot}resources/faq/commontasks.html#addmenuitems">menus</a>. </p>
+ <p>You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add
+shortcut keys for menu items</a>, <a
+href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create
+submenus</a> and even <a href="{@docRoot}resources/faq/commontasks.html#addingtoothermenus">add
menu items to other applications!</a>. </p>
</div>
+ </div>
<p>Fill out the body of the <code>onCreateOptionsMenu()</code> method.</p>
@@ -486,19 +479,17 @@
<h2>Step 12</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">List adapters</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Our example uses a {@link android.widget.SimpleCursorAdapter
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>List adapters</h2>
+ <p>Our example uses a {@link android.widget.SimpleCursorAdapter
SimpleCursorAdapter} to bind a database {@link android.database.Cursor Cursor}
into a ListView, and this is a common way to use a {@link android.widget.ListAdapter
ListAdapter}. Other options exist like {@link android.widget.ArrayAdapter ArrayAdapter} which
can be used to take a List or Array of in-memory data and bind it in to
a list as well.</p>
</div>
+ </div>
<p>Define the <code>fillData()</code> method:</p>
<p>This
diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
index 3b8fa0b..a945a62 100644
--- a/docs/html/guide/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
@@ -1,11 +1,11 @@
-page.title=Notepad Exercise 2
+Rpage.title=Notepad Exercise 2
parent.title=Notepad Tutorial
parent.link=index.html
@jd:body
<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user
-create and edit notes. You will also allow the user to delete existing notes through a context menu.
+create and edit notes. You will also allow the user to delete existing notes through a context menu.
The new Activity assumes responsibility for creating new notes by
collecting user input and packing it into a return Bundle provided by the intent. This exercise
demonstrates:</em></p>
@@ -55,7 +55,7 @@
</li>
<li>
There are also a couple of new overridden methods
- (<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
+ (<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
<code>onListItemClick()</code> and <code>onActivityResult()</code>)
which we will be filling in below.
</li>
@@ -63,19 +63,20 @@
<h2>Step 2</h2>
-
+<div class="sidebox-wrapper">
<div class="sidebox">
<p>Context menus should always be used when performing actions upon specific elements in the UI.
When you register a View to a context menu, the context menu is revealed by performing a "long-click"
on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
</div>
+</div>
<p>First, let's create the context menu that will allow users to delete individual notes.
Open the Notepadv2 class.</p>
<ol>
<li>In order for each list item in the ListView to register for the context menu, we call
- <code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
+ <code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
the <code>onCreate()</code> method add this line:
<pre>registerForContextMenu(getListView());</pre>
<p>Because our Activity extends the ListActivity class, <code>getListView()</code> will return us
@@ -91,7 +92,7 @@
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}</pre>
- <p>The <code>onCreateContextMenu()</code> callback some passes other information in addition to the Menu object,
+ <p>The <code>onCreateContextMenu()</code> callback passes some other information in addition to the Menu object,
such as the View that has been triggered for the menu and
an extra object that may contain additional information about the object selected. However, we don't care about
these here, because we only have one kind of object in the Activity that uses context menus. In the next
@@ -102,7 +103,7 @@
<h2>Step 3</h2>
<p>Now that the we've registered our ListView for a context menu and defined our context menu item, we need
to handle the callback when it is selected. For this, we need to identify the list ID of the
- selected item, then delete it. So fill in the
+ selected item, then delete it. So fill in the
<code>onContextItemSelected()</code> method like this:</p>
<pre>
public boolean onContextItemSelected(MenuItem item) {
@@ -122,26 +123,23 @@
can now be deleted.</p>
<h2 style="clear:right;">Step 4</h2>
-<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Starting Other Activities</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">In this example our Intent uses a class name specifically.
- As well as
- <a href="{@docRoot}guide/appendix/faq/commontasks.html#intentexamples">starting intents</a> in
- classes we already know about, be they in our own application or another
- application, we can also create Intents without knowing exactly which
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Starting Other Activities</h2>
+ <p>In this example our Intent uses a class name specifically.
+ As well as
+ <a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
+ classes we already know about, be they in our own application or another
+ application, we can also create Intents without knowing exactly which
application will handle it.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">For example, we might want to open a page in a
+ <p>For example, we might want to open a page in a
browser, and for this we still use
an Intent. But instead of specifying a class to handle it, we use
a predefined Intent constant, and a content URI that describes what we
want to do. See {@link android.content.Intent
android.content.Intent} for more information.</p>
-</div>
+ </div>
+ </div>
<p>Fill in the body of the <code>createNote()</code> method:
<p>Create a new <code>Intent</code> to create a note
@@ -155,7 +153,7 @@
<code>NoteEdit</code>. Since the Intent class will need to communicate with the Android
operating system to route requests, we also have to provide a Context (<code>this</code>).</p>
<p>The <code>startActivityForResult()</code> method fires the Intent in a way that causes a method
- in our Activity to be called when the new Activity is completed. The method in our Activity
+ in our Activity to be called when the new Activity is completed. The method in our Activity
that receives the callback is called
<code>onActivityResult()</code> and we will implement it in a later step. The other way
to call an Activity is using <code>startActivity()</code> but this is a "fire-and-forget" way
@@ -164,7 +162,7 @@
<p>Don't worry about the fact that <code>NoteEdit</code> doesn't exist yet,
we will fix that soon. </p>
</li>
-
+
<h2>Step 5</h2>
@@ -180,7 +178,7 @@
interested in is the <code>position</code> that the user selected. We use
this to get the data from the correct row, and bundle it up to send to
the <code>NoteEdit</code> Activity.</p>
- <p>In our implementation of the callback, the method creates an
+ <p>In our implementation of the callback, the method creates an
<code>Intent</code> to edit the note using
the <code>NoteEdit</code> class. It then adds data into the extras Bundle of
the Intent, which will be passed to the called Activity. We use it
@@ -207,7 +205,7 @@
</li>
<li>
The details of the note are pulled out from our query Cursor, which we move to the
- proper position for the element that was selected in the list, with
+ proper position for the element that was selected in the list, with
the <code>moveToPosition()</code> method.</li>
<li>With the extras added to the Intent, we invoke the Intent on the
<code>NoteEdit</code> class by passing <code>startActivityForResult()</code>
@@ -219,7 +217,7 @@
variable is much more efficient than accessing a field in the Dalvik VM, so by doing this
we make only one access to the field, and five accesses to the local variable, making the
routine much more efficient. It is recommended that you use this optimization when possible.</p>
-
+
<h2>Step 6</h2>
@@ -243,7 +241,7 @@
<li><code>intent</code> — this is an Intent created by the Activity returning
results. It can be used to return data in the Intent "extras."
</li>
- </ul>
+ </ul>
<p>The combination of <code>startActivityForResult()</code> and
<code>onActivityResult()</code> can be thought of as an asynchronous RPC
(remote procedure call) and forms the recommended way for an Activity to invoke
@@ -277,7 +275,7 @@
<code>ACTIVITY_EDIT</code> activity results in this method.
</li>
<li>
- In the case of a create, we pull the title and body from the extras (retrieved from the
+ In the case of a create, we pull the title and body from the extras (retrieved from the
returned Intent) and use them to create a new note.
</li>
<li>
@@ -288,29 +286,27 @@
<code>fillData()</code> at the end ensures everything is up to date .
</li>
</ul>
-
+
<h2>Step 7</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">The Art of Layout</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">The provided
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>The Art of Layout</h2>
+ <p>The provided
note_edit.xml layout file is the most sophisticated one in the application we will be building,
but that doesn't mean it is even close to the kind of sophistication you will be likely to want
in real Android applications.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">Creating a
+ <p>Creating a
good UI is part art and part science, and the rest is work. Mastery of <a
href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> is an essential part of creating
a good looking Android application.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Take a look at the
- <a href="{@docRoot}guide/tutorials/views/index.html">Hello Views</a>
+ <p>Take a look at the
+ <a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
for some example layouts and how to use them. The ApiDemos sample project is also a
great resource from which to learn how to create different layouts.</p>
</div>
+ </div>
<p>Open the file <code>note_edit.xml</code> that has been provided and take a
look at it. This is the UI code for the Note Editor.</p>
@@ -335,7 +331,7 @@
<code>layout_weight</code> specified, so it takes up the minimum space
required to render. If the <code>layout_weight</code> of each of the two
text edit elements is set to 1, the remaining width in the parent layout will
- be split equally between them (because we claim they are equally important).
+ be split equally between them (because we claim they are equally important).
If the first one has a <code>layout_weight</code> of 1
and the second has a <code>layout_weight</code> of 2, then one third of the
remaining space will be given to the first, and two thirds to the
@@ -373,7 +369,7 @@
<code>onCreate(Bundle)</code> — and check the box next to it.</li>
<li>Click <strong>OK</strong>.<p>The method should now appear in your class.</p></li>
</ol>
-
+
<h2>Step 9</h2>
<p>Fill in the body of the <code>onCreate()</code> method for <code>NoteEdit</code>.</p>
@@ -388,7 +384,7 @@
<p>We can then unbundle the values that were passed in to the Activity
with the extras Bundle attached to the calling Intent. We'll use them to pre-populate
the title and body text edit views so that the user can edit them.
- Then we will grab and store the <code>mRowId</code> so we can keep
+ Then we will grab and store the <code>mRowId</code> so we can keep
track of what note the user is editing.</p>
<ol>
@@ -406,14 +402,14 @@
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);</pre>
- <p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
+ <p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
fields (you need to declare them at the top of the class definition).</p>
</li>
<li>At the top of the class, declare a <code>Long mRowId</code> private field to store
the current <code>mRowId</code> being edited (if any).
</li>
- <li>Continuing inside <code>onCreate()</code>,
- add code to initialize the <code>title</code>, <code>body</code> and
+ <li>Continuing inside <code>onCreate()</code>,
+ add code to initialize the <code>title</code>, <code>body</code> and
<code>mRowId</code> from the extras Bundle in
the Intent (if it is present):<br>
<pre>
@@ -423,7 +419,7 @@
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
-
+
if (title != null) {
mTitleText.setText(title);
}
@@ -459,16 +455,16 @@
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
-
+
}
-
+
});</pre>
</li>
</ol>
<h2>Step 10</h2>
<p>Fill in the body of the <code>onClick()</code> method of the <code>OnClickListener</code> created in the last step.</p>
-
+
<p>This is the code that will be run when the user clicks on the
confirm button. We want this to grab the title and body text from the edit
text fields, and put them into the return Bundle so that they can be passed
@@ -483,7 +479,7 @@
constants defined in Notepadv2 as keys:<br>
<pre>
Bundle bundle = new Bundle();
-
+
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
@@ -498,7 +494,7 @@
setResult(RESULT_OK, mIntent);
finish();</pre>
<ul>
- <li>The Intent is simply our data carrier that carries our Bundle
+ <li>The Intent is simply our data carrier that carries our Bundle
(with the title, body and mRowId).</li>
<li>The <code>setResult()</code> method is used to set the result
code and return Intent to be passed back to the
@@ -521,19 +517,19 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_edit);
-
+
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
-
+
Button confirmButton = (Button) findViewById(R.id.confirm);
-
+
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
-
+
if (title != null) {
mTitleText.setText(title);
}
@@ -541,12 +537,12 @@
mBodyText.setText(body);
}
}
-
+
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Bundle bundle = new Bundle();
-
+
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
@@ -562,24 +558,22 @@
}</pre>
</li>
</ol>
-
+
<h2>Step 11</h2>
-<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">The All-Important Android Manifest File</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The AndroidManifest.xml file is the way in which Android sees your
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>The All-Important Android Manifest File</h2>
+ <p>The AndroidManifest.xml file is the way in which Android sees your
application. This file defines the category of the application, where
it shows up (or even if it shows up) in the launcher or settings, what
activities, services, and content providers it defines, what intents it can
receive, and more. </p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">For more information, see the reference document
- <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a></p>
- </div>
+ <p>For more information, see the reference document
+ <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
+File</a></p>
+ </div>
+ </div>
<p>Finally, the new Activity has to be defined in the manifest file:</p>
<p>Before the new Activity can be seen by Android, it needs its own
@@ -597,10 +591,10 @@
</li>
<li>Click the <strong>Application</strong> tab at the bottom of the Manifest editor.</li>
<li>Click <strong>Add...</strong> in the Application Nodes section.
- <p>If you see a dialog with radiobuttons at the top, select the top radio button:
+ <p>If you see a dialog with radiobuttons at the top, select the top radio button:
"Create a new element at the top level, in Application".</p></li>
<li>Make sure "(A) Activity" is selected in the selection pane of the dialog, and click <strong>OK</strong>.</li>
- <li>Click on the new "Activity" node, in the Application Nodes section, then
+ <li>Click on the new "Activity" node, in the Application Nodes section, then
type <code>.NoteEdit</code> into the <em>Name*</em>
field to the right. Press Return/Enter.</li>
</ol>
@@ -608,28 +602,28 @@
file, have a look around at some of the other options available (but be careful not to select
them otherwise they will be added to your Manifest). This editor should help you understand
and alter the AndroidManifest.xml file as you move on to more advanced Android applications.</p>
-
+
<p class="note">If you prefer to edit this file directly, simply open the
<code>AndroidManifest.xml</code> file and look at the source (use the
<code>AndroidManifest.xml</code> tab in the eclipse editor to see the source code directly).
Then edit the file as follows:<br>
- <code><activity android:name=".NoteEdit"></activity></code><br><br>
+ <code><activity android:name=".NoteEdit" /></code><br><br>
This should be placed just below the line that reads:<br>
<code></activity></code> for the <code>.Notepadv2</code> activity.</p>
<h2 style="clear:right;">Step 12</h2>
<p>Now Run it!</p>
-<p>You should now be able to add real notes from
-the menu, as well as delete an existing one. Notice that in order to delete, you must
+<p>You should now be able to add real notes from
+the menu, as well as delete an existing one. Notice that in order to delete, you must
first use the directional controls on the device to highlight the note.
-Furthermore, selecting a note title from the list should bring up the note
-editor to let you edit it. Press confirm when finished to save the changes
+Furthermore, selecting a note title from the list should bring up the note
+editor to let you edit it. Press confirm when finished to save the changes
back to the database.
<h2>Solution and Next Steps</h2>
-<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
+<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
from the zip file to compare with your own.</p>
<p>Now try editing a note, and then hitting the back button on the emulator
instead of the confirm button (the back button is below the menu button). You
diff --git a/docs/html/guide/tutorials/notepad/notepad-ex3.jd b/docs/html/guide/tutorials/notepad/notepad-ex3.jd
index 8737280..573500f 100644
--- a/docs/html/guide/tutorials/notepad/notepad-ex3.jd
+++ b/docs/html/guide/tutorials/notepad/notepad-ex3.jd
@@ -36,7 +36,7 @@
<ol>
<li>Remove the code in <code>NoteEdit</code> that parses the title and body
- from the extras Bundle.
+ from the extras Bundle.
<p>Instead, we are going to use the <code>DBHelper</code> class
to access the notes from the database directly. All we need passed into the
NoteEdit Activity is a <code>mRowId</code> (but only if we are editing, if creating we pass
@@ -57,7 +57,7 @@
}</pre>
</li>
</ol>
-
+
<h2>Step 2</h2>
<p>Create a class field for a <code>NotesDbAdapter</code> at the top of the NoteEdit class:</p>
@@ -67,11 +67,11 @@
<pre>
mDbHelper = new NotesDbAdapter(this);<br>
mDbHelper.open();</pre>
-
+
<h2>Step 3</h2>
<p>In <code>NoteEdit</code>, we need to check the <var>savedInstanceState</var> for the
-<code>mRowId</code>, in case the note
+<code>mRowId</code>, in case the note
editing contains a saved state in the Bundle, which we should recover (this would happen
if our Activity lost focus and then restarted).</p>
<ol>
@@ -87,11 +87,11 @@
</pre>
with this:
<pre>
- mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
- : null;
+ mRowId = (savedInstanceState == null) ? null :
+ (Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
- Bundle extras = getIntent().getExtras();
- mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
+ Bundle extras = getIntent().getExtras();
+ mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
</pre>
@@ -100,10 +100,15 @@
Note the null check for <code>savedInstanceState</code>, and we still need to load up
<code>mRowId</code> from the <code>extras</code> Bundle if it is not
provided by the <code>savedInstanceState</code>. This is a ternary operator shorthand
- to safely either use the value or null if it is not present.
+ to safely either use the value or null if it is not present.
+ </li>
+ <li>
+ Note the use of <code>Bundle.getSerializable()</code> instead of
+ <code>Bundle.getLong()</code>. The latter encoding returns a <code>long</code> primitive and
+ so can not be used to represent the case when <code>mRowId</code> is <code>null</code>.
</li>
</ol>
-
+
<h2>Step 4</h2>
<p>Next, we need to populate the fields based on the <code>mRowId</code> if we
@@ -126,38 +131,38 @@
}</pre>
<p>We will take care of storing the updates or new notes in the database
ourselves, using the life-cycle methods.</p>
-
+
<p>The whole <code>onCreate()</code> method should now look like this:</p>
<pre>
super.onCreate(savedInstanceState);
-
+
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
-
+
setContentView(R.layout.note_edit);
-
+
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
-
+
Button confirmButton = (Button) findViewById(R.id.confirm);
-
-mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
- : null;
+
+mRowId = (savedInstanceState == null) ? null :
+ (Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
- mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
+ mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
-
+
populateFields();
-
+
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}
-
+
});</pre>
<h2>Step 6</h2>
@@ -180,38 +185,36 @@
and re-create resources as dictated by the Activity life-cycle, so we don't need to worry about
doing that ourselves. After that, we just look up the title and body values from the Cursor
and populate the View elements with them.</p>
-
+
<h2>Step 7</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;margin-bottom:.5em;
- margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Why handling life-cycle events is important</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">If you are used to always having control in your applications, you
- might not understand why all this life-cycle work is necessary. The reason
- is that in Android, you are not in control of your Activity, the
- operating system is!</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">As we have already seen, the Android model is based around activities
- calling each other. When one Activity calls another, the current Activity
- is paused at the very least, and may be killed altogether if the
- system starts to run low on resources. If this happens, your Activity will
- have to store enough state to come back up later, preferably in the same
- state it was in when it was killed.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;padding:.0em .5em .5em 1em;">
- Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life cycle</a>.
- Lifecycle events can happen even if you are not handing off control to
- another Activity explicitly. For example, perhaps a call comes in to the
- handset. If this happens, and your Activity is running, it will be swapped
- out while the call Activity takes over.</p>
- </div>
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Why handling life-cycle events is important</h2>
+ <p>If you are used to always having control in your applications, you
+ might not understand why all this life-cycle work is necessary. The reason
+ is that in Android, you are not in control of your Activity, the
+ operating system is!</p>
+ <p>As we have already seen, the Android model is based around activities
+ calling each other. When one Activity calls another, the current Activity
+ is paused at the very least, and may be killed altogether if the
+ system starts to run low on resources. If this happens, your Activity will
+ have to store enough state to come back up later, preferably in the same
+ state it was in when it was killed.</p>
+ <p>
+ Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life
+cycle</a>.
+ Lifecycle events can happen even if you are not handing off control to
+ another Activity explicitly. For example, perhaps a call comes in to the
+ handset. If this happens, and your Activity is running, it will be swapped
+ out while the call Activity takes over.</p>
+ </div>
+ </div>
-<p>Still in the <code>NoteEdit</code> class, we now override the methods
- <code>onSaveInstanceState()</code>, <code>onPause()</code> and
- <code>onResume()</code>. These are our life-cycle methods
+<p>Still in the <code>NoteEdit</code> class, we now override the methods
+ <code>onSaveInstanceState()</code>, <code>onPause()</code> and
+ <code>onResume()</code>. These are our life-cycle methods
(along with <code>onCreate()</code> which we already have).</p>
<p><code>onSaveInstanceState()</code> is called by Android if the
@@ -241,8 +244,10 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ saveState();
+ outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId);
}</pre>
+ <p>We'll define <code>saveState()</code> next.</p>
</li>
<li><code>
onPause()</code>:
@@ -252,7 +257,6 @@
super.onPause();
saveState();
}</pre>
- <p>We'll define <code>saveState()</code> next.</p>
</li>
<li><code>
onResume()</code>:
@@ -264,6 +268,10 @@
}</pre>
</li>
</ol>
+<p>Note that <code>saveState()</code> must be called in both <code>onSaveInstanceState()</code>
+and <code>onPause()</code> to ensure that the data is saved. This is because there is no
+guarantee that <code>onSaveInstanceState()</code> will be called and because when it <em>is</em>
+called, it is called before <code>onPause()</code>.</p>
<h2 style="clear:right;">Step 8</h2>
@@ -301,19 +309,18 @@
necessary. The resulting method should look like this:</p>
<pre>
@Override
-protected void onActivityResult(int requestCode, int resultCode,
- Intent intent) {
+protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}</pre>
<p>Because the other class now does the work, all this has to do is refresh
the data.</p>
-
+
<h2>Step 10</h2>
<p>Also remove the lines which set the title and body from the
- <code>onListItemClick()</code> method (again they are no longer needed,
+ <code>onListItemClick()</code> method (again they are no longer needed,
only the <code>mRowId</code> is):</p>
<pre>
Cursor c = mNotesCursor;
@@ -344,13 +351,13 @@
other occurrences of <code>mNotesCursor</code> in your <code>fillData()</code> method.
</ol>
<p>
-Run it! (use <em>Run As -> Android Application</em> on the project right
+Run it! (use <em>Run As -> Android Application</em> on the project right
click menu again)</p>
<h2>Solution and Next Steps</h2>
<p>You can see the solution to this exercise in <code>Notepadv3Solution</code>
-from
+from
the zip file to compare with your own.</p>
<p>
When you are ready, move on to the <a href="notepad-extra-credit.html">Tutorial
diff --git a/docs/html/images/search/search-suggest-custom.png b/docs/html/images/search/search-suggest-custom.png
new file mode 100644
index 0000000..be14219
--- /dev/null
+++ b/docs/html/images/search/search-suggest-custom.png
Binary files differ
diff --git a/docs/html/images/search/search-suggest-recent-queries.png b/docs/html/images/search/search-suggest-recent-queries.png
new file mode 100644
index 0000000..869edf2
--- /dev/null
+++ b/docs/html/images/search/search-suggest-recent-queries.png
Binary files differ
diff --git a/docs/html/images/search/search-ui.png b/docs/html/images/search/search-ui.png
new file mode 100644
index 0000000..19ecc19
--- /dev/null
+++ b/docs/html/images/search/search-ui.png
Binary files differ
diff --git a/docs/html/intl/ja/guide/publishing/preparing.jd b/docs/html/intl/ja/guide/publishing/preparing.jd
index f1e7b45..e232f3b 100644
--- a/docs/html/intl/ja/guide/publishing/preparing.jd
+++ b/docs/html/intl/ja/guide/publishing/preparing.jd
@@ -113,9 +113,13 @@
<h3 id="mapsApiKey">7. アプリケーションが MapView 要素を使用している場合は、Maps API キーに登録する</h3>
-<div class="sidebox" style="margin-bottom:.5em;padding:1em;"><p>
+<div class="sidebox-wrapper">
+<div class="sidebox"><p>
Maps API キーを取得する方法は、<a
-href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Maps API キーの取得(英語のみ)</a> をご覧ください。</p></div>
+href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Maps API キーの取得(英語のみ)</a>
+をご覧ください。</p>
+</div>
+</div>
<p>アプリケーションが Mapview 要素を使用する場合、アプリケーションを Google Maps サービスで登録し、Maps API キーを取得する必要があります。その後、MapView で Google Maps からデータを取得できるようになります。この処理を行うため、Maps サービスに署名証明書の MD5 フィンガープリントを提出します。 </p>
diff --git a/docs/html/intl/ja/guide/topics/fundamentals.jd b/docs/html/intl/ja/guide/topics/fundamentals.jd
index 247d076..d329a8c 100644
--- a/docs/html/intl/ja/guide/topics/fundamentals.jd
+++ b/docs/html/intl/ja/guide/topics/fundamentals.jd
@@ -613,7 +613,7 @@
</p>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<h2>スーパークラスの呼び出し</h2>
<p>
どのアクティビティ ライフサイクル メソッドの実装でも、必ず最初にスーパークラス バージョンを呼び出す必要があります。次に例を示します:
diff --git a/docs/html/intl/ja/guide/tutorials/hello-world.jd b/docs/html/intl/ja/guide/tutorials/hello-world.jd
index c12965c..647c828 100644
--- a/docs/html/intl/ja/guide/tutorials/hello-world.jd
+++ b/docs/html/intl/ja/guide/tutorials/hello-world.jd
@@ -25,7 +25,7 @@
<h2 id="avd">AVD の作成</h2>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>AVD の使用方法と使用可能なオプションについて詳しくは、<a href="{@docRoot}guide/developing/tools/avd.html">Android 仮想デバイス</a> のドキュメントを参照してください。</p>
</div>
</div>
@@ -155,7 +155,7 @@
</ol>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>Eclipse での起動構成の作成と編集について詳しくは、「<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">ADT を使用した Eclipse での開発</a>」を参照してください。</p>
</div>
</div>
@@ -232,11 +232,12 @@
<p>これらの XML レイアウト ファイルは、作成するプロジェクトの <code>res/layout/</code> ディレクトリ内に置かれます。「res」は「resources」の略で、アプリケーションに必要なコード以外のすべてのアセットがこのディレクトリに格納されます。リソースには、レイアウト ファイルの他に、画像、音声、ローカライズされた文字列などのアセットがあります。</p>
-
+<div class="sidebox-wrapper">
<div class="sidebox">
<h2>横表示レイアウト</h2>
<p>横表示の場合に異なるデザインで表示するには、レイアウト XML ファイルを /res/layout-land 内に入れます。Android 端末のレイアウトが横表示に変わると自動的にこのディレクトリが参照されます。このように横表示向けに定義されたレイアウトが存在しない場合、自動的にデフォルトのレイアウトが拡大して使用されます。</p>
</div>
+</div>
<p>Eclipse プラグインでは、このようなレイアウト ファイルの 1 つである「main.xml」が自動的に作成されます。先ほど「Hello World」アプリケーションを作成した際には、このファイルは無視してプログラマティックにレイアウトを作成しました。この作成方法は Android フレームワークについてより深く理解していただくことを意図したもので、実際にはほとんどの場合レイアウトはコードではなく XML ファイルで定義します。以下の手順では、既存のアプリケーションを変更して XML レイアウトが使用されるようにする方法を説明します。</p>
diff --git a/docs/html/intl/ja/resources/tutorials/hello-world.jd b/docs/html/intl/ja/resources/tutorials/hello-world.jd
index c12965c..0cda2fb 100644
--- a/docs/html/intl/ja/resources/tutorials/hello-world.jd
+++ b/docs/html/intl/ja/resources/tutorials/hello-world.jd
@@ -25,7 +25,7 @@
<h2 id="avd">AVD の作成</h2>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>AVD の使用方法と使用可能なオプションについて詳しくは、<a href="{@docRoot}guide/developing/tools/avd.html">Android 仮想デバイス</a> のドキュメントを参照してください。</p>
</div>
</div>
@@ -155,7 +155,7 @@
</ol>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>Eclipse での起動構成の作成と編集について詳しくは、「<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">ADT を使用した Eclipse での開発</a>」を参照してください。</p>
</div>
</div>
@@ -233,10 +233,12 @@
<p>これらの XML レイアウト ファイルは、作成するプロジェクトの <code>res/layout/</code> ディレクトリ内に置かれます。「res」は「resources」の略で、アプリケーションに必要なコード以外のすべてのアセットがこのディレクトリに格納されます。リソースには、レイアウト ファイルの他に、画像、音声、ローカライズされた文字列などのアセットがあります。</p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<h2>横表示レイアウト</h2>
<p>横表示の場合に異なるデザインで表示するには、レイアウト XML ファイルを /res/layout-land 内に入れます。Android 端末のレイアウトが横表示に変わると自動的にこのディレクトリが参照されます。このように横表示向けに定義されたレイアウトが存在しない場合、自動的にデフォルトのレイアウトが拡大して使用されます。</p>
</div>
+</div>
<p>Eclipse プラグインでは、このようなレイアウト ファイルの 1 つである「main.xml」が自動的に作成されます。先ほど「Hello World」アプリケーションを作成した際には、このファイルは無視してプログラマティックにレイアウトを作成しました。この作成方法は Android フレームワークについてより深く理解していただくことを意図したもので、実際にはほとんどの場合レイアウトはコードではなく XML ファイルで定義します。以下の手順では、既存のアプリケーションを変更して XML レイアウトが使用されるようにする方法を説明します。</p>
diff --git a/docs/html/resources/tutorials/hello-world.jd b/docs/html/resources/tutorials/hello-world.jd
index 58d1a16..6e315b2 100644
--- a/docs/html/resources/tutorials/hello-world.jd
+++ b/docs/html/resources/tutorials/hello-world.jd
@@ -35,7 +35,7 @@
<h2 id="avd">Create an AVD</h2>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>To learn more about how to use AVDs and the options
available to you, refer to the
<a href="{@docRoot}guide/developing/tools/avd.html">Android
@@ -243,7 +243,7 @@
</ol>
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+ <div class="sidebox">
<p>To learn more about creating and editing run configurations in Eclipse, refer to
<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">Developing In Eclipse,
with ADT</a>.</p>
@@ -357,12 +357,14 @@
your application requires. In addition to layout files, resources also include assets
such as images, sounds, and localized strings.</p>
+<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Landscape layout</h2>
<p>When you want a different design for landscape, put your layout XML file
inside /res/layout-land. Android will automatically look here when the layout changes.
Without this special landscape layout defined, Android will stretch the default layout.</p>
</div>
+</div>
<p>The Eclipse plugin automatically creates one of these layout files for you: main.xml.
In the "Hello World" application you just completed, this file was ignored and you created a
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex1.jd b/docs/html/resources/tutorials/notepad/notepad-ex1.jd
index b26440b..cf7765e 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex1.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex1.jd
@@ -64,23 +64,22 @@
<h2>Step 2</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Accessing and modifying data</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">For this
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Accessing and modifying data</h2>
+ <p>For this
exercise, we are using a SQLite database to store our data. This is useful
if only <em>your</em> application will need to access or modify the data. If you wish for
other activities to access or modify the data, you have to expose the data using a
{@link android.content.ContentProvider ContentProvider}.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">If you are interested, you can find out more about
- <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the whole
+ <p>If you are interested, you can find out more about
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the
+whole
subject of <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.
The NotePad sample in the <code>samples/</code> folder of the SDK also has an example of how
to create a ContentProvider.</p>
</div>
+ </div>
<p>Take a look at the <code>NotesDbAdapter</code> class — this class is provided to
encapsulate data access to a SQLite database that will hold our notes data
@@ -137,17 +136,13 @@
<h2 style="clear:right;">Step 3</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Layouts and activities</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Most Activity classes will have a layout associated with them. The layout
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Layouts and activities</h2>
+ <p>Most Activity classes will have a layout associated with them. The layout
will be the "face" of the Activity to the user. In this case our layout will
take over the whole screen and provide a list of notes.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Full screen layouts are not the only option for an Activity however. You
+ <p>Full screen layouts are not the only option for an Activity however. You
might also want to use a <a
href="{@docRoot}resources/faq/commontasks.html#floatingorfull">floating
layout</a> (for example, a <a
@@ -155,7 +150,8 @@
or alert</a>),
or perhaps you don't need a layout at all (the Activity will be invisible
to the user unless you specify some kind of layout for it to use).</p>
- </div>
+ </div>
+ </div>
<p>Open the <code>notepad_list.xml</code> file in <code>res/layout</code>
and
@@ -233,23 +229,22 @@
<h2 style="clear:right;">Step 5</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Resources and the R class</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The folders under res/ in the Eclipse project are for resources.
- There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a> to the
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Resources and the R class</h2>
+ <p>The folders under res/ in the Eclipse project are for resources.
+ There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a>
+to the
folders and files under res/.</p>
- <p style="padding-left:.5em;font-size:12px;
-margin:0; padding:.0em .5em .5em 1em;">Resources defined in these folders and files will have
+ <p>Resources defined in these folders and files will have
corresponding entries in the R class allowing them to be easily accessed
and used from your application. The R class is automatically generated using the contents
of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools).
Furthermore, they will be bundled and deployed for you as part of the application.</p>
</p>
</div>
+ </div>
+
<p>To make the list of notes in the ListView, we also need to define a View for each row:</p>
<ol>
<li>
@@ -373,20 +368,18 @@
<h2>Step 9</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">More on menus</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The notepad application we are constructing only scratches the
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>More about menus</h2>
+ <p>The notepad application we are constructing only scratches the
surface with <a href="{@docRoot}resources/faq/commontasks.html#addmenuitems">menus</a>. </p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add
-shortcut keys for menu items</a>, <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create
+ <p>You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add
+shortcut keys for menu items</a>, <a
+href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create
submenus</a> and even <a href="{@docRoot}resources/faq/commontasks.html#addingtoothermenus">add
menu items to other applications!</a>. </p>
</div>
+ </div>
<p>Fill out the body of the <code>onCreateOptionsMenu()</code> method.</p>
@@ -486,19 +479,17 @@
<h2>Step 12</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">List adapters</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Our example uses a {@link android.widget.SimpleCursorAdapter
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>List adapters</h2>
+ <p>Our example uses a {@link android.widget.SimpleCursorAdapter
SimpleCursorAdapter} to bind a database {@link android.database.Cursor Cursor}
into a ListView, and this is a common way to use a {@link android.widget.ListAdapter
ListAdapter}. Other options exist like {@link android.widget.ArrayAdapter ArrayAdapter} which
can be used to take a List or Array of in-memory data and bind it in to
a list as well.</p>
</div>
+ </div>
<p>Define the <code>fillData()</code> method:</p>
<p>This
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index 44a04ab..a945a62 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -63,12 +63,13 @@
<h2>Step 2</h2>
-
+<div class="sidebox-wrapper">
<div class="sidebox">
<p>Context menus should always be used when performing actions upon specific elements in the UI.
When you register a View to a context menu, the context menu is revealed by performing a "long-click"
on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
</div>
+</div>
<p>First, let's create the context menu that will allow users to delete individual notes.
Open the Notepadv2 class.</p>
@@ -122,26 +123,23 @@
can now be deleted.</p>
<h2 style="clear:right;">Step 4</h2>
-<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Starting Other Activities</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">In this example our Intent uses a class name specifically.
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Starting Other Activities</h2>
+ <p>In this example our Intent uses a class name specifically.
As well as
<a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
classes we already know about, be they in our own application or another
application, we can also create Intents without knowing exactly which
application will handle it.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">For example, we might want to open a page in a
+ <p>For example, we might want to open a page in a
browser, and for this we still use
an Intent. But instead of specifying a class to handle it, we use
a predefined Intent constant, and a content URI that describes what we
want to do. See {@link android.content.Intent
android.content.Intent} for more information.</p>
-</div>
+ </div>
+ </div>
<p>Fill in the body of the <code>createNote()</code> method:
<p>Create a new <code>Intent</code> to create a note
@@ -292,25 +290,23 @@
<h2>Step 7</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">The Art of Layout</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">The provided
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>The Art of Layout</h2>
+ <p>The provided
note_edit.xml layout file is the most sophisticated one in the application we will be building,
but that doesn't mean it is even close to the kind of sophistication you will be likely to want
in real Android applications.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">Creating a
+ <p>Creating a
good UI is part art and part science, and the rest is work. Mastery of <a
href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> is an essential part of creating
a good looking Android application.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">Take a look at the
+ <p>Take a look at the
<a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
for some example layouts and how to use them. The ApiDemos sample project is also a
great resource from which to learn how to create different layouts.</p>
</div>
+ </div>
<p>Open the file <code>note_edit.xml</code> that has been provided and take a
look at it. This is the UI code for the Note Editor.</p>
@@ -565,20 +561,18 @@
<h2>Step 11</h2>
-<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;
- margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">The All-Important Android Manifest File</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">The AndroidManifest.xml file is the way in which Android sees your
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>The All-Important Android Manifest File</h2>
+ <p>The AndroidManifest.xml file is the way in which Android sees your
application. This file defines the category of the application, where
it shows up (or even if it shows up) in the launcher or settings, what
activities, services, and content providers it defines, what intents it can
receive, and more. </p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">For more information, see the reference document
- <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a></p>
+ <p>For more information, see the reference document
+ <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
+File</a></p>
+ </div>
</div>
<p>Finally, the new Activity has to be defined in the manifest file:</p>
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex3.jd b/docs/html/resources/tutorials/notepad/notepad-ex3.jd
index 2da113f..573500f 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex3.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex3.jd
@@ -189,30 +189,28 @@
<h2>Step 7</h2>
- <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
- background-color:#FFFFEE;margin-right:0px;margin-bottom:.5em;
- margin-top:1em;padding:0em;width:240px;">
- <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
- background-color:#FFFFDD;">Why handling life-cycle events is important</h2>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">If you are used to always having control in your applications, you
- might not understand why all this life-cycle work is necessary. The reason
- is that in Android, you are not in control of your Activity, the
- operating system is!</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;
- padding:.0em .5em .5em 1em;">As we have already seen, the Android model is based around activities
- calling each other. When one Activity calls another, the current Activity
- is paused at the very least, and may be killed altogether if the
- system starts to run low on resources. If this happens, your Activity will
- have to store enough state to come back up later, preferably in the same
- state it was in when it was killed.</p>
- <p style="padding-left:.5em;font-size:12px;margin:0;padding:.0em .5em .5em 1em;">
- Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life cycle</a>.
- Lifecycle events can happen even if you are not handing off control to
- another Activity explicitly. For example, perhaps a call comes in to the
- handset. If this happens, and your Activity is running, it will be swapped
- out while the call Activity takes over.</p>
- </div>
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Why handling life-cycle events is important</h2>
+ <p>If you are used to always having control in your applications, you
+ might not understand why all this life-cycle work is necessary. The reason
+ is that in Android, you are not in control of your Activity, the
+ operating system is!</p>
+ <p>As we have already seen, the Android model is based around activities
+ calling each other. When one Activity calls another, the current Activity
+ is paused at the very least, and may be killed altogether if the
+ system starts to run low on resources. If this happens, your Activity will
+ have to store enough state to come back up later, preferably in the same
+ state it was in when it was killed.</p>
+ <p>
+ Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life
+cycle</a>.
+ Lifecycle events can happen even if you are not handing off control to
+ another Activity explicitly. For example, perhaps a call comes in to the
+ handset. If this happens, and your Activity is running, it will be swapped
+ out while the call Activity takes over.</p>
+ </div>
+ </div>
<p>Still in the <code>NoteEdit</code> class, we now override the methods
<code>onSaveInstanceState()</code>, <code>onPause()</code> and
diff --git a/docs/html/sdk/1.1_r1/upgrading.jd b/docs/html/sdk/1.1_r1/upgrading.jd
index 5628d04..bc71149 100644
--- a/docs/html/sdk/1.1_r1/upgrading.jd
+++ b/docs/html/sdk/1.1_r1/upgrading.jd
@@ -4,7 +4,7 @@
<!--
<div class="sidebox-wrapper">
- <div class="sidebox-inner">
+<div class="sidebox">
<h2>Useful Links</h2>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 698c347..7b98e7f 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -130,7 +130,7 @@
<h2 id="components">Add Android Platforms and Other Components</h2>
-<div class="sidebox-wrapper" style="margin-right:2.5em;">
+<div class="sidebox-wrapper">
<div class="sidebox">
<p>The <strong>Android SDK and AVD Manager</strong> tool is pre-installed in
your SDK. Using the tool is a key part of performing the initial setup of your
diff --git a/docs/html/sdk/win-usb.jd b/docs/html/sdk/win-usb.jd
index e0c2431..d3dce9e 100644
--- a/docs/html/sdk/win-usb.jd
+++ b/docs/html/sdk/win-usb.jd
@@ -50,7 +50,7 @@
<h2 id="WinUsbDriver">Installing the USB Driver for Windows</h2>
<div class="sidebox-wrapper">
-<div class="sidebox-inner">
+<div class="sidebox">
<p>The USB driver for Windows provides support for the following
Android-powered
devices:</p>
diff --git a/docs/html/shareables/search_icons.zip b/docs/html/shareables/search_icons.zip
new file mode 100644
index 0000000..bc98465
--- /dev/null
+++ b/docs/html/shareables/search_icons.zip
Binary files differ
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c3f63d7..281823a 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -153,16 +153,15 @@
}
/**
- * Compute the bounds of the path, and write the answer into bounds. If the
- * path contains 0 or 1 points, the bounds is set to (0,0,0,0)
+ * Compute the bounds of the control points of the path, and write the
+ * answer into bounds. If the path contains 0 or 1 points, the bounds is
+ * set to (0,0,0,0)
*
- * @param bounds Returns the computed bounds of the path
- * @param exact If true, return the exact (but slower) bounds, else return
- * just the bounds of all control points
+ * @param bounds Returns the computed bounds of the path's control points.
+ * @param exact This parameter is no longer used.
*/
public void computeBounds(RectF bounds, boolean exact) {
- // 1-exact, 0-fast correspond to the values in SkPath.h
- native_computeBounds(mNativePath, bounds, exact ? 1 : 0);
+ native_computeBounds(mNativePath, bounds);
}
/**
@@ -552,8 +551,7 @@
private static native void native_setFillType(int nPath, int ft);
private static native boolean native_isEmpty(int nPath);
private static native boolean native_isRect(int nPath, RectF rect);
- private static native void native_computeBounds(int nPath, RectF bounds,
- int btype);
+ private static native void native_computeBounds(int nPath, RectF bounds);
private static native void native_incReserve(int nPath, int extraPtCount);
private static native void native_moveTo(int nPath, float x, float y);
private static native void native_rMoveTo(int nPath, float dx, float dy);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 91a2bc1..63d1446 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -778,8 +778,8 @@
com.android.internal.R.styleable.DrawableCorners_bottomLeftRadius, radius);
int bottomRightRadius = a.getDimensionPixelSize(
com.android.internal.R.styleable.DrawableCorners_bottomRightRadius, radius);
- if (topLeftRadius != radius && topRightRadius != radius &&
- bottomLeftRadius != radius && bottomRightRadius != radius) {
+ if (topLeftRadius != radius || topRightRadius != radius ||
+ bottomLeftRadius != radius || bottomRightRadius != radius) {
setCornerRadii(new float[] {
topLeftRadius, topLeftRadius,
topRightRadius, topRightRadius,
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
index b6176eb..b5d1e7a 100644
--- a/include/media/stagefright/HTTPDataSource.h
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -54,7 +54,11 @@
private:
enum {
- kBufferSize = 32 * 1024
+ kBufferSize = 32 * 1024,
+
+ // If we encounter a socket-read error we'll try reconnecting
+ // and restarting the read for at most this many times.
+ kMaxNumRetries = 3,
};
enum State {
@@ -84,6 +88,8 @@
bool mContentLengthValid;
unsigned long long mContentLength;
+ int32_t mNumRetriesLeft;
+
void init(const KeyedVector<String8, String8> *headers);
ssize_t sendRangeRequest(size_t offset);
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 24c2f46..d58c711 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -167,7 +167,7 @@
status_t setupMPEG4EncoderParameters();
status_t setupAVCEncoderParameters();
- void setVideoOutputFormat(
+ status_t setVideoOutputFormat(
const char *mime, OMX_U32 width, OMX_U32 height);
void setImageOutputFormat(
@@ -220,6 +220,8 @@
void dumpPortStatus(OMX_U32 portIndex);
+ status_t configureCodec(const sp<MetaData> &meta);
+
static uint32_t getComponentQuirks(const char *componentName);
static void findMatchingCodecs(
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 28b0d2f..998e353 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -26,6 +26,12 @@
namespace android {
+#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
+#define ANDROID_TTS_ENGINE_PROPERTY_PITCH "pitch"
+#define ANDROID_TTS_ENGINE_PROPERTY_RATE "rate"
+#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
+
+
enum tts_synth_status {
TTS_SYNTH_DONE = 0,
TTS_SYNTH_PENDING = 1
@@ -85,7 +91,7 @@
// Initialize the TTS engine and returns whether initialization succeeded.
// @param synthDoneCBPtr synthesis callback function pointer
// @return TTS_SUCCESS, or TTS_FAILURE
- virtual tts_result init(synthDoneCB_t synthDoneCBPtr);
+ virtual tts_result init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig);
// Shut down the TTS engine and releases all associated resources.
// @return TTS_SUCCESS, or TTS_FAILURE
@@ -122,7 +128,7 @@
// @param variant pointer to the variant code
// @return TTS_SUCCESS, or TTS_FAILURE
virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
-
+
// Load the resources associated with the specified language, country and Locale variant.
// The loaded language will only be used once a call to setLanguageFromLocale() with the same
// language value is issued. Language and country values are coded according to the ISO three
@@ -220,19 +226,6 @@
virtual tts_result synthesizeText(const char *text, int8_t *buffer,
size_t bufferSize, void *userdata);
- // Synthesize IPA text.
- // As the synthesis is performed, the engine invokes the callback to notify
- // the TTS framework that it has filled the given buffer, and indicates how
- // many bytes it wrote. The callback is called repeatedly until the engine
- // has generated all the audio data corresponding to the IPA data.
- // @param ipa the IPA data to synthesize
- // @param userdata pointer to be returned when the call is invoked
- // @param buffer the location where the synthesized data must be written
- // @param bufferSize the number of bytes that can be written in buffer
- // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported,
- // otherwise TTS_SUCCESS or TTS_FAILURE
- virtual tts_result synthesizeIpa(const char *ipa, int8_t *buffer,
- size_t bufferSize, void *userdata);
};
} // namespace android
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index be9c79b..741d763 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -86,7 +86,6 @@
GraphicBufferAllocator();
~GraphicBufferAllocator();
- mutable Mutex mLock;
alloc_device_t *mAllocDev;
};
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 130d83c..5ac0c5e 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -209,7 +209,7 @@
class Mutex {
public:
enum {
- NORMAL = 0,
+ PRIVATE = 0,
SHARED = 1
};
@@ -305,7 +305,13 @@
*/
class Condition {
public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
Condition();
+ Condition(int type);
~Condition();
// Wait on the condition variable. Lock the mutex before calling.
status_t wait(Mutex& mutex);
@@ -329,6 +335,17 @@
inline Condition::Condition() {
pthread_cond_init(&mCond, NULL);
}
+inline Condition::Condition(int type) {
+ if (type == SHARED) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&mCond, &attr);
+ pthread_condattr_destroy(&attr);
+ } else {
+ pthread_cond_init(&mCond, NULL);
+ }
+}
inline Condition::~Condition() {
pthread_cond_destroy(&mCond);
}
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 7902212..815a367 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -72,6 +72,10 @@
// 50 * ~20msecs = 1 second
static const int8_t kMaxTrackRetries = 50;
static const int8_t kMaxTrackStartupRetries = 50;
+// allow less retry attempts on direct output thread.
+// direct outputs can be a scarce resource in audio hardware and should
+// be released as quickly as possible.
+static const int8_t kMaxTrackRetriesDirect = 2;
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 20000;
@@ -1794,6 +1798,9 @@
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
+ // use shorter standby delay as on normal output to release
+ // hardware resources as soon as possible
+ nsecs_t standbyDelay = microseconds(activeSleepTime*2);
while (!exitPending())
@@ -1810,6 +1817,7 @@
mixBufferSize = mFrameCount*mFrameSize;
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
+ standbyDelay = microseconds(activeSleepTime*2);
}
// put audio hardware into standby after short delay
@@ -1842,7 +1850,7 @@
}
}
- standbyTime = systemTime() + kStandbyTimeInNsecs;
+ standbyTime = systemTime() + standbyDelay;
sleepTime = idleSleepTime;
continue;
}
@@ -1896,7 +1904,7 @@
}
// reset retry count
- track->mRetryCount = kMaxTrackRetries;
+ track->mRetryCount = kMaxTrackRetriesDirect;
activeTrack = t;
mixerStatus = MIXER_TRACKS_READY;
} else {
@@ -1949,7 +1957,7 @@
activeTrack->releaseBuffer(&buffer);
}
sleepTime = 0;
- standbyTime = systemTime() + kStandbyTimeInNsecs;
+ standbyTime = systemTime() + standbyDelay;
} else {
if (sleepTime == 0) {
if (mixerStatus == MIXER_TRACKS_ENABLED) {
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index a61221a..c8b3f48 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -458,11 +458,8 @@
}
#endif //AUDIO_POLICY_TEST
- // open a direct output if:
- // 1 a direct output is explicitely requested
- // 2 the audio format is compressed
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
- (format !=0 && !AudioSystem::isLinearPCM(format))) {
+ // open a direct output if required by specified parameters
+ if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
LOGV("getOutput() opening direct output device %x", device);
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
@@ -472,7 +469,7 @@
outputDesc->mChannels = channels;
outputDesc->mLatency = 0;
outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
- outputDesc->mRefCount[stream] = 1;
+ outputDesc->mRefCount[stream] = 0;
output = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
@@ -609,6 +606,9 @@
setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2);
}
#endif
+ if (output != mHardwareOutput) {
+ setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
+ }
return NO_ERROR;
} else {
LOGW("stopOutput() refcount is already 0 for output %d", output);
@@ -1550,10 +1550,10 @@
}
#ifdef WITH_A2DP
// filter devices according to output selected
- if (output == mHardwareOutput) {
- device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
- } else {
+ if (output == mA2dpOutput) {
device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
+ } else {
+ device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
}
#endif
@@ -1562,8 +1562,7 @@
// - the requestede device is 0
// - the requested device is the same as current device and force is not specified.
// Doing this check here allows the caller to call setOutputDevice() without conditions
- if (device == 0 ||
- (device == prevDevice && !force)) {
+ if ((device == 0 || device == prevDevice) && !force) {
LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
return;
}
@@ -1666,7 +1665,7 @@
int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
volume = AudioSystem::linearToLog(volInt);
- // if a heaset is connected, apply the following rules to ring tones and notifications
+ // if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
// - always attenuate ring tones and notifications volume by 6dB
// - if music is playing, always limit the volume to current music volume,
@@ -1825,6 +1824,17 @@
}
}
+bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags,
+ uint32_t device)
+{
+ return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format !=0 && !AudioSystem::isLinearPCM(format)));
+}
+
// --- AudioOutputDescriptor class implementation
AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 473f580..0016503 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -935,17 +935,27 @@
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
- bool doBackground = !gDisableBackgroundScheduling &&
- getpriority(PRIO_PROCESS, mMyThreadId)
- >= ANDROID_PRIORITY_BACKGROUND;
- if (doBackground) {
- // We have inherited a background priority from the caller.
- // Ensure this thread is in the background scheduling class,
- // since the driver won't modify scheduling classes for us.
- androidSetThreadSchedulingGroup(mMyThreadId,
- ANDROID_TGROUP_BG_NONINTERACT);
+ int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
+ if (gDisableBackgroundScheduling) {
+ if (curPrio > ANDROID_PRIORITY_NORMAL) {
+ // We have inherited a reduced priority from the caller, but do not
+ // want to run in that state in this process. The driver set our
+ // priority already (though not our scheduling class), so bounce
+ // it back to the default before invoking the transaction.
+ setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
+ }
+ } else {
+ if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
+ // We want to use the inherited priority from the caller.
+ // Ensure this thread is in the background scheduling class,
+ // since the driver won't modify scheduling classes for us.
+ // The scheduling group is reset to default by the caller
+ // once this method returns after the transaction is complete.
+ androidSetThreadSchedulingGroup(mMyThreadId,
+ ANDROID_TGROUP_BG_NONINTERACT);
+ }
}
-
+
//LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
Parcel reply;
@@ -982,14 +992,7 @@
mCallingPid = origPid;
mCallingUid = origUid;
-
- if (doBackground) {
- // We moved to the background scheduling group to execute
- // this transaction, so now that we are done go back in the
- // foreground.
- androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
- }
-
+
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index 8620e71..f19c502 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -117,6 +117,7 @@
LOGV("disconnect");
if (mCamera != 0) {
mCamera->disconnect();
+ mCamera->asBinder()->unlinkToDeath(this);
mCamera = 0;
}
}
diff --git a/libs/rs/java/Fountain/res/raw/fountain2.rs b/libs/rs/java/Fountain/res/raw/fountain2.rs
new file mode 100644
index 0000000..3301140
--- /dev/null
+++ b/libs/rs/java/Fountain/res/raw/fountain2.rs
@@ -0,0 +1,73 @@
+// Fountain test script
+#pragma version(1)
+
+#include "rs_types.rsh"
+#include "rs_math.rsh"
+#include "rs_graphics.rsh"
+
+static int newPart = 0;
+
+typedef struct Control_s {
+ int x, y;
+ int rate;
+ int count;
+ float r, g, b;
+ rs_allocation partBuffer;
+ rs_mesh partMesh;
+} Control_t;
+Control_t *Control;
+
+typedef struct Point_s{
+ float2 delta;
+ float2 position;
+ unsigned int color;
+} Point_t;
+Point_t *point;
+
+int main(int launchID) {
+ int ct;
+ int count = Control->count;
+ int rate = Control->rate;
+ float height = getHeight();
+ Point_t * p = point;
+
+ if (rate) {
+ float rMax = ((float)rate) * 0.005f;
+ int x = Control->x;
+ int y = Control->y;
+ int color = ((int)(Control->r * 255.f)) |
+ ((int)(Control->g * 255.f)) << 8 |
+ ((int)(Control->b * 255.f)) << 16 |
+ (0xf0 << 24);
+ Point_t * np = &p[newPart];
+
+ while (rate--) {
+ np->delta = vec2Rand(rMax);
+ np->position.x = x;
+ np->position.y = y;
+ np->color = color;
+ newPart++;
+ np++;
+ if (newPart >= count) {
+ newPart = 0;
+ np = &p[newPart];
+ }
+ }
+ }
+
+ for (ct=0; ct < count; ct++) {
+ float dy = p->delta.y + 0.15f;
+ float posy = p->position.y + dy;
+ if ((posy > height) && (dy > 0)) {
+ dy *= -0.3f;
+ }
+ p->delta.y = dy;
+ p->position.x += p->delta.x;
+ p->position.y = posy;
+ p++;
+ }
+
+ uploadToBufferObject(Control->partBuffer);
+ drawSimpleMesh(Control->partMesh);
+ return 1;
+}
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
new file mode 100644
index 0000000..70cd562
--- /dev/null
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -0,0 +1,65 @@
+
+
+extern float2 vec2Rand(float len);
+
+extern float3 float3Norm(float3);
+extern float float3Length(float3);
+extern float3 float3Add(float3 lhs, float3 rhs);
+extern float3 float3Sub(float3 lhs, float3 rhs);
+extern float3 float3Cross(float3 lhs, float3 rhs);
+extern float float3Dot(float3 lhs, float3 rhs);
+extern float3 float3Scale(float3 v, float scale);
+
+extern float4 float4Add(float4 lhs, float4 rhs);
+extern float4 float4Sub(float4 lhs, float4 rhs);
+extern float4 float4Cross(float4 lhs, float4 rhs);
+extern float float4Dot(float4 lhs, float4 rhs);
+extern float4 float4Scale(float4 v, float scale);
+
+ // context
+extern void bindProgramFragment(rs_program_fragment);
+extern void bindProgramStore(rs_program_store);
+extern void bindProgramVertex(rs_program_vertex);
+
+extern void bindSampler(rs_program_fragment, int slot, rs_sampler);
+extern void bindSampler(rs_program_fragment, int slot, rs_allocation);
+
+extern void vpLoadModelMatrix(const float *);
+extern void vpLoadTextureMatrix(const float *);
+
+
+// drawing
+extern void drawRect(float x1, float y1, float x2, float y2, float z);
+extern void drawQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4);
+extern void drawQuadTexCoords(float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, float x4, float y4, float z4, float u4, float v4);
+extern void drawSprite(float x, float y, float z, float w, float h);
+extern void drawSpriteScreenspace(float x, float y, float z, float w, float h);
+extern void drawLine(float x1, float y1, float z1, float x2, float y2, float z2);
+extern void drawPoint(float x1, float y1, float z1);
+extern void drawSimpleMesh(int ism);
+extern void drawSimpleMeshRange(int ism, int start, int len);
+
+// misc
+extern void pfClearColor(float, float, float, float);
+extern void color(float, float, float, float);
+extern void hsb(float, float, float, float);
+extern void hsbToRgb(float, float, float, float*);
+extern int hsbToAbgr(float, float, float, float);
+
+extern void uploadToTexture(int, int);
+extern void uploadToBufferObject(int);
+
+extern int colorFloatRGBAtoUNorm8(float, float, float, float);
+extern int colorFloatRGBto565(float, float, float);
+
+extern int getWidth();
+extern int getHeight();
+
+extern int sendToClient(void *data, int cmdID, int len, int waitForSpace);
+
+extern void debugF(const char *, float);
+extern void debugI32(const char *, int);
+extern void debugHexI32(const char *, int);
+
+
+
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
new file mode 100644
index 0000000..613c7ca
--- /dev/null
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -0,0 +1,287 @@
+// Float ops
+
+extern float __attribute__((overloadable)) abs(float);
+extern float2 __attribute__((overloadable)) abs(float2);
+extern float3 __attribute__((overloadable)) abs(float3);
+extern float4 __attribute__((overloadable)) abs(float4);
+extern float8 __attribute__((overloadable)) abs(float8);
+extern float16 __attribute__((overloadable)) abs(float16);
+
+extern float __attribute__((overloadable)) acos(float);
+extern float2 __attribute__((overloadable)) acos(float2);
+extern float3 __attribute__((overloadable)) acos(float3);
+extern float4 __attribute__((overloadable)) acos(float4);
+extern float8 __attribute__((overloadable)) acos(float8);
+extern float16 __attribute__((overloadable)) acos(float16);
+
+extern float __attribute__((overloadable)) asin(float);
+extern float2 __attribute__((overloadable)) asin(float2);
+extern float3 __attribute__((overloadable)) asin(float3);
+extern float4 __attribute__((overloadable)) asin(float4);
+extern float8 __attribute__((overloadable)) asin(float8);
+extern float16 __attribute__((overloadable)) asin(float16);
+
+extern float __attribute__((overloadable)) atan(float);
+extern float2 __attribute__((overloadable)) atan(float2);
+extern float3 __attribute__((overloadable)) atan(float3);
+extern float4 __attribute__((overloadable)) atan(float4);
+extern float8 __attribute__((overloadable)) atan(float8);
+extern float16 __attribute__((overloadable)) atan(float16);
+
+extern float __attribute__((overloadable)) atan2(float, float);
+extern float2 __attribute__((overloadable)) atan2(float2, float2);
+extern float3 __attribute__((overloadable)) atan2(float3, float3);
+extern float4 __attribute__((overloadable)) atan2(float4, float4);
+extern float8 __attribute__((overloadable)) atan2(float8, float8);
+extern float16 __attribute__((overloadable)) atan2(float16, float16);
+
+extern float __attribute__((overloadable)) ceil(float);
+extern float2 __attribute__((overloadable)) ceil(float2);
+extern float3 __attribute__((overloadable)) ceil(float3);
+extern float4 __attribute__((overloadable)) ceil(float4);
+extern float8 __attribute__((overloadable)) ceil(float8);
+extern float16 __attribute__((overloadable)) ceil(float16);
+
+extern float __attribute__((overloadable)) clamp(float, float, float);
+extern float2 __attribute__((overloadable)) clamp(float2, float2, float2);
+extern float3 __attribute__((overloadable)) clamp(float3, float3, float3);
+extern float4 __attribute__((overloadable)) clamp(float4, float4, float4);
+extern float8 __attribute__((overloadable)) clamp(float8, float8, float8);
+extern float16 __attribute__((overloadable)) clamp(float16, float16, float16);
+extern float __attribute__((overloadable)) clamp(float, float, float);
+extern float2 __attribute__((overloadable)) clamp(float2, float, float);
+extern float3 __attribute__((overloadable)) clamp(float3, float, float);
+extern float4 __attribute__((overloadable)) clamp(float4, float, float);
+extern float8 __attribute__((overloadable)) clamp(float8, float, float);
+extern float16 __attribute__((overloadable)) clamp(float16, float, float);
+
+extern float __attribute__((overloadable)) copysign(float, float);
+extern float2 __attribute__((overloadable)) copysign(float2, float2);
+extern float3 __attribute__((overloadable)) copysign(float3, float3);
+extern float4 __attribute__((overloadable)) copysign(float4, float4);
+extern float8 __attribute__((overloadable)) copysign(float8, float8);
+extern float16 __attribute__((overloadable)) copysign(float16, float16);
+
+extern float __attribute__((overloadable)) cos(float);
+extern float2 __attribute__((overloadable)) cos(float2);
+extern float3 __attribute__((overloadable)) cos(float3);
+extern float4 __attribute__((overloadable)) cos(float4);
+extern float8 __attribute__((overloadable)) cos(float8);
+extern float16 __attribute__((overloadable)) cos(float16);
+
+extern float __attribute__((overloadable)) degrees(float);
+extern float2 __attribute__((overloadable)) degrees(float2);
+extern float3 __attribute__((overloadable)) degrees(float3);
+extern float4 __attribute__((overloadable)) degrees(float4);
+extern float8 __attribute__((overloadable)) degrees(float8);
+extern float16 __attribute__((overloadable)) degrees(float16);
+
+extern float __attribute__((overloadable)) exp(float);
+extern float2 __attribute__((overloadable)) exp(float2);
+extern float3 __attribute__((overloadable)) exp(float3);
+extern float4 __attribute__((overloadable)) exp(float4);
+extern float8 __attribute__((overloadable)) exp(float8);
+extern float16 __attribute__((overloadable)) exp(float16);
+
+extern float __attribute__((overloadable)) exp2(float);
+extern float2 __attribute__((overloadable)) exp2(float2);
+extern float3 __attribute__((overloadable)) exp2(float3);
+extern float4 __attribute__((overloadable)) exp2(float4);
+extern float8 __attribute__((overloadable)) exp2(float8);
+extern float16 __attribute__((overloadable)) exp2(float16);
+
+extern float __attribute__((overloadable)) exp10(float);
+extern float2 __attribute__((overloadable)) exp10(float2);
+extern float3 __attribute__((overloadable)) exp10(float3);
+extern float4 __attribute__((overloadable)) exp10(float4);
+extern float8 __attribute__((overloadable)) exp10(float8);
+extern float16 __attribute__((overloadable)) exp10(float16);
+
+extern float __attribute__((overloadable)) fabs(float);
+extern float2 __attribute__((overloadable)) fabs(float2);
+extern float3 __attribute__((overloadable)) fabs(float3);
+extern float4 __attribute__((overloadable)) fabs(float4);
+extern float8 __attribute__((overloadable)) fabs(float8);
+extern float16 __attribute__((overloadable)) fabs(float16);
+
+extern float __attribute__((overloadable)) floor(float);
+extern float2 __attribute__((overloadable)) floor(float2);
+extern float3 __attribute__((overloadable)) floor(float3);
+extern float4 __attribute__((overloadable)) floor(float4);
+extern float8 __attribute__((overloadable)) floor(float8);
+extern float16 __attribute__((overloadable)) floor(float16);
+
+extern float __attribute__((overloadable)) fmax(float, float);
+extern float2 __attribute__((overloadable)) fmax(float2, float2);
+extern float3 __attribute__((overloadable)) fmax(float3, float3);
+extern float4 __attribute__((overloadable)) fmax(float4, float4);
+extern float8 __attribute__((overloadable)) fmax(float8, float8);
+extern float16 __attribute__((overloadable)) fmax(float16, float16);
+extern float2 __attribute__((overloadable)) fmax(float2, float);
+extern float3 __attribute__((overloadable)) fmax(float3, float);
+extern float4 __attribute__((overloadable)) fmax(float4, float);
+extern float8 __attribute__((overloadable)) fmax(float8, float);
+extern float16 __attribute__((overloadable)) fmax(float16, float);
+
+extern float __attribute__((overloadable)) fmin(float, float);
+extern float2 __attribute__((overloadable)) fmin(float2, float2);
+extern float3 __attribute__((overloadable)) fmin(float3, float3);
+extern float4 __attribute__((overloadable)) fmin(float4, float4);
+extern float8 __attribute__((overloadable)) fmin(float8, float8);
+extern float16 __attribute__((overloadable)) fmin(float16, float16);
+extern float2 __attribute__((overloadable)) fmin(float2, float);
+extern float3 __attribute__((overloadable)) fmin(float3, float);
+extern float4 __attribute__((overloadable)) fmin(float4, float);
+extern float8 __attribute__((overloadable)) fmin(float8, float);
+extern float16 __attribute__((overloadable)) fmin(float16, float);
+
+extern float __attribute__((overloadable)) fmod(float, float);
+extern float2 __attribute__((overloadable)) fmod(float2, float2);
+extern float3 __attribute__((overloadable)) fmod(float3, float3);
+extern float4 __attribute__((overloadable)) fmod(float4, float4);
+extern float8 __attribute__((overloadable)) fmod(float8, float8);
+extern float16 __attribute__((overloadable)) fmod(float16, float16);
+
+extern float __attribute__((overloadable)) log(float);
+extern float2 __attribute__((overloadable)) log(float2);
+extern float3 __attribute__((overloadable)) log(float3);
+extern float4 __attribute__((overloadable)) log(float4);
+extern float8 __attribute__((overloadable)) log(float8);
+extern float16 __attribute__((overloadable)) log(float16);
+
+extern float __attribute__((overloadable)) log2(float);
+extern float2 __attribute__((overloadable)) log2(float2);
+extern float3 __attribute__((overloadable)) log2(float3);
+extern float4 __attribute__((overloadable)) log2(float4);
+extern float8 __attribute__((overloadable)) log2(float8);
+extern float16 __attribute__((overloadable)) log2(float16);
+
+extern float __attribute__((overloadable)) log10(float);
+extern float2 __attribute__((overloadable)) log10(float2);
+extern float3 __attribute__((overloadable)) log10(float3);
+extern float4 __attribute__((overloadable)) log10(float4);
+extern float8 __attribute__((overloadable)) log10(float8);
+extern float16 __attribute__((overloadable)) log10(float16);
+
+extern float __attribute__((overloadable)) max(float, float);
+extern float2 __attribute__((overloadable)) max(float2, float2);
+extern float3 __attribute__((overloadable)) max(float3, float3);
+extern float4 __attribute__((overloadable)) max(float4, float4);
+extern float8 __attribute__((overloadable)) max(float8, float8);
+extern float16 __attribute__((overloadable)) max(float16, float16);
+
+extern float __attribute__((overloadable)) min(float, float);
+extern float2 __attribute__((overloadable)) min(float2, float2);
+extern float3 __attribute__((overloadable)) min(float3, float3);
+extern float4 __attribute__((overloadable)) min(float4, float4);
+extern float8 __attribute__((overloadable)) min(float8, float8);
+extern float16 __attribute__((overloadable)) min(float16, float16);
+
+extern float __attribute__((overloadable)) mix(float, float, float);
+extern float2 __attribute__((overloadable)) mix(float2, float2, float2);
+extern float3 __attribute__((overloadable)) mix(float3, float3, float3);
+extern float4 __attribute__((overloadable)) mix(float4, float4, float4);
+extern float8 __attribute__((overloadable)) mix(float8, float8, float8);
+extern float16 __attribute__((overloadable)) mix(float16, float16, float16);
+extern float __attribute__((overloadable)) mix(float, float, float);
+extern float2 __attribute__((overloadable)) mix(float2, float2, float);
+extern float3 __attribute__((overloadable)) mix(float3, float3, float);
+extern float4 __attribute__((overloadable)) mix(float4, float4, float);
+extern float8 __attribute__((overloadable)) mix(float8, float8, float);
+extern float16 __attribute__((overloadable)) mix(float16, float16, float);
+
+extern float __attribute__((overloadable)) pow(float, float);
+extern float2 __attribute__((overloadable)) pow(float2, float2);
+extern float3 __attribute__((overloadable)) pow(float3, float3);
+extern float4 __attribute__((overloadable)) pow(float4, float4);
+extern float8 __attribute__((overloadable)) pow(float8, float8);
+extern float16 __attribute__((overloadable)) pow(float16, float16);
+
+extern float __attribute__((overloadable)) radians(float);
+extern float2 __attribute__((overloadable)) radians(float2);
+extern float3 __attribute__((overloadable)) radians(float3);
+extern float4 __attribute__((overloadable)) radians(float4);
+extern float8 __attribute__((overloadable)) radians(float8);
+extern float16 __attribute__((overloadable)) radians(float16);
+
+extern float __attribute__((overloadable)) rint(float);
+extern float2 __attribute__((overloadable)) rint(float2);
+extern float3 __attribute__((overloadable)) rint(float3);
+extern float4 __attribute__((overloadable)) rint(float4);
+extern float8 __attribute__((overloadable)) rint(float8);
+extern float16 __attribute__((overloadable)) rint(float16);
+
+extern float __attribute__((overloadable)) round(float);
+extern float2 __attribute__((overloadable)) round(float2);
+extern float3 __attribute__((overloadable)) round(float3);
+extern float4 __attribute__((overloadable)) round(float4);
+extern float8 __attribute__((overloadable)) round(float8);
+extern float16 __attribute__((overloadable)) round(float16);
+
+extern float __attribute__((overloadable)) rsqrt(float);
+extern float2 __attribute__((overloadable)) rsqrt(float2);
+extern float3 __attribute__((overloadable)) rsqrt(float3);
+extern float4 __attribute__((overloadable)) rsqrt(float4);
+extern float8 __attribute__((overloadable)) rsqrt(float8);
+extern float16 __attribute__((overloadable)) rsqrt(float16);
+
+extern float __attribute__((overloadable)) sign(float);
+extern float2 __attribute__((overloadable)) sign(float2);
+extern float3 __attribute__((overloadable)) sign(float3);
+extern float4 __attribute__((overloadable)) sign(float4);
+extern float8 __attribute__((overloadable)) sign(float8);
+extern float16 __attribute__((overloadable)) sign(float16);
+
+extern float __attribute__((overloadable)) sin(float);
+extern float2 __attribute__((overloadable)) sin(float2);
+extern float3 __attribute__((overloadable)) sin(float3);
+extern float4 __attribute__((overloadable)) sin(float4);
+extern float8 __attribute__((overloadable)) sin(float8);
+extern float16 __attribute__((overloadable)) sin(float16);
+
+extern float __attribute__((overloadable)) sqrt(float);
+extern float2 __attribute__((overloadable)) sqrt(float2);
+extern float3 __attribute__((overloadable)) sqrt(float3);
+extern float4 __attribute__((overloadable)) sqrt(float4);
+extern float8 __attribute__((overloadable)) sqrt(float8);
+extern float16 __attribute__((overloadable)) sqrt(float16);
+
+extern float __attribute__((overloadable)) tan(float);
+extern float2 __attribute__((overloadable)) tan(float2);
+extern float3 __attribute__((overloadable)) tan(float3);
+extern float4 __attribute__((overloadable)) tan(float4);
+extern float8 __attribute__((overloadable)) tan(float8);
+extern float16 __attribute__((overloadable)) tan(float16);
+
+extern float __attribute__((overloadable)) trunc(float);
+extern float2 __attribute__((overloadable)) trunc(float2);
+extern float3 __attribute__((overloadable)) trunc(float3);
+extern float4 __attribute__((overloadable)) trunc(float4);
+extern float8 __attribute__((overloadable)) trunc(float8);
+extern float16 __attribute__((overloadable)) trunc(float16);
+
+
+
+
+
+
+// Int ops
+
+extern int __attribute__((overloadable)) abs(int);
+extern int2 __attribute__((overloadable)) abs(int2);
+extern int3 __attribute__((overloadable)) abs(int3);
+extern int4 __attribute__((overloadable)) abs(int4);
+extern int8 __attribute__((overloadable)) abs(int8);
+extern int16 __attribute__((overloadable)) abs(int16);
+
+
+
+/*
+extern float modf(float, float);
+extern float randf(float);
+extern float randf2(float, float);
+extern float fracf(float);
+extern float lerpf(float, float, float);
+extern float mapf(float, float, float, float, float);
+*/
+
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
new file mode 100644
index 0000000..4198a74
--- /dev/null
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -0,0 +1,71 @@
+
+typedef char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+//typedef long int64_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+//typedef long uint64_t;
+
+typedef uint8_t uchar;
+typedef uint16_t ushort;
+typedef uint32_t uint;
+//typedef uint64_t ulong;
+
+typedef int rs_element;
+typedef int rs_type;
+typedef int rs_allocation;
+typedef int rs_sampler;
+typedef int rs_script;
+typedef int rs_mesh;
+typedef int rs_program_fragment;
+typedef int rs_program_vertex;
+typedef int rs_program_raster;
+typedef int rs_program_store;
+
+typedef float float2 __attribute__((ext_vector_type(2)));
+typedef float float3 __attribute__((ext_vector_type(3)));
+typedef float float4 __attribute__((ext_vector_type(4)));
+typedef float float8 __attribute__((ext_vector_type(8)));
+typedef float float16 __attribute__((ext_vector_type(16)));
+
+typedef uchar uchar2 __attribute__((ext_vector_type(2)));
+typedef uchar uchar3 __attribute__((ext_vector_type(3)));
+typedef uchar uchar4 __attribute__((ext_vector_type(4)));
+typedef uchar uchar8 __attribute__((ext_vector_type(8)));
+typedef uchar uchar16 __attribute__((ext_vector_type(16)));
+
+typedef ushort ushort2 __attribute__((ext_vector_type(2)));
+typedef ushort ushort3 __attribute__((ext_vector_type(3)));
+typedef ushort ushort4 __attribute__((ext_vector_type(4)));
+typedef ushort ushort8 __attribute__((ext_vector_type(8)));
+typedef ushort ushort16 __attribute__((ext_vector_type(16)));
+
+typedef uint uint2 __attribute__((ext_vector_type(2)));
+typedef uint uint3 __attribute__((ext_vector_type(3)));
+typedef uint uint4 __attribute__((ext_vector_type(4)));
+typedef uint uint8 __attribute__((ext_vector_type(8)));
+typedef uint uint16 __attribute__((ext_vector_type(16)));
+
+typedef char char2 __attribute__((ext_vector_type(2)));
+typedef char char3 __attribute__((ext_vector_type(3)));
+typedef char char4 __attribute__((ext_vector_type(4)));
+typedef char char8 __attribute__((ext_vector_type(8)));
+typedef char char16 __attribute__((ext_vector_type(16)));
+
+typedef short short2 __attribute__((ext_vector_type(2)));
+typedef short short3 __attribute__((ext_vector_type(3)));
+typedef short short4 __attribute__((ext_vector_type(4)));
+typedef short short8 __attribute__((ext_vector_type(8)));
+typedef short short16 __attribute__((ext_vector_type(16)));
+
+typedef int int2 __attribute__((ext_vector_type(2)));
+typedef int int3 __attribute__((ext_vector_type(3)));
+typedef int int4 __attribute__((ext_vector_type(4)));
+typedef int int8 __attribute__((ext_vector_type(8)));
+typedef int int16 __attribute__((ext_vector_type(16)));
+
+
+
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 140f10c..efbc77a 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -214,7 +214,6 @@
if ((front.w != temp.w) || (front.h != temp.h)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
- this->contentDirty = true;
}
if (temp.sequence != front.sequence) {
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index a6e5644..62ec839 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -157,11 +157,11 @@
/**
* setCoveredRegion - called when the covered region changes. The covered
- * region correspond to any area of the surface that is covered
+ * region corresponds to any area of the surface that is covered
* (transparently or not) by another surface.
*/
virtual void setCoveredRegion(const Region& coveredRegion);
-
+
/**
* validateVisibility - cache a bunch of things
*/
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 26ee285..0722fda 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -674,6 +674,8 @@
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
+ const DisplayHardware& hw(plane.displayHardware());
+ const Region screenRegion(hw.bounds());
Region aboveOpaqueLayers;
Region aboveCoveredLayers;
@@ -689,31 +691,56 @@
// start with the whole surface at its current location
const Layer::State& s(layer->drawingState());
- // handle hidden surfaces by setting the visible region to empty
+ /*
+ * opaqueRegion: area of a surface that is fully opaque.
+ */
Region opaqueRegion;
+
+ /*
+ * visibleRegion: area of a surface that is visible on screen
+ * and not fully transparent. This is essentially the layer's
+ * footprint minus the opaque regions above it.
+ * Areas covered by a translucent surface are considered visible.
+ */
Region visibleRegion;
+
+ /*
+ * coveredRegion: area of a surface that is covered by all
+ * visible regions above it (which includes the translucent areas).
+ */
Region coveredRegion;
+
+
+ // handle hidden surfaces by setting the visible region to empty
if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
const bool translucent = layer->needsBlending();
const Rect bounds(layer->visibleBounds());
visibleRegion.set(bounds);
- coveredRegion = visibleRegion;
+ visibleRegion.andSelf(screenRegion);
+ if (!visibleRegion.isEmpty()) {
+ // Remove the transparent area from the visible region
+ if (translucent) {
+ visibleRegion.subtractSelf(layer->transparentRegionScreen);
+ }
- // Remove the transparent area from the visible region
- if (translucent) {
- visibleRegion.subtractSelf(layer->transparentRegionScreen);
- }
-
- // compute the opaque region
- if (s.alpha==255 && !translucent && layer->getOrientation()>=0) {
- // the opaque region is the visible region
- opaqueRegion = visibleRegion;
+ // compute the opaque region
+ const int32_t layerOrientation = layer->getOrientation();
+ if (s.alpha==255 && !translucent &&
+ ((layerOrientation & Transform::ROT_INVALID) == false)) {
+ // the opaque region is the layer's footprint
+ opaqueRegion = visibleRegion;
+ }
}
}
+ // Clip the covered region to the visible region
+ coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
+
+ // Update aboveCoveredLayers for next (lower) layer
+ aboveCoveredLayers.orSelf(visibleRegion);
+
// subtract the opaque region covered by the layers above us
visibleRegion.subtractSelf(aboveOpaqueLayers);
- coveredRegion.andSelf(aboveCoveredLayers);
// compute this layer's dirty region
if (layer->contentDirty) {
@@ -724,19 +751,30 @@
layer->contentDirty = false;
} else {
/* compute the exposed region:
- * exposed = what's VISIBLE and NOT COVERED now
- * but was COVERED before
+ * the exposed region consists of two components:
+ * 1) what's VISIBLE now and was COVERED before
+ * 2) what's EXPOSED now less what was EXPOSED before
+ *
+ * note that (1) is conservative, we start with the whole
+ * visible region but only keep what used to be covered by
+ * something -- which mean it may have been exposed.
+ *
+ * (2) handles areas that were not covered by anything but got
+ * exposed because of a resize.
*/
- dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
+ const Region newExposed = visibleRegion - coveredRegion;
+ const Region oldVisibleRegion = layer->visibleRegionScreen;
+ const Region oldCoveredRegion = layer->coveredRegionScreen;
+ const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+ dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
}
dirty.subtractSelf(aboveOpaqueLayers);
// accumulate to the screen dirty region
dirtyRegion.orSelf(dirty);
- // Update aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
+ // Update aboveOpaqueLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
- aboveCoveredLayers.orSelf(visibleRegion);
// Store the visible region is screen space
layer->setVisibleRegion(visibleRegion);
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 65ce1c1..a17e8ac 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -34,7 +34,7 @@
// ----------------------------------------------------------------------------
SharedClient::SharedClient()
- : lock(Mutex::SHARED)
+ : lock(Mutex::SHARED), cv(Condition::SHARED)
{
}
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 57d5fc3..6ae7e74 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -78,8 +78,6 @@
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
- Mutex::Autolock _l(mLock);
-
// make sure to not allocate a 0 x 0 buffer
w = clamp(w);
h = clamp(h);
@@ -118,8 +116,6 @@
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
- Mutex::Autolock _l(mLock);
-
status_t err;
if (sw_gralloc_handle_t::validate(handle) < 0) {
err = mAllocDev->free(mAllocDev, handle);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 9e4a16b..28bc599 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -117,6 +117,37 @@
*/
public static final String KEY_LOCATION_CHANGED = "location";
+ /**
+ * Broadcast intent action indicating that the GPS has either been
+ * enabled or disabled. An intent extra provides this state as a boolean,
+ * where {@code true} means enabled.
+ * @see #EXTRA_GPS_ENABLED
+ *
+ * {@hide}
+ */
+ public static final String GPS_ENABLED_CHANGE_ACTION =
+ "android.location.GPS_ENABLED_CHANGE";
+
+ /**
+ * Broadcast intent action indicating that the GPS has either started or
+ * stopped receiving GPS fixes. An intent extra provides this state as a
+ * boolean, where {@code true} means that the GPS is actively receiving fixes.
+ * @see #EXTRA_GPS_ENABLED
+ *
+ * {@hide}
+ */
+ public static final String GPS_FIX_CHANGE_ACTION =
+ "android.location.GPS_FIX_CHANGE";
+
+ /**
+ * The lookup key for a boolean that indicates whether GPS is enabled or
+ * disabled. {@code true} means GPS is enabled. Retrieve it with
+ * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_GPS_ENABLED = "enabled";
+
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index a5466d1..d3a71b3 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -23,6 +23,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.location.LocationManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
@@ -81,7 +82,7 @@
private final Context mContext;
// parent gps location provider
- private final GpsLocationProvider mGpsLocationProvider;
+ private final LocationManager mLocationManager;
// configuration of notificaiton behavior
private boolean mPlaySounds = false;
@@ -93,25 +94,25 @@
public static class GpsNiNotification
{
- int notificationId;
- int niType;
- boolean needNotify;
- boolean needVerify;
- boolean privacyOverride;
- int timeout;
- int defaultResponse;
- String requestorId;
- String text;
- int requestorIdEncoding;
- int textEncoding;
- Bundle extras;
+ public int notificationId;
+ public int niType;
+ public boolean needNotify;
+ public boolean needVerify;
+ public boolean privacyOverride;
+ public int timeout;
+ public int defaultResponse;
+ public String requestorId;
+ public String text;
+ public int requestorIdEncoding;
+ public int textEncoding;
+ public Bundle extras;
};
public static class GpsNiResponse {
- /* User reponse, one of the values in GpsUserResponseType */
- int userResponse;
- /* Optional extra data to pass with the user response */
- Bundle extras;
+ /* User reponse, one of the values in GpsUserResponseType */
+ int userResponse;
+ /* Optional extra data to pass with the user response */
+ Bundle extras;
};
/**
@@ -122,63 +123,57 @@
*/
private Notification mNiNotification;
- public GpsNetInitiatedHandler(Context context, GpsLocationProvider gpsLocationProvider) {
- mContext = context;
- mGpsLocationProvider = gpsLocationProvider;
+ public GpsNetInitiatedHandler(Context context) {
+ mContext = context;
+ mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
}
// Handles NI events from HAL
public void handleNiNotification(GpsNiNotification notif)
{
- if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
- + " requestorId: " + notif.requestorId + " text: " + notif.text);
-
- // Notify and verify with immediate pop-up
- if (notif.needNotify && notif.needVerify && mPopupImmediately)
- {
- // Popup the dialog box now
- openNiDialog(notif);
- }
-
- // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
- if (notif.needNotify && !notif.needVerify ||
- notif.needNotify && notif.needVerify && !mPopupImmediately)
- {
- // Show the notification
+ if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
+ + " requestorId: " + notif.requestorId + " text: " + notif.text);
- // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
- // when the user opens the notification message
-
- setNiNotification(notif);
- }
-
- // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
- if ( notif.needNotify && !notif.needVerify ||
- !notif.needNotify && !notif.needVerify ||
- notif.privacyOverride)
- {
- try {
- mGpsLocationProvider.getNetInitiatedListener().sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
- }
- catch (RemoteException e)
- {
- Log.e(TAG, e.getMessage());
- }
- }
-
- //////////////////////////////////////////////////////////////////////////
- // A note about timeout
- // According to the protocol, in the need_notify and need_verify case,
- // a default response should be sent when time out.
- //
- // In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
- // and this class GpsNetInitiatedHandler does not need to do anything.
- //
- // However, the UI should at least close the dialog when timeout. Further,
- // for more general handling, timeout response should be added to the Handler here.
- //
+ // Notify and verify with immediate pop-up
+ if (notif.needNotify && notif.needVerify && mPopupImmediately)
+ {
+ // Popup the dialog box now
+ openNiDialog(notif);
+ }
+
+ // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
+ if (notif.needNotify && !notif.needVerify ||
+ notif.needNotify && notif.needVerify && !mPopupImmediately)
+ {
+ // Show the notification
+
+ // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
+ // when the user opens the notification message
+
+ setNiNotification(notif);
+ }
+
+ // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
+ if ( notif.needNotify && !notif.needVerify ||
+ !notif.needNotify && !notif.needVerify ||
+ notif.privacyOverride)
+ {
+ mLocationManager.sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // A note about timeout
+ // According to the protocol, in the need_notify and need_verify case,
+ // a default response should be sent when time out.
+ //
+ // In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
+ // and this class GpsNetInitiatedHandler does not need to do anything.
+ //
+ // However, the UI should at least close the dialog when timeout. Further,
+ // for more general handling, timeout response should be added to the Handler here.
+ //
}
-
+
// Sets the NI notification.
private synchronized void setNiNotification(GpsNiNotification notif) {
NotificationManager notificationManager = (NotificationManager) mContext
@@ -186,272 +181,272 @@
if (notificationManager == null) {
return;
}
-
- String title = getNotifTitle(notif);
- String message = getNotifMessage(notif);
-
+
+ String title = getNotifTitle(notif);
+ String message = getNotifMessage(notif);
+
if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
- ", title: " + title +
- ", message: " + message);
-
- // Construct Notification
- if (mNiNotification == null) {
- mNiNotification = new Notification();
- mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
- mNiNotification.when = 0;
+ ", title: " + title +
+ ", message: " + message);
+
+ // Construct Notification
+ if (mNiNotification == null) {
+ mNiNotification = new Notification();
+ mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
+ mNiNotification.when = 0;
}
-
+
if (mPlaySounds) {
- mNiNotification.defaults |= Notification.DEFAULT_SOUND;
+ mNiNotification.defaults |= Notification.DEFAULT_SOUND;
} else {
- mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
}
-
+
mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
mNiNotification.tickerText = getNotifTicker(notif);
-
+
// if not to popup dialog immediately, pending intent will open the dialog
- Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
+ Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
mNiNotification.setLatestEventInfo(mContext, title, message, pi);
-
+
if (visible) {
notificationManager.notify(notif.notificationId, mNiNotification);
} else {
notificationManager.cancel(notif.notificationId);
}
}
-
- // Opens the notification dialog and waits for user input
- private void openNiDialog(GpsNiNotification notif)
- {
- Intent intent = getDlgIntent(notif);
-
- if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
- ", requestorId: " + notif.requestorId +
- ", text: " + notif.text);
- mContext.startActivity(intent);
+ // Opens the notification dialog and waits for user input
+ private void openNiDialog(GpsNiNotification notif)
+ {
+ Intent intent = getDlgIntent(notif);
+
+ if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
+ ", requestorId: " + notif.requestorId +
+ ", text: " + notif.text);
+
+ mContext.startActivity(intent);
}
-
+
// Construct the intent for bringing up the dialog activity, which shows the
// notification and takes user input
private Intent getDlgIntent(GpsNiNotification notif)
{
- Intent intent = new Intent();
- String title = getDialogTitle(notif);
- String message = getDialogMessage(notif);
-
- // directly bring up the NI activity
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
+ Intent intent = new Intent();
+ String title = getDialogTitle(notif);
+ String message = getDialogMessage(notif);
- // put data in the intent
- intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
- intent.putExtra(NI_INTENT_KEY_TITLE, title);
- intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
- intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
- intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
-
- if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
- ", timeout: " + notif.timeout);
-
- return intent;
+ // directly bring up the NI activity
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
+
+ // put data in the intent
+ intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
+ intent.putExtra(NI_INTENT_KEY_TITLE, title);
+ intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
+ intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
+ intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
+
+ if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
+ ", timeout: " + notif.timeout);
+
+ return intent;
}
-
+
// Converts a string (or Hex string) to a char array
static byte[] stringToByteArray(String original, boolean isHex)
{
- int length = isHex ? original.length() / 2 : original.length();
- byte[] output = new byte[length];
- int i;
-
- if (isHex)
- {
- for (i = 0; i < length; i++)
- {
- output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
- }
- }
- else {
- for (i = 0; i < length; i++)
- {
- output[i] = (byte) original.charAt(i);
- }
- }
-
- return output;
+ int length = isHex ? original.length() / 2 : original.length();
+ byte[] output = new byte[length];
+ int i;
+
+ if (isHex)
+ {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
+ }
+ }
+ else {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) original.charAt(i);
+ }
+ }
+
+ return output;
}
-
+
/**
* Unpacks an byte array containing 7-bit packed characters into a String.
- *
+ *
* @param input a 7-bit packed char array
* @return the unpacked String
*/
static String decodeGSMPackedString(byte[] input)
{
- final char CHAR_CR = 0x0D;
- int nStridx = 0;
- int nPckidx = 0;
- int num_bytes = input.length;
- int cPrev = 0;
- int cCurr = 0;
- byte nShift;
- byte nextChar;
- byte[] stringBuf = new byte[input.length * 2];
- String result = "";
-
- while(nPckidx < num_bytes)
- {
- nShift = (byte) (nStridx & 0x07);
- cCurr = input[nPckidx++];
- if (cCurr < 0) cCurr += 256;
+ final char CHAR_CR = 0x0D;
+ int nStridx = 0;
+ int nPckidx = 0;
+ int num_bytes = input.length;
+ int cPrev = 0;
+ int cCurr = 0;
+ byte nShift;
+ byte nextChar;
+ byte[] stringBuf = new byte[input.length * 2];
+ String result = "";
- /* A 7-bit character can be split at the most between two bytes of packed
- ** data.
- */
- nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
- stringBuf[nStridx++] = nextChar;
+ while(nPckidx < num_bytes)
+ {
+ nShift = (byte) (nStridx & 0x07);
+ cCurr = input[nPckidx++];
+ if (cCurr < 0) cCurr += 256;
- /* Special case where the whole of the next 7-bit character fits inside
- ** the current byte of packed data.
- */
- if(nShift == 6)
- {
- /* If the next 7-bit character is a CR (0x0D) and it is the last
- ** character, then it indicates a padding character. Drop it.
- */
- if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
- {
- break;
- }
-
- nextChar = (byte) (cCurr >> 1);
- stringBuf[nStridx++] = nextChar;
- }
+ /* A 7-bit character can be split at the most between two bytes of packed
+ ** data.
+ */
+ nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
+ stringBuf[nStridx++] = nextChar;
- cPrev = cCurr;
- }
-
- try{
- result = new String(stringBuf, 0, nStridx, "US-ASCII");
- }
- catch (UnsupportedEncodingException e)
- {
- Log.e(TAG, e.getMessage());
- }
-
- return result;
+ /* Special case where the whole of the next 7-bit character fits inside
+ ** the current byte of packed data.
+ */
+ if(nShift == 6)
+ {
+ /* If the next 7-bit character is a CR (0x0D) and it is the last
+ ** character, then it indicates a padding character. Drop it.
+ */
+ if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
+ {
+ break;
+ }
+
+ nextChar = (byte) (cCurr >> 1);
+ stringBuf[nStridx++] = nextChar;
+ }
+
+ cPrev = cCurr;
+ }
+
+ try {
+ result = new String(stringBuf, 0, nStridx, "US-ASCII");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+
+ return result;
}
-
+
static String decodeUTF8String(byte[] input)
{
- String decoded = "";
- try {
- decoded = new String(input, "UTF-8");
- }
- catch (UnsupportedEncodingException e)
- {
- Log.e(TAG, e.getMessage());
- }
- return decoded;
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
}
-
+
static String decodeUCS2String(byte[] input)
{
- String decoded = "";
- try {
- decoded = new String(input, "UTF-16");
- }
- catch (UnsupportedEncodingException e)
- {
- Log.e(TAG, e.getMessage());
- }
- return decoded;
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-16");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
}
-
+
/** Decode NI string
- *
+ *
* @param original The text string to be decoded
* @param isHex Specifies whether the content of the string has been encoded as a Hex string. Encoding
- * a string as Hex can allow zeros inside the coded text.
+ * a string as Hex can allow zeros inside the coded text.
* @param coding Specifies the coding scheme of the string, such as GSM, UTF8, UCS2, etc. This coding scheme
- * needs to match those used passed to HAL from the native GPS driver. Decoding is done according
+ * needs to match those used passed to HAL from the native GPS driver. Decoding is done according
* to the <code> coding </code>, after a Hex string is decoded. Generally, if the
- * notification strings don't need further decoding, <code> coding </code> encoding can be
+ * notification strings don't need further decoding, <code> coding </code> encoding can be
* set to -1, and <code> isHex </code> can be false.
* @return the decoded string
*/
static private String decodeString(String original, boolean isHex, int coding)
{
- String decoded = original;
- byte[] input = stringToByteArray(original, isHex);
+ String decoded = original;
+ byte[] input = stringToByteArray(original, isHex);
- switch (coding) {
- case GPS_ENC_NONE:
- decoded = original;
- break;
-
- case GPS_ENC_SUPL_GSM_DEFAULT:
- decoded = decodeGSMPackedString(input);
- break;
-
- case GPS_ENC_SUPL_UTF8:
- decoded = decodeUTF8String(input);
- break;
-
- case GPS_ENC_SUPL_UCS2:
- decoded = decodeUCS2String(input);
- break;
-
- case GPS_ENC_UNKNOWN:
- decoded = original;
- break;
-
- default:
- Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
- break;
- }
- return decoded;
+ switch (coding) {
+ case GPS_ENC_NONE:
+ decoded = original;
+ break;
+
+ case GPS_ENC_SUPL_GSM_DEFAULT:
+ decoded = decodeGSMPackedString(input);
+ break;
+
+ case GPS_ENC_SUPL_UTF8:
+ decoded = decodeUTF8String(input);
+ break;
+
+ case GPS_ENC_SUPL_UCS2:
+ decoded = decodeUCS2String(input);
+ break;
+
+ case GPS_ENC_UNKNOWN:
+ decoded = original;
+ break;
+
+ default:
+ Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
+ break;
+ }
+ return decoded;
}
-
+
// change this to configure notification display
static private String getNotifTicker(GpsNiNotification notif)
{
- String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
- decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
- decodeString(notif.text, mIsHexInput, notif.textEncoding));
- return ticker;
+ String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return ticker;
}
-
+
// change this to configure notification display
static private String getNotifTitle(GpsNiNotification notif)
{
- String title = String.format("Position Request");
- return title;
+ String title = String.format("Position Request");
+ return title;
}
-
+
// change this to configure notification display
static private String getNotifMessage(GpsNiNotification notif)
{
- String message = String.format(
- "NI Request received from [%s] for client [%s]!",
- decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
- decodeString(notif.text, mIsHexInput, notif.textEncoding));
- return message;
+ String message = String.format(
+ "NI Request received from [%s] for client [%s]!",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return message;
}
-
+
// change this to configure dialog display (for verification)
static public String getDialogTitle(GpsNiNotification notif)
{
- return getNotifTitle(notif);
+ return getNotifTitle(notif);
}
-
+
// change this to configure dialog display (for verification)
static private String getDialogMessage(GpsNiNotification notif)
{
- return getNotifMessage(notif);
+ return getNotifMessage(notif);
}
-
+
}
diff --git a/location/java/com/android/internal/location/NmeaParser.java b/location/java/com/android/internal/location/NmeaParser.java
deleted file mode 100644
index 43afa1d..0000000
--- a/location/java/com/android/internal/location/NmeaParser.java
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.location;
-
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import android.location.Location;
-import android.os.Bundle;
-import android.util.Log;
-
-/**
- * {@hide}
- */
-public class NmeaParser {
-
- private static final String TAG = "NmeaParser";
-
- private static final TimeZone sUtcTimeZone = TimeZone.getTimeZone("UTC");
-
- private static final float KNOTS_TO_METERS_PER_SECOND = 0.51444444444f;
-
- private final String mName;
-
- private int mYear = -1;
- private int mMonth;
- private int mDay;
-
- private long mTime = -1;
- private long mBaseTime;
- private double mLatitude;
- private double mLongitude;
-
- private boolean mHasAltitude;
- private double mAltitude;
- private boolean mHasBearing;
- private float mBearing;
- private boolean mHasSpeed;
- private float mSpeed;
-
- private boolean mNewWaypoint = false;
- private Location mLocation = null;
- private Bundle mExtras;
-
- public NmeaParser(String name) {
- mName = name;
- }
-
- private boolean updateTime(String time) {
- if (time.length() < 6) {
- return false;
- }
- if (mYear == -1) {
- // Since we haven't seen a day/month/year yet,
- // we can't construct a meaningful time stamp.
- // Clean up any old data.
- mLatitude = 0.0;
- mLongitude = 0.0;
- mHasAltitude = false;
- mHasBearing = false;
- mHasSpeed = false;
- mExtras = null;
- return false;
- }
-
- int hour, minute;
- float second;
- try {
- hour = Integer.parseInt(time.substring(0, 2));
- minute = Integer.parseInt(time.substring(2, 4));
- second = Float.parseFloat(time.substring(4, time.length()));
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Error parsing timestamp " + time);
- return false;
- }
-
- int isecond = (int) second;
- int millis = (int) ((second - isecond) * 1000);
- Calendar c = new GregorianCalendar(sUtcTimeZone);
- c.set(mYear, mMonth, mDay, hour, minute, isecond);
- long newTime = c.getTimeInMillis() + millis;
-
- if (mTime == -1) {
- mTime = 0;
- mBaseTime = newTime;
- }
- newTime -= mBaseTime;
-
- // If the timestamp has advanced, copy the temporary data
- // into a new Location
- if (newTime != mTime) {
- mNewWaypoint = true;
- mLocation = new Location(mName);
- mLocation.setTime(mTime);
- mLocation.setLatitude(mLatitude);
- mLocation.setLongitude(mLongitude);
- if (mHasAltitude) {
- mLocation.setAltitude(mAltitude);
- }
- if (mHasBearing) {
- mLocation.setBearing(mBearing);
- }
- if (mHasSpeed) {
- mLocation.setSpeed(mSpeed);
- }
- mLocation.setExtras(mExtras);
- mExtras = null;
-
- mTime = newTime;
- mHasAltitude = false;
- mHasBearing = false;
- mHasSpeed = false;
- }
- return true;
- }
-
- private boolean updateDate(String date) {
- if (date.length() != 6) {
- return false;
- }
- int month, day, year;
- try {
- day = Integer.parseInt(date.substring(0, 2));
- month = Integer.parseInt(date.substring(2, 4));
- year = 2000 + Integer.parseInt(date.substring(4, 6));
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Error parsing date " + date);
- return false;
- }
-
- mYear = year;
- mMonth = month;
- mDay = day;
- return true;
- }
-
- private boolean updateTime(String time, String date) {
- if (!updateDate(date)) {
- return false;
- }
- return updateTime(time);
- }
-
- private boolean updateIntExtra(String name, String value) {
- int val;
- try {
- val = Integer.parseInt(value);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing int " + name + ": " + value, nfe);
- return false;
- }
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putInt(name, val);
- return true;
- }
-
- private boolean updateFloatExtra(String name, String value) {
- float val;
- try {
- val = Float.parseFloat(value);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing float " + name + ": " + value, nfe);
- return false;
- }
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putFloat(name, val);
- return true;
- }
-
- private boolean updateDoubleExtra(String name, String value) {
- double val;
- try {
- val = Double.parseDouble(value);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing double " + name + ": " + value, nfe);
- return false;
- }
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putDouble(name, val);
- return true;
- }
-
- private double convertFromHHMM(String coord) {
- double val = Double.parseDouble(coord);
- int degrees = ((int) Math.floor(val)) / 100;
- double minutes = val - (degrees * 100);
- double dcoord = degrees + minutes / 60.0;
- return dcoord;
- }
-
- private boolean updateLatLon(String latitude, String latitudeHemi,
- String longitude, String longitudeHemi) {
- if (latitude.length() == 0 || longitude.length() == 0) {
- return false;
- }
-
- // Lat/long values are expressed as {D}DDMM.MMMM
- double lat, lon;
- try {
- lat = convertFromHHMM(latitude);
- if (latitudeHemi.charAt(0) == 'S') {
- lat = -lat;
- }
- } catch (NumberFormatException nfe1) {
- Log.e(TAG, "Exception parsing lat/long: " + nfe1, nfe1);
- return false;
- }
-
- try {
- lon = convertFromHHMM(longitude);
- if (longitudeHemi.charAt(0) == 'W') {
- lon = -lon;
- }
- } catch (NumberFormatException nfe2) {
- Log.e(TAG, "Exception parsing lat/long: " + nfe2, nfe2);
- return false;
- }
-
- // Only update if both were parsed cleanly
- mLatitude = lat;
- mLongitude = lon;
- return true;
- }
-
- private boolean updateAltitude(String altitude) {
- if (altitude.length() == 0) {
- return false;
- }
- double alt;
- try {
- alt = Double.parseDouble(altitude);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing altitude " + altitude + ": " + nfe,
- nfe);
- return false;
- }
-
- mHasAltitude = true;
- mAltitude = alt;
- return true;
- }
-
- private boolean updateBearing(String bearing) {
- float brg;
- try {
- brg = Float.parseFloat(bearing);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing bearing " + bearing + ": " + nfe,
- nfe);
- return false;
- }
-
- mHasBearing = true;
- mBearing = brg;
- return true;
- }
-
- private boolean updateSpeed(String speed) {
- float spd;
- try {
- spd = Float.parseFloat(speed) * KNOTS_TO_METERS_PER_SECOND;
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Exception parsing speed " + speed + ": " + nfe, nfe);
- return false;
- }
-
- mHasSpeed = true;
- mSpeed = spd;
- return true;
- }
-
- public boolean parseSentence(String s) {
- int len = s.length();
- if (len < 9) {
- return false;
- }
- if (s.charAt(len - 3) == '*') {
- // String checksum = s.substring(len - 4, len);
- s = s.substring(0, len - 3);
- }
- String[] tokens = s.split(",");
- String sentenceId = tokens[0].substring(3, 6);
-
- int idx = 1;
- try {
- if (sentenceId.equals("GGA")) {
- String time = tokens[idx++];
- String latitude = tokens[idx++];
- String latitudeHemi = tokens[idx++];
- String longitude = tokens[idx++];
- String longitudeHemi = tokens[idx++];
- String fixQuality = tokens[idx++];
- String numSatellites = tokens[idx++];
- String horizontalDilutionOfPrecision = tokens[idx++];
- String altitude = tokens[idx++];
- String altitudeUnits = tokens[idx++];
- String heightOfGeoid = tokens[idx++];
- String heightOfGeoidUnits = tokens[idx++];
- String timeSinceLastDgpsUpdate = tokens[idx++];
-
- updateTime(time);
- updateLatLon(latitude, latitudeHemi,
- longitude, longitudeHemi);
- updateAltitude(altitude);
- // updateQuality(fixQuality);
- updateIntExtra("numSatellites", numSatellites);
- updateFloatExtra("hdop", horizontalDilutionOfPrecision);
-
- if (mNewWaypoint) {
- mNewWaypoint = false;
- return true;
- }
- } else if (sentenceId.equals("GSA")) {
- // DOP and active satellites
- String selectionMode = tokens[idx++]; // m=manual, a=auto 2d/3d
- String mode = tokens[idx++]; // 1=no fix, 2=2d, 3=3d
- for (int i = 0; i < 12; i++) {
- String id = tokens[idx++];
- }
- String pdop = tokens[idx++];
- String hdop = tokens[idx++];
- String vdop = tokens[idx++];
-
- // TODO - publish satellite ids
- updateFloatExtra("pdop", pdop);
- updateFloatExtra("hdop", hdop);
- updateFloatExtra("vdop", vdop);
- } else if (sentenceId.equals("GSV")) {
- // Satellites in view
- String numMessages = tokens[idx++];
- String messageNum = tokens[idx++];
- String svsInView = tokens[idx++];
- for (int i = 0; i < 4; i++) {
- if (idx + 2 < tokens.length) {
- String prnNumber = tokens[idx++];
- String elevation = tokens[idx++];
- String azimuth = tokens[idx++];
- if (idx < tokens.length) {
- String snr = tokens[idx++];
- }
- }
- }
- // TODO - publish this info
- } else if (sentenceId.equals("RMC")) {
- // Recommended minimum navigation information
- String time = tokens[idx++];
- String fixStatus = tokens[idx++];
- String latitude = tokens[idx++];
- String latitudeHemi = tokens[idx++];
- String longitude = tokens[idx++];
- String longitudeHemi = tokens[idx++];
- String speed = tokens[idx++];
- String bearing = tokens[idx++];
- String utcDate = tokens[idx++];
- String magneticVariation = tokens[idx++];
- String magneticVariationDir = tokens[idx++];
- String mode = tokens[idx++];
-
- if (fixStatus.charAt(0) == 'A') {
- updateTime(time, utcDate);
- updateLatLon(latitude, latitudeHemi,
- longitude, longitudeHemi);
- updateBearing(bearing);
- updateSpeed(speed);
- }
-
- if (mNewWaypoint) {
- return true;
- }
- } else {
- Log.e(TAG, "Unknown sentence: " + s);
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // do nothing - sentence will have no effect
- Log.e(TAG, "AIOOBE", e);
-
- for (int i = 0; i < tokens.length; i++) {
- Log.e(TAG, "Got token #" + i + " = " + tokens[i]);
- }
- }
-
- return false;
- }
-
-// } else if (sentenceId.equals("GLL")) {
-// // Geographics position lat/long
-// String latitude = tokens[idx++];
-// String latitudeHemi = tokens[idx++];
-// String longitude = tokens[idx++];
-// String longitudeHemi = tokens[idx++];
-// String time = tokens[idx++];
-// String status = tokens[idx++];
-// String mode = tokens[idx++];
-// String checksum = tokens[idx++];
-//
-// if (status.charAt(0) == 'A') {
-// updateTime(time);
-// updateLatLon(latitude, latitudeHemi, longitude, longitudeHemi);
-// }
-//} else if (sentenceId.equals("VTG")) {
-// String trackMadeGood = tokens[idx++];
-// String t = tokens[idx++];
-// String unused1 = tokens[idx++];
-// String unused2 = tokens[idx++];
-// String groundSpeedKnots = tokens[idx++];
-// String n = tokens[idx++];
-// String groundSpeedKph = tokens[idx++];
-// String k = tokens[idx++];
-// String checksum = tokens[idx++];
-//
-// updateSpeed(groundSpeedKph);
-
- public Location getLocation() {
- return mLocation;
- }
-}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4fa3327..b56e638 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Binder;
@@ -29,6 +30,7 @@
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
+import android.view.KeyEvent;
import java.util.Iterator;
import java.util.HashMap;
@@ -688,6 +690,116 @@
}
}
+ //====================================================================
+ // Bluetooth SCO control
+ /**
+ * Sticky broadcast intent action indicating that the bluetoooth SCO audio
+ * connection state has changed. The intent contains on extra {@link #EXTRA_SCO_AUDIO_STATE}
+ * indicating the new state which is either {@link #SCO_AUDIO_STATE_DISCONNECTED}
+ * or {@link #SCO_AUDIO_STATE_CONNECTED}
+ *
+ * @see #startBluetoothSco()
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
+ "android.media.SCO_AUDIO_STATE_CHANGED";
+ /**
+ * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new
+ * bluetooth SCO connection state.
+ */
+ public static final String EXTRA_SCO_AUDIO_STATE =
+ "android.media.extra.SCO_AUDIO_STATE";
+
+ /**
+ * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
+ * SCO audio channel is not established
+ */
+ public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
+ /**
+ * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
+ * SCO audio channel is established
+ */
+ public static final int SCO_AUDIO_STATE_CONNECTED = 1;
+ /**
+ * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that
+ * there was an error trying to obtain the state
+ */
+ public static final int SCO_AUDIO_STATE_ERROR = -1;
+
+
+ /**
+ * Indicates if current platform supports use of SCO for off call use cases.
+ * Application wanted to use bluetooth SCO audio when the phone is not in call
+ * must first call thsi method to make sure that the platform supports this
+ * feature.
+ * @return true if bluetooth SCO can be used for audio when not in call
+ * false otherwise
+ * @see #startBluetoothSco()
+ */
+ public boolean isBluetoothScoAvailableOffCall() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_bluetooth_sco_off_call);
+ }
+
+ /**
+ * Start bluetooth SCO audio connection.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * <p>This method can be used by applications wanting to send and received audio
+ * to/from a bluetooth SCO headset while the phone is not in call.
+ * <p>As the SCO connection establishment can take several seconds,
+ * applications should not rely on the connection to be available when the method
+ * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED}
+ * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
+ * <p>As the connection is not guaranteed to succeed, applications must wait for this intent with
+ * a timeout.
+ * <p>When finished with the SCO connection or if the establishment times out,
+ * the application must call {@link #stopBluetoothSco()} to clear the request and turn
+ * down the bluetooth connection.
+ * <p>Even if a SCO connection is established, the following restrictions apply on audio
+ * output streams so that they can be routed to SCO headset:
+ * - the stream type must be {@link #STREAM_VOICE_CALL}
+ * - the format must be mono
+ * - the sampling must be 16kHz or 8kHz
+ * <p>The following restrictions apply on input streams:
+ * - the format must be mono
+ * - the sampling must be 8kHz
+ *
+ * <p>Note that the phone application always has the priority on the usage of the SCO
+ * connection for telephony. If this method is called while the phone is in call
+ * it will be ignored. Similarly, if a call is received or sent while an application
+ * is using the SCO connection, the connection will be lost for the application and NOT
+ * returned automatically when the call ends.
+ * @see #stopBluetoothSco()
+ * @see #ACTION_SCO_AUDIO_STATE_CHANGED
+ */
+ public void startBluetoothSco(){
+ IAudioService service = getService();
+ try {
+ service.startBluetoothSco(mICallBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in startBluetoothSco", e);
+ }
+ }
+
+ /**
+ * Stop bluetooth SCO audio connection.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * <p>This method must be called by applications having requested the use of
+ * bluetooth SCO audio with {@link #startBluetoothSco()}
+ * when finished with the SCO connection or if the establishment times out.
+ * @see #startBluetoothSco()
+ */
+ public void stopBluetoothSco(){
+ IAudioService service = getService();
+ try {
+ service.stopBluetoothSco(mICallBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in stopBluetoothSco", e);
+ }
+ }
+
/**
* Request use of Bluetooth SCO headset for communications.
* <p>
@@ -1132,16 +1244,6 @@
}
/**
- * Used to indicate a loss of audio focus of unknown duration.
- * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
- */
- public static final int AUDIOFOCUS_LOSS = -1;
- /**
- * Used to indicate a transient loss of audio focus.
- * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
- */
- public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
- /**
* Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
* @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
@@ -1155,6 +1257,34 @@
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
*/
public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
+ /**
+ * Used to indicate a temporary request of audio focus, anticipated to last a short
+ * amount of time, and where it is acceptable for other audio applications to keep playing
+ * after having lowered their output level (also referred to as "ducking").
+ * Examples of temporary changes are the playback of driving directions where playback of music
+ * in the background is acceptable.
+ * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+ * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
+ */
+ public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
+ /**
+ * Used to indicate a loss of audio focus of unknown duration.
+ * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+ */
+ public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
+ /**
+ * Used to indicate a transient loss of audio focus.
+ * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+ */
+ public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
+ /**
+ * Used to indicate a transient loss of audio focus where the loser of the audio focus can
+ * lower its output volume if it wants to continue playing (also referred to as "ducking"), as
+ * the new focus owner doesn't require others to be silent.
+ * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+ */
+ public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
+ -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
/**
* Interface definition for a callback to be invoked when the audio focus of the system is
@@ -1166,11 +1296,12 @@
* The focusChange value indicates whether the focus was gained,
* whether the focus was lost, and whether that loss is transient, or whether the new focus
* holder will hold it for an unknown amount of time.
- * When losing focus, listeners can use the duration hint to decide what
- * behavior to adopt when losing focus. A music player could for instance elect to duck its
- * music stream for transient focus losses, and pause otherwise.
- * @param focusChange one of {@link AudioManager#AUDIOFOCUS_GAIN},
- * {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}.
+ * When losing focus, listeners can use the focus change information to decide what
+ * behavior to adopt when losing focus. A music player could for instance elect to lower
+ * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
+ * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
+ * {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
+ * and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
*/
public void onAudioFocusChanged(int focusChange);
}
@@ -1179,7 +1310,7 @@
* Map to convert focus event listener IDs, as used in the AudioService audio focus stack,
* to actual listener objects.
*/
- private HashMap<String, OnAudioFocusChangeListener> mFocusIdListenerMap =
+ private HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap =
new HashMap<String, OnAudioFocusChangeListener>();
/**
* Lock to prevent concurrent changes to the list of focus listeners for this AudioManager
@@ -1188,21 +1319,15 @@
private final Object mFocusListenerLock = new Object();
private OnAudioFocusChangeListener findFocusListener(String id) {
- return mFocusIdListenerMap.get(id);
+ return mAudioFocusIdListenerMap.get(id);
}
/**
* Handler for audio focus events coming from the audio service.
*/
- private FocusEventHandlerDelegate mFocusEventHandlerDelegate = new FocusEventHandlerDelegate();
- /**
- * Event id denotes a loss of focus
- */
- private static final int AUDIOFOCUS_EVENT_LOSS = 0;
- /**
- * Event id denotes a gain of focus
- */
- private static final int AUDIOFOCUS_EVENT_GAIN = 1;
+ private FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
+ new FocusEventHandlerDelegate();
+
/**
* Helper class to handle the forwarding of audio focus events to the appropriate listener
*/
@@ -1239,18 +1364,18 @@
}
}
- private IAudioFocusDispatcher mFocusDispatcher = new IAudioFocusDispatcher.Stub() {
+ private IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
public void dispatchAudioFocusChange(int focusChange, String id) {
- Message m = mFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
- mFocusEventHandlerDelegate.getHandler().sendMessage(m);
+ Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
+ mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);
}
};
- private String getIdForFocusListener(OnAudioFocusChangeListener l) {
+ private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
if (l == null) {
- return new String();
+ return new String(this.toString());
} else {
return new String(this.toString() + l.toString());
}
@@ -1260,14 +1385,11 @@
* Register a listener for audio focus updates.
*/
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
- if (l == null) {
- return;
- }
synchronized(mFocusListenerLock) {
- if (mFocusIdListenerMap.containsKey(getIdForFocusListener(l))) {
+ if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
return;
}
- mFocusIdListenerMap.put(getIdForFocusListener(l), l);
+ mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
}
}
@@ -1278,13 +1400,13 @@
// notify service to remove it from audio focus stack
IAudioService service = getService();
try {
- service.unregisterFocusClient(getIdForFocusListener(l));
+ service.unregisterAudioFocusClient(getIdForAudioFocusListener(l));
} catch (RemoteException e) {
Log.e(TAG, "Can't call unregisterFocusClient() from AudioService due to "+e);
}
// remove locally
synchronized(mFocusListenerLock) {
- mFocusIdListenerMap.remove(getIdForFocusListener(l));
+ mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
}
}
@@ -1306,8 +1428,10 @@
* @param streamType the main audio stream type affected by the focus request
* @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
* is temporary, and focus will be abandonned shortly. Examples of transient requests are
- * for the playback of driving directions, or notifications sounds. Use
- * {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
+ * for the playback of driving directions, or notifications sounds.
+ * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
+ * the previous focus owner to keep playing if it ducks its audio output.
+ * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
* as the playback of a song or a video.
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
*/
@@ -1318,7 +1442,7 @@
IAudioService service = getService();
try {
status = service.requestAudioFocus(streamType, durationHint, mICallBack,
- mFocusDispatcher, getIdForFocusListener(l));
+ mAudioFocusDispatcher, getIdForAudioFocusListener(l));
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocus() from AudioService due to "+e);
}
@@ -1336,7 +1460,8 @@
registerAudioFocusListener(l);
IAudioService service = getService();
try {
- status = service.abandonAudioFocus(mFocusDispatcher, getIdForFocusListener(l));
+ status = service.abandonAudioFocus(mAudioFocusDispatcher,
+ getIdForAudioFocusListener(l));
} catch (RemoteException e) {
Log.e(TAG, "Can't call abandonAudioFocus() from AudioService due to "+e);
}
@@ -1344,6 +1469,36 @@
}
+ //====================================================================
+ // Remote Control
+ /**
+ * TODO document for SDK
+ * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
+ * that will receive the media button intent. This broadcast receiver must be declared
+ * in the application manifest.
+ */
+ public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
+ //TODO enforce the rule about the receiver being declared in the manifest
+ IAudioService service = getService();
+ try {
+ service.registerMediaButtonEventReceiver(eventReceiver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in registerMediaButtonEventReceiver"+e);
+ }
+ }
+
+ /**
+ * TODO document for SDK
+ */
+ public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
+ IAudioService service = getService();
+ try {
+ service.unregisterMediaButtonEventReceiver(eventReceiver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiver"+e);
+ }
+ }
+
/**
* @hide
* Reload audio settings. This method is called by Settings backup
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 618f239..74e6157 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -18,6 +18,7 @@
import android.app.ActivityManagerNative;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +42,7 @@
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.VolumePanel;
import android.os.SystemProperties;
@@ -206,6 +208,9 @@
/** @see System#MODE_RINGER_STREAMS_AFFECTED */
private int mRingerModeAffectedStreams;
+ // Streams currently muted by ringer mode
+ private int mRingerModeMutedStreams;
+
/** @see System#MUTE_STREAMS_AFFECTED */
private int mMuteAffectedStreams;
@@ -224,6 +229,10 @@
// Broadcast receiver for device connections intent broadcasts
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
+ // Broadcast receiver for media button broadcasts (separate from mReceiver to
+ // independently change its priority)
+ private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
+
// Devices currently connected
private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
@@ -234,6 +243,15 @@
// The last process to have called setMode() is at the top of the list.
private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
+ // List of clients having issued a SCO start request
+ private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
+
+ // BluetoothHeadset API to control SCO connection
+ private BluetoothHeadset mBluetoothHeadset;
+
+ // Bluetooth headset connection state
+ private boolean mBluetoothHeadsetConnected;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -261,14 +279,23 @@
AudioSystem.setErrorCallback(mAudioSystemCallback);
loadSoundEffects();
+ mBluetoothHeadsetConnected = false;
+ mBluetoothHeadset = new BluetoothHeadset(context,
+ mBluetoothHeadsetServiceListener);
+
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_HEADSET_PLUG);
intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+ intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
context.registerReceiver(mReceiver, intentFilter);
+ // Register for media button intent broadcasts.
+ intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiver(mMediaButtonReceiver, intentFilter);
}
private void createAudioSystemThread() {
@@ -380,7 +407,7 @@
VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
- final int oldIndex = streamState.mIndex;
+ final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
boolean adjustVolume = true;
// If either the client forces allowing ringer modes for this adjustment,
@@ -392,30 +419,43 @@
adjustVolume = checkForRingerModeChange(oldIndex, direction);
}
- if (adjustVolume && streamState.adjustIndex(direction)) {
- // Post message to set system volume (it in turn will post a message
- // to persist). Do not change volume if stream is muted.
- if (streamState.muteCount() == 0) {
+ // If stream is muted, adjust last audible index only
+ int index;
+ if (streamState.muteCount() != 0) {
+ if (adjustVolume) {
+ streamState.adjustLastAudibleIndex(direction);
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+ SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ }
+ index = streamState.mLastAudibleIndex;
+ } else {
+ if (adjustVolume && streamState.adjustIndex(direction)) {
+ // Post message to set system volume (it in turn will post a message
+ // to persist). Do not change volume if stream is muted.
sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
streamState, 0);
}
+ index = streamState.mIndex;
}
-
// UI
mVolumePanel.postVolumeChanged(streamType, flags);
// Broadcast Intent
- sendVolumeUpdate(streamType, oldIndex, streamState.mIndex);
+ sendVolumeUpdate(streamType, oldIndex, index);
}
/** @see AudioManager#setStreamVolume(int, int, int) */
public void setStreamVolume(int streamType, int index, int flags) {
ensureValidStreamType(streamType);
+ VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
- final int oldIndex = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
+ final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
+ index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
+
// UI, etc.
mVolumePanel.postVolumeChanged(streamType, flags);
// Broadcast Intent
@@ -446,22 +486,30 @@
*/
private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
VolumeStreamState streamState = mStreamStates[streamType];
- if (streamState.setIndex(index, lastAudible) || force) {
- // Post message to set system volume (it in turn will post a message
- // to persist).
- // If stream is muted or we are in silent mode and stream is affected by ringer mode
- // and the new volume is not 0, just persist the new volume but do not change
- // current value
- if (streamState.muteCount() == 0 &&
- (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
- !isStreamAffectedByRingerMode(streamType) ||
- index == 0)) {
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
- streamState, 0);
- } else {
- // Post a persist volume msg
- sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
- SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+
+ // If stream is muted, set last audible index only
+ if (streamState.muteCount() != 0) {
+ streamState.setLastAudibleIndex(index);
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+ SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ } else {
+ if (streamState.setIndex(index, lastAudible) || force) {
+ // Post message to set system volume (it in turn will post a message
+ // to persist).
+ // If we are in silent mode and stream is affected by ringer mode
+ // and the new volume is not 0, just persist the new volume but do not change
+ // current value
+ if (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
+ !isStreamAffectedByRingerMode(streamType) ||
+ index == 0) {
+ sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
+ streamState, 0);
+ } else {
+ // Post a persist volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+ SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
+ }
}
}
}
@@ -513,27 +561,24 @@
private void setRingerModeInt(int ringerMode, boolean persist) {
mRingerMode = ringerMode;
- // Adjust volumes via posting message
+ // Mute stream if not previously muted by ringer mode and ringer mode
+ // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
+ // Unmute stream if previously muted by ringer mode and ringer mode
+ // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
int numStreamTypes = AudioSystem.getNumStreamTypes();
- if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
- for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- if (!isStreamAffectedByRingerMode(streamType)) continue;
- // Bring back last audible volume
- setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
- true, false);
- }
- } else {
- for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- if (isStreamAffectedByRingerMode(streamType)) {
- // Either silent or vibrate, either way volume is 0
- setStreamVolumeInt(streamType, 0, false, false);
- } else {
- // restore stream volume in the case the stream changed from affected
- // to non affected by ringer mode. Does not arm to do it for streams that
- // are not affected as well.
- setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
- true, false);
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (isStreamMutedByRingerMode(streamType)) {
+ if (!isStreamAffectedByRingerMode(streamType) ||
+ mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ mStreamStates[streamType].mute(null, false);
+ mRingerModeMutedStreams &= ~(1 << streamType);
}
+ } else {
+ if (isStreamAffectedByRingerMode(streamType) &&
+ mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
+ mStreamStates[streamType].mute(null, true);
+ mRingerModeMutedStreams |= (1 << streamType);
+ }
}
}
@@ -663,6 +708,7 @@
}
if (mode != mMode) {
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
+ checkForUndispatchedAudioFocusChange(mMode, mode);
mMode = mode;
synchronized(mSetModeDeathHandlers) {
@@ -695,11 +741,15 @@
mSetModeDeathHandlers.add(0, hdlr);
hdlr.setMode(mode);
}
+
+ if (mode != AudioSystem.MODE_NORMAL) {
+ clearAllScoClients();
+ }
}
}
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
- setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
}
}
@@ -833,7 +883,7 @@
}
streamState.mLastAudibleIndex = streamState.getValidIndex(index);
- // unmute stream that whas muted but is not affect by mute anymore
+ // unmute stream that was muted but is not affect by mute anymore
if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
int size = streamState.mDeathHandlers.size();
for (int i = 0; i < size; i++) {
@@ -899,6 +949,157 @@
}
}
+ /** @see AudioManager#startBluetoothSco() */
+ public void startBluetoothSco(IBinder cb){
+ if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+ return;
+ }
+ ScoClient client = getScoClient(cb);
+ client.incCount();
+ }
+
+ /** @see AudioManager#stopBluetoothSco() */
+ public void stopBluetoothSco(IBinder cb){
+ if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
+ return;
+ }
+ ScoClient client = getScoClient(cb);
+ client.decCount();
+ }
+
+ private class ScoClient implements IBinder.DeathRecipient {
+ private IBinder mCb; // To be notified of client's death
+ private int mStartcount; // number of SCO connections started by this client
+
+ ScoClient(IBinder cb) {
+ mCb = cb;
+ mStartcount = 0;
+ }
+
+ public void binderDied() {
+ synchronized(mScoClients) {
+ Log.w(TAG, "SCO client died");
+ int index = mScoClients.indexOf(this);
+ if (index < 0) {
+ Log.w(TAG, "unregistered SCO client died");
+ } else {
+ clearCount(true);
+ mScoClients.remove(this);
+ }
+ }
+ }
+
+ public void incCount() {
+ synchronized(mScoClients) {
+ requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
+ if (mStartcount == 0) {
+ try {
+ mCb.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // client has already died!
+ Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
+ }
+ }
+ mStartcount++;
+ }
+ }
+
+ public void decCount() {
+ synchronized(mScoClients) {
+ if (mStartcount == 0) {
+ Log.w(TAG, "ScoClient.decCount() already 0");
+ } else {
+ mStartcount--;
+ if (mStartcount == 0) {
+ mCb.unlinkToDeath(this, 0);
+ }
+ requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+ }
+ }
+ }
+
+ public void clearCount(boolean stopSco) {
+ synchronized(mScoClients) {
+ mStartcount = 0;
+ mCb.unlinkToDeath(this, 0);
+ if (stopSco) {
+ requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+ }
+ }
+ }
+
+ public int getCount() {
+ return mStartcount;
+ }
+
+ public IBinder getBinder() {
+ return mCb;
+ }
+
+ public int totalCount() {
+ synchronized(mScoClients) {
+ int count = 0;
+ int size = mScoClients.size();
+ for (int i = 0; i < size; i++) {
+ count += mScoClients.get(i).getCount();
+ }
+ return count;
+ }
+ }
+
+ private void requestScoState(int state) {
+ if (totalCount() == 0 &&
+ mBluetoothHeadsetConnected &&
+ AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
+ if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
+ mBluetoothHeadset.startVoiceRecognition();
+ } else {
+ mBluetoothHeadset.stopVoiceRecognition();
+ }
+ }
+ }
+ }
+
+ public ScoClient getScoClient(IBinder cb) {
+ synchronized(mScoClients) {
+ ScoClient client;
+ int size = mScoClients.size();
+ for (int i = 0; i < size; i++) {
+ client = mScoClients.get(i);
+ if (client.getBinder() == cb)
+ return client;
+ }
+ client = new ScoClient(cb);
+ mScoClients.add(client);
+ return client;
+ }
+ }
+
+ public void clearAllScoClients() {
+ synchronized(mScoClients) {
+ int size = mScoClients.size();
+ for (int i = 0; i < size; i++) {
+ mScoClients.get(i).clearCount(false);
+ }
+ }
+ }
+
+ private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
+ new BluetoothHeadset.ServiceListener() {
+ public void onServiceConnected() {
+ if (mBluetoothHeadset != null &&
+ mBluetoothHeadset.getState() == BluetoothHeadset.STATE_CONNECTED) {
+ mBluetoothHeadsetConnected = true;
+ }
+ }
+ public void onServiceDisconnected() {
+ if (mBluetoothHeadset != null &&
+ mBluetoothHeadset.getState() == BluetoothHeadset.STATE_DISCONNECTED) {
+ mBluetoothHeadsetConnected = false;
+ clearAllScoClients();
+ }
+ }
+ };
///////////////////////////////////////////////////////////////////////////
// Internal methods
@@ -947,6 +1148,10 @@
return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
}
+ private boolean isStreamMutedByRingerMode(int streamType) {
+ return (mRingerModeMutedStreams & (1 << streamType)) != 0;
+ }
+
public boolean isStreamAffectedByMute(int streamType) {
return (mMuteAffectedStreams & (1 << streamType)) != 0;
}
@@ -1113,6 +1318,14 @@
}
}
+ public void setLastAudibleIndex(int index) {
+ mLastAudibleIndex = getValidIndex(index);
+ }
+
+ public void adjustLastAudibleIndex(int deltaIndex) {
+ setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
+ }
+
public int getMaxIndex() {
return mIndexMax;
}
@@ -1150,7 +1363,10 @@
if (mMuteCount == 0) {
// Register for client death notification
try {
- mICallback.linkToDeath(this, 0);
+ // mICallback can be 0 if muted by AudioService
+ if (mICallback != null) {
+ mICallback.linkToDeath(this, 0);
+ }
mDeathHandlers.add(this);
// If the stream is not yet muted by any client, set lvel to 0
if (muteCount() == 0) {
@@ -1176,9 +1392,12 @@
if (mMuteCount == 0) {
// Unregistr from client death notification
mDeathHandlers.remove(this);
- mICallback.unlinkToDeath(this, 0);
+ // mICallback can be 0 if muted by AudioService
+ if (mICallback != null) {
+ mICallback.unlinkToDeath(this, 0);
+ }
if (muteCount() == 0) {
- // If the stream is not mut any more, restore it's volume if
+ // If the stream is not muted any more, restore it's volume if
// ringer mode allows it
if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
setIndex(mLastAudibleIndex, false);
@@ -1218,7 +1437,7 @@
int size = mDeathHandlers.size();
for (int i = 0; i < size; i++) {
handler = mDeathHandlers.get(i);
- if (cb.equals(handler.mICallback)) {
+ if (cb == handler.mICallback) {
return handler;
}
}
@@ -1567,11 +1786,14 @@
AudioSystem.DEVICE_STATE_UNAVAILABLE,
address);
mConnectedDevices.remove(device);
+ mBluetoothHeadsetConnected = false;
+ clearAllScoClients();
} else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE,
address);
mConnectedDevices.put(new Integer(device), address);
+ mBluetoothHeadsetConnected = true;
}
} else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", 0);
@@ -1604,6 +1826,29 @@
mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
}
}
+ } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
+ BluetoothHeadset.STATE_ERROR);
+ synchronized (mScoClients) {
+ if (!mScoClients.isEmpty()) {
+ switch (state) {
+ case BluetoothHeadset.AUDIO_STATE_CONNECTED:
+ state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ break;
+ case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
+ state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ break;
+ default:
+ state = AudioManager.SCO_AUDIO_STATE_ERROR;
+ break;
+ }
+ if (state != AudioManager.SCO_AUDIO_STATE_ERROR) {
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
+ mContext.sendStickyBroadcast(newIntent);
+ }
+ }
+ }
}
}
}
@@ -1611,13 +1856,47 @@
//==========================================================================================
// AudioFocus
//==========================================================================================
+ /**
+ * Flag to indicate that the top of the audio focus stack needs to recover focus
+ * but hasn't been signaled yet.
+ */
+ private boolean mHasUndispatchedAudioFocus = false;
+
+ private void checkForUndispatchedAudioFocusChange(int prevMode, int newMode) {
+ // when exiting a call
+ if ((prevMode == AudioSystem.MODE_IN_CALL) && (newMode != AudioSystem.MODE_IN_CALL)) {
+ // check for undispatched remote control focus gain
+ if (mHasUndispatchedAudioFocus) {
+ notifyTopOfAudioFocusStack();
+ }
+ }
+ }
+
+ private void notifyTopOfAudioFocusStack() {
+ // notify the top of the stack it gained focus
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ if (canReassignAudioFocus()) {
+ try {
+ mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+ AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
+ mHasUndispatchedAudioFocus = false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
+ e.printStackTrace();
+ }
+ } else {
+ mHasUndispatchedAudioFocus = true;
+ }
+ }
+ }
+
private static class FocusStackEntry {
public int mStreamType = -1;// no stream type
public boolean mIsTransportControlReceiver = false;
public IAudioFocusDispatcher mFocusDispatcher = null;
public IBinder mSourceRef = null;
public String mClientId;
- public int mDurationHint;
+ public int mFocusChangeType;
public FocusStackEntry() {
}
@@ -1629,7 +1908,7 @@
mFocusDispatcher = afl;
mSourceRef = source;
mClientId = id;
- mDurationHint = duration;
+ mFocusChangeType = duration;
}
}
@@ -1646,7 +1925,7 @@
while(stackIterator.hasNext()) {
FocusStackEntry fse = stackIterator.next();
pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
- + " -- duration: " +fse.mDurationHint);
+ + " -- duration: " +fse.mFocusChangeType);
}
}
}
@@ -1666,16 +1945,7 @@
mFocusStack.pop();
if (signal) {
// notify the new top of the stack it gained focus
- if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)
- && canReassignFocus()) {
- try {
- mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
- AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
- } catch (RemoteException e) {
- Log.e(TAG, " Failure to signal gain of focus due to "+ e);
- e.printStackTrace();
- }
- }
+ notifyTopOfAudioFocusStack();
}
} else {
// focus is abandoned by a client that's not at the top of the stack,
@@ -1697,8 +1967,9 @@
* Remove focus listeners from the focus stack for a particular client.
*/
private void removeFocusStackEntryForClient(IBinder cb) {
- // focus is abandoned by a client that's not at the top of the stack,
- // no need to update focus.
+ // is the owner of the audio focus part of the client to remove?
+ boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
+ mFocusStack.peek().mSourceRef.equals(cb);
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
@@ -1708,13 +1979,18 @@
mFocusStack.remove(fse);
}
}
+ if (isTopOfStackForClientToRemove) {
+ // we removed an entry at the top of the stack:
+ // notify the new top of the stack it gained focus.
+ notifyTopOfAudioFocusStack();
+ }
}
/**
* Helper function:
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
*/
- private boolean canReassignFocus() {
+ private boolean canReassignAudioFocus() {
// focus requests are rejected during a phone call
if (getMode() == AudioSystem.MODE_IN_CALL) {
Log.i(TAG, " AudioFocus can't be reassigned during a call, exiting");
@@ -1747,8 +2023,8 @@
}
- /** @see AudioManager#requestAudioFocus(int, int, IBinder, IAudioFocusDispatcher, String) */
- public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+ /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
+ public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId) {
Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
// the main stream type for the audio focus request is currently not used. It may
@@ -1759,24 +2035,27 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
- if (!canReassignFocus()) {
+ if (!canReassignAudioFocus()) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
synchronized(mFocusStack) {
if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
- mFocusStack.peek().mDurationHint = durationHint;
- // if focus is already owned by this client, don't do anything
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ // if focus is already owned by this client and the reason for acquiring the focus
+ // hasn't changed, don't do anything
+ if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+ // the reason for the audio focus request has changed: remove the current top of
+ // stack and respond as if we had a new focus owner
+ mFocusStack.pop();
}
// notify current top of stack it is losing focus
if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
try {
mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
- (durationHint == AudioManager.AUDIOFOCUS_GAIN) ?
- AudioManager.AUDIOFOCUS_LOSS :
- AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
+ -1 * focusChangeHint, // loss and gain codes are inverse of each other
mFocusStack.peek().mClientId);
} catch (RemoteException e) {
Log.e(TAG, " Failure to signal loss of focus due to "+ e);
@@ -1785,7 +2064,7 @@
}
// push focus requester at the top of the audio focus stack
- mFocusStack.push(new FocusStackEntry(mainStreamType, durationHint, false, fd, cb,
+ mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
clientId));
}//synchronized(mFocusStack)
@@ -1803,7 +2082,7 @@
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
- /** @see AudioManager#abandonAudioFocus(IBinder, IAudioFocusDispatcher, String) */
+ /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
@@ -1814,15 +2093,141 @@
}
- public void unregisterFocusClient(String clientId) {
+ public void unregisterAudioFocusClient(String clientId) {
removeFocusStackEntry(clientId, false);
}
+ //==========================================================================================
+ // RemoteControl
+ //==========================================================================================
+ /**
+ * Receiver for media button intents. Handles the dispatching of the media button event
+ * to one of the registered listeners, or if there was none, resumes the intent broadcast
+ * to the rest of the system.
+ */
+ private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
+ return;
+ }
+ KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+ if (event != null) {
+ // if in a call or ringing, do not break the current phone app behavior
+ // TODO modify this to let the phone app specifically get the RC focus
+ // add modify the phone app to take advantage of the new API
+ if ((getMode() == AudioSystem.MODE_IN_CALL) ||
+ (getMode() == AudioSystem.MODE_RINGTONE)) {
+ return;
+ }
+ synchronized(mRCStack) {
+ if (!mRCStack.empty()) {
+ // create a new intent specifically aimed at the current registered listener
+ Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ targetedIntent.putExtras(intent.getExtras());
+ targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
+ // trap the current broadcast
+ abortBroadcast();
+ //Log.v(TAG, " Sending intent" + targetedIntent);
+ context.sendBroadcast(targetedIntent, null);
+ }
+ }
+ }
+ }
+ }
+
+ private static class RemoteControlStackEntry {
+ public ComponentName mReceiverComponent;// always non null
+ // TODO implement registration expiration?
+ //public int mRegistrationTime;
+
+ public RemoteControlStackEntry() {
+ }
+
+ public RemoteControlStackEntry(ComponentName r) {
+ mReceiverComponent = r;
+ }
+ }
+
+ private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
+
+ /**
+ * Helper function:
+ * Display in the log the current entries in the remote control focus stack
+ */
+ private void dumpRCStack(PrintWriter pw) {
+ pw.println("Remote Control stack entries:");
+ synchronized(mRCStack) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry fse = stackIterator.next();
+ pw.println(" receiver:" + fse.mReceiverComponent);
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Set the new remote control receiver at the top of the RC focus stack
+ */
+ private void pushMediaButtonReceiver(ComponentName newReceiver) {
+ // already at top of stack?
+ if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
+ return;
+ }
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+ if(rcse.mReceiverComponent.equals(newReceiver)) {
+ mRCStack.remove(rcse);
+ break;
+ }
+ }
+ mRCStack.push(new RemoteControlStackEntry(newReceiver));
+ }
+
+ /**
+ * Helper function:
+ * Remove the remote control receiver from the RC focus stack
+ */
+ private void removeMediaButtonReceiver(ComponentName newReceiver) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+ if(rcse.mReceiverComponent.equals(newReceiver)) {
+ mRCStack.remove(rcse);
+ break;
+ }
+ }
+ }
+
+
+ /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
+ public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
+ Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver);
+
+ synchronized(mRCStack) {
+ pushMediaButtonReceiver(eventReceiver);
+ }
+ }
+
+ /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
+ public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
+ Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver);
+
+ synchronized(mRCStack) {
+ removeMediaButtonReceiver(eventReceiver);
+ }
+ }
+
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // TODO probably a lot more to do here than just the audio focus stack
+ // TODO probably a lot more to do here than just the audio focus and remote control stacks
dumpFocusStack(pw);
+ dumpRCStack(pw);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index b275488..384b8da 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.content.ComponentName;
import android.media.IAudioFocusDispatcher;
/**
@@ -76,5 +77,13 @@
int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
- void unregisterFocusClient(String clientId);
+ void unregisterAudioFocusClient(String clientId);
+
+ void registerMediaButtonEventReceiver(in ComponentName eventReceiver);
+
+ void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
+
+ void startBluetoothSco(IBinder cb);
+
+ void stopBluetoothSco(IBinder cb);
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2c5cbf6..5988d34 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -52,6 +52,7 @@
*
* <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a>
* documentation for additional help with using MediaRecorder.
+ * <p>Note: Currently, MediaRecorder does not work on the emulator.
*/
public class MediaRecorder
{
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 1047fa4..f845fec1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -634,8 +634,12 @@
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
- map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING));
- map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING));
+ map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
+ mArtist : MediaStore.UNKNOWN_STRING);
+ map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
+ mAlbumArtist.length() > 0) ? mAlbumArtist : null);
+ map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
+ mAlbum : MediaStore.UNKNOWN_STRING);
map.put(Audio.Media.COMPOSER, mComposer);
if (mYear != 0) {
map.put(Audio.Media.YEAR, mYear);
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 4b364f2..5e6ce42 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -358,7 +358,7 @@
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
unsigned int result = 0;
if (af == 0) return result;
- if (ioHandle == NULL) return result;
+ if (ioHandle == 0) return result;
result = af->getInputFramesLost(ioHandle);
return result;
@@ -556,7 +556,18 @@
output_flags flags)
{
audio_io_handle_t output = 0;
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) {
+ // Do not use stream to output map cache if the direct output
+ // flag is set or if we are likely to use a direct output
+ // (e.g voice call stream @ 8kHz could use BT SCO device and be routed to
+ // a direct output on some platforms).
+ // TODO: the output cache and stream to output mapping implementation needs to
+ // be reworked for proper operation with direct outputs. This code is too specific
+ // to the first use case we want to cover (Voice Recognition and Voice Dialer over
+ // Bluetooth SCO
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0 &&
+ ((stream != AudioSystem::VOICE_CALL && stream != AudioSystem::BLUETOOTH_SCO) ||
+ channels != AudioSystem::CHANNEL_OUT_MONO ||
+ (samplingRate != 8000 && samplingRate != 16000))) {
Mutex::Autolock _l(gLock);
output = AudioSystem::gStreamOutputMap.valueFor(stream);
LOGV_IF((output != 0), "getOutput() read %d from cache for stream %d", output, stream);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 74852dc..cd7bcd5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -983,8 +983,10 @@
// =========================================================================
audio_track_cblk_t::audio_track_cblk_t()
- : lock(Mutex::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
- loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0)
+ : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
+ userBase(0), serverBase(0), buffers(0), frameCount(0),
+ loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+ flowControlFlag(1), forceReady(0)
{
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 10b7be3..05e23d1 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -624,8 +624,6 @@
}
bool AwesomePlayer::isPlaying() const {
- Mutex::Autolock autoLock(mLock);
-
return mFlags & PLAYING;
}
@@ -1108,6 +1106,13 @@
mPreparedCondition.broadcast();
}
+// static
+bool AwesomePlayer::ContinuePreparation(void *cookie) {
+ AwesomePlayer *me = static_cast<AwesomePlayer *>(cookie);
+
+ return (me->mFlags & PREPARE_CANCELLED) == 0;
+}
+
void AwesomePlayer::onPrepareAsyncEvent() {
sp<Prefetcher> prefetcher;
@@ -1155,16 +1160,30 @@
Mutex::Autolock autoLock(mLock);
if (mFlags & PREPARE_CANCELLED) {
LOGI("prepare was cancelled before preparing the prefetcher");
+
+ prefetcher.clear();
abortPrepare(UNKNOWN_ERROR);
return;
}
}
LOGI("calling prefetcher->prepare()");
- prefetcher->prepare();
- LOGV("prefetcher is done preparing");
+ status_t result =
+ prefetcher->prepare(&AwesomePlayer::ContinuePreparation, this);
prefetcher.clear();
+
+ if (result == OK) {
+ LOGV("prefetcher is done preparing");
+ } else {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK_EQ(result, -EINTR);
+
+ LOGI("prefetcher->prepare() was cancelled early.");
+ abortPrepare(UNKNOWN_ERROR);
+ return;
+ }
}
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
index 451fc26..00af8a3 100644
--- a/media/libstagefright/HTTPDataSource.cpp
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -154,8 +154,8 @@
initHeaders(headers);
mBuffer = malloc(kBufferSize);
- mBufferLength = 0;
- mBufferOffset = 0;
+
+ mNumRetriesLeft = kMaxNumRetries;
}
status_t HTTPDataSource::connect() {
@@ -169,6 +169,8 @@
mState = CONNECTING;
}
+ mBufferLength = 0;
+ mBufferOffset = 0;
mContentLengthValid = false;
string host = mStartingHost.string();
@@ -328,6 +330,7 @@
ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
LOGV("readAt %ld, size %d", offset, size);
+rinse_repeat:
{
Mutex::Autolock autoLock(mStateLock);
if (mState != CONNECTED) {
@@ -383,6 +386,15 @@
ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
if (num_bytes_received < 0) {
+ if (mNumRetriesLeft-- > 0) {
+ disconnect();
+ if (connect() == OK) {
+ LOGI("retrying connection succeeded.");
+ goto rinse_repeat;
+ }
+ LOGE("retrying connection failed");
+ }
+
mBufferLength = 0;
return num_bytes_received;
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 66eadf6..3aad72c 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -289,11 +289,15 @@
continue;
}
+ LOGE("recv failed, errno = %d (%s)", errno, strerror(errno));
+
disconnect();
- return total == 0 ? (ssize_t)ERROR_IO : total;
+ return (ssize_t)ERROR_IO;
} else if (n == 0) {
disconnect();
+ LOGE("recv failed, server is gone");
+
return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total;
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 974413d2..20d9540 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -102,19 +102,26 @@
{ MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
+// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
+// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
+// { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
+// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
+// { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
+// { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
+// { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
};
static const CodecInfo kEncoderInfo[] = {
@@ -366,7 +373,6 @@
sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node = 0;
- success = false;
const char *componentName;
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
@@ -389,22 +395,27 @@
if (err == OK) {
LOGV("Successfully allocated OMX node '%s'", componentName);
- success = true;
- break;
+ sp<OMXCodec> codec = new OMXCodec(
+ omx, node, getComponentQuirks(componentName),
+ createEncoder, mime, componentName,
+ source);
+
+ observer->setCodec(codec);
+
+ err = codec->configureCodec(meta);
+
+ if (err == OK) {
+ return codec;
+ }
+
+ LOGV("Failed to configure codec '%s'", componentName);
}
}
- if (!success) {
- return NULL;
- }
+ return NULL;
+}
- sp<OMXCodec> codec = new OMXCodec(
- omx, node, getComponentQuirks(componentName),
- createEncoder, mime, componentName,
- source);
-
- observer->setCodec(codec);
-
+status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
uint32_t type;
const void *data;
size_t size;
@@ -417,7 +428,7 @@
esds.getCodecSpecificInfo(
&codec_specific_data, &codec_specific_data_size);
- codec->addCodecSpecificData(
+ addCodecSpecificData(
codec_specific_data, codec_specific_data_size);
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
@@ -453,7 +464,7 @@
CHECK(size >= length);
- codec->addCodecSpecificData(ptr, length);
+ addCodecSpecificData(ptr, length);
ptr += length;
size -= length;
@@ -473,7 +484,7 @@
CHECK(size >= length);
- codec->addCodecSpecificData(ptr, length);
+ addCodecSpecificData(ptr, length);
ptr += length;
size -= length;
@@ -482,44 +493,49 @@
LOGV("AVC profile = %d (%s), level = %d",
(int)profile, AVCProfileToString(profile), (int)level / 10);
- if (!strcmp(componentName, "OMX.TI.Video.Decoder")
+ if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 39)) {
// This stream exceeds the decoder's capabilities. The decoder
// does not handle this gracefully and would clobber the heap
// and wreak havoc instead...
LOGE("Profile and/or level exceed the decoder's capabilities.");
- return NULL;
+ return ERROR_UNSUPPORTED;
}
}
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
- codec->setAMRFormat(false /* isWAMR */);
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) {
+ setAMRFormat(false /* isWAMR */);
}
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
- codec->setAMRFormat(true /* isWAMR */);
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
+ setAMRFormat(true /* isWAMR */);
}
- if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
int32_t numChannels, sampleRate;
CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
- codec->setAACFormat(numChannels, sampleRate);
+ setAACFormat(numChannels, sampleRate);
}
- if (!strncasecmp(mime, "video/", 6)) {
+ if (!strncasecmp(mMIME, "video/", 6)) {
int32_t width, height;
bool success = meta->findInt32(kKeyWidth, &width);
success = success && meta->findInt32(kKeyHeight, &height);
CHECK(success);
- if (createEncoder) {
- codec->setVideoInputFormat(mime, width, height);
+ if (mIsEncoder) {
+ setVideoInputFormat(mMIME, width, height);
} else {
- codec->setVideoOutputFormat(mime, width, height);
+ status_t err = setVideoOutputFormat(
+ mMIME, width, height);
+
+ if (err != OK) {
+ return err;
+ }
}
}
- if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_JPEG)
- && !strcmp(componentName, "OMX.TI.JPEG.decode")) {
+ if (!strcasecmp(mMIME, MEDIA_MIMETYPE_IMAGE_JPEG)
+ && !strcmp(mComponentName, "OMX.TI.JPEG.decode")) {
OMX_COLOR_FORMATTYPE format =
OMX_COLOR_Format32bitARGB8888;
// OMX_COLOR_FormatYUV420PackedPlanar;
@@ -537,23 +553,23 @@
CHECK(success);
CHECK(compressedSize > 0);
- codec->setImageOutputFormat(format, width, height);
- codec->setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
+ setImageOutputFormat(format, width, height);
+ setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
}
int32_t maxInputSize;
if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
- codec->setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
+ setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
}
- if (!strcmp(componentName, "OMX.TI.AMR.encode")
- || !strcmp(componentName, "OMX.TI.WBAMR.encode")) {
- codec->setMinBufferSize(kPortIndexOutput, 8192); // XXX
+ if (!strcmp(mComponentName, "OMX.TI.AMR.encode")
+ || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")) {
+ setMinBufferSize(kPortIndexOutput, 8192); // XXX
}
- codec->initOutputFormat(meta);
+ initOutputFormat(meta);
- return codec;
+ return OK;
}
void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) {
@@ -902,7 +918,7 @@
return OK;
}
-void OMXCodec::setVideoOutputFormat(
+status_t OMXCodec::setVideoOutputFormat(
const char *mime, OMX_U32 width, OMX_U32 height) {
CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height);
@@ -918,9 +934,13 @@
CHECK(!"Should not be here. Not a supported video mime type.");
}
- setVideoPortFormatType(
+ status_t err = setVideoPortFormatType(
kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+ if (err != OK) {
+ return err;
+ }
+
#if 1
{
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
@@ -944,7 +964,10 @@
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
&format, sizeof(format));
- CHECK_EQ(err, OK);
+
+ if (err != OK) {
+ return err;
+ }
}
#endif
@@ -954,7 +977,7 @@
OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
- status_t err = mOMX->getParameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
@@ -977,7 +1000,10 @@
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+
+ if (err != OK) {
+ return err;
+ }
////////////////////////////////////////////////////////////////////////////
@@ -999,7 +1025,8 @@
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- CHECK_EQ(err, OK);
+
+ return err;
}
OMXCodec::OMXCodec(
@@ -2942,6 +2969,16 @@
return OK;
}
+ if (strncmp(componentName, "OMX.", 4)) {
+ // Not an OpenMax component but a software codec.
+
+ results->push();
+ CodecCapabilities *caps = &results->editItemAt(results->size() - 1);
+ caps->mComponentName = componentName;
+
+ continue;
+ }
+
sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node;
status_t err = omx->allocateNode(componentName, observer, &node);
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index debb60d..944a0c1 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -220,12 +220,19 @@
return minCacheDurationUs < 0 ? 0 : minCacheDurationUs;
}
-status_t Prefetcher::prepare() {
+status_t Prefetcher::prepare(
+ bool (*continueFunc)(void *cookie), void *cookie) {
// Fill the cache.
int64_t duration;
bool noMoreData;
do {
+ usleep(100000);
+
+ if (continueFunc && !(*continueFunc)(cookie)) {
+ return -EINTR;
+ }
+
duration = getCachedDurationUs(&noMoreData);
} while (!noMoreData && duration < 2000000ll);
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index f0b66c9..fe84b38 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -199,7 +199,16 @@
mConfig->pOutputBuffer_plus = NULL;
mConfig->repositionFlag = false;
- CHECK_EQ(PVMP4AudioDecodeFrame(mConfig, mDecoderBuf), MP4AUDEC_SUCCESS);
+ Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
+
+ if (decoderErr != MP4AUDEC_SUCCESS) {
+ LOGE("AAC decoder returned error %d", decoderErr);
+
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_MALFORMED;
+ }
buffer->set_range(
0, mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels);
diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
index fbb6598..553be87 100644
--- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
@@ -21,6 +21,7 @@
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
namespace android {
@@ -161,7 +162,14 @@
buffer->set_range(0, kNumSamplesPerFrame * sizeof(int16_t));
- CHECK(numBytesRead <= mInputBuffer->range_length());
+ if (numBytesRead > mInputBuffer->range_length()) {
+ // This is bad, should never have happened, but did. Abort now.
+
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_MALFORMED;
+ }
mInputBuffer->set_range(
mInputBuffer->range_offset() + numBytesRead,
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index 6d6e408..4dc96be 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -52,7 +52,7 @@
mBufferGroup->add_buffer(new MediaBuffer(4608 * 2));
mConfig->equalizerType = flat;
- mConfig->crcEnabled = true;
+ mConfig->crcEnabled = false;
uint32_t memRequirements = pvmp3_decoderMemRequirements();
mDecoderBuf = malloc(memRequirements);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 32c28c1..610f913 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -222,6 +222,8 @@
status_t finishSetDataSource_l();
+ static bool ContinuePreparation(void *cookie);
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h
index d227864..b411d1b 100644
--- a/media/libstagefright/include/Prefetcher.h
+++ b/media/libstagefright/include/Prefetcher.h
@@ -36,7 +36,12 @@
int64_t getCachedDurationUs(bool *noMoreData = NULL);
- status_t prepare();
+ // If provided (non-NULL), "continueFunc" will be called repeatedly
+ // while preparing and preparation will finish early if it returns
+ // false. In this case "-EINTR" is returned as a result.
+ status_t prepare(
+ bool (*continueFunc)(void *cookie) = NULL,
+ void *cookie = NULL);
protected:
virtual ~Prefetcher();
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 2458d2a..c1a010c 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -25,6 +25,7 @@
#include <binder/IMemory.h>
#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
namespace android {
@@ -105,7 +106,14 @@
}
static status_t StatusFromOMXError(OMX_ERRORTYPE err) {
- return (err == OMX_ErrorNone) ? OK : UNKNOWN_ERROR;
+ switch (err) {
+ case OMX_ErrorNone:
+ return OK;
+ case OMX_ErrorUnsupportedSetting:
+ return ERROR_UNSUPPORTED;
+ default:
+ return UNKNOWN_ERROR;
+ }
}
status_t OMXNodeInstance::freeNode(OMXMaster *master) {
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index c05d90a..fcf506d 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -595,6 +595,9 @@
static const int32_t kNumIterations = 5000;
+ // We are always going to seek beyond EOS in the first iteration (i == 0)
+ // followed by a linear read for the second iteration (i == 1).
+ // After that it's all random.
for (int32_t i = 0; i < kNumIterations; ++i) {
int64_t requestedSeekTimeUs;
int64_t actualSeekTimeUs;
@@ -602,14 +605,14 @@
double r = uniform_rand();
- if (i > 0 && r < 0.5) {
+ if ((i == 1) || (i > 0 && r < 0.5)) {
// 50% chance of just continuing to decode from last position.
requestedSeekTimeUs = -1;
LOGI("requesting linear read");
} else {
- if (i > 0 && r < 0.55) {
+ if (i == 0 || r < 0.55) {
// 5% chance of seeking beyond end of stream.
requestedSeekTimeUs = durationUs;
@@ -674,7 +677,15 @@
buffer = NULL;
}
} else if (actualSeekTimeUs < 0) {
- CHECK(err != OK);
+ EXPECT(err != OK,
+ "We attempted to seek beyond EOS and expected "
+ "ERROR_END_OF_STREAM to be returned, but instead "
+ "we got a valid buffer.");
+ EXPECT(err == ERROR_END_OF_STREAM,
+ "We attempted to seek beyond EOS and expected "
+ "ERROR_END_OF_STREAM to be returned, but instead "
+ "we found some other error.");
+ CHECK_EQ(err, ERROR_END_OF_STREAM);
CHECK_EQ(buffer, NULL);
} else {
EXPECT(err == OK,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
index 784db97..2e599f2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CameraTest.java
@@ -30,6 +30,7 @@
import android.util.Log;
import android.view.SurfaceHolder;
+import android.os.ConditionVariable;
import android.os.Looper;
import android.test.suitebuilder.annotation.LargeTest;
@@ -55,8 +56,8 @@
private boolean mInitialized = false;
private Looper mLooper = null;
- private final Object lock = new Object();
- private final Object previewDone = new Object();
+ private final ConditionVariable mPreviewDone = new ConditionVariable();
+ private final ConditionVariable mSnapshotDone = new ConditionVariable();
Camera mCamera;
Context mContext;
@@ -74,6 +75,7 @@
* receive the callback messages.
*/
private void initializeMessageLooper() {
+ final ConditionVariable startDone = new ConditionVariable();
Log.v(TAG, "start looper");
new Thread() {
@Override
@@ -83,29 +85,30 @@
Log.v(TAG, "start loopRun");
// Save the looper so that we can terminate this thread
// after we are done with it.
- mLooper = Looper.myLooper();
- mCamera = Camera.open();
- synchronized (lock) {
- mInitialized = true;
- lock.notify();
- }
+ mLooper = Looper.myLooper();
+ mCamera = Camera.open();
+ startDone.open();
Looper.loop(); // Blocks forever until Looper.quit() is called.
Log.v(TAG, "initializeMessageLooper: quit.");
}
}.start();
+
+ if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
+ fail("initializeMessageLooper: start timeout");
+ }
}
/*
* Terminates the message looper thread.
*/
- private void terminateMessageLooper() {
+ private void terminateMessageLooper() throws Exception {
mLooper.quit();
- //TODO yslau : take out the sleep until bug#1693519 fix
- try {
- Thread.sleep(1000);
- } catch (Exception e){
- Log.v(TAG, e.toString());
- }
+ // Looper.quit() is asynchronous. The looper may still has some
+ // preview callbacks in the queue after quit is called. The preview
+ // callback still uses the camera object (setHasPreviewCallback).
+ // After camera is released, RuntimeException will be thrown from
+ // the method. So we need to join the looper thread here.
+ mLooper.getThread().join();
mCamera.release();
}
@@ -122,11 +125,7 @@
} else {
rawPreviewCallbackResult = false;
}
- synchronized (previewDone) {
- Log.v(TAG, "notify the preview callback");
- previewDone.notify();
- }
-
+ mPreviewDone.open();
Log.v(TAG, "Preview callback stop");
}
};
@@ -142,13 +141,8 @@
//Implement the RawPictureCallback
private final class RawPictureCallback implements PictureCallback {
public void onPictureTaken(byte [] rawData, Camera camera) {
- // no support for raw data - success if we get the callback
- rawPictureCallbackResult = true;
- //if (rawData != null) {
- // rawPictureCallbackResult = true;
- //} else {
- // rawPictureCallbackResult = false;
- //}
+ // no support for raw data - success if we get the callback
+ rawPictureCallbackResult = true;
Log.v(TAG, "RawPictureCallback callback");
}
};
@@ -167,6 +161,7 @@
} else {
jpegPictureCallbackResult = false;
}
+ mSnapshotDone.open();
Log.v(TAG, "Jpeg Picture callback");
} catch (Exception e) {
Log.v(TAG, e.toString());
@@ -174,7 +169,22 @@
}
};
-
+ private void waitForPreviewDone() {
+ if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
+ Log.v(TAG, "waitForPreviewDone: timeout");
+ }
+ mPreviewDone.close();
+ }
+
+ private void waitForSnapshotDone() {
+ if (!mSnapshotDone.block(MediaNames.WAIT_SNAPSHOT_TIME)) {
+ // timeout could be expected or unexpected. The caller will decide.
+ Log.v(TAG, "waitForSnapshotDone: timeout");
+ }
+ mSnapshotDone.close();
+ }
+
+
private void checkTakePicture() {
SurfaceHolder mSurfaceHolder;
try {
@@ -182,17 +192,10 @@
mCamera.setPreviewDisplay(mSurfaceHolder);
Log.v(TAG, "Start preview");
mCamera.startPreview();
- synchronized (previewDone) {
- try {
- previewDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- Log.v(TAG, "Preview Done");
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted.");
- }
- }
+ waitForPreviewDone();
mCamera.setPreviewCallback(null);
mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
- Thread.sleep(MediaNames.WAIT_SNAPSHOT_TIME);
+ waitForSnapshotDone();
} catch (Exception e) {
Log.v(TAG, e.toString());
}
@@ -204,15 +207,8 @@
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
mCamera.setPreviewDisplay(mSurfaceHolder);
Log.v(TAG, "start preview");
- mCamera.startPreview();
- synchronized (previewDone) {
- try {
- previewDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- Log.v(TAG, "setPreview done");
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted.");
- }
- }
+ mCamera.startPreview();
+ waitForPreviewDone();
mCamera.setPreviewCallback(null);
} catch (Exception e) {
Log.v(TAG, e.toString());
@@ -228,14 +224,7 @@
*/
@LargeTest
public void testTakePicture() throws Exception {
- synchronized (lock) {
- initializeMessageLooper();
- try {
- lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch(Exception e) {
- Log.v(TAG, "runTestOnMethod: wait was interrupted.");
- }
- }
+ initializeMessageLooper();
mCamera.setPreviewCallback(mRawPreviewCallback);
checkTakePicture();
terminateMessageLooper();
@@ -250,14 +239,7 @@
*/
@LargeTest
public void testCheckPreview() throws Exception {
- synchronized (lock) {
- initializeMessageLooper();
- try {
- lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch(Exception e) {
- Log.v(TAG, "wait was interrupted.");
- }
- }
+ initializeMessageLooper();
mCamera.setPreviewCallback(mRawPreviewCallback);
checkPreviewCallback();
terminateMessageLooper();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 8202efd..2652281 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -24,6 +24,7 @@
import android.hardware.Camera.PreviewCallback;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
+import android.os.ConditionVariable;
import android.os.Looper;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase;
@@ -68,11 +69,9 @@
private static int mStartPid = 0;
private static int mEndPid = 0;
- private boolean mInitialized = false;
private Looper mLooper = null;
private RawPreviewCallback mRawPreviewCallback = new RawPreviewCallback();
- private final Object lock = new Object();
- private final Object previewDone = new Object();
+ private final ConditionVariable mPreviewDone = new ConditionVariable();
private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; // Milliseconds.
//the tolerant memory leak
@@ -194,6 +193,7 @@
}
private void initializeMessageLooper() {
+ final ConditionVariable startDone = new ConditionVariable();
new Thread() {
@Override
public void run() {
@@ -201,51 +201,49 @@
Log.v(TAG, "start loopRun");
mLooper = Looper.myLooper();
mCamera = Camera.open();
- synchronized (lock) {
- mInitialized = true;
- lock.notify();
- }
+ startDone.open();
Looper.loop();
Log.v(TAG, "initializeMessageLooper: quit.");
}
}.start();
+
+ if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
+ fail("initializeMessageLooper: start timeout");
+ }
}
- private void terminateMessageLooper() {
+ private void terminateMessageLooper() throws Exception {
mLooper.quit();
+ // Looper.quit() is asynchronous. The looper may still has some
+ // preview callbacks in the queue after quit is called. The preview
+ // callback still uses the camera object (setHasPreviewCallback).
+ // After camera is released, RuntimeException will be thrown from
+ // the method. So we need to join the looper thread here.
+ mLooper.getThread().join();
mCamera.release();
}
private final class RawPreviewCallback implements PreviewCallback {
public void onPreviewFrame(byte[] rawData, Camera camera) {
- synchronized (previewDone) {
- previewDone.notify();
- }
+ mPreviewDone.open();
}
}
+ private void waitForPreviewDone() {
+ if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
+ Log.v(TAG, "waitForPreviewDone: timeout");
+ }
+ mPreviewDone.close();
+ }
+
public void stressCameraPreview() {
try {
- synchronized (lock) {
- initializeMessageLooper();
- try {
- lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch (Exception e) {
- Log.v(TAG, "runTestOnMethod: wait was interrupted.");
- }
- }
+ initializeMessageLooper();
mCamera.setPreviewCallback(mRawPreviewCallback);
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
- synchronized (previewDone) {
- try {
- previewDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- Log.v(TAG, "Preview Done");
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted.");
- }
- }
+ waitForPreviewDone();
Thread.sleep(1000);
mCamera.stopPreview();
terminateMessageLooper();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index 4c259f1..7174e2b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -40,6 +40,7 @@
Log.v(TAG, "testAlbumArt starts.");
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
+ boolean hasFailed = false;
boolean supportWMV = MediaProfileReader.getWMVEnable();
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
for (int i = 0, n = MediaNames.ALBUMART_TEST_FILES.length; i < n; ++i) {
@@ -58,15 +59,18 @@
// A better test would be to compare the retrieved album art with the
// known result.
if (albumArt == null) { // Do we have expect in JUnit?
- fail("Fails to extract album art for " + MediaNames.ALBUMART_TEST_FILES[i]);
+ Log.e(TAG, "Fails to extract album art for " + MediaNames.ALBUMART_TEST_FILES[i]);
+ hasFailed = true;
}
} catch(Exception e) {
- throw new Exception("Fails to setDataSource for " + MediaNames.ALBUMART_TEST_FILES[i], e);
+ Log.e(TAG, "Fails to setDataSource for " + MediaNames.ALBUMART_TEST_FILES[i]);
+ hasFailed = true;
}
Thread.yield(); // Don't be evil
}
retriever.release();
Log.v(TAG, "testAlbumArt completes.");
+ assertTrue(!hasFailed);
}
// Test frame capture
@@ -75,6 +79,7 @@
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
+ boolean hasFailed = false;
Log.v(TAG, "Thumbnail processing starts");
long startedAt = System.currentTimeMillis();
for(int i = 0, n = MediaNames.THUMBNAIL_CAPTURE_TEST_FILES.length; i < n; ++i) {
@@ -94,22 +99,26 @@
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, stream);
stream.close();
} catch (Exception e) {
- throw new Exception("Fails to convert the bitmap to a JPEG file for " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i], e);
+ Log.e(TAG, "Fails to convert the bitmap to a JPEG file for " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i]);
+ hasFailed = true;
}
} catch(Exception e) {
- throw new Exception("Fails to setDataSource for file " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i], e);
+ Log.e(TAG, "Fails to setDataSource for file " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i]);
+ hasFailed = true;
}
Thread.yield(); // Don't be evil
}
long endedAt = System.currentTimeMillis();
- Log.v(TAG, "Average processing time per thumbnail: " + (endedAt - startedAt)/MediaNames.THUMBNAIL_CAPTURE_TEST_FILES.length + " ms");
retriever.release();
+ assertTrue(!hasFailed);
+ Log.v(TAG, "Average processing time per thumbnail: " + (endedAt - startedAt)/MediaNames.THUMBNAIL_CAPTURE_TEST_FILES.length + " ms");
}
@LargeTest
public static void testMetadataRetrieval() throws Exception {
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
+ boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
for(int i = 0, n = MediaNames.METADATA_RETRIEVAL_TEST_FILES.length; i < n; ++i) {
@@ -124,17 +133,20 @@
retriever.setDataSource(MediaNames.METADATA_RETRIEVAL_TEST_FILES[i]);
extractAllSupportedMetadataValues(retriever);
} catch(Exception e) {
- throw new Exception("Fails to setDataSource for file " + MediaNames.METADATA_RETRIEVAL_TEST_FILES[i], e);
+ Log.e(TAG, "Fails to setDataSource for file " + MediaNames.METADATA_RETRIEVAL_TEST_FILES[i]);
+ hasFailed = true;
}
Thread.yield(); // Don't be evil
}
retriever.release();
+ assertTrue(!hasFailed);
}
// If the specified call order and valid media file is used, no exception
// should be thrown.
@MediumTest
public static void testBasicNormalMethodCallSequence() throws Exception {
+ boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
try {
@@ -153,19 +165,29 @@
*/
extractAllSupportedMetadataValues(retriever);
} catch(Exception e) {
- throw new Exception("Fails to setDataSource for " + MediaNames.TEST_PATH_1, e);
+ Log.e(TAG, "Fails to setDataSource for " + MediaNames.TEST_PATH_1, e);
+ hasFailed = true;
}
retriever.release();
+ assertTrue(!hasFailed);
}
// If setDataSource() has not been called, both captureFrame() and extractMetadata() must
// return null.
@MediumTest
public static void testBasicAbnormalMethodCallSequence() {
+ boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
- assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) == null);
- assertTrue(retriever.captureFrame() == null);
+ if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) != null) {
+ Log.e(TAG, "No album metadata expected, but is available");
+ hasFailed = true;
+ }
+ if (retriever.captureFrame() != null) {
+ Log.e(TAG, "No frame expected, but is available");
+ hasFailed = true;
+ }
+ assertTrue(!hasFailed);
}
// Test setDataSource()
@@ -173,42 +195,60 @@
public static void testSetDataSource() {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
+ boolean hasFailed = false;
// Null pointer argument
try {
String path = null;
retriever.setDataSource(path);
- fail("IllegalArgumentException must be thrown.");
+ Log.e(TAG, "IllegalArgumentException failed to be thrown.");
+ hasFailed = true;
} catch(Exception e) {
- assertTrue(e instanceof IllegalArgumentException);
+ if (!(e instanceof IllegalArgumentException)) {
+ Log.e(TAG, "Expected a IllegalArgumentException, but got a different exception");
+ hasFailed = true;
+ }
}
// Use mem:// path
try {
retriever.setDataSource(MediaNames.TEST_PATH_5);
- fail("IllegalArgumentException must be thrown.");
+ Log.e(TAG, "IllegalArgumentException failed to be thrown.");
+ hasFailed = true;
} catch(Exception e) {
- assertTrue(e instanceof IllegalArgumentException);
+ if (!(e instanceof IllegalArgumentException)) {
+ Log.e(TAG, "Expected a IllegalArgumentException, but got a different exception");
+ hasFailed = true;
+ }
}
// The pathname does not correspond to any existing file
try {
retriever.setDataSource(MediaNames.TEST_PATH_4);
- fail("Runtime exception must be thrown.");
+ Log.e(TAG, "RuntimeException failed to be thrown.");
+ hasFailed = true;
} catch(Exception e) {
- assertTrue(e instanceof RuntimeException);
+ if (!(e instanceof RuntimeException)) {
+ Log.e(TAG, "Expected a RuntimeException, but got a different exception");
+ hasFailed = true;
+ }
}
// The pathname does correspond to a file, but this file
// is not a valid media file
try {
retriever.setDataSource(MediaNames.TEST_PATH_3);
- fail("Runtime exception must be thrown.");
+ Log.e(TAG, "RuntimeException failed to be thrown.");
+ hasFailed = true;
} catch(Exception e) {
- assertTrue(e instanceof RuntimeException);
+ if (!(e instanceof RuntimeException)) {
+ Log.e(TAG, "Expected a RuntimeException, but got a different exception");
+ hasFailed = true;
+ }
}
retriever.release();
+ assertTrue(!hasFailed);
}
// Due to the lack of permission to access hardware decoder, any calls
@@ -218,6 +258,7 @@
public static void testIntendedUsage() {
// By default, capture frame and retrieve metadata
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ boolean hasFailed = false;
// retriever.setDataSource(MediaNames.TEST_PATH_1);
// assertTrue(retriever.captureFrame() != null);
// assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
@@ -225,8 +266,14 @@
// Do not capture frame or retrieve metadata
retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY & MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
retriever.setDataSource(MediaNames.TEST_PATH_1);
- assertTrue(retriever.captureFrame() == null);
- assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) == null);
+ if (retriever.captureFrame() != null) {
+ Log.e(TAG, "No frame expected, but is available");
+ hasFailed = true;
+ }
+ if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null) {
+ Log.e(TAG, "No num track metadata expected, but is available");
+ hasFailed = true;
+ }
// Capture frame only
// retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
@@ -236,7 +283,10 @@
// Retriever metadata only
retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
retriever.setDataSource(MediaNames.TEST_PATH_1);
- assertTrue(retriever.captureFrame() == null);
+ if (retriever.captureFrame() != null) {
+ Log.e(TAG, "No frame expected, but is available");
+ hasFailed = true;
+ }
// Capture frame and retrieve metadata
// retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY | MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
@@ -244,6 +294,7 @@
// assertTrue(retriever.captureFrame() != null);
// assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null);
retriever.release();
+ assertTrue(!hasFailed);
}
// TODO:
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 3f029a6..0f79b15 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -156,9 +156,11 @@
*/
public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private final static boolean LOG_THREADS = false;
- private final static boolean LOG_SURFACE = false;
- private final static boolean LOG_RENDERER = false;
+ private final static boolean LOG_PAUSE_RESUME = true;
+ private final static boolean LOG_SURFACE = true;
+ private final static boolean LOG_RENDERER = true;
private final static boolean LOG_RENDERER_DRAW_FRAME = false;
+ private final static boolean LOG_EGL = true;
// Work-around for bug 2263168
private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
/**
@@ -683,7 +685,14 @@
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
- egl.eglDestroyContext(display, context);
+ if (!egl.eglDestroyContext(display, context)) {
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ if (LOG_THREADS) {
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ }
+ throw new RuntimeException("eglDestroyContext failed: "
+ + EGLLogWrapper.getErrorString(egl.eglGetError()));
+ }
}
}
@@ -880,6 +889,9 @@
* @param configSpec
*/
public void start() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ }
/*
* Get an EGL instance
*/
@@ -909,8 +921,12 @@
*/
mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ mEglContext = null;
throwEglException("createContext");
}
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ }
mEglSurface = null;
}
@@ -920,6 +936,21 @@
* OpenGL interface that renders to that surface.
*/
public GL createSurface(SurfaceHolder holder) {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Check preconditions.
+ */
+ if (mEgl == null) {
+ throw new RuntimeException("egl not initialized");
+ }
+ if (mEglDisplay == null) {
+ throw new RuntimeException("eglDisplay not initialized");
+ }
+ if (mEglConfig == null) {
+ throw new RuntimeException("mEglConfig not initialized");
+ }
/*
* The window size has changed, so we need to create a new
* surface.
@@ -942,6 +973,8 @@
mEglDisplay, mEglConfig, holder);
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ Log.w("EglHelper", "createWindowSurface failed. mEglDisplay: " + mEglDisplay +
+ " mEglConfig: " + mEglConfig + " holder: " + holder);
throwEglException("createWindowSurface");
}
@@ -996,6 +1029,9 @@
}
public void destroySurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ }
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
@@ -1006,6 +1042,9 @@
}
public void finish() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ }
if (mEglContext != null) {
mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
mEglContext = null;
@@ -1021,7 +1060,11 @@
}
private void throwEglException(String function, int error) {
- throw new RuntimeException(function + " failed: " + error);
+ String message = function + " failed: " + EGLLogWrapper.getErrorString(error);
+ if (LOG_THREADS) {
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message);
+ }
+ throw new RuntimeException(message);
}
EGL10 mEgl;
@@ -1118,6 +1161,15 @@
break;
}
+ // Update the pause state.
+ if (mPaused != mRequestPaused) {
+ mPaused = mRequestPaused;
+ sGLThreadManager.notifyAll();
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
+ }
+ }
+
// Have we lost the EGL context?
if (lostEglContext) {
stopEglSurfaceLocked();
@@ -1137,6 +1189,12 @@
Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
}
}
+ if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
+ mEglHelper.finish();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "terminating EGL because paused tid=" + getId());
+ }
+ }
}
// Have we lost the surface view surface?
@@ -1174,9 +1232,14 @@
// If we don't have an EGL context, try to acquire one.
if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ try {
+ mEglHelper.start();
+ } catch (RuntimeException t) {
+ sGLThreadManager.releaseEglContextLocked(this);
+ throw t;
+ }
mHaveEglContext = true;
createEglContext = true;
- mEglHelper.start();
sGLThreadManager.notifyAll();
}
@@ -1306,6 +1369,13 @@
}
mHasSurface = true;
sGLThreadManager.notifyAll();
+ while((mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
}
@@ -1328,16 +1398,43 @@
public void onPause() {
synchronized (sGLThreadManager) {
- mPaused = true;
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onPause tid=" + getId());
+ }
+ mRequestPaused = true;
sGLThreadManager.notifyAll();
+ while ((! mExited) && (! mPaused)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onPause waiting for mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
}
public void onResume() {
synchronized (sGLThreadManager) {
- mPaused = false;
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onResume tid=" + getId());
+ }
+ mRequestPaused = false;
mRequestRender = true;
+ mRenderComplete = false;
sGLThreadManager.notifyAll();
+ while ((! mExited) && mPaused && (!mRenderComplete)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onResume waiting for !mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
}
@@ -1398,6 +1495,7 @@
// variables are protected by the sGLThreadManager monitor
private boolean mShouldExit;
private boolean mExited;
+ private boolean mRequestPaused;
private boolean mPaused;
private boolean mHasSurface;
private boolean mWaitingForSurface;
@@ -1502,6 +1600,13 @@
}
public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+ // Release the EGL context when pausing even if
+ // the hardware supports multiple EGL contexts.
+ // Otherwise the device could run out of EGL contexts.
+ return true;
+ }
+
+ public synchronized boolean shouldTerminateEGLWhenPausing() {
checkGLESVersion();
return !mMultipleGLESContextsAllowed;
}
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 1224a96..27bb545 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -37,7 +37,7 @@
// ----------------------------------------------------------------------------
static char const * const gVendorString = "Android";
-static char const * const gRendererString = "Android PixelFlinger 1.2";
+static char const * const gRendererString = "Android PixelFlinger 1.3";
static char const * const gVersionString = "OpenGL ES-CM 1.0";
static char const * const gExtensionsString =
"GL_OES_byte_coordinates " // OK
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 145e25e..89b3e1f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -650,6 +650,7 @@
if (dp->refs > 0) {
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
+ dp->refs++;
return EGL_TRUE;
}
@@ -755,8 +756,10 @@
}
// this is specific to Android, display termination is ref-counted.
- if (dp->refs > 1)
+ if (dp->refs > 1) {
+ dp->refs--;
return EGL_TRUE;
+ }
EGLBoolean res = EGL_FALSE;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
diff --git a/packages/DefaultContainerService/res/values-cs/strings.xml b/packages/DefaultContainerService/res/values-cs/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-da/strings.xml b/packages/DefaultContainerService/res/values-da/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-de/strings.xml b/packages/DefaultContainerService/res/values-de/strings.xml
new file mode 100644
index 0000000..5d12956
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Medien-Containerdienst"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-el/strings.xml b/packages/DefaultContainerService/res/values-el/strings.xml
new file mode 100644
index 0000000..b0b5794
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Υπηρεσία Media Container"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es-rUS/strings.xml b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..cf893de
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Servicio de contención de medios"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es/strings.xml b/packages/DefaultContainerService/res/values-es/strings.xml
new file mode 100644
index 0000000..6817520
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Servicio de contenedor de medios"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
new file mode 100644
index 0000000..3b4a90d
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Service de support multimédia"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-it/strings.xml b/packages/DefaultContainerService/res/values-it/strings.xml
new file mode 100644
index 0000000..55bd6e5
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Servizio Media Container"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ja/strings.xml b/packages/DefaultContainerService/res/values-ja/strings.xml
new file mode 100644
index 0000000..dc1dfea
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"メディアコンテナサービス"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ko/strings.xml b/packages/DefaultContainerService/res/values-ko/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nb/strings.xml b/packages/DefaultContainerService/res/values-nb/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nl/strings.xml b/packages/DefaultContainerService/res/values-nl/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pl/strings.xml b/packages/DefaultContainerService/res/values-pl/strings.xml
new file mode 100644
index 0000000..0c96f3d
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Usługa kontenera multimediów"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt/strings.xml b/packages/DefaultContainerService/res/values-pt/strings.xml
new file mode 100644
index 0000000..00b90de
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Serviço de recipiente de mídia"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ru/strings.xml b/packages/DefaultContainerService/res/values-ru/strings.xml
new file mode 100644
index 0000000..0179e85
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Media Container Service"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-sv/strings.xml b/packages/DefaultContainerService/res/values-sv/strings.xml
new file mode 100644
index 0000000..b097814
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Medietjänst"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-tr/strings.xml b/packages/DefaultContainerService/res/values-tr/strings.xml
new file mode 100644
index 0000000..afd870f
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"Ortam Kapsayıcı Hizmeti"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..4f99d1b
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"媒体容器服务"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..38870f6
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="2260781993795858516">"媒體庫服務"</string>
+</resources>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index c6b617d..14c8806 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -24,7 +24,6 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
@@ -45,7 +44,6 @@
import java.io.InputStream;
import android.os.FileUtils;
-import android.os.storage.IMountService;
import android.provider.Settings;
/*
@@ -107,7 +105,7 @@
* @return Returns PackageInfoLite object containing
* the package info and recommended app location.
*/
- public PackageInfoLite getMinimalPackageInfo(final Uri fileUri) {
+ public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags) {
PackageInfoLite ret = new PackageInfoLite();
if (fileUri == null) {
Log.i(TAG, "Invalid package uri " + fileUri);
@@ -130,26 +128,21 @@
ret.packageName = pkg.packageName;
ret.installLocation = pkg.installLocation;
// Nuke the parser reference right away and force a gc
- Runtime.getRuntime().gc();
packageParser = null;
+ Runtime.getRuntime().gc();
if (pkg == null) {
Log.w(TAG, "Failed to parse package");
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
ret.packageName = pkg.packageName;
- int loc = recommendAppInstallLocation(pkg.installLocation, archiveFilePath);
- if (loc == PackageManager.INSTALL_EXTERNAL) {
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- } else if (loc == ERR_LOC) {
- Log.i(TAG, "Failed to install insufficient storage");
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- } else {
- // Implies install on internal storage.
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
+ ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
return ret;
}
+
+ public boolean checkFreeStorage(boolean external, Uri fileUri) {
+ return checkFreeStorageInner(external, fileUri);
+ }
};
public DefaultContainerService() {
@@ -190,6 +183,12 @@
}
private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName) {
+ // Make sure the sdcard is mounted.
+ String status = Environment.getExternalStorageState();
+ if (!status.equals(Environment.MEDIA_MOUNTED)) {
+ Log.w(TAG, "Make sure sdcard is mounted.");
+ return null;
+ }
// Create new container at newCachePath
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
@@ -306,56 +305,40 @@
private static final int ERR_LOC = -1;
private int recommendAppInstallLocation(int installLocation,
- String archiveFilePath) {
- // Initial implementation:
- // Package size = code size + cache size + data size
- // If code size > 1 MB, install on SD card.
- // Else install on internal NAND flash, unless space on NAND is less than 10%
- String status = Environment.getExternalStorageState();
- long availSDSize = -1;
- if (status.equals(Environment.MEDIA_MOUNTED)) {
- StatFs sdStats = new StatFs(
- Environment.getExternalStorageDirectory().getPath());
- availSDSize = (long)sdStats.getAvailableBlocks() *
- (long)sdStats.getBlockSize();
- }
- StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- long totalInternalSize = (long)internalStats.getBlockCount() *
- (long)internalStats.getBlockSize();
- long availInternalSize = (long)internalStats.getAvailableBlocks() *
- (long)internalStats.getBlockSize();
-
- double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
-
- File apkFile = new File(archiveFilePath);
- long pkgLen = apkFile.length();
-
- boolean auto = true;
- // To make final copy
- long reqInstallSize = pkgLen;
- // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
- long reqInternalSize = 0;
- boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
- boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
- (reqInternalSize < availInternalSize);
- boolean fitsOnInt = intThresholdOk && intAvailOk;
-
- // Consider application flags preferences as well...
- boolean installOnlyOnSd = (installLocation ==
- PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- boolean installOnlyInternal = (installLocation ==
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
- if (installOnlyInternal) {
- // If set explicitly in manifest,
- // let that override everything else
- auto = false;
- } else if (installOnlyOnSd){
- // Check if this can be accommodated on the sdcard
- if (fitsOnSd) {
- auto = false;
+ String archiveFilePath, int flags) {
+ boolean checkInt = false;
+ boolean checkExt = false;
+ boolean checkBoth = false;
+ check_inner : {
+ // Check flags.
+ if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+ // Check for forward locked app
+ checkInt = true;
+ break check_inner;
+ } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+ // Explicit flag to install internally.
+ // Check internal storage and return
+ checkInt = true;
+ break check_inner;
+ } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ // Explicit flag to install externally.
+ // Check external storage and return
+ checkExt = true;
+ break check_inner;
}
- } else {
+ // Check for manifest option
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ checkInt = true;
+ break check_inner;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ checkExt = true;
+ checkBoth = true;
+ break check_inner;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ checkInt = true;
+ checkBoth = true;
+ break check_inner;
+ }
// Check if user option is enabled
boolean setInstallLoc = Settings.System.getInt(getApplicationContext()
.getContentResolver(),
@@ -367,31 +350,114 @@
Settings.System.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
- installOnlyInternal = true;
- auto = false;
+ checkInt = true;
+ checkBoth = true;
+ break check_inner;
} else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
- installOnlyOnSd = true;
- auto = false;
+ checkExt = true;
+ checkBoth = true;
+ break check_inner;
}
}
+ // Fall back to default policy if nothing else is specified.
+ checkInt = true;
+ checkBoth = true;
}
- if (!auto) {
- if (installOnlyOnSd) {
- return fitsOnSd ? PackageManager.INSTALL_EXTERNAL : ERR_LOC;
- } else if (installOnlyInternal){
- // Check on internal flash
- return fitsOnInt ? 0 : ERR_LOC;
+
+ // Package size = code size + cache size + data size
+ // If code size > 1 MB, install on SD card.
+ // Else install on internal NAND flash, unless space on NAND is less than 10%
+ String status = Environment.getExternalStorageState();
+ long availSDSize = -1;
+ boolean mediaAvailable = false;
+ if (status.equals(Environment.MEDIA_MOUNTED)) {
+ StatFs sdStats = new StatFs(
+ Environment.getExternalStorageDirectory().getPath());
+ availSDSize = (long)sdStats.getAvailableBlocks() *
+ (long)sdStats.getBlockSize();
+ mediaAvailable = true;
+ }
+ StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
+ long totalInternalSize = (long)internalStats.getBlockCount() *
+ (long)internalStats.getBlockSize();
+ long availInternalSize = (long)internalStats.getAvailableBlocks() *
+ (long)internalStats.getBlockSize();
+
+ double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
+
+ File apkFile = new File(archiveFilePath);
+ long pkgLen = apkFile.length();
+
+ // To make final copy
+ long reqInstallSize = pkgLen;
+ // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
+ long reqInternalSize = 0;
+ boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
+ boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
+ boolean fitsOnSd = false;
+ if (mediaAvailable && (reqInstallSize < availSDSize)) {
+ // If we do not have an internal size requirement
+ // don't do a threshold check.
+ if (reqInternalSize == 0) {
+ fitsOnSd = true;
+ } else if ((reqInternalSize < availInternalSize) && intThresholdOk) {
+ fitsOnSd = true;
}
}
- // Try to install internally
- if (fitsOnInt) {
- return 0;
+ boolean fitsOnInt = intThresholdOk && intAvailOk;
+ if (checkInt) {
+ // Check for internal memory availability
+ if (fitsOnInt) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ } else if (checkExt) {
+ if (fitsOnSd) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
}
- // Try the sdcard now.
- if (fitsOnSd) {
- return PackageManager.INSTALL_EXTERNAL;
+ if (checkBoth) {
+ // Check for internal first
+ if (fitsOnInt) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ // Check for external next
+ if (fitsOnSd) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
}
- // Return error code
- return ERR_LOC;
+ if (checkExt || checkBoth && !mediaAvailable) {
+ return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
+ }
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+
+ private boolean checkFreeStorageInner(boolean external, Uri packageURI) {
+ File apkFile = new File(packageURI.getPath());
+ long size = apkFile.length();
+ if (external) {
+ String status = Environment.getExternalStorageState();
+ long availSDSize = -1;
+ if (status.equals(Environment.MEDIA_MOUNTED)) {
+ StatFs sdStats = new StatFs(
+ Environment.getExternalStorageDirectory().getPath());
+ availSDSize = (long)sdStats.getAvailableBlocks() *
+ (long)sdStats.getBlockSize();
+ }
+ return availSDSize > size;
+ }
+ StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
+ long totalInternalSize = (long)internalStats.getBlockCount() *
+ (long)internalStats.getBlockSize();
+ long availInternalSize = (long)internalStats.getAvailableBlocks() *
+ (long)internalStats.getBlockSize();
+
+ double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
+ // To make final copy
+ long reqInstallSize = size;
+ // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
+ long reqInternalSize = 0;
+ boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
+ boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
+ return intThresholdOk && intAvailOk;
}
}
diff --git a/packages/SettingsProvider/res/values-cs/strings.xml b/packages/SettingsProvider/res/values-cs/strings.xml
index 2b089d9..a28ffa1 100644
--- a/packages/SettingsProvider/res/values-cs/strings.xml
+++ b/packages/SettingsProvider/res/values-cs/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Paměť pro nastavení"</string>
diff --git a/packages/SettingsProvider/res/values-da/strings.xml b/packages/SettingsProvider/res/values-da/strings.xml
index bc160f3..ed450d5 100644
--- a/packages/SettingsProvider/res/values-da/strings.xml
+++ b/packages/SettingsProvider/res/values-da/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring af indstillinger"</string>
diff --git a/packages/SettingsProvider/res/values-de/strings.xml b/packages/SettingsProvider/res/values-de/strings.xml
index a293522..6effbae 100644
--- a/packages/SettingsProvider/res/values-de/strings.xml
+++ b/packages/SettingsProvider/res/values-de/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Einstellungsspeicher"</string>
diff --git a/packages/SettingsProvider/res/values-el/strings.xml b/packages/SettingsProvider/res/values-el/strings.xml
index 1cac86d..5bd1fa6 100644
--- a/packages/SettingsProvider/res/values-el/strings.xml
+++ b/packages/SettingsProvider/res/values-el/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Αποθηκευτικός χώρος ρυθμίσεων"</string>
diff --git a/packages/SettingsProvider/res/values-es-rUS/strings.xml b/packages/SettingsProvider/res/values-es-rUS/strings.xml
index de3958b..7a15d8b 100644
--- a/packages/SettingsProvider/res/values-es-rUS/strings.xml
+++ b/packages/SettingsProvider/res/values-es-rUS/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
diff --git a/packages/SettingsProvider/res/values-es/strings.xml b/packages/SettingsProvider/res/values-es/strings.xml
index de3958b..7a15d8b 100644
--- a/packages/SettingsProvider/res/values-es/strings.xml
+++ b/packages/SettingsProvider/res/values-es/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
diff --git a/packages/SettingsProvider/res/values-fr/strings.xml b/packages/SettingsProvider/res/values-fr/strings.xml
index 7a1386a..c90eb09 100644
--- a/packages/SettingsProvider/res/values-fr/strings.xml
+++ b/packages/SettingsProvider/res/values-fr/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Stockage des paramètres"</string>
diff --git a/packages/SettingsProvider/res/values-it/strings.xml b/packages/SettingsProvider/res/values-it/strings.xml
index f88a654..40735cc 100644
--- a/packages/SettingsProvider/res/values-it/strings.xml
+++ b/packages/SettingsProvider/res/values-it/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Archiviazione impostazioni"</string>
diff --git a/packages/SettingsProvider/res/values-ja/strings.xml b/packages/SettingsProvider/res/values-ja/strings.xml
index ffd57e6..d222ca9 100644
--- a/packages/SettingsProvider/res/values-ja/strings.xml
+++ b/packages/SettingsProvider/res/values-ja/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"ストレージの設定"</string>
diff --git a/packages/SettingsProvider/res/values-ko/strings.xml b/packages/SettingsProvider/res/values-ko/strings.xml
index aab51d6..8e0cc75 100644
--- a/packages/SettingsProvider/res/values-ko/strings.xml
+++ b/packages/SettingsProvider/res/values-ko/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"설정 저장소"</string>
diff --git a/packages/SettingsProvider/res/values-nb/strings.xml b/packages/SettingsProvider/res/values-nb/strings.xml
index c96b1eb..ad18f94 100644
--- a/packages/SettingsProvider/res/values-nb/strings.xml
+++ b/packages/SettingsProvider/res/values-nb/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring av innstillinger"</string>
diff --git a/packages/SettingsProvider/res/values-nl/strings.xml b/packages/SettingsProvider/res/values-nl/strings.xml
index 7a0e416..010fdb5 100644
--- a/packages/SettingsProvider/res/values-nl/strings.xml
+++ b/packages/SettingsProvider/res/values-nl/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Opslagruimte voor instellingen"</string>
diff --git a/packages/SettingsProvider/res/values-pl/strings.xml b/packages/SettingsProvider/res/values-pl/strings.xml
index ccff82e3..9f81e63 100644
--- a/packages/SettingsProvider/res/values-pl/strings.xml
+++ b/packages/SettingsProvider/res/values-pl/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Pamięć ustawień"</string>
diff --git a/packages/SettingsProvider/res/values-pt-rPT/strings.xml b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
index 1e1dccb..6bd62e3 100644
--- a/packages/SettingsProvider/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Armazenamento de Definições"</string>
diff --git a/packages/SettingsProvider/res/values-pt/strings.xml b/packages/SettingsProvider/res/values-pt/strings.xml
index c4af964..ade1746 100644
--- a/packages/SettingsProvider/res/values-pt/strings.xml
+++ b/packages/SettingsProvider/res/values-pt/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Armazenamento de configurações"</string>
diff --git a/packages/SettingsProvider/res/values-ru/strings.xml b/packages/SettingsProvider/res/values-ru/strings.xml
index bcf92fa..4c15984 100644
--- a/packages/SettingsProvider/res/values-ru/strings.xml
+++ b/packages/SettingsProvider/res/values-ru/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Хранилище настроек"</string>
diff --git a/packages/SettingsProvider/res/values-sv/strings.xml b/packages/SettingsProvider/res/values-sv/strings.xml
index fa3f5cb..b6de084 100644
--- a/packages/SettingsProvider/res/values-sv/strings.xml
+++ b/packages/SettingsProvider/res/values-sv/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Lagring av inställningar"</string>
diff --git a/packages/SettingsProvider/res/values-tr/strings.xml b/packages/SettingsProvider/res/values-tr/strings.xml
index dc36cda..e99e99b 100644
--- a/packages/SettingsProvider/res/values-tr/strings.xml
+++ b/packages/SettingsProvider/res/values-tr/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Ayarlar Deposu"</string>
diff --git a/packages/SettingsProvider/res/values-zh-rCN/strings.xml b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
index daf1254..11c5d6c 100644
--- a/packages/SettingsProvider/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"设置存储"</string>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/strings.xml b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
index 0700a76..977c9b9 100644
--- a/packages/SettingsProvider/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+/**
+ * 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.
+ */
+ -->
- 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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"設定儲存空間"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index fb39ac0..2ad663d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -301,18 +301,19 @@
* enabled or disabled based on product resources.
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.WINDOW_ANIMATION_SCALE + "'");
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.TRANSITION_ANIMATION_SCALE + "'");
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadDefaultAnimationSettings(stmt);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 32;
}
@@ -349,14 +350,15 @@
if (upgradeVersion == 34) {
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
loadSecure35Settings(stmt);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 35;
}
@@ -391,15 +393,16 @@
if (upgradeVersion == 37) {
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ " VALUES(?,?);");
loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
R.string.airplane_mode_toggleable_radios);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 38;
}
@@ -440,18 +443,19 @@
* All animations are now turned on by default!
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.WINDOW_ANIMATION_SCALE + "'");
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.TRANSITION_ANIMATION_SCALE + "'");
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadDefaultAnimationSettings(stmt);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 41;
}
@@ -461,16 +465,17 @@
* Initialize newly public haptic feedback setting
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.HAPTIC_FEEDBACK_ENABLED + "'");
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadDefaultHapticSettings(stmt);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 42;
}
@@ -480,15 +485,16 @@
* Initialize new notification pulse setting
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
R.bool.def_notification_pulse);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 43;
}
@@ -498,15 +504,16 @@
* This upgrade stores bluetooth volume separately from voice volume
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ " VALUES(?,?);");
loadSetting(stmt, Settings.System.VOLUME_BLUETOOTH_SCO,
AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 44;
}
@@ -585,14 +592,15 @@
* New settings for new user interface noises.
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadUISoundEffectsSettings(stmt);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 50;
@@ -603,15 +611,16 @@
* New settings for set install location UI.
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION,
R.bool.set_install_location);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 51;
@@ -637,15 +646,16 @@
if (upgradeVersion == 52) {
// new vibration/silent mode settings
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT,
R.bool.def_vibrate_in_silent);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 53;
@@ -656,15 +666,16 @@
* New settings for set install location UI.
*/
db.beginTransaction();
+ SQLiteStatement stmt = null;
try {
- SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+ stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
loadIntegerSetting(stmt, Settings.System.DEFAULT_INSTALL_LOCATION,
R.integer.def_install_location);
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
+ if (stmt != null) stmt.close();
}
upgradeVersion = 54;
@@ -844,47 +855,50 @@
* @param db the database to insert the volume levels into
*/
private void loadVolumeLevels(SQLiteDatabase db) {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
- + " VALUES(?,?);");
-
- loadSetting(stmt, Settings.System.VOLUME_MUSIC,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
- loadSetting(stmt, Settings.System.VOLUME_RING,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_RING]);
- loadSetting(stmt, Settings.System.VOLUME_SYSTEM,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]);
- loadSetting(
- stmt,
- Settings.System.VOLUME_VOICE,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]);
- loadSetting(stmt, Settings.System.VOLUME_ALARM,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_ALARM]);
- loadSetting(
- stmt,
- Settings.System.VOLUME_NOTIFICATION,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_NOTIFICATION]);
- loadSetting(
- stmt,
- Settings.System.VOLUME_BLUETOOTH_SCO,
- AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);
-
- loadSetting(stmt, Settings.System.MODE_RINGER,
- AudioManager.RINGER_MODE_NORMAL);
-
- loadVibrateSetting(db, false);
-
- // By default, only the ring/notification and system streams are affected
- loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- (1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |
- (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED));
-
- loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
- ((1 << AudioManager.STREAM_MUSIC) |
- (1 << AudioManager.STREAM_RING) |
- (1 << AudioManager.STREAM_NOTIFICATION) |
- (1 << AudioManager.STREAM_SYSTEM)));
-
- stmt.close();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ + " VALUES(?,?);");
+
+ loadSetting(stmt, Settings.System.VOLUME_MUSIC,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
+ loadSetting(stmt, Settings.System.VOLUME_RING,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_RING]);
+ loadSetting(stmt, Settings.System.VOLUME_SYSTEM,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]);
+ loadSetting(
+ stmt,
+ Settings.System.VOLUME_VOICE,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]);
+ loadSetting(stmt, Settings.System.VOLUME_ALARM,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_ALARM]);
+ loadSetting(
+ stmt,
+ Settings.System.VOLUME_NOTIFICATION,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_NOTIFICATION]);
+ loadSetting(
+ stmt,
+ Settings.System.VOLUME_BLUETOOTH_SCO,
+ AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);
+
+ loadSetting(stmt, Settings.System.MODE_RINGER,
+ AudioManager.RINGER_MODE_NORMAL);
+
+ loadVibrateSetting(db, false);
+
+ // By default, only the ring/notification and system streams are affected
+ loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ (1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |
+ (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED));
+
+ loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
+ ((1 << AudioManager.STREAM_MUSIC) |
+ (1 << AudioManager.STREAM_RING) |
+ (1 << AudioManager.STREAM_NOTIFICATION) |
+ (1 << AudioManager.STREAM_SYSTEM)));
+ } finally {
+ if (stmt != null) stmt.close();
+ }
}
private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) {
@@ -892,17 +906,21 @@
db.execSQL("DELETE FROM system WHERE name='" + Settings.System.VIBRATE_ON + "'");
}
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
- + " VALUES(?,?);");
-
- // Vibrate off by default for ringer, on for notification
- int vibrate = 0;
- vibrate = AudioService.getValueForVibrateSetting(vibrate,
- AudioManager.VIBRATE_TYPE_NOTIFICATION, AudioManager.VIBRATE_SETTING_ON);
- vibrate = AudioService.getValueForVibrateSetting(vibrate,
- AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
- loadSetting(stmt, Settings.System.VIBRATE_ON, vibrate);
- stmt.close();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ + " VALUES(?,?);");
+
+ // Vibrate off by default for ringer, on for notification
+ int vibrate = 0;
+ vibrate = AudioService.getValueForVibrateSetting(vibrate,
+ AudioManager.VIBRATE_TYPE_NOTIFICATION, AudioManager.VIBRATE_SETTING_ON);
+ vibrate = AudioService.getValueForVibrateSetting(vibrate,
+ AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
+ loadSetting(stmt, Settings.System.VIBRATE_ON, vibrate);
+ } finally {
+ if (stmt != null) stmt.close();
+ }
}
private void loadSettings(SQLiteDatabase db) {
@@ -911,69 +929,72 @@
}
private void loadSystemSettings(SQLiteDatabase db) {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
- + " VALUES(?,?);");
-
- loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
- R.bool.def_dim_screen);
- loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
- "1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0);
- loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
- R.integer.def_screen_off_timeout);
-
- // Set default cdma emergency tone
- loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0);
-
- // Set default cdma call auto retry
- loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0);
-
- // Set default cdma DTMF type
- loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
-
- // Set default hearing aid
- loadSetting(stmt, Settings.System.HEARING_AID, 0);
-
- // Set default tty mode
- loadSetting(stmt, Settings.System.TTY_MODE, 0);
-
- loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
- R.bool.def_airplane_mode_on);
-
- loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
- R.string.def_airplane_mode_radios);
-
- loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
- R.string.airplane_mode_toggleable_radios);
-
- loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
- R.bool.def_auto_time); // Sync time to NITZ
-
- loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
- R.integer.def_screen_brightness);
-
- loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
- R.bool.def_screen_brightness_automatic_mode);
-
- loadDefaultAnimationSettings(stmt);
-
- loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
- R.bool.def_accelerometer_rotation);
-
- loadDefaultHapticSettings(stmt);
-
- loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
- R.bool.def_notification_pulse);
- loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION,
- R.bool.set_install_location);
- loadIntegerSetting(stmt, Settings.System.DEFAULT_INSTALL_LOCATION,
- R.integer.def_install_location);
-
- loadUISoundEffectsSettings(stmt);
-
- loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT,
- R.bool.def_vibrate_in_silent);
-
- stmt.close();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ + " VALUES(?,?);");
+
+ loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
+ R.bool.def_dim_screen);
+ loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
+ "1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0);
+ loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
+ R.integer.def_screen_off_timeout);
+
+ // Set default cdma emergency tone
+ loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0);
+
+ // Set default cdma call auto retry
+ loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0);
+
+ // Set default cdma DTMF type
+ loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
+
+ // Set default hearing aid
+ loadSetting(stmt, Settings.System.HEARING_AID, 0);
+
+ // Set default tty mode
+ loadSetting(stmt, Settings.System.TTY_MODE, 0);
+
+ loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
+ R.bool.def_airplane_mode_on);
+
+ loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
+ R.string.def_airplane_mode_radios);
+
+ loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+ R.string.airplane_mode_toggleable_radios);
+
+ loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
+ R.bool.def_auto_time); // Sync time to NITZ
+
+ loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
+ R.integer.def_screen_brightness);
+
+ loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
+ R.bool.def_screen_brightness_automatic_mode);
+
+ loadDefaultAnimationSettings(stmt);
+
+ loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
+ R.bool.def_accelerometer_rotation);
+
+ loadDefaultHapticSettings(stmt);
+
+ loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
+ R.bool.def_notification_pulse);
+ loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION,
+ R.bool.set_install_location);
+ loadIntegerSetting(stmt, Settings.System.DEFAULT_INSTALL_LOCATION,
+ R.integer.def_install_location);
+
+ loadUISoundEffectsSettings(stmt);
+
+ loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT,
+ R.bool.def_vibrate_in_silent);
+ } finally {
+ if (stmt != null) stmt.close();
+ }
}
private void loadUISoundEffectsSettings(SQLiteStatement stmt) {
@@ -1014,79 +1035,82 @@
}
private void loadSecureSettings(SQLiteDatabase db) {
- SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
- + " VALUES(?,?);");
-
- loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON,
- R.bool.def_bluetooth_on);
-
- // Data roaming default, based on build
- loadSetting(stmt, Settings.Secure.DATA_ROAMING,
- "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.dataroaming",
- "false")) ? 1 : 0);
-
- loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
- R.bool.def_install_non_market_apps);
-
- loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- R.string.def_location_providers_allowed);
-
- loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
- R.bool.assisted_gps_enabled);
-
- loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
- R.integer.def_network_preference);
-
- loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED,
- R.bool.def_usb_mass_storage_enabled);
-
- loadBooleanSetting(stmt, Settings.Secure.WIFI_ON,
- R.bool.def_wifi_on);
- loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- R.bool.def_networks_available_notification_on);
-
- String wifiWatchList = SystemProperties.get("ro.com.android.wifi-watchlist");
- if (!TextUtils.isEmpty(wifiWatchList)) {
- loadSetting(stmt, Settings.Secure.WIFI_WATCHDOG_WATCH_LIST, wifiWatchList);
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ + " VALUES(?,?);");
+
+ loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON,
+ R.bool.def_bluetooth_on);
+
+ // Data roaming default, based on build
+ loadSetting(stmt, Settings.Secure.DATA_ROAMING,
+ "true".equalsIgnoreCase(
+ SystemProperties.get("ro.com.android.dataroaming",
+ "false")) ? 1 : 0);
+
+ loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
+ R.bool.def_install_non_market_apps);
+
+ loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ R.string.def_location_providers_allowed);
+
+ loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
+ R.bool.assisted_gps_enabled);
+
+ loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
+ R.integer.def_network_preference);
+
+ loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED,
+ R.bool.def_usb_mass_storage_enabled);
+
+ loadBooleanSetting(stmt, Settings.Secure.WIFI_ON,
+ R.bool.def_wifi_on);
+ loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+ R.bool.def_networks_available_notification_on);
+
+ String wifiWatchList = SystemProperties.get("ro.com.android.wifi-watchlist");
+ if (!TextUtils.isEmpty(wifiWatchList)) {
+ loadSetting(stmt, Settings.Secure.WIFI_WATCHDOG_WATCH_LIST, wifiWatchList);
+ }
+
+ // Set the preferred network mode to 0 = Global, CDMA default
+ int type = SystemProperties.getInt("ro.telephony.default_network",
+ RILConstants.PREFERRED_NETWORK_MODE);
+ loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
+
+ // Enable or disable Cell Broadcast SMS
+ loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS,
+ RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
+
+ // Set the preferred cdma subscription to 0 = Subscription from RUIM, when available
+ loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
+ RILConstants.PREFERRED_CDMA_SUBSCRIPTION);
+
+ // Don't do this. The SystemServer will initialize ADB_ENABLED from a
+ // persistent system property instead.
+ //loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0);
+
+ // Allow mock locations default, based on build
+ loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
+ "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0);
+
+ loadSecure35Settings(stmt);
+
+ loadBooleanSetting(stmt, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND,
+ R.bool.def_mount_play_notification_snd);
+
+ loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_AUTOSTART,
+ R.bool.def_mount_ums_autostart);
+
+ loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_PROMPT,
+ R.bool.def_mount_ums_prompt);
+
+ loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
+ R.bool.def_mount_ums_notify_enabled);
+ } finally {
+ if (stmt != null) stmt.close();
}
-
- // Set the preferred network mode to 0 = Global, CDMA default
- int type = SystemProperties.getInt("ro.telephony.default_network",
- RILConstants.PREFERRED_NETWORK_MODE);
- loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
-
- // Enable or disable Cell Broadcast SMS
- loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS,
- RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
-
- // Set the preferred cdma subscription to 0 = Subscription from RUIM, when available
- loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
- RILConstants.PREFERRED_CDMA_SUBSCRIPTION);
-
- // Don't do this. The SystemServer will initialize ADB_ENABLED from a
- // persistent system property instead.
- //loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0);
-
- // Allow mock locations default, based on build
- loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
- "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0);
-
- loadSecure35Settings(stmt);
-
- loadBooleanSetting(stmt, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND,
- R.bool.def_mount_play_notification_snd);
-
- loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_AUTOSTART,
- R.bool.def_mount_ums_autostart);
-
- loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_PROMPT,
- R.bool.def_mount_ums_prompt);
-
- loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
- R.bool.def_mount_ums_notify_enabled);
-
- stmt.close();
}
private void loadSecure35Settings(SQLiteStatement stmt) {
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index b7acd96..1d69361 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -383,7 +383,7 @@
// ----------------------------------------------------------------------------
static int
android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jstring nativeSoLib)
+ jobject weak_this, jstring nativeSoLib, jstring engConfig)
{
int result = TTS_FAILURE;
@@ -395,6 +395,7 @@
DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
+ const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
void *engine_lib_handle = dlopen(nativeSoLibNativeString,
RTLD_NOW | RTLD_LOCAL);
@@ -409,7 +410,7 @@
if (pJniStorage->mNativeSynthInterface) {
Mutex::Autolock l(engineMutex);
- pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
+ pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB, engConfigString);
}
result = TTS_SUCCESS;
@@ -422,6 +423,7 @@
env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+ env->ReleaseStringUTFChars(engConfig, engConfigString);
return result;
}
@@ -482,6 +484,29 @@
return result;
}
+static int
+android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
+{
+ int result = TTS_FAILURE;
+
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
+ return result;
+ }
+
+ Mutex::Autolock l(engineMutex);
+
+ SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
+
+ if (pSynthData->mNativeSynthInterface) {
+ result = pSynthData->mNativeSynthInterface->setProperty(ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
+ engineConfigNativeString, strlen(engineConfigNativeString));
+ }
+ env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
+
+ return result;
+}
static int
android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
@@ -867,6 +892,10 @@
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)android_tts_SynthProxy_isLanguageAvailable
},
+ { "native_setConfig",
+ "(ILjava/lang/String;)I",
+ (void*)android_tts_SynthProxy_setConfig
+ },
{ "native_setLanguage",
"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)android_tts_SynthProxy_setLanguage
@@ -896,7 +925,7 @@
(void*)android_tts_SynthProxy_shutdown
},
{ "native_setup",
- "(Ljava/lang/Object;Ljava/lang/String;)I",
+ "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
(void*)android_tts_SynthProxy_native_setup
},
{ "native_setLowShelf",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index cece94e..525a504 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -48,10 +48,10 @@
/**
* Constructor; pass the location of the native TTS .so to use.
*/
- public SynthProxy(String nativeSoLib) {
+ public SynthProxy(String nativeSoLib, String engineConfig) {
boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
- native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
+ native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
}
@@ -105,6 +105,13 @@
}
/**
+ * Updates the engine configuration.
+ */
+ public int setConfig(String engineConfig) {
+ return native_setConfig(mJniData, engineConfig);
+ }
+
+ /**
* Sets the language.
*/
public int setLanguage(String language, String country, String variant) {
@@ -173,7 +180,8 @@
*/
private int mJniData = 0;
- private native final int native_setup(Object weak_this, String nativeSoLib);
+ private native final int native_setup(Object weak_this, String nativeSoLib,
+ String engineConfig);
private native final int native_setLowShelf(boolean applyFilter, float filterGain,
float attenuationInDb, float freqInHz, float slope);
@@ -197,6 +205,8 @@
private native final int native_loadLanguage(int jniData, String language, String country,
String variant);
+ private native final int native_setConfig(int jniData, String engineConfig);
+
private native final int native_setSpeechRate(int jniData, int speechRate);
private native final int native_setPitch(int jniData, int speechRate);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 6b7ab3f..ae48e0e 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
@@ -122,7 +123,8 @@
private static final int MAX_FILENAME_LENGTH = 250;
// TODO use the TTS stream type when available
private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
+ // TODO use TextToSpeech.DEFAULT_SYNTH once it is unhidden
+ private static final String DEFAULT_SYNTH = "com.svox.pico";
private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
private static final String CATEGORY = "android.intent.category.TTS";
private static final String PKGNAME = "android.tts";
@@ -163,11 +165,6 @@
currentSpeechEngineSOFile = "";
setEngine(getDefaultEngine());
- String soLibPath = "/system/lib/libttspico.so";
- if (sNativeSynth == null) {
- sNativeSynth = new SynthProxy(soLibPath);
- }
-
mSelf = this;
mIsSpeaking = false;
mSynthBusy = false;
@@ -210,10 +207,29 @@
if (isDefaultEnforced()) {
enginePackageName = getDefaultEngine();
}
+
+ // Make sure that the engine has been allowed by the user
+ if (!enginePackageName.equals(DEFAULT_SYNTH)) {
+ String[] enabledEngines = android.provider.Settings.Secure.getString(mResolver,
+ android.provider.Settings.Secure.TTS_ENABLED_PLUGINS).split(" ");
+ boolean isEnabled = false;
+ for (int i=0; i<enabledEngines.length; i++) {
+ if (enabledEngines[i].equals(enginePackageName)) {
+ isEnabled = true;
+ break;
+ }
+ }
+ if (!isEnabled) {
+ // Do not use an engine that the user has not enabled; fall back
+ // to using the default synthesizer.
+ enginePackageName = DEFAULT_SYNTH;
+ }
+ }
+
// The SVOX TTS is an exception to how the TTS packaging scheme works
// because it is part of the system and not a 3rd party add-on; thus
// its binary is actually located under /system/lib/
- if (enginePackageName.equals("com.svox.pico")) {
+ if (enginePackageName.equals(DEFAULT_SYNTH)) {
soFilename = "/system/lib/libttspico.so";
} else {
// Find the package
@@ -249,7 +265,25 @@
sNativeSynth.shutdown();
sNativeSynth = null;
}
- sNativeSynth = new SynthProxy(soFilename);
+
+ // Load the engineConfig from the plugin if it has any special configuration
+ // to be loaded. By convention, if an engine wants the TTS framework to pass
+ // in any configuration, it must put it into its content provider which has the URI:
+ // content://<packageName>.providers.SettingsProvider
+ // That content provider must provide a Cursor which returns the String that
+ // is to be passed back to the native .so file for the plugin when getString(0) is
+ // called on it.
+ // Note that the TTS framework does not care what this String data is: it is something
+ // that comes from the engine plugin and is consumed only by the engine plugin itself.
+ String engineConfig = "";
+ Cursor c = getContentResolver().query(Uri.parse("content://" + enginePackageName
+ + ".providers.SettingsProvider"), null, null, null, null);
+ if (c != null){
+ c.moveToFirst();
+ engineConfig = c.getString(0);
+ c.close();
+ }
+ sNativeSynth = new SynthProxy(soFilename, engineConfig);
currentSpeechEngineSOFile = soFilename;
return TextToSpeech.SUCCESS;
}
@@ -1402,6 +1436,15 @@
return mSelf.setEngine(packageName);
}
+ /**
+ * Returns the packagename of the default speech synthesis engine.
+ *
+ * @return Packagename of the TTS engine that the user has chosen as their default.
+ */
+ public String getDefaultEngine() {
+ return mSelf.getDefaultEngine();
+ }
+
};
}
diff --git a/packages/VpnServices/res/values-cs/strings.xml b/packages/VpnServices/res/values-cs/strings.xml
index 9e20c19..96d4cc5 100644
--- a/packages/VpnServices/res/values-cs/strings.xml
+++ b/packages/VpnServices/res/values-cs/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Služby VPN"</string>
diff --git a/packages/VpnServices/res/values-da/strings.xml b/packages/VpnServices/res/values-da/strings.xml
index fb5ddf9..0f05bbc 100644
--- a/packages/VpnServices/res/values-da/strings.xml
+++ b/packages/VpnServices/res/values-da/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-tjenester"</string>
diff --git a/packages/VpnServices/res/values-de/strings.xml b/packages/VpnServices/res/values-de/strings.xml
index f120253..b907be8b 100644
--- a/packages/VpnServices/res/values-de/strings.xml
+++ b/packages/VpnServices/res/values-de/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-Dienste"</string>
diff --git a/packages/VpnServices/res/values-el/strings.xml b/packages/VpnServices/res/values-el/strings.xml
index 06f907c..d96f3e0 100644
--- a/packages/VpnServices/res/values-el/strings.xml
+++ b/packages/VpnServices/res/values-el/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Υπηρεσίες VPN"</string>
diff --git a/packages/VpnServices/res/values-es-rUS/strings.xml b/packages/VpnServices/res/values-es-rUS/strings.xml
index f6ace53d..8f5053c 100644
--- a/packages/VpnServices/res/values-es-rUS/strings.xml
+++ b/packages/VpnServices/res/values-es-rUS/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Servicios VPN"</string>
diff --git a/packages/VpnServices/res/values-es/strings.xml b/packages/VpnServices/res/values-es/strings.xml
index 2864c75..9182459 100644
--- a/packages/VpnServices/res/values-es/strings.xml
+++ b/packages/VpnServices/res/values-es/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Servicios VPN"</string>
diff --git a/packages/VpnServices/res/values-fr/strings.xml b/packages/VpnServices/res/values-fr/strings.xml
index 519a9ab..80fefa4 100644
--- a/packages/VpnServices/res/values-fr/strings.xml
+++ b/packages/VpnServices/res/values-fr/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Services VPN"</string>
diff --git a/packages/VpnServices/res/values-it/strings.xml b/packages/VpnServices/res/values-it/strings.xml
index fd6e76b..1c7a588 100644
--- a/packages/VpnServices/res/values-it/strings.xml
+++ b/packages/VpnServices/res/values-it/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Servizi VPN"</string>
diff --git a/packages/VpnServices/res/values-ja/strings.xml b/packages/VpnServices/res/values-ja/strings.xml
index 2bad0e3..548d8a9 100644
--- a/packages/VpnServices/res/values-ja/strings.xml
+++ b/packages/VpnServices/res/values-ja/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPNサービス"</string>
diff --git a/packages/VpnServices/res/values-ko/strings.xml b/packages/VpnServices/res/values-ko/strings.xml
index a48eff6..4185291 100644
--- a/packages/VpnServices/res/values-ko/strings.xml
+++ b/packages/VpnServices/res/values-ko/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN 서비스"</string>
diff --git a/packages/VpnServices/res/values-nb/strings.xml b/packages/VpnServices/res/values-nb/strings.xml
index 9aac828..4790600 100644
--- a/packages/VpnServices/res/values-nb/strings.xml
+++ b/packages/VpnServices/res/values-nb/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-tjenester"</string>
diff --git a/packages/VpnServices/res/values-nl/strings.xml b/packages/VpnServices/res/values-nl/strings.xml
index d5a55e8..175c7dd 100644
--- a/packages/VpnServices/res/values-nl/strings.xml
+++ b/packages/VpnServices/res/values-nl/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-services"</string>
diff --git a/packages/VpnServices/res/values-pl/strings.xml b/packages/VpnServices/res/values-pl/strings.xml
index 6626c69..565d249 100644
--- a/packages/VpnServices/res/values-pl/strings.xml
+++ b/packages/VpnServices/res/values-pl/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Usługi VPN"</string>
diff --git a/packages/VpnServices/res/values-pt-rPT/strings.xml b/packages/VpnServices/res/values-pt-rPT/strings.xml
index 182e600..020188f 100644
--- a/packages/VpnServices/res/values-pt-rPT/strings.xml
+++ b/packages/VpnServices/res/values-pt-rPT/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Serviços VPN"</string>
diff --git a/packages/VpnServices/res/values-pt/strings.xml b/packages/VpnServices/res/values-pt/strings.xml
index a2877d6..f47652a 100644
--- a/packages/VpnServices/res/values-pt/strings.xml
+++ b/packages/VpnServices/res/values-pt/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Serviços de VPN"</string>
diff --git a/packages/VpnServices/res/values-ru/strings.xml b/packages/VpnServices/res/values-ru/strings.xml
index 7458f0c..8a839c3 100644
--- a/packages/VpnServices/res/values-ru/strings.xml
+++ b/packages/VpnServices/res/values-ru/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Службы VPN"</string>
diff --git a/packages/VpnServices/res/values-sv/strings.xml b/packages/VpnServices/res/values-sv/strings.xml
index ba541b8..24f9f58 100644
--- a/packages/VpnServices/res/values-sv/strings.xml
+++ b/packages/VpnServices/res/values-sv/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-tjänster"</string>
diff --git a/packages/VpnServices/res/values-tr/strings.xml b/packages/VpnServices/res/values-tr/strings.xml
index 41a0b4e..8666b35 100644
--- a/packages/VpnServices/res/values-tr/strings.xml
+++ b/packages/VpnServices/res/values-tr/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN Hizmetleri"</string>
diff --git a/packages/VpnServices/res/values-zh-rCN/strings.xml b/packages/VpnServices/res/values-zh-rCN/strings.xml
index ee8878f..cad08e1 100644
--- a/packages/VpnServices/res/values-zh-rCN/strings.xml
+++ b/packages/VpnServices/res/values-zh-rCN/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"虚拟专用网服务"</string>
diff --git a/packages/VpnServices/res/values-zh-rTW/strings.xml b/packages/VpnServices/res/values-zh-rTW/strings.xml
index 931d4ed..ee5a42b 100644
--- a/packages/VpnServices/res/values-zh-rTW/strings.xml
+++ b/packages/VpnServices/res/values-zh-rTW/strings.xml
@@ -1,18 +1,4 @@
<?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.
--->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN 服務"</string>
diff --git a/preloaded-classes b/preloaded-classes
index 5672c66..a12c0a0 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -269,7 +269,6 @@
android.location.ILocationManager$Stub$Proxy
android.location.Location
android.location.LocationManager
-android.location.LocationProviderInterface
android.media.AudioFormat
android.media.AudioManager
android.media.AudioRecord
@@ -633,7 +632,6 @@
com.android.internal.content.SyncStateContentProviderHelper
com.android.internal.graphics.NativeUtils
com.android.internal.location.DummyLocationProvider
-com.android.internal.location.GpsLocationProvider
com.android.internal.logging.AndroidHandler
com.android.internal.os.AndroidPrintStream
com.android.internal.os.BinderInternal
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index a555244..7fb7db0 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -18,6 +18,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -45,7 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Xml;
@@ -149,7 +150,7 @@
maximumFailedPasswordsForWipe = Integer.parseInt(
parser.getAttributeValue(null, "value"));
} else {
- Log.w(TAG, "Unknown admin tag: " + tag);
+ Slog.w(TAG, "Unknown admin tag: " + tag);
}
XmlUtils.skipCurrentTag(parser);
}
@@ -164,8 +165,8 @@
pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag);
}
}
- pw.print(prefix); pw.print("passwordQuality=");
- pw.print(passwordQuality);
+ pw.print(prefix); pw.print("passwordQuality=0x");
+ pw.print(Integer.toHexString(passwordQuality));
pw.print(" minimumPasswordLength=");
pw.println(minimumPasswordLength);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
@@ -184,7 +185,7 @@
int change = isPackageDisappearing(aa.info.getPackageName());
if (change == PACKAGE_PERMANENT_CHANGE
|| change == PACKAGE_TEMPORARY_CHANGE) {
- Log.w(TAG, "Admin unexpectedly uninstalled: "
+ Slog.w(TAG, "Admin unexpectedly uninstalled: "
+ aa.info.getComponent());
removed = true;
mAdminList.remove(i);
@@ -193,7 +194,7 @@
mContext.getPackageManager().getReceiverInfo(
aa.info.getComponent(), 0);
} catch (NameNotFoundException e) {
- Log.w(TAG, "Admin package change removed component: "
+ Slog.w(TAG, "Admin package change removed component: "
+ aa.info.getComponent());
removed = true;
mAdminList.remove(i);
@@ -307,10 +308,10 @@
try {
return new DeviceAdminInfo(mContext, infos.get(0));
} catch (XmlPullParserException e) {
- Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ Slog.w(TAG, "Bad device admin requested: " + adminName, e);
return null;
} catch (IOException e) {
- Log.w(TAG, "Bad device admin requested: " + adminName, e);
+ Slog.w(TAG, "Bad device admin requested: " + adminName, e);
return null;
}
}
@@ -342,8 +343,6 @@
}
}
- out.endTag(null, "policies");
-
if (mPasswordOwner >= 0) {
out.startTag(null, "password-owner");
out.attribute(null, "value", Integer.toString(mPasswordOwner));
@@ -356,6 +355,15 @@
out.endTag(null, "failed-password-attempts");
}
+ if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) {
+ out.startTag(null, "active-password");
+ out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
+ out.attribute(null, "length", Integer.toString(mActivePasswordLength));
+ out.endTag(null, "active-password");
+ }
+
+ out.endTag(null, "policies");
+
out.endDocument();
stream.close();
journal.commit();
@@ -409,7 +417,7 @@
mAdminList.add(ap);
}
} catch (RuntimeException e) {
- Log.w(TAG, "Failed loading admin " + name, e);
+ Slog.w(TAG, "Failed loading admin " + name, e);
}
} else if ("failed-password-attempts".equals(tag)) {
mFailedPasswordAttempts = Integer.parseInt(
@@ -419,21 +427,27 @@
mPasswordOwner = Integer.parseInt(
parser.getAttributeValue(null, "value"));
XmlUtils.skipCurrentTag(parser);
+ } else if ("active-password".equals(tag)) {
+ mActivePasswordQuality = Integer.parseInt(
+ parser.getAttributeValue(null, "quality"));
+ mActivePasswordLength = Integer.parseInt(
+ parser.getAttributeValue(null, "length"));
+ XmlUtils.skipCurrentTag(parser);
} else {
- Log.w(TAG, "Unknown tag: " + tag);
+ Slog.w(TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
}
}
} catch (NullPointerException e) {
- Log.w(TAG, "failed parsing " + file + " " + e);
+ Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (NumberFormatException e) {
- Log.w(TAG, "failed parsing " + file + " " + e);
+ Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (XmlPullParserException e) {
- Log.w(TAG, "failed parsing " + file + " " + e);
+ Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IOException e) {
- Log.w(TAG, "failed parsing " + file + " " + e);
+ Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IndexOutOfBoundsException e) {
- Log.w(TAG, "failed parsing " + file + " " + e);
+ Slog.w(TAG, "failed parsing " + file + " " + e);
}
try {
if (stream != null) {
@@ -443,6 +457,20 @@
// Ignore
}
+ // Validate that what we stored for the password quality matches
+ // sufficiently what is currently set. Note that this is only
+ // a sanity check in case the two get out of sync; this should
+ // never normally happen.
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if (utils.getActivePasswordQuality() < mActivePasswordQuality) {
+ Slog.w(TAG, "Active password quality 0x"
+ + Integer.toHexString(mActivePasswordQuality)
+ + " does not match actual quality 0x"
+ + Integer.toHexString(utils.getActivePasswordQuality()));
+ mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ mActivePasswordLength = 0;
+ }
+
validatePasswordOwnerLocked();
long timeMs = getMaximumTimeToLock(null);
@@ -452,10 +480,23 @@
try {
getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
} catch (RemoteException e) {
- Log.w(TAG, "Failure talking with power manager", e);
+ Slog.w(TAG, "Failure talking with power manager", e);
}
}
+ static void validateQualityConstant(int quality) {
+ switch (quality) {
+ case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ return;
+ }
+ throw new IllegalArgumentException("Invalid quality constant: 0x"
+ + Integer.toHexString(quality));
+ }
+
void validatePasswordOwnerLocked() {
if (mPasswordOwner >= 0) {
boolean haveOwner = false;
@@ -466,7 +507,7 @@
}
}
if (!haveOwner) {
- Log.w(TAG, "Previous password owner " + mPasswordOwner
+ Slog.w(TAG, "Previous password owner " + mPasswordOwner
+ " no longer active; disabling");
mPasswordOwner = -1;
}
@@ -556,15 +597,17 @@
}
}
- public void setPasswordQuality(ComponentName who, int mode) {
+ public void setPasswordQuality(ComponentName who, int quality) {
+ validateQualityConstant(quality);
+
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
}
ActiveAdmin ap = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
- if (ap.passwordQuality != mode) {
- ap.passwordQuality = mode;
+ if (ap.passwordQuality != quality) {
+ ap.passwordQuality = quality;
saveSettingsLocked();
}
}
@@ -692,23 +735,27 @@
DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
quality = getPasswordQuality(null);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- int adjQuality = LockPatternUtils.adjustPasswordMode(password, quality);
- if (adjQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- Log.w(TAG, "resetPassword: password does not meet quality " + quality);
+ int realQuality = LockPatternUtils.computePasswordQuality(password);
+ if (realQuality < quality) {
+ Slog.w(TAG, "resetPassword: password quality 0x"
+ + Integer.toHexString(quality)
+ + " does not meet required quality 0x"
+ + Integer.toHexString(quality));
return false;
}
- quality = adjQuality;
+ quality = realQuality;
}
int length = getPasswordMinimumLength(null);
if (password.length() < length) {
- Log.w(TAG, "resetPassword: password does not meet length " + length);
+ Slog.w(TAG, "resetPassword: password length " + password.length()
+ + " does not meet required length " + length);
return false;
}
}
int callingUid = Binder.getCallingUid();
if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
- Log.w(TAG, "resetPassword: already set by another uid and not entered by user");
+ Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
return false;
}
@@ -718,11 +765,13 @@
try {
LockPatternUtils utils = new LockPatternUtils(mContext);
utils.saveLockPassword(password, quality);
- int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
- != 0 ? callingUid : -1;
- if (mPasswordOwner != newOwner) {
- mPasswordOwner = newOwner;
- saveSettingsLocked();
+ synchronized (this) {
+ int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
+ != 0 ? callingUid : -1;
+ if (mPasswordOwner != newOwner) {
+ mPasswordOwner = newOwner;
+ saveSettingsLocked();
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -753,7 +802,7 @@
try {
getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
} catch (RemoteException e) {
- Log.w(TAG, "Failure talking with power manager", e);
+ Slog.w(TAG, "Failure talking with power manager", e);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -806,7 +855,7 @@
try {
RecoverySystem.rebootWipeUserData(mContext);
} catch (IOException e) {
- Log.w(TAG, "Failed requesting data wipe", e);
+ Slog.w(TAG, "Failed requesting data wipe", e);
}
}
@@ -856,6 +905,8 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ validateQualityConstant(quality);
+
synchronized (this) {
if (mActivePasswordQuality != quality || mActivePasswordLength != length
|| mFailedPasswordAttempts != 0) {
@@ -863,10 +914,8 @@
try {
mActivePasswordQuality = quality;
mActivePasswordLength = length;
- if (mFailedPasswordAttempts != 0) {
- mFailedPasswordAttempts = 0;
- saveSettingsLocked();
- }
+ mFailedPasswordAttempts = 0;
+ saveSettingsLocked();
sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
} finally {
@@ -945,7 +994,8 @@
}
pw.println(" ");
- pw.print(" mActivePasswordQuality="); pw.println(mActivePasswordQuality);
+ pw.print(" mActivePasswordQuality=0x");
+ pw.println(Integer.toHexString(mActivePasswordQuality));
pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength);
pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);
diff --git a/services/java/com/android/server/DiskStatsService.java b/services/java/com/android/server/DiskStatsService.java
new file mode 100644
index 0000000..8ef974a
--- /dev/null
+++ b/services/java/com/android/server/DiskStatsService.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.StatFs;
+import android.os.SystemClock;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * This service exists only as a "dumpsys" target which reports
+ * statistics about the status of the disk.
+ */
+public class DiskStatsService extends Binder {
+ private final Context mContext;
+
+ public DiskStatsService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // This data is accessible to any app -- no permission check needed.
+
+ // Run a quick-and-dirty performance test: write 512 bytes
+ byte[] junk = new byte[512];
+ for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
+
+ File tmp = new File(Environment.getDataDirectory(), "system/perftest.tmp");
+ FileOutputStream fos = null;
+ IOException error = null;
+
+ long before = SystemClock.uptimeMillis();
+ try {
+ fos = new FileOutputStream(tmp);
+ fos.write(junk);
+ } catch (IOException e) {
+ error = e;
+ } finally {
+ try { if (fos != null) fos.close(); } catch (IOException e) {}
+ }
+
+ long after = SystemClock.uptimeMillis();
+ if (tmp.exists()) tmp.delete();
+
+ if (error != null) {
+ pw.print("Test-Error: ");
+ pw.println(error.toString());
+ } else {
+ pw.print("Latency: ");
+ pw.print(after - before);
+ pw.println("ms [512B Data Write]");
+ }
+
+ reportFreeSpace(Environment.getDataDirectory(), "Data", pw);
+ reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw);
+ reportFreeSpace(new File("/system"), "System", pw);
+
+ // TODO: Read /proc/yaffs and report interesting values;
+ // add configurable (through args) performance test parameters.
+ }
+
+ private void reportFreeSpace(File path, String name, PrintWriter pw) {
+ try {
+ StatFs statfs = new StatFs(path.getPath());
+ long bsize = statfs.getBlockSize();
+ long avail = statfs.getAvailableBlocks();
+ long total = statfs.getBlockCount();
+ if (bsize <= 0 || total <= 0) {
+ throw new IllegalArgumentException(
+ "Invalid stat: bsize=" + bsize + " avail=" + avail + " total=" + total);
+ }
+
+ pw.print(name);
+ pw.print("-Free: ");
+ pw.print(avail * bsize / 1024);
+ pw.print("K / ");
+ pw.print(total * bsize / 1024);
+ pw.print("K total = ");
+ pw.print(avail * 100 / total);
+ pw.println("% free");
+ } catch (IllegalArgumentException e) {
+ pw.print(name);
+ pw.print("-Error: ");
+ pw.println(e.toString());
+ return;
+ }
+ }
+}
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index b38f1d2..0de11c6 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -61,10 +61,11 @@
*/
public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
private static final String TAG = "DropBoxManagerService";
- private static final int DEFAULT_RESERVE_PERCENT = 10;
- private static final int DEFAULT_QUOTA_PERCENT = 10;
- private static final int DEFAULT_QUOTA_KB = 5 * 1024;
private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
+ private static final int DEFAULT_MAX_FILES = 1000;
+ private static final int DEFAULT_QUOTA_KB = 5 * 1024;
+ private static final int DEFAULT_QUOTA_PERCENT = 10;
+ private static final int DEFAULT_RESERVE_PERCENT = 10;
private static final int QUOTA_RESCAN_MILLIS = 5000;
private static final boolean PROFILE_DUMP = false;
@@ -99,12 +100,20 @@
@Override
public void onReceive(Context context, Intent intent) {
mCachedQuotaUptimeMillis = 0; // Force a re-check of quota size
- try {
- init();
- trimToFit();
- } catch (IOException e) {
- Slog.e(TAG, "Can't init", e);
- }
+
+ // Run the initialization in the background (not this main thread).
+ // The init() and trimToFit() methods are synchronized, so they still
+ // block other users -- but at least the onReceive() call can finish.
+ new Thread() {
+ public void run() {
+ try {
+ init();
+ trimToFit();
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't init", e);
+ }
+ }
+ }.start();
}
};
@@ -631,10 +640,12 @@
int ageSeconds = Settings.Secure.getInt(mContentResolver,
Settings.Secure.DROPBOX_AGE_SECONDS, DEFAULT_AGE_SECONDS);
+ int maxFiles = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.DROPBOX_MAX_FILES, DEFAULT_MAX_FILES);
long cutoffMillis = System.currentTimeMillis() - ageSeconds * 1000;
while (!mAllFiles.contents.isEmpty()) {
EntryFile entry = mAllFiles.contents.first();
- if (entry.timestampMillis > cutoffMillis) break;
+ if (entry.timestampMillis > cutoffMillis && mAllFiles.contents.size() < maxFiles) break;
FileList tag = mFilesByTag.get(entry.tag);
if (tag != null && tag.contents.remove(entry)) tag.blocks -= entry.blocks;
@@ -673,7 +684,7 @@
// A single circular buffer (a la logcat) would be simpler, but this
// way we can handle fat/bursty data (like 1MB+ bugreports, 300KB+
// kernel crash dumps, and 100KB+ ANR reports) without swamping small,
- // well-behaved data // streams (event statistics, profile data, etc).
+ // well-behaved data streams (event statistics, profile data, etc).
//
// Deleted files are replaced with zero-length tombstones to mark what
// was lost. Tombstones are expunged by age (see above).
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 0a53e9c..81ae26f 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -48,14 +48,15 @@
* instead of periodically.
*/
public class EntropyService extends Binder {
- private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat";
private static final String TAG = "EntropyService";
private static final int ENTROPY_WHAT = 1;
private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
- private static final String RANDOM_DEV = "/dev/urandom";
private static final long START_TIME = System.currentTimeMillis();
private static final long START_NANOTIME = System.nanoTime();
+ private final String randomDevice;
+ private final String entropyFile;
+
/**
* Handler that periodically updates the entropy on disk.
*/
@@ -72,6 +73,16 @@
};
public EntropyService() {
+ this(getSystemDir() + "/entropy.dat", "/dev/urandom");
+ }
+
+ /** Test only interface, not for public use */
+ public EntropyService(String entropyFile, String randomDevice) {
+ if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
+ if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
+
+ this.randomDevice = randomDevice;
+ this.entropyFile = entropyFile;
loadInitialEntropy();
addDeviceSpecificEntropy();
writeEntropy();
@@ -85,7 +96,7 @@
private void loadInitialEntropy() {
try {
- RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV);
+ RandomBlock.fromFile(entropyFile).toFile(randomDevice);
} catch (IOException e) {
Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
}
@@ -93,7 +104,7 @@
private void writeEntropy() {
try {
- RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME);
+ RandomBlock.fromFile(randomDevice).toFile(entropyFile);
} catch (IOException e) {
Slog.w(TAG, "unable to write entropy", e);
}
@@ -116,7 +127,7 @@
private void addDeviceSpecificEntropy() {
PrintWriter out = null;
try {
- out = new PrintWriter(new FileOutputStream(RANDOM_DEV));
+ out = new PrintWriter(new FileOutputStream(randomDevice));
out.println("Copyright (C) 2009 The Android Open Source Project");
out.println("All Your Randomness Are Belong To Us");
out.println(START_TIME);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index e336a35..0c205ca 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -46,6 +46,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
@@ -60,6 +61,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -1394,9 +1396,18 @@
private boolean chooseNewDefaultIMELocked() {
List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
if (enabled != null && enabled.size() > 0) {
+ // We'd prefer to fall back on a system IME, since that is safer.
+ int i=enabled.size();
+ while (i > 0) {
+ i--;
+ if ((enabled.get(i).getServiceInfo().applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ break;
+ }
+ }
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD,
- enabled.get(0).getId());
+ enabled.get(i).getId());
return true;
}
@@ -1409,6 +1420,11 @@
map.clear();
PackageManager pm = mContext.getPackageManager();
+ final Configuration config = mContext.getResources().getConfiguration();
+ final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY;
+ String disabledSysImes = Settings.Secure.getString(mContext.getContentResolver(),
+ Secure.DISABLED_SYSTEM_INPUT_METHODS);
+ if (disabledSysImes == null) disabledSysImes = "";
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(InputMethod.SERVICE_INTERFACE),
@@ -1431,11 +1447,13 @@
try {
InputMethodInfo p = new InputMethodInfo(mContext, ri);
list.add(p);
- map.put(p.getId(), p);
+ final String id = p.getId();
+ map.put(id, p);
- // System IMEs are enabled by default
- if (isSystemIme(p)) {
- setInputMethodEnabled(p.getId(), true);
+ // System IMEs are enabled by default, unless there's a hard keyboard
+ // and the system IME was explicitly disabled
+ if (isSystemIme(p) && (!haveHardKeyboard || disabledSysImes.indexOf(id) < 0)) {
+ setInputMethodEnabledLocked(id, true);
}
if (DEBUG) {
@@ -1463,8 +1481,6 @@
void showInputMethodMenu() {
if (DEBUG) Slog.v(TAG, "Show switching menu");
- hideInputMethodMenu();
-
final Context context = mContext;
final PackageManager pm = context.getPackageManager();
@@ -1479,67 +1495,69 @@
return;
}
- int N = immis.size();
+ synchronized (mMethodMap) {
+ hideInputMethodMenuLocked();
- mItems = new CharSequence[N];
- mIms = new InputMethodInfo[N];
-
- for (int i = 0; i < N; ++i) {
- InputMethodInfo property = immis.get(i);
- if (property == null) {
- i--;
- N--;
- continue;
+ int N = immis.size();
+
+ mItems = new CharSequence[N];
+ mIms = new InputMethodInfo[N];
+
+ int j = 0;
+ for (int i = 0; i < N; ++i) {
+ InputMethodInfo property = immis.get(i);
+ if (property == null) {
+ continue;
+ }
+ mItems[j] = property.loadLabel(pm);
+ mIms[j] = property;
+ j++;
}
- mItems[i] = property.loadLabel(pm);
- mIms[i] = property;
- }
-
- int checkedItem = 0;
- for (int i = 0; i < N; ++i) {
- if (mIms[i].getId().equals(lastInputMethodId)) {
- checkedItem = i;
- break;
+
+ int checkedItem = 0;
+ for (int i = 0; i < N; ++i) {
+ if (mIms[i].getId().equals(lastInputMethodId)) {
+ checkedItem = i;
+ break;
+ }
}
- }
-
- AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- hideInputMethodMenu();
- }
- };
-
- TypedArray a = context.obtainStyledAttributes(null,
- com.android.internal.R.styleable.DialogPreference,
- com.android.internal.R.attr.alertDialogStyle, 0);
- mDialogBuilder = new AlertDialog.Builder(context)
- .setTitle(com.android.internal.R.string.select_input_method)
- .setOnCancelListener(new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- hideInputMethodMenu();
- }
- })
- .setIcon(a.getDrawable(
- com.android.internal.R.styleable.DialogPreference_dialogTitle));
- a.recycle();
-
- mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
- new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- synchronized (mMethodMap) {
- if (mIms == null || mIms.length <= which) {
- return;
- }
- InputMethodInfo im = mIms[which];
+
+ AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ hideInputMethodMenu();
+ }
+ };
+
+ TypedArray a = context.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.DialogPreference,
+ com.android.internal.R.attr.alertDialogStyle, 0);
+ mDialogBuilder = new AlertDialog.Builder(context)
+ .setTitle(com.android.internal.R.string.select_input_method)
+ .setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
hideInputMethodMenu();
- if (im != null) {
- setInputMethodLocked(im.getId());
+ }
+ })
+ .setIcon(a.getDrawable(
+ com.android.internal.R.styleable.DialogPreference_dialogTitle));
+ a.recycle();
+
+ mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ synchronized (mMethodMap) {
+ if (mIms == null || mIms.length <= which) {
+ return;
+ }
+ InputMethodInfo im = mIms[which];
+ hideInputMethodMenu();
+ if (im != null) {
+ setInputMethodLocked(im.getId());
+ }
}
}
- }
- });
+ });
- synchronized (mMethodMap) {
mSwitchingDialog = mDialogBuilder.create();
mSwitchingDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 19b0a76..8ab65e9 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -91,38 +91,64 @@
}
}
- void dumpMap(PrintWriter out, String prefix, Map<String, ArrayList<F>> map) {
+ boolean dumpMap(PrintWriter out, String titlePrefix, String title,
+ String prefix, Map<String, ArrayList<F>> map, String packageName) {
String eprefix = prefix + " ";
String fprefix = prefix + " ";
+ boolean printedSomething = false;
for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
- out.print(eprefix); out.print(e.getKey()); out.println(":");
ArrayList<F> a = e.getValue();
final int N = a.size();
+ boolean printedHeader = false;
for (int i=0; i<N; i++) {
- dumpFilter(out, fprefix, a.get(i));
+ F filter = a.get(i);
+ if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ continue;
+ }
+ if (title != null) {
+ out.print(titlePrefix); out.println(title);
+ title = null;
+ }
+ if (!printedHeader) {
+ out.print(eprefix); out.print(e.getKey()); out.println(":");
+ printedHeader = true;
+ }
+ printedSomething = true;
+ dumpFilter(out, fprefix, filter);
}
}
+ return printedSomething;
}
- public void dump(PrintWriter out, String prefix) {
+ public boolean dump(PrintWriter out, String title, String prefix, String packageName) {
String innerPrefix = prefix + " ";
- out.print(prefix); out.println("Full MIME Types:");
- dumpMap(out, innerPrefix, mTypeToFilter);
- out.println(" ");
- out.print(prefix); out.println("Base MIME Types:");
- dumpMap(out, innerPrefix, mBaseTypeToFilter);
- out.println(" ");
- out.print(prefix); out.println("Wild MIME Types:");
- dumpMap(out, innerPrefix, mWildTypeToFilter);
- out.println(" ");
- out.print(prefix); out.println("Schemes:");
- dumpMap(out, innerPrefix, mSchemeToFilter);
- out.println(" ");
- out.print(prefix); out.println("Non-Data Actions:");
- dumpMap(out, innerPrefix, mActionToFilter);
- out.println(" ");
- out.print(prefix); out.println("MIME Typed Actions:");
- dumpMap(out, innerPrefix, mTypedActionToFilter);
+ String sepPrefix = "\n" + prefix;
+ String curPrefix = title + "\n" + prefix;
+ if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
+ mTypeToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
+ mBaseTypeToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
+ mWildTypeToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
+ mSchemeToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
+ mActionToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
+ mTypedActionToFilter, packageName)) {
+ curPrefix = sepPrefix;
+ }
+ return curPrefix == sepPrefix;
}
private class IteratorWrapper implements Iterator<F> {
@@ -286,6 +312,10 @@
return true;
}
+ protected String packageForFilter(F filter) {
+ return null;
+ }
+
protected R newResult(F filter, int match) {
return (R)filter;
}
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 8cd9578..f30346b 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -839,8 +839,6 @@
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
}
-
- ms.finish();
}
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index ef57056..b92480f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,17 +16,6 @@
package com.android.server;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.Set;
-
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -50,7 +39,6 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -68,12 +56,25 @@
import android.util.Slog;
import android.util.PrintWriterPrinter;
-import com.android.internal.location.GeocoderProxy;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.LocationProviderProxy;
-import com.android.internal.location.MockProvider;
-import com.android.internal.location.PassiveProvider;
+
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
@@ -461,10 +462,11 @@
mEnabledProviders.add(passiveProvider.getName());
// initialize external network location and geocoder services
+ PackageManager pm = mContext. getPackageManager();
Resources resources = mContext.getResources();
String serviceName = resources.getString(
com.android.internal.R.string.config_networkLocationProvider);
- if (serviceName != null) {
+ if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
mNetworkLocationProvider =
new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
serviceName, mLocationHandler);
@@ -472,7 +474,7 @@
}
serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider);
- if (serviceName != null) {
+ if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
mGeocodeProvider = new GeocoderProxy(mContext, serviceName);
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d4d5336..90cb585 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -29,6 +29,8 @@
import android.os.storage.IMountShutdownObserver;
import android.os.storage.StorageResultCode;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.IBinder;
@@ -36,7 +38,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.HashSet;
@@ -49,6 +51,8 @@
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks {
private static final boolean LOCAL_LOGD = false;
+ private static final boolean DEBUG_UNMOUNT = false;
+ private static final boolean DEBUG_EVENTS = false;
private static final String TAG = "MountService";
@@ -96,6 +100,7 @@
public static final int OpFailedMediaCorrupt = 403;
public static final int OpFailedVolNotMounted = 404;
public static final int OpFailedStorageBusy = 405;
+ public static final int OpFailedStorageNotFound = 406;
/*
* 600 series - Unsolicited broadcasts.
@@ -131,17 +136,6 @@
private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
private static final int MAX_UNMOUNT_RETRIES = 4;
- private IntentFilter mPmFilter = new IntentFilter(
- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- private BroadcastReceiver mPmReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
- }
- }
- };
-
class UnmountCallBack {
String path;
int retries;
@@ -154,6 +148,7 @@
}
void handleFinished() {
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
doUnmountVolume(path, true);
}
}
@@ -187,47 +182,43 @@
try {
observer.onShutDownComplete(ret);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException when shutting down");
+ Slog.w(TAG, "RemoteException when shutting down");
}
}
}
}
- final private Handler mHandler = new Handler() {
+ class MountServiceHandler extends Handler {
ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
- boolean mRegistered = false;
+ boolean mUpdatingStatus = false;
- void registerReceiver() {
- mRegistered = true;
- mContext.registerReceiver(mPmReceiver, mPmFilter);
- }
-
- void unregisterReceiver() {
- mRegistered = false;
- mContext.unregisterReceiver(mPmReceiver);
+ MountServiceHandler(Looper l) {
+ super(l);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
mForceUnmounts.add(ucb);
+ if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
// Register only if needed.
- if (!mRegistered) {
- registerReceiver();
- boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
- if (!hasExtPkgs) {
- // Unregister right away
- mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
- }
+ if (!mUpdatingStatus) {
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
+ mUpdatingStatus = true;
+ mPms.updateExternalMediaStatus(false, true);
}
break;
}
case H_UNMOUNT_PM_DONE: {
- // Unregister now.
- if (mRegistered) {
- unregisterReceiver();
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
+ if (!mUpdatingStatus) {
+ // Does not correspond to unmount's status update.
+ return;
}
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
+ mUpdatingStatus = false;
int size = mForceUnmounts.size();
int sizeArr[] = new int[size];
int sizeArrN = 0;
@@ -246,7 +237,7 @@
ActivityManagerService ams = (ActivityManagerService)
ServiceManager.getService("activity");
// Eliminate system process here?
- boolean ret = ams.killPidsForMemory(pids);
+ boolean ret = ams.killPids(pids, "Unmount media");
if (ret) {
// Confirm if file references have been freed.
pids = getStorageUsers(path);
@@ -262,8 +253,8 @@
ucb));
} else {
if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
- Log.i(TAG, "Cannot unmount inspite of " +
- MAX_UNMOUNT_RETRIES + " to unmount media");
+ Slog.i(TAG, "Cannot unmount media inspite of " +
+ MAX_UNMOUNT_RETRIES + " retries");
// Send final broadcast indicating failure to unmount.
} else {
mHandler.sendMessageDelayed(
@@ -280,6 +271,7 @@
break;
}
case H_UNMOUNT_MS : {
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
UnmountCallBack ucb = (UnmountCallBack) msg.obj;
ucb.handleFinished();
break;
@@ -287,6 +279,8 @@
}
}
};
+ final private HandlerThread mHandlerThread;
+ final private Handler mHandler;
private void waitForReady() {
while (mReady == false) {
@@ -296,7 +290,7 @@
}
SystemClock.sleep(1000);
}
- Log.w(TAG, "Waiting too long for mReady!");
+ Slog.w(TAG, "Waiting too long for mReady!");
}
}
@@ -319,16 +313,23 @@
public void run() {
try {
String path = Environment.getExternalStorageDirectory().getPath();
- if (getVolumeState(
- Environment.getExternalStorageDirectory().getPath()).equals(
- Environment.MEDIA_UNMOUNTED)) {
+ String state = getVolumeState(path);
+
+ if (state.equals(Environment.MEDIA_UNMOUNTED)) {
int rc = doMountVolume(path);
if (rc != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
+ Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
}
+ } else if (state.equals(Environment.MEDIA_SHARED)) {
+ /*
+ * Bootstrap UMS enabled state since vold indicates
+ * the volume is shared (runtime restart while ums enabled)
+ */
+ notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
}
+
/*
- * If UMS is connected in boot, send the connected event
+ * If UMS was connected on boot, send the connected event
* now that we're up.
*/
if (mSendUmsConnectedOnBoot) {
@@ -336,7 +337,7 @@
mSendUmsConnectedOnBoot = false;
}
} catch (Exception ex) {
- Log.e(TAG, "Boot-time mount exception", ex);
+ Slog.e(TAG, "Boot-time mount exception", ex);
}
}
}.start();
@@ -353,7 +354,7 @@
}
public void binderDied() {
- if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!");
+ if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
synchronized(mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
@@ -371,21 +372,26 @@
mConnector.doCommand(String.format(
"volume %sshare %s %s", (enable ? "" : "un"), path, method));
} catch (NativeDaemonConnectorException e) {
- Log.e(TAG, "Failed to share/unshare", e);
+ Slog.e(TAG, "Failed to share/unshare", e);
}
}
private void updatePublicVolumeState(String path, String state) {
if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
- Log.w(TAG, "Multiple volumes not currently supported");
+ Slog.w(TAG, "Multiple volumes not currently supported");
return;
}
if (mLegacyState.equals(state)) {
- Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
+ Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
return;
}
-
+ // Update state on PackageManager
+ if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(false, false);
+ } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(true, false);
+ }
String oldState = mLegacyState;
mLegacyState = state;
@@ -395,10 +401,10 @@
try {
bl.mListener.onStorageStateChanged(path, oldState, state);
} catch (RemoteException rex) {
- Log.e(TAG, "Listener dead");
+ Slog.e(TAG, "Listener dead");
mListeners.remove(i);
} catch (Exception ex) {
- Log.e(TAG, "Listener failed", ex);
+ Slog.e(TAG, "Listener failed", ex);
}
}
}
@@ -428,7 +434,7 @@
String[] tok = volstr.split(" ");
// FMT: <label> <mountpoint> <state>
if (!tok[1].equals(path)) {
- Log.w(TAG, String.format(
+ Slog.w(TAG, String.format(
"Skipping unknown volume '%s'",tok[1]));
continue;
}
@@ -439,19 +445,20 @@
state = Environment.MEDIA_UNMOUNTED;
} else if (st == VolumeState.Mounted) {
state = Environment.MEDIA_MOUNTED;
- Log.i(TAG, "Media already mounted on daemon connection");
+ Slog.i(TAG, "Media already mounted on daemon connection");
} else if (st == VolumeState.Shared) {
state = Environment.MEDIA_SHARED;
- Log.i(TAG, "Media shared on daemon connection");
+ Slog.i(TAG, "Media shared on daemon connection");
} else {
throw new Exception(String.format("Unexpected state %d", st));
}
}
if (state != null) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
updatePublicVolumeState(path, state);
}
} catch (Exception e) {
- Log.e(TAG, "Error processing initial volume state", e);
+ Slog.e(TAG, "Error processing initial volume state", e);
updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
}
@@ -459,7 +466,7 @@
boolean avail = doGetShareMethodAvailable("ums");
notifyShareAvailabilityChange("ums", avail);
} catch (Exception ex) {
- Log.w(TAG, "Failed to get share availability");
+ Slog.w(TAG, "Failed to get share availability");
}
/*
* Now that we've done our initialization, release
@@ -476,6 +483,18 @@
public boolean onEvent(int code, String raw, String[] cooked) {
Intent in = null;
+ if (DEBUG_EVENTS) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("onEvent::");
+ builder.append(" raw= " + raw);
+ if (cooked != null) {
+ builder.append(" cooked = " );
+ for (String str : cooked) {
+ builder.append(" " + str);
+ }
+ }
+ Slog.i(TAG, builder.toString());
+ }
if (code == VoldResponseCode.VolumeStateChange) {
/*
* One of the volumes we're managing has changed state.
@@ -509,7 +528,7 @@
major = Integer.parseInt(devTok[0]);
minor = Integer.parseInt(devTok[1]);
} catch (Exception ex) {
- Log.e(TAG, "Failed to parse major/minor", ex);
+ Slog.e(TAG, "Failed to parse major/minor", ex);
}
if (code == VoldResponseCode.VolumeDiskInserted) {
@@ -518,10 +537,10 @@
try {
int rc;
if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
- Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
+ Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
}
} catch (Exception ex) {
- Log.w(TAG, "Failed to mount media on insertion", ex);
+ Slog.w(TAG, "Failed to mount media on insertion", ex);
}
}
}.start();
@@ -533,22 +552,26 @@
return true;
}
/* Send the media unmounted event first */
+ if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
mContext.sendBroadcast(in);
+ if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
} else if (code == VoldResponseCode.VolumeBadRemoval) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
/* Send the media unmounted event first */
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
mContext.sendBroadcast(in);
+ if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
} else {
- Log.e(TAG, String.format("Unknown code {%d}", code));
+ Slog.e(TAG, String.format("Unknown code {%d}", code));
}
} else {
return false;
@@ -556,17 +579,18 @@
if (in != null) {
mContext.sendBroadcast(in);
- }
- return true;
+ }
+ return true;
}
private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
String vs = getVolumeState(path);
+ if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
Intent in = null;
if (oldState == VolumeState.Shared && newState != oldState) {
- if (LOCAL_LOGD) Log.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
+ if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
Uri.parse("file://" + path)));
}
@@ -583,37 +607,39 @@
Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
Environment.MEDIA_NOFS) && !vs.equals(
Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
}
} else if (newState == VolumeState.Pending) {
} else if (newState == VolumeState.Checking) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
} else if (newState == VolumeState.Mounted) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
- // Update media status on PackageManagerService to mount packages on sdcard
- mPms.updateExternalMediaStatus(true);
in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
in.putExtra("read-only", false);
} else if (newState == VolumeState.Unmounting) {
- mPms.updateExternalMediaStatus(false);
in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
} else if (newState == VolumeState.Formatting) {
} else if (newState == VolumeState.Shared) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
/* Send the media unmounted event first */
updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
mContext.sendBroadcast(in);
+ if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
updatePublicVolumeState(path, Environment.MEDIA_SHARED);
in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
- if (LOCAL_LOGD) Log.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
+ if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
} else if (newState == VolumeState.SharedMnt) {
- Log.e(TAG, "Live shared mounts not supported yet!");
+ Slog.e(TAG, "Live shared mounts not supported yet!");
return;
} else {
- Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
+ Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
}
if (in != null) {
@@ -630,7 +656,7 @@
try {
code = Integer.parseInt(tok[0]);
} catch (NumberFormatException nfe) {
- Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+ Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
return false;
}
if (code == VoldResponseCode.ShareStatusResult) {
@@ -638,17 +664,18 @@
return true;
return false;
} else {
- Log.e(TAG, String.format("Unexpected response code %d", code));
+ Slog.e(TAG, String.format("Unexpected response code %d", code));
return false;
}
}
- Log.e(TAG, "Got an empty response");
+ Slog.e(TAG, "Got an empty response");
return false;
}
private int doMountVolume(String path) {
int rc = StorageResultCode.OperationSucceeded;
+ if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
try {
mConnector.doCommand(String.format("volume mount %s", path));
} catch (NativeDaemonConnectorException e) {
@@ -663,6 +690,7 @@
*/
rc = StorageResultCode.OperationFailedNoMedia;
} else if (code == VoldResponseCode.OpFailedMediaBlank) {
+ if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
/*
* Media is blank or does not contain a supported filesystem
*/
@@ -670,6 +698,7 @@
in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
rc = StorageResultCode.OperationFailedMediaBlank;
} else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
+ if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
/*
* Volume consistency check failed
*/
@@ -704,19 +733,15 @@
if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
return VoldResponseCode.OpFailedVolNotMounted;
}
-
- // We unmounted the volume. No of the asec containers are available now.
- synchronized (mAsecMountSet) {
- mAsecMountSet.clear();
- }
- // Notify PackageManager of potential media removal and deal with
- // return code later on. The caller of this api should be aware or have been
- // notified that the applications installed on the media will be killed.
// Redundant probably. But no harm in updating state again.
- mPms.updateExternalMediaStatus(false);
+ mPms.updateExternalMediaStatus(false, false);
try {
mConnector.doCommand(String.format(
"volume unmount %s%s", path, (force ? " force" : "")));
+ // We unmounted the volume. None of the asec containers are available now.
+ synchronized (mAsecMountSet) {
+ mAsecMountSet.clear();
+ }
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
// Don't worry about mismatch in PackageManager since the
@@ -759,7 +784,7 @@
try {
code = Integer.parseInt(tok[0]);
} catch (NumberFormatException nfe) {
- Log.e(TAG, String.format("Error parsing code %s", tok[0]));
+ Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
return false;
}
if (code == VoldResponseCode.ShareEnabledResult) {
@@ -767,17 +792,17 @@
return true;
return false;
} else {
- Log.e(TAG, String.format("Unexpected response code %d", code));
+ Slog.e(TAG, String.format("Unexpected response code %d", code));
return false;
}
}
- Log.e(TAG, "Got an empty response");
+ Slog.e(TAG, "Got an empty response");
return false;
}
private void notifyShareAvailabilityChange(String method, final boolean avail) {
if (!method.equals("ums")) {
- Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
+ Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
return;
}
@@ -787,10 +812,10 @@
try {
bl.mListener.onUsbMassStorageConnectionChanged(avail);
} catch (RemoteException rex) {
- Log.e(TAG, "Listener dead");
+ Slog.e(TAG, "Listener dead");
mListeners.remove(i);
} catch (Exception ex) {
- Log.e(TAG, "Listener failed", ex);
+ Slog.e(TAG, "Listener failed", ex);
}
}
}
@@ -810,15 +835,15 @@
public void run() {
try {
int rc;
- Log.w(TAG, "Disabling UMS after cable disconnect");
+ Slog.w(TAG, "Disabling UMS after cable disconnect");
doShareUnshareVolume(path, "ums", false);
if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, String.format(
+ Slog.e(TAG, String.format(
"Failed to remount {%s} on UMS enabled-disconnect (%d)",
path, rc));
}
} catch (Exception ex) {
- Log.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
+ Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
}
}
}.start();
@@ -850,6 +875,10 @@
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+ mHandlerThread = new HandlerThread("MountService");
+ mHandlerThread.start();
+ mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -877,7 +906,7 @@
listener.asBinder().linkToDeath(bl, 0);
mListeners.add(bl);
} catch (RemoteException rex) {
- Log.e(TAG, "Failed to link to listener death");
+ Slog.e(TAG, "Failed to link to listener death");
}
}
}
@@ -896,7 +925,7 @@
public void shutdown(final IMountShutdownObserver observer) {
validatePermission(android.Manifest.permission.SHUTDOWN);
- Log.i(TAG, "Shutting down");
+ Slog.i(TAG, "Shutting down");
String path = Environment.getExternalStorageDirectory().getPath();
String state = getVolumeState(path);
@@ -921,13 +950,13 @@
try {
Thread.sleep(1000);
} catch (InterruptedException iex) {
- Log.e(TAG, "Interrupted while waiting for media", iex);
+ Slog.e(TAG, "Interrupted while waiting for media", iex);
break;
}
state = Environment.getExternalStorageState();
}
if (retries == 0) {
- Log.e(TAG, "Timed out waiting for media to check");
+ Slog.e(TAG, "Timed out waiting for media to check");
}
}
@@ -985,7 +1014,7 @@
if (!enable) {
doShareUnshareVolume(path, method, enable);
if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
- Log.e(TAG, "Failed to remount " + path +
+ Slog.e(TAG, "Failed to remount " + path +
" after disabling share method " + method);
/*
* Even though the mount failed, the unshare didn't so don't indicate an error.
@@ -1010,7 +1039,7 @@
* this to /sdcard
*/
if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
- Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
+ Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
throw new IllegalArgumentException();
}
@@ -1028,6 +1057,16 @@
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
+ String volState = getVolumeState(path);
+ if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
+ if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
+ Environment.MEDIA_REMOVED.equals(volState) ||
+ Environment.MEDIA_SHARED.equals(volState) ||
+ Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
+ // Media already unmounted or cannot be unmounted.
+ // TODO return valid return code when adding observer call back.
+ return;
+ }
UnmountCallBack ucb = new UnmountCallBack(path, force);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
}
@@ -1053,20 +1092,20 @@
try {
data[i] = Integer.parseInt(tok[0]);
} catch (NumberFormatException nfe) {
- Log.e(TAG, String.format("Error parsing pid %s", tok[0]));
+ Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
return new int[0];
}
}
return data;
} catch (NativeDaemonConnectorException e) {
- Log.e(TAG, "Failed to retrieve storage users list", e);
+ Slog.e(TAG, "Failed to retrieve storage users list", e);
return new int[0];
}
}
private void warnOnNotMounted() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- Log.w(TAG, "getSecureContainerList() called when storage not mounted");
+ Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
}
}
@@ -1165,7 +1204,10 @@
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
- rc = StorageResultCode.OperationFailedInternalError;
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
}
if (rc == StorageResultCode.OperationSucceeded) {
@@ -1249,21 +1291,26 @@
waitForReady();
warnOnNotMounted();
- ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
-
- for (String line : rsp) {
- String []tok = line.split(" ");
+ try {
+ ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
+ String []tok = rsp.get(0).split(" ");
int code = Integer.parseInt(tok[0]);
- if (code == VoldResponseCode.AsecPathResult) {
- return tok[1];
+ if (code != VoldResponseCode.AsecPathResult) {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ return tok[1];
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ throw new IllegalArgumentException(String.format("Container '%s' not found", id));
} else {
- Log.e(TAG, String.format("Unexpected response code %d", code));
- return "";
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
}
+ }
- Log.e(TAG, "Got an empty response");
- return "";
+ public void finishMediaUpdate() {
+ mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 39c847a..08d7ce6 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -48,6 +48,8 @@
private String mSocket;
private INativeDaemonConnectorCallbacks mCallbacks;
+ private final int BUFFER_SIZE = 4096;
+
class ResponseCode {
public static final int ActionInitiated = 100;
@@ -87,7 +89,7 @@
}
private void listenToSocket() throws IOException {
- LocalSocket socket = null;
+ LocalSocket socket = null;
try {
socket = new LocalSocket();
@@ -100,13 +102,13 @@
InputStream inputStream = socket.getInputStream();
mOutputStream = socket.getOutputStream();
- byte[] buffer = new byte[4096];
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int start = 0;
while (true) {
- int count = inputStream.read(buffer);
+ int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) break;
- int start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
String event = new String(buffer, start, i - start);
@@ -139,6 +141,13 @@
start = i + 1;
}
}
+ if (start != count) {
+ final int remaining = BUFFER_SIZE - start;
+ System.arraycopy(buffer, start, buffer, 0, remaining);
+ start = remaining;
+ } else {
+ start = 0;
+ }
}
} catch (IOException ex) {
Slog.e(TAG, "Communications error", ex);
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
index ac3445e..7fe6743 100644
--- a/services/java/com/android/server/NetStatService.java
+++ b/services/java/com/android/server/NetStatService.java
@@ -19,11 +19,16 @@
import android.content.Context;
import android.net.TrafficStats;
import android.os.INetStatService;
+import android.os.SystemClock;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
public class NetStatService extends INetStatService.Stub {
+ private final Context mContext;
public NetStatService(Context context) {
-
+ mContext = context;
}
public long getMobileTxPackets() {
@@ -57,4 +62,35 @@
public long getTotalRxBytes() {
return TrafficStats.getTotalRxBytes();
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // This data is accessible to any app -- no permission check needed.
+
+ pw.print("Elapsed: total=");
+ pw.print(SystemClock.elapsedRealtime());
+ pw.print("ms awake=");
+ pw.print(SystemClock.uptimeMillis());
+ pw.println("ms");
+
+ pw.print("Mobile: Tx=");
+ pw.print(getMobileTxBytes());
+ pw.print("B/");
+ pw.print(getMobileTxPackets());
+ pw.print("Pkts Rx=");
+ pw.print(getMobileRxBytes());
+ pw.print("B/");
+ pw.print(getMobileRxPackets());
+ pw.println("Pkts");
+
+ pw.print("Total: Tx=");
+ pw.print(getTotalTxBytes());
+ pw.print("B/");
+ pw.print(getTotalTxPackets());
+ pw.print("Pkts Rx=");
+ pw.print(getTotalRxBytes());
+ pw.print("B/");
+ pw.print(getTotalRxPackets());
+ pw.println("Pkts");
+ }
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index aa9ced8..072fc1b 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -26,10 +26,13 @@
import android.net.Uri;
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.INetworkManagementService;
import android.os.Handler;
import android.os.SystemProperties;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
import java.util.StringTokenizer;
@@ -309,11 +312,17 @@
mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
}
- public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
+ public void startTethering(String[] dhcpRange)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
+ // cmd is "tether start first_start first_stop second_start second_stop ..."
+ // an odd number of addrs will fail
+ String cmd = "tether start";
+ for (String d : dhcpRange) {
+ cmd += " " + d;
+ }
+ mConnector.doCommand(cmd);
}
public void stopTethering() throws IllegalStateException {
@@ -457,22 +466,62 @@
throw new IllegalStateException("Got an empty response");
}
- public void startAccessPoint()
+ public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("softap set"));
- mConnector.doCommand(String.format("softap start"));
+ android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ mConnector.doCommand(String.format("softap stop " + wlanIface));
+ mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
+ mConnector.doCommand(String.format("softap start " + wlanIface));
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ /**
+ * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
+ * argv1 - wlan interface
+ * argv2 - softap interface
+ * argv3 - SSID
+ * argv4 - Security
+ * argv5 - Key
+ * argv6 - Channel
+ * argv7 - Preamble
+ * argv8 - Max SCB
+ */
+ String str = String.format("softap set " + wlanIface + " " + softapIface +
+ " \"%s\" %s %s", wifiConfig.SSID,
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+ "wpa2-psk" : "open",
+ wifiConfig.preSharedKey);
+ mConnector.doCommand(str);
+ }
+ mConnector.doCommand(String.format("softap startap"));
}
public void stopAccessPoint() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand("softap stop");
+ android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ mConnector.doCommand("softap stopap");
}
+ public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ String str = String.format("softap set " + wlanIface + " " + softapIface +
+ " \"%s\" %s %s", wifiConfig.SSID,
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+ "wpa2-psk" : "open",
+ wifiConfig.preSharedKey);
+ mConnector.doCommand(str);
+ }
+ }
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index da9232b..3e1adf1 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1136,11 +1136,13 @@
if (mAdbNotification == null) {
mAdbNotification = new Notification();
- mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
+ mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;
mAdbNotification.when = 0;
mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
mAdbNotification.tickerText = title;
- mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
+ mAdbNotification.defaults = 0; // please be quiet
+ mAdbNotification.sound = null;
+ mAdbNotification.vibrate = null;
}
Intent intent = new Intent(
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 139c05f..33a824b 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -20,8 +20,8 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
-import com.android.server.JournaledFile;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -29,7 +29,6 @@
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -79,14 +78,11 @@
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.StatFs;
-import android.os.storage.StorageResultCode;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.security.SystemKeyStore;
import android.util.*;
import android.view.Display;
@@ -122,6 +118,17 @@
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
+/**
+ * Keep track of all those .apks everywhere.
+ *
+ * This is very central to the platform's security; please run the unit
+ * tests whenever making modifications here:
+ *
+mmm frameworks/base/tests/AndroidTests
+adb install -r -f out/target/product/passion/data/app/AndroidTests.apk
+adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner
+ *
+ */
class PackageManagerService extends IPackageManager.Stub {
private static final String TAG = "PackageManager";
private static final boolean DEBUG_SETTINGS = false;
@@ -336,6 +343,7 @@
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
+ static final int UPDATED_MEDIA_STATUS = 12;
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
@@ -589,16 +597,26 @@
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
} break;
+ case UPDATED_MEDIA_STATUS: {
+ try {
+ PackageHelper.getMountService().finishMediaUpdate();
+ } catch (RemoteException e) {
+ Log.e(TAG, "MountService not running?");
+ }
+ } break;
}
}
}
static boolean installOnSd(int flags) {
if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) ||
- ((flags & PackageManager.INSTALL_EXTERNAL) == 0)) {
+ ((flags & PackageManager.INSTALL_INTERNAL) != 0)) {
return false;
}
- return true;
+ if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ return true;
+ }
+ return false;
}
static boolean isFwdLocked(int flags) {
@@ -841,7 +859,8 @@
mFrameworkInstallObserver = new AppDirObserver(
mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
- scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
+ scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode | SCAN_NO_DEX);
// Collect all system packages.
@@ -849,7 +868,8 @@
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
- scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
+ scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode);
if (mInstaller != null) {
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
@@ -909,7 +929,7 @@
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- updatePermissionsLP();
+ updatePermissionsLP(null, null, true, false);
mSettings.writeLP();
@@ -1204,6 +1224,36 @@
return cur;
}
+ static int[] removeInt(int[] cur, int val) {
+ if (cur == null) {
+ return null;
+ }
+ final int N = cur.length;
+ for (int i=0; i<N; i++) {
+ if (cur[i] == val) {
+ int[] ret = new int[N-1];
+ if (i > 0) {
+ System.arraycopy(cur, 0, ret, 0, i);
+ }
+ if (i < (N-1)) {
+ System.arraycopy(cur, i, ret, i+1, N-i-1);
+ }
+ return ret;
+ }
+ }
+ return cur;
+ }
+
+ static int[] removeInts(int[] cur, int[] rem) {
+ if (rem == null) return cur;
+ if (cur == null) return cur;
+ final int N = rem.length;
+ for (int i=0; i<N; i++) {
+ cur = removeInt(cur, rem[i]);
+ }
+ return cur;
+ }
+
PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
// The package has been uninstalled but has retained data and resources.
@@ -1286,11 +1336,24 @@
return new int[0];
}
+ static final PermissionInfo generatePermissionInfo(
+ BasePermission bp, int flags) {
+ if (bp.perm != null) {
+ return PackageParser.generatePermissionInfo(bp.perm, flags);
+ }
+ PermissionInfo pi = new PermissionInfo();
+ pi.name = bp.name;
+ pi.packageName = bp.sourcePackage;
+ pi.nonLocalizedLabel = bp.name;
+ pi.protectionLevel = bp.protectionLevel;
+ return pi;
+ }
+
public PermissionInfo getPermissionInfo(String name, int flags) {
synchronized (mPackages) {
final BasePermission p = mSettings.mPermissions.get(name);
- if (p != null && p.perm != null) {
- return PackageParser.generatePermissionInfo(p.perm, flags);
+ if (p != null) {
+ return generatePermissionInfo(p, flags);
}
return null;
}
@@ -1301,11 +1364,11 @@
ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission p : mSettings.mPermissions.values()) {
if (group == null) {
- if (p.perm.info.group == null) {
- out.add(PackageParser.generatePermissionInfo(p.perm, flags));
+ if (p.perm == null || p.perm.info.group == null) {
+ out.add(generatePermissionInfo(p, flags));
}
} else {
- if (group.equals(p.perm.info.group)) {
+ if (p.perm != null && group.equals(p.perm.info.group)) {
out.add(PackageParser.generatePermissionInfo(p.perm, flags));
}
}
@@ -1597,6 +1660,7 @@
"Not allowed to modify non-dynamic permission "
+ info.name);
}
+ bp.protectionLevel = info.protectionLevel;
bp.perm = new PackageParser.Permission(tree.perm.owner,
new PermissionInfo(info));
bp.perm.info.packageName = tree.perm.info.packageName;
@@ -2657,8 +2721,6 @@
SharedUserSetting suid = null;
PackageSetting pkgSetting = null;
- boolean removeExisting = false;
-
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
// Only system apps can use these features.
pkg.mOriginalPackages = null;
@@ -2849,7 +2911,7 @@
if (!verifySignaturesLP(pkgSetting, pkg, parseFlags,
(scanMode&SCAN_UPDATE_SIGNATURE) != 0)) {
- if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) {
+ if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
return null;
}
@@ -2868,7 +2930,10 @@
return null;
}
}
- removeExisting = true;
+ // File a report about this.
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
}
// Verify that this new package doesn't have any content providers
@@ -2901,23 +2966,6 @@
final String pkgName = pkg.packageName;
- if (removeExisting) {
- boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
- if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
- if (ret != 0) {
- String msg = "System package " + pkg.packageName
- + " could not have data directory erased after signature change.";
- reportSettingsProblem(Log.WARN, msg);
- mLastScanError = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
- return null;
- }
- }
- Slog.w(TAG, "System package " + pkg.packageName
- + " signature changed: existing data removed.");
- mLastScanError = PackageManager.INSTALL_SUCCEEDED;
- }
-
if (pkg.mAdoptPermissions != null) {
// This package wants to adopt ownership of permissions from
// another package.
@@ -3265,6 +3313,7 @@
BasePermission tree = findPermissionTreeLP(p.info.name);
if (tree == null
|| tree.sourcePackage.equals(p.info.packageName)) {
+ bp.packageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
@@ -3295,6 +3344,9 @@
r.append("DUP:");
r.append(p.info.name);
}
+ if (bp.perm == p) {
+ bp.protectionLevel = p.info.protectionLevel;
+ }
} else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: no group "
@@ -3720,15 +3772,7 @@
bp = mSettings.mPermissionTrees.get(p.info.name);
}
if (bp != null && bp.perm == p) {
- if (bp.type != BasePermission.TYPE_BUILTIN) {
- if (tree) {
- mSettings.mPermissionTrees.remove(p.info.name);
- } else {
- mSettings.mPermissions.remove(p.info.name);
- }
- } else {
- bp.perm = null;
- }
+ bp.perm = null;
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -3767,16 +3811,38 @@
return name != null && name.endsWith(".apk");
}
- private void updatePermissionsLP() {
+ private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
+ for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+ if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updatePermissionsLP(String changingPkg,
+ PackageParser.Package pkgInfo, boolean grantPermissions, boolean replace) {
// Make sure there are no dangling permission trees.
Iterator<BasePermission> it = mSettings.mPermissionTrees
.values().iterator();
while (it.hasNext()) {
BasePermission bp = it.next();
- if (bp.perm == null) {
+ if (bp.packageSetting == null) {
+ // We may not yet have parsed the package, so just see if
+ // we still know about its settings.
+ bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+ }
+ if (bp.packageSetting == null) {
Slog.w(TAG, "Removing dangling permission tree: " + bp.name
+ " from package " + bp.sourcePackage);
it.remove();
+ } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
+ Slog.i(TAG, "Removing old permission tree: " + bp.name
+ + " from package " + bp.sourcePackage);
+ grantPermissions = true;
+ it.remove();
+ }
}
}
@@ -3789,9 +3855,10 @@
if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ bp.name + " pkg=" + bp.sourcePackage
+ " info=" + bp.pendingInfo);
- if (bp.perm == null && bp.pendingInfo != null) {
+ if (bp.packageSetting == null && bp.pendingInfo != null) {
BasePermission tree = findPermissionTreeLP(bp.name);
if (tree != null) {
+ bp.packageSetting = tree.packageSetting;
bp.perm = new PackageParser.Permission(tree.perm.owner,
new PermissionInfo(bp.pendingInfo));
bp.perm.info.packageName = tree.perm.info.packageName;
@@ -3800,17 +3867,37 @@
}
}
}
- if (bp.perm == null) {
+ if (bp.packageSetting == null) {
+ // We may not yet have parsed the package, so just see if
+ // we still know about its settings.
+ bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+ }
+ if (bp.packageSetting == null) {
Slog.w(TAG, "Removing dangling permission: " + bp.name
+ " from package " + bp.sourcePackage);
it.remove();
+ } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
+ if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
+ Slog.i(TAG, "Removing old permission: " + bp.name
+ + " from package " + bp.sourcePackage);
+ grantPermissions = true;
+ it.remove();
+ }
}
}
// Now update the permissions for all packages, in particular
// replace the granted permissions of the system packages.
- for (PackageParser.Package pkg : mPackages.values()) {
- grantPermissionsLP(pkg, false);
+ if (grantPermissions) {
+ for (PackageParser.Package pkg : mPackages.values()) {
+ if (pkg != pkgInfo) {
+ grantPermissionsLP(pkg, false);
+ }
+ }
+ }
+
+ if (pkgInfo != null) {
+ grantPermissionsLP(pkgInfo, replace);
}
}
@@ -3820,7 +3907,7 @@
return;
}
final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- boolean addedPermission = false;
+ boolean changedPermission = false;
if (replace) {
ps.permissionsFixed = false;
@@ -3838,26 +3925,26 @@
for (int i=0; i<N; i++) {
String name = pkg.requestedPermissions.get(i);
BasePermission bp = mSettings.mPermissions.get(name);
- PackageParser.Permission p = bp != null ? bp.perm : null;
if (false) {
if (gp != ps) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + name
- + ": " + p);
+ + ": " + bp);
}
}
- if (p != null) {
- final String perm = p.info.name;
+ if (bp != null && bp.packageSetting != null) {
+ final String perm = bp.name;
boolean allowed;
- if (p.info.protectionLevel == PermissionInfo.PROTECTION_NORMAL
- || p.info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+ boolean allowedSig = false;
+ if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL
+ || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
allowed = true;
- } else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
- || p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(p.owner.mSignatures, pkg.mSignatures)
+ } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
+ || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+ allowed = (checkSignaturesLP(bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
- if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+ if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
@@ -3874,6 +3961,9 @@
}
}
}
+ if (allowed) {
+ allowedSig = true;
+ }
} else {
allowed = false;
}
@@ -3887,7 +3977,7 @@
&& ps.permissionsFixed) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it.
- if (!gp.loadedPermissions.contains(perm)) {
+ if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
allowed = false;
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
@@ -3899,7 +3989,7 @@
if (npi.name.equals(perm)
&& pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
allowed = true;
- Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg "
+ Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ pkg.packageName);
break;
}
@@ -3908,9 +3998,11 @@
}
if (allowed) {
if (!gp.grantedPermissions.contains(perm)) {
- addedPermission = true;
+ changedPermission = true;
gp.grantedPermissions.add(perm);
gp.gids = appendInts(gp.gids, bp.gids);
+ } else if (!ps.haveGids) {
+ gp.gids = appendInts(gp.gids, bp.gids);
}
} else {
Slog.w(TAG, "Not granting permission " + perm
@@ -3918,11 +4010,21 @@
+ " because it was previously installed without");
}
} else {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " (protectionLevel=" + p.info.protectionLevel
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
+ if (gp.grantedPermissions.remove(perm)) {
+ changedPermission = true;
+ gp.gids = removeInts(gp.gids, bp.gids);
+ Slog.i(TAG, "Un-granting permission " + perm
+ + " from package " + pkg.packageName
+ + " (protectionLevel=" + bp.protectionLevel
+ + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ + ")");
+ } else {
+ Slog.w(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " (protectionLevel=" + bp.protectionLevel
+ + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ + ")");
+ }
}
} else {
Slog.w(TAG, "Unknown permission " + name
@@ -3930,17 +4032,17 @@
}
}
- if ((addedPermission || replace) && !ps.permissionsFixed &&
+ if ((changedPermission || replace) && !ps.permissionsFixed &&
((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) ||
((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
ps.permissionsFixed = true;
- gp.loadedPermissions = new HashSet<String>(gp.grantedPermissions);
}
+ ps.haveGids = true;
}
-
+
private final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
@@ -4027,6 +4129,11 @@
}
@Override
+ protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
+ return info.activity.owner.packageName;
+ }
+
+ @Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match) {
if (!mSettings.isEnabledLP(info.activity.info, mFlags)) {
@@ -4066,7 +4173,9 @@
out.print(prefix); out.print(
Integer.toHexString(System.identityHashCode(filter.activity)));
out.print(' ');
- out.println(filter.activity.getComponentShortName());
+ out.print(filter.activity.getComponentShortName());
+ out.print(" filter ");
+ out.println(Integer.toHexString(System.identityHashCode(filter)));
}
// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -4177,6 +4286,11 @@
}
@Override
+ protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
+ return info.service.owner.packageName;
+ }
+
+ @Override
protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
int match) {
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
@@ -4217,7 +4331,9 @@
out.print(prefix); out.print(
Integer.toHexString(System.identityHashCode(filter.service)));
out.print(' ');
- out.println(filter.service.getComponentShortName());
+ out.print(filter.service.getComponentShortName());
+ out.print(" filter ");
+ out.println(Integer.toHexString(System.identityHashCode(filter)));
}
// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -4380,13 +4496,15 @@
if ((event&ADD_EVENTS) != 0) {
if (p == null) {
p = scanPackageLI(fullPath,
- (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
+ (mIsRom ? PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
SCAN_MONITOR | SCAN_NO_PATHS);
if (p != null) {
synchronized (mPackages) {
- grantPermissionsLP(p, false);
+ updatePermissionsLP(p.packageName, p,
+ p.permissions.size() > 0, false);
}
addedPackage = p.applicationInfo.packageName;
addedUid = p.applicationInfo.uid;
@@ -4452,8 +4570,8 @@
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
- args.doPreInstall(res.returnCode);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, true, res);
}
@@ -4580,23 +4698,22 @@
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
- // When replacing apps make sure we honour
- // the existing app location if not overwritten by other options
- boolean prevOnSd = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
if (onSd) {
// Install flag overrides everything.
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
- // If current upgrade does not specify install location.
+ // If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
// App explictly prefers external. Let policy decide
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ } else {
// Prefer previous location
- return prevOnSd ? PackageHelper.RECOMMEND_INSTALL_EXTERNAL:
- PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
}
} else {
@@ -4613,20 +4730,29 @@
return pkgLite.recommendedInstallLocation;
}
+ /*
+ * Invoke remote method to get package information and install
+ * location values. Override install location based on default
+ * policy if needed and then create install arguments based
+ * on the install location.
+ */
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
- // Dont need to invoke getInstallLocation for forward locked apps.
- if (fwdLocked && onSd) {
+ boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+ if (onInt && onSd) {
+ // Check if both bits are set.
+ Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (fwdLocked && onSd) {
+ // Check for forward locked apps
Slog.w(TAG, "Cannot install fwd locked apps on sdcard");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// Remote call to find out default install location
- PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI);
- int loc = installLocationPolicy(pkgLite, flags);
- // Use install location to create InstallArgs and temporary
- // install location
+ PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS){
@@ -4635,15 +4761,21 @@
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
+ } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
+ ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
- // Override install location with flags
- if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){
+ // Override with defaults if needed.
+ loc = installLocationPolicy(pkgLite, flags);
+ if (!onSd && !onInt) {
+ // Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
flags |= PackageManager.INSTALL_EXTERNAL;
+ flags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
+ flags |= PackageManager.INSTALL_INTERNAL;
flags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
@@ -4701,6 +4833,12 @@
}
public void handleStartCopy() throws RemoteException {
+ mRet = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ // Check for storage space on target medium
+ if (!targetArgs.checkFreeStorage(mContainerService)) {
+ Log.w(TAG, "Insufficient storage to install");
+ return;
+ }
// Create the file args now.
mRet = targetArgs.copyApk(mContainerService, false);
targetArgs.doPreInstall(mRet);
@@ -4714,15 +4852,20 @@
builder.append(" target : ");
builder.append(targetArgs.getCodePath());
}
- Log.i(TAG, "Posting move MCS_UNBIND for " + builder.toString());
+ Log.i(TAG, builder.toString());
}
}
@Override
void handleReturnCode() {
targetArgs.doPostInstall(mRet);
- // TODO invoke pending move
- processPendingMove(this, mRet);
+ int currentStatus = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ currentStatus = PackageManager.MOVE_SUCCEEDED;
+ } else if (mRet == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){
+ currentStatus = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ }
+ processPendingMove(this, currentStatus);
}
@Override
@@ -4782,6 +4925,7 @@
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
abstract boolean doPostDeleteLI(boolean delete);
+ abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
}
class FileInstallArgs extends InstallArgs {
@@ -4812,6 +4956,10 @@
resourceFileName = getResourcePathFromCodePath();
}
+ boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ return imcs.checkFreeStorage(false, packageURI);
+ }
+
String getCodePath() {
return codeFileName;
}
@@ -5013,6 +5161,10 @@
cid = getTempContainerId();
}
+ boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ return imcs.checkFreeStorage(true, packageURI);
+ }
+
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (temp) {
createCopyFile();
@@ -5062,8 +5214,19 @@
}
}
if (!PackageHelper.renameSdDir(cid, newCacheId)) {
- Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
- return false;
+ Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId +
+ " which might be stale. Will try to clean up.");
+ // Clean up the stale container and proceed to recreate.
+ if (!PackageHelper.destroySdDir(newCacheId)) {
+ Slog.e(TAG, "Very strange. Cannot clean up stale container " + newCacheId);
+ return false;
+ }
+ // Successfully cleaned up stale container. Try to rename again.
+ if (!PackageHelper.renameSdDir(cid, newCacheId)) {
+ Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId
+ + " inspite of cleaning it up.");
+ return false;
+ }
}
if (!PackageHelper.isContainerMounted(newCacheId)) {
Slog.w(TAG, "Mounting container " + newCacheId);
@@ -5106,7 +5269,7 @@
String sourceFile = getCodePath();
// Remove dex file
if (mInstaller != null) {
- int retCode = mInstaller.rmdex(sourceFile.toString());
+ int retCode = mInstaller.rmdex(sourceFile);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove dex file for package: "
+ " at location "
@@ -5372,7 +5535,8 @@
parseFlags |= ~PackageManager.INSTALL_REPLACE_EXISTING;
scanPackageLI(restoreFile, parseFlags, scanMode);
synchronized (mPackages) {
- grantPermissionsLP(deletedPackage, false);
+ updatePermissionsLP(deletedPackage.packageName, deletedPackage,
+ true, false);
mSettings.writeLP();
}
if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -5460,7 +5624,7 @@
}
// Utility method used to move dex files during install.
- private int moveDexFiles(PackageParser.Package newPackage) {
+ private int moveDexFilesLI(PackageParser.Package newPackage) {
int retCode;
if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
@@ -5483,7 +5647,7 @@
mSettings.writeLP();
}
- if ((res.returnCode = moveDexFiles(newPackage))
+ if ((res.returnCode = moveDexFilesLI(newPackage))
!= PackageManager.INSTALL_SUCCEEDED) {
// Discontinue if moving dex files failed.
return;
@@ -5498,7 +5662,8 @@
Log.d(TAG, "New package installed in " + newPackage.mPath);
}
synchronized (mPackages) {
- grantPermissionsLP(newPackage, true);
+ updatePermissionsLP(newPackage.packageName, newPackage,
+ newPackage.permissions.size() > 0, true);
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
@@ -5879,19 +6044,23 @@
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
- schedulePackageCleaning(packageName);
- synchronized (mPackages) {
- if (outInfo != null) {
- outInfo.removedUid = mSettings.removePackageLP(packageName);
- }
- }
}
synchronized (mPackages) {
- if ( (deletedPs != null) && (deletedPs.sharedUser != null)) {
- // remove permissions associated with package
- mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
- }
if (deletedPs != null) {
+ schedulePackageCleaning(packageName);
+
+ if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ if (outInfo != null) {
+ outInfo.removedUid = mSettings.removePackageLP(packageName);
+ }
+ if (deletedPs != null) {
+ updatePermissionsLP(deletedPs.name, null, false, false);
+ if (deletedPs.sharedUser != null) {
+ // remove permissions associated with package
+ mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
+ }
+ }
+ }
// remove from preferred activities.
ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
@@ -5965,7 +6134,7 @@
return false;
}
synchronized (mPackages) {
- grantPermissionsLP(newPkg, true);
+ updatePermissionsLP(newPkg.packageName, newPkg, true, true);
mSettings.writeLP();
}
return true;
@@ -6621,35 +6790,102 @@
return;
}
+ String packageName = null;
+
+ int opti = 0;
+ while (opti < args.length) {
+ String opt = args[opti];
+ if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+ break;
+ }
+ opti++;
+ if ("-a".equals(opt)) {
+ // Right now we only know how to print all.
+ } else if ("-h".equals(opt)) {
+ pw.println("Package manager dump options:");
+ pw.println(" [-h] [cmd] ...");
+ pw.println(" cmd may be one of:");
+ pw.println(" [package.name]: info about given package");
+ return;
+ } else {
+ pw.println("Unknown argument: " + opt + "; use -h for help");
+ }
+ }
+
+ // Is the caller requesting to dump a particular piece of data?
+ if (opti < args.length) {
+ String cmd = args[opti];
+ opti++;
+ // Is this a package name?
+ if ("android".equals(cmd) || cmd.contains(".")) {
+ packageName = cmd;
+ }
+ }
+
+ boolean printedTitle = false;
+
synchronized (mPackages) {
- pw.println("Activity Resolver Table:");
- mActivities.dump(pw, " ");
- pw.println(" ");
- pw.println("Receiver Resolver Table:");
- mReceivers.dump(pw, " ");
- pw.println(" ");
- pw.println("Service Resolver Table:");
- mServices.dump(pw, " ");
- pw.println(" ");
- pw.println("Preferred Activities:");
- mSettings.mPreferredActivities.dump(pw, " ");
- pw.println(" ");
- pw.println("Permissions:");
+ if (mActivities.dump(pw, "Activity Resolver Table:", " ", packageName)) {
+ printedTitle = true;
+ }
+ if (mReceivers.dump(pw, printedTitle
+ ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
+ " ", packageName)) {
+ printedTitle = true;
+ }
+ if (mServices.dump(pw, printedTitle
+ ? "\nService Resolver Table:" : "Service Resolver Table:",
+ " ", packageName)) {
+ printedTitle = true;
+ }
+ if (mSettings.mPreferredActivities.dump(pw, printedTitle
+ ? "\nPreferred Activities:" : "Preferred Activities:",
+ " ", packageName)) {
+ printedTitle = true;
+ }
+ boolean printedSomething = false;
{
for (BasePermission p : mSettings.mPermissions.values()) {
+ if (packageName != null && !packageName.equals(p.sourcePackage)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Permissions:");
+ printedSomething = true;
+ printedTitle = true;
+ }
pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(p)));
pw.println("):");
pw.print(" sourcePackage="); pw.println(p.sourcePackage);
pw.print(" uid="); pw.print(p.uid);
pw.print(" gids="); pw.print(arrayToString(p.gids));
- pw.print(" type="); pw.println(p.type);
+ pw.print(" type="); pw.print(p.type);
+ pw.print(" prot="); pw.println(p.protectionLevel);
+ if (p.packageSetting != null) {
+ pw.print(" packageSetting="); pw.println(p.packageSetting);
+ }
+ if (p.perm != null) {
+ pw.print(" perm="); pw.println(p.perm);
+ }
}
}
- pw.println(" ");
- pw.println("Packages:");
+ printedSomething = false;
+ SharedUserSetting packageSharedUser = null;
{
for (PackageSetting ps : mSettings.mPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ packageSharedUser = ps.sharedUser;
pw.print(" Package [");
pw.print(ps.realName != null ? ps.realName : ps.name);
pw.print("] (");
@@ -6704,7 +6940,8 @@
pw.print(" timeStamp="); pw.println(ps.getTimeStampStr());
pw.print(" signatures="); pw.println(ps.signatures);
pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
- pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
+ pw.print(" haveGids="); pw.println(ps.haveGids);
+ pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
pw.print(" installStatus="); pw.print(ps.installStatus);
pw.print(" enabled="); pw.println(ps.enabled);
if (ps.disabledComponents.size() > 0) {
@@ -6725,28 +6962,40 @@
pw.print(" "); pw.println(s);
}
}
- if (ps.loadedPermissions.size() > 0) {
- pw.println(" loadedPermissions:");
- for (String s : ps.loadedPermissions) {
- pw.print(" "); pw.println(s);
- }
- }
}
}
+ printedSomething = false;
if (mSettings.mRenamedPackages.size() > 0) {
- pw.println(" ");
- pw.println("Renamed packages:");
for (HashMap.Entry<String, String> e
: mSettings.mRenamedPackages.entrySet()) {
+ if (packageName != null && !packageName.equals(e.getKey())
+ && !packageName.equals(e.getValue())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
pw.println(e.getValue());
}
}
+ printedSomething = false;
if (mSettings.mDisabledSysPackages.size() > 0) {
- pw.println(" ");
- pw.println("Hidden system packages:");
for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
- pw.print(" Package [");
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Hidden system packages:");
+ printedSomething = true;
+ printedTitle = true;
+ }
+ pw.print(" Package [");
pw.print(ps.realName != null ? ps.realName : ps.name);
pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(ps)));
@@ -6760,10 +7009,18 @@
pw.print(" resourcePath="); pw.println(ps.resourcePathString);
}
}
- pw.println(" ");
- pw.println("Shared Users:");
+ printedSomething = false;
{
for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
+ if (packageName != null && su != packageSharedUser) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Shared users:");
+ printedSomething = true;
+ printedTitle = true;
+ }
pw.print(" SharedUser ["); pw.print(su.name); pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(su)));
pw.println("):");
@@ -6773,36 +7030,43 @@
for (String s : su.grantedPermissions) {
pw.print(" "); pw.println(s);
}
- pw.println(" loadedPermissions:");
- for (String s : su.loadedPermissions) {
- pw.print(" "); pw.println(s);
- }
}
}
- pw.println(" ");
- pw.println("Settings parse messages:");
- pw.println(mSettings.mReadMessages.toString());
-
- pw.println(" ");
- pw.println("Package warning messages:");
- File fname = getSettingsProblemFile();
- FileInputStream in;
- try {
- in = new FileInputStream(fname);
- int avail = in.available();
- byte[] data = new byte[avail];
- in.read(data);
- pw.println(new String(data));
- } catch (FileNotFoundException e) {
- } catch (IOException e) {
+ if (packageName == null) {
+ if (printedTitle) pw.println(" ");
+ printedTitle = true;
+ pw.println("Settings parse messages:");
+ pw.println(mSettings.mReadMessages.toString());
+
+ pw.println(" ");
+ pw.println("Package warning messages:");
+ File fname = getSettingsProblemFile();
+ FileInputStream in;
+ try {
+ in = new FileInputStream(fname);
+ int avail = in.available();
+ byte[] data = new byte[avail];
+ in.read(data);
+ pw.println(new String(data));
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ }
}
}
synchronized (mProviders) {
- pw.println(" ");
- pw.println("Registered ContentProviders:");
+ boolean printedSomething = false;
for (PackageParser.Provider p : mProviders.values()) {
+ if (packageName != null && !packageName.equals(p.info.packageName)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (printedTitle) pw.println(" ");
+ pw.println("Registered ContentProviders:");
+ printedSomething = true;
+ printedTitle = true;
+ }
pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
pw.println(p.toString());
}
@@ -6816,7 +7080,9 @@
final String name;
String sourcePackage;
+ PackageSettingBase packageSetting;
final int type;
+ int protectionLevel;
PackageParser.Permission perm;
PermissionInfo pendingInfo;
int uid;
@@ -6826,6 +7092,14 @@
name = _name;
sourcePackage = _sourcePackage;
type = _type;
+ // Default to most conservative protection level.
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ public String toString() {
+ return "BasePermission{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + "}";
}
}
@@ -7258,8 +7532,6 @@
HashSet<String> grantedPermissions = new HashSet<String>();
int[] gids;
- HashSet<String> loadedPermissions = new HashSet<String>();
-
GrantedPermissions(int pkgFlags) {
setFlags(pkgFlags);
}
@@ -7291,6 +7563,7 @@
PackageSignatures signatures = new PackageSignatures();
boolean permissionsFixed;
+ boolean haveGids;
/* Explicitly disabled components */
HashSet<String> disabledComponents = new HashSet<String>(0);
@@ -7359,12 +7632,12 @@
public void copyFrom(PackageSettingBase base) {
grantedPermissions = base.grantedPermissions;
gids = base.gids;
- loadedPermissions = base.loadedPermissions;
timeStamp = base.timeStamp;
timeStampString = base.timeStampString;
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
+ haveGids = base.haveGids;
disabledComponents = base.disabledComponents;
enabledComponents = base.enabledComponents;
enabled = base.enabled;
@@ -7458,6 +7731,10 @@
private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
new IntentResolver<PreferredActivity, PreferredActivity>() {
@Override
+ protected String packageForFilter(PreferredActivity filter) {
+ return filter.mActivity.getPackageName();
+ }
+ @Override
protected void dumpFilter(PrintWriter out, String prefix,
PreferredActivity filter) {
out.print(prefix); out.print(
@@ -7696,9 +7973,10 @@
+ " from pkg " + bp.sourcePackage
+ " to " + newPkg);
bp.sourcePackage = newPkg;
+ bp.packageSetting = null;
bp.perm = null;
if (bp.pendingInfo != null) {
- bp.sourcePackage = newPkg;
+ bp.pendingInfo.packageName = newPkg;
}
bp.uid = 0;
bp.gids = null;
@@ -7788,7 +8066,6 @@
p.userId = dis.userId;
// Clone permissions
p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
- p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
// Clone component info
p.disabledComponents = new HashSet<String>(dis.disabledComponents);
p.enabledComponents = new HashSet<String>(dis.enabledComponents);
@@ -7893,7 +8170,7 @@
}
for (PackageSetting pkg:sus.packages) {
if (pkg.pkg != null &&
- !pkg.pkg.packageName.equalsIgnoreCase(deletedPs.pkg.packageName) &&
+ !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
pkg.pkg.requestedPermissions.contains(eachPerm)) {
used = true;
break;
@@ -7902,7 +8179,6 @@
if (!used) {
// can safely delete this permission from list
sus.grantedPermissions.remove(eachPerm);
- sus.loadedPermissions.remove(eachPerm);
}
}
// Update gids
@@ -8013,6 +8289,7 @@
return;
}
} else {
+ mSettingsFilename.delete();
Slog.w(TAG, "Preserving older settings backup");
}
}
@@ -8205,7 +8482,7 @@
// be set.
for (final String name : pkg.grantedPermissions) {
BasePermission bp = mPermissions.get(name);
- if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) {
+ if (bp != null) {
// We only need to write signature or system permissions but this wont
// match the semantics of grantedPermissions. So write all permissions.
serializer.startTag(null, "item");
@@ -8299,6 +8576,11 @@
serializer.startTag(null, "item");
serializer.attribute(null, "name", bp.name);
serializer.attribute(null, "package", bp.sourcePackage);
+ if (bp.protectionLevel !=
+ PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection",
+ Integer.toString(bp.protectionLevel));
+ }
if (DEBUG_SETTINGS) Log.v(TAG,
"Writing perm: name=" + bp.name + " type=" + bp.type);
if (bp.type == BasePermission.TYPE_DYNAMIC) {
@@ -8314,11 +8596,6 @@
serializer.attribute(null, "label",
pi.nonLocalizedLabel.toString());
}
- if (pi.protectionLevel !=
- PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection",
- Integer.toString(pi.protectionLevel));
- }
}
}
serializer.endTag(null, "item");
@@ -8520,6 +8797,8 @@
dynamic
? BasePermission.TYPE_DYNAMIC
: BasePermission.TYPE_NORMAL);
+ bp.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_NORMAL);
if (dynamic) {
PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -8527,8 +8806,7 @@
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(
null, "label");
- pi.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_NORMAL);
+ pi.protectionLevel = bp.protectionLevel;
bp.pendingInfo = pi;
}
out.put(bp.name, bp);
@@ -8775,7 +9053,7 @@
packageSetting.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals("perms")) {
readGrantedPermissionsLP(parser,
- packageSetting.loadedPermissions);
+ packageSetting.grantedPermissions);
packageSetting.permissionsFixed = true;
} else {
reportSettingsProblem(Log.WARN,
@@ -8904,7 +9182,7 @@
if (tagName.equals("sigs")) {
su.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals("perms")) {
- readGrantedPermissionsLP(parser, su.loadedPermissions);
+ readGrantedPermissionsLP(parser, su.grantedPermissions);
} else {
reportSettingsProblem(Log.WARN,
"Unknown element under <shared-user>: "
@@ -9104,36 +9382,47 @@
}
/*
- * Return true if PackageManager does have packages to be updated.
+ * Update media status on PackageManager.
*/
- public boolean updateExternalMediaStatus(final boolean mediaStatus) {
- final boolean ret;
+ public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Media status can only be updated by the system");
+ }
synchronized (mPackages) {
+ Log.i(TAG, "Updating external media status from " +
+ (mMediaMounted ? "mounted" : "unmounted") + " to " +
+ (mediaStatus ? "mounted" : "unmounted"));
if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
mediaStatus+", mMediaMounted=" + mMediaMounted);
if (mediaStatus == mMediaMounted) {
- return false;
+ if (reportStatus) {
+ mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+ }
+ return;
}
mMediaMounted = mediaStatus;
- Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE);
- ret = appList != null && appList.size() > 0;
}
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- updateExternalMediaStatusInner(mediaStatus, ret);
+ try {
+ updateExternalMediaStatusInner(mediaStatus);
+ } finally {
+ if (reportStatus) {
+ mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
+ }
+ }
}
});
- return ret;
}
- private void updateExternalMediaStatusInner(boolean mediaStatus,
- boolean sendUpdateBroadcast) {
+ private void updateExternalMediaStatusInner(boolean mediaStatus) {
// If we are up here that means there are packages to be
// enabled or disabled.
final String list[] = PackageHelper.getSecureContainerList();
if (list == null || list.length == 0) {
+ Log.i(TAG, "No secure containers on sdcard");
return;
}
@@ -9141,16 +9430,6 @@
int num = 0;
HashSet<String> removeCids = new HashSet<String>();
HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
- /*HashMap<String, String> cidPathMap = new HashMap<String, String>();
- // Don't hold any locks when getting cache paths
- for (String cid : list) {
- String cpath = PackageHelper.getSdDir(cid);
- if (cpath == null) {
- removeCids.add(cid);
- } else {
- cidPathMap.put(cid, cpath);
- }
- }*/
synchronized (mPackages) {
for (String cid : list) {
SdInstallArgs args = new SdInstallArgs(cid);
@@ -9203,11 +9482,11 @@
// Process packages with valid entries.
if (mediaStatus) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, sendUpdateBroadcast, removeCids);
+ loadMediaPackages(processCids, uidArr, removeCids);
startCleaningPackages();
} else {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, sendUpdateBroadcast);
+ unloadMediaPackages(processCids, uidArr);
}
}
@@ -9238,8 +9517,7 @@
* to avoid unnecessary crashes.
*/
private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], boolean sendUpdateBroadcast,
- HashSet<String> removeCids) {
+ int uidArr[], HashSet<String> removeCids) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<SdInstallArgs> keys = processCids.keySet();
boolean doGc = false;
@@ -9282,10 +9560,6 @@
// Scan the package
if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
synchronized (mPackages) {
- // Grant permissions
- grantPermissionsLP(pkg, false);
- // Persist settings
- mSettings.writeLP();
retCode = PackageManager.INSTALL_SUCCEEDED;
pkgList.add(pkg.packageName);
// Post process args
@@ -9304,24 +9578,30 @@
}
}
}
+ synchronized (mPackages) {
+ // Make sure group IDs have been assigned, and any permission
+ // changes in other apps are accounted for
+ updatePermissionsLP(null, null, true, false);
+ // Persist settings
+ mSettings.writeLP();
+ }
// Send a broadcast to let everyone know we are done processing
- if (sendUpdateBroadcast) {
+ if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, pkgList, uidArr);
}
if (doGc) {
Runtime.getRuntime().gc();
}
- // Delete any stale containers if needed.
+ // List stale containers.
if (removeCids != null) {
for (String cid : removeCids) {
- Log.i(TAG, "Destroying stale container : " + cid);
- PackageHelper.destroySdDir(cid);
+ Log.w(TAG, "Container " + cid + " is stale");
}
}
}
private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], boolean sendUpdateBroadcast) {
+ int uidArr[]) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
ArrayList<String> pkgList = new ArrayList<String>();
ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
@@ -9344,7 +9624,7 @@
}
}
// Send broadcasts
- if (sendUpdateBroadcast) {
+ if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr);
}
// Force gc
@@ -9431,41 +9711,43 @@
sendResourcesChangedBroadcast(false, pkgList, uidArr);
// Update package code and resource paths
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg != null) {
- String oldCodePath = pkg.mPath;
- String newCodePath = mp.targetArgs.getCodePath();
- String newResPath = mp.targetArgs.getResourcePath();
- pkg.mPath = newCodePath;
- // Move dex files around
- if (moveDexFiles(pkg)
- != PackageManager.INSTALL_SUCCEEDED) {
- // Moving of dex files failed. Set
- // error code and abort move.
- pkg.mPath = pkg.mScanPath;
- returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
- moveSucceeded = false;
- } else {
- pkg.mScanPath = newCodePath;
- pkg.applicationInfo.sourceDir = newCodePath;
- pkg.applicationInfo.publicSourceDir = newResPath;
- PackageSetting ps = (PackageSetting) pkg.mExtras;
- ps.codePath = new File(pkg.applicationInfo.sourceDir);
- ps.codePathString = ps.codePath.getPath();
- ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
- ps.resourcePathString = ps.resourcePath.getPath();
- // Set the application info flag correctly.
- if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg != null) {
+ String oldCodePath = pkg.mPath;
+ String newCodePath = mp.targetArgs.getCodePath();
+ String newResPath = mp.targetArgs.getResourcePath();
+ pkg.mPath = newCodePath;
+ // Move dex files around
+ if (moveDexFilesLI(pkg)
+ != PackageManager.INSTALL_SUCCEEDED) {
+ // Moving of dex files failed. Set
+ // error code and abort move.
+ pkg.mPath = pkg.mScanPath;
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ moveSucceeded = false;
} else {
- pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ pkg.mScanPath = newCodePath;
+ pkg.applicationInfo.sourceDir = newCodePath;
+ pkg.applicationInfo.publicSourceDir = newResPath;
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ ps.codePath = new File(pkg.applicationInfo.sourceDir);
+ ps.codePathString = ps.codePath.getPath();
+ ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
+ ps.resourcePathString = ps.resourcePath.getPath();
+ // Set the application info flag correctly.
+ if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ } else {
+ pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ }
+ ps.setFlags(pkg.applicationInfo.flags);
+ mAppDirs.remove(oldCodePath);
+ mAppDirs.put(newCodePath, pkg);
+ // Persist settings
+ mSettings.writeLP();
}
- ps.setFlags(pkg.applicationInfo.flags);
- mAppDirs.remove(oldCodePath);
- mAppDirs.put(newCodePath, pkg);
- // Persist settings
- mSettings.writeLP();
}
}
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index d72416d..848997b 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -571,6 +571,7 @@
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
+ pid = Binder.getCallingPid();
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -595,6 +596,7 @@
final IBinder binder;
final String tag;
final int uid;
+ final int pid;
final int monitorType;
boolean activated = true;
int minState;
@@ -998,7 +1000,8 @@
activated = " activated";
}
pw.println(" " + type + " '" + wl.tag + "'" + acquireCausesWakeup
- + activated + " (minState=" + wl.minState + ")");
+ + activated + " (minState=" + wl.minState + ", uid=" + wl.uid
+ + ", pid=" + wl.pid + ")");
}
pw.println();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b023958..25a60a6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -399,6 +399,13 @@
} catch (Throwable e) {
Slog.e(TAG, "Failure installing status bar icons", e);
}
+
+ try {
+ Slog.i(TAG, "DiskStats Service");
+ ServiceManager.addService("diskstats", new DiskStatsService(context));
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting DiskStats Service", e);
+ }
}
// make sure the ADB_ENABLED setting value matches the secure property value
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 37a3cdf..a83af95 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -20,7 +20,6 @@
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.IActivityManager;
-import android.app.KeyguardManager;
import android.app.IUiModeManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -46,6 +45,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Slog;
@@ -103,7 +103,6 @@
private LocationManager mLocationManager;
private Location mLocation;
private StatusBarManager mStatusBarManager;
- private KeyguardManager.KeyguardLock mKeyguardLock;
private final PowerManager.WakeLock mWakeLock;
// The broadcast receiver which receives the result of the ordered broadcast sent when
@@ -129,20 +128,39 @@
category = null;
}
if (category != null) {
+ // This is the new activity that will serve as home while
+ // we are in care mode.
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(category);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ // Now we are going to be careful about switching the
+ // configuration and starting the activity -- we need to
+ // do this in a specific order under control of the
+ // activity manager, to do it cleanly. So compute the
+ // new config, but don't set it yet, and let the
+ // activity manager take care of both the start and config
+ // change.
+ Configuration newConfig = null;
+ if (mHoldingConfiguration) {
+ mHoldingConfiguration = false;
+ updateConfigurationLocked(false);
+ newConfig = mConfiguration;
+ }
try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
+ ActivityManagerNative.getDefault().startActivityWithConfig(
+ null, intent, null, null, 0, null, null, 0, false, false,
+ newConfig);
+ mHoldingConfiguration = false;
+ } catch (RemoteException e) {
Slog.w(TAG, e.getCause());
}
}
if (mHoldingConfiguration) {
mHoldingConfiguration = false;
- updateConfigurationLocked();
+ updateConfigurationLocked(true);
}
}
}
@@ -172,7 +190,7 @@
mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
synchronized (mLock) {
if (mSystemReady) {
- updateLocked();
+ updateLocked(0);
}
}
}
@@ -277,13 +295,16 @@
com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
mDeskModeKeepsScreenOn = (context.getResources().getInteger(
com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
+
+ mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
}
- public void disableCarMode() {
+ public void disableCarMode(int flags) {
synchronized (mLock) {
setCarModeLocked(false);
if (mSystemReady) {
- updateLocked();
+ updateLocked(flags);
}
}
}
@@ -295,7 +316,7 @@
synchronized (mLock) {
setCarModeLocked(true);
if (mSystemReady) {
- updateLocked();
+ updateLocked(0);
}
}
}
@@ -321,8 +342,12 @@
}
if (mNightMode != mode) {
+ long ident = Binder.clearCallingIdentity();
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.UI_NIGHT_MODE, mode);
+ Binder.restoreCallingIdentity(ident);
mNightMode = mode;
- updateLocked();
+ updateLocked(0);
}
}
}
@@ -335,7 +360,7 @@
synchronized (mLock) {
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
- updateLocked();
+ updateLocked(0);
mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
}
}
@@ -347,24 +372,6 @@
void setCarModeLocked(boolean enabled) {
if (mCarModeEnabled != enabled) {
mCarModeEnabled = enabled;
-
- // Disable keyguard when in car mode
- if (mKeyguardLock == null) {
- KeyguardManager km =
- (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
- if (km != null) {
- mKeyguardLock = km.newKeyguardLock(TAG);
- }
- }
- if (mKeyguardLock != null) {
- long ident = Binder.clearCallingIdentity();
- if (enabled) {
- mKeyguardLock.disableKeyguard();
- } else {
- mKeyguardLock.reenableKeyguard();
- }
- Binder.restoreCallingIdentity(ident);
- }
}
}
@@ -374,13 +381,13 @@
mDockState = newState;
setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
if (mSystemReady) {
- updateLocked();
+ updateLocked(0);
}
}
}
}
- final void updateConfigurationLocked() {
+ final void updateConfigurationLocked(boolean sendIt) {
int uiMode = 0;
if (mCarModeEnabled) {
uiMode = Configuration.UI_MODE_TYPE_CAR;
@@ -405,18 +412,19 @@
if (!mHoldingConfiguration && uiMode != mSetUiMode) {
mSetUiMode = uiMode;
+ mConfiguration.uiMode = uiMode;
- try {
- final IActivityManager am = ActivityManagerNative.getDefault();
- mConfiguration.uiMode = uiMode;
- am.updateConfiguration(mConfiguration);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure communicating with activity manager", e);
+ if (sendIt) {
+ try {
+ ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure communicating with activity manager", e);
+ }
}
}
}
- final void updateLocked() {
+ final void updateLocked(int flags) {
long ident = Binder.clearCallingIdentity();
try {
@@ -467,7 +475,25 @@
mHoldingConfiguration = true;
}
- updateConfigurationLocked();
+ if (oldAction != null && (flags&UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
+ // We are exiting the special mode, and have been asked to return
+ // to the main home screen while doing so. To keep this clean, we
+ // have the activity manager switch the configuration for us at the
+ // same time as the switch.
+ try {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ updateConfigurationLocked(false);
+ ActivityManagerNative.getDefault().startActivityWithConfig(
+ null, intent, null, null, 0, null, null, 0, false, false,
+ mConfiguration);
+ } catch (RemoteException e) {
+ Slog.w(TAG, e.getCause());
+ }
+ } else {
+ updateConfigurationLocked(true);
+ }
// keep screen on when charging and in car mode
boolean keepScreenOn = mCharging &&
@@ -540,7 +566,7 @@
if (isDoingNightMode() && mLocation != null
&& mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateTwilightLocked();
- updateLocked();
+ updateLocked(0);
}
}
break;
@@ -568,7 +594,7 @@
if (isDoingNightMode() && mLocation != null
&& mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateTwilightLocked();
- updateLocked();
+ updateLocked(0);
}
}
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index f4bdd1f..124da4e 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -68,6 +68,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.service.wallpaper.ImageWallpaper;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
import com.android.server.DevicePolicyManagerService.ActiveAdmin;
import com.android.server.DevicePolicyManagerService.MyPackageMonitor;
@@ -804,6 +805,9 @@
}
res = r.openRawResource(resId);
+ if (WALLPAPER_FILE.exists()) {
+ WALLPAPER_FILE.delete();
+ }
fos = new FileOutputStream(WALLPAPER_FILE);
byte[] buffer = new byte[32768];
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 97a4329..e4d7623 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -46,6 +46,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.NetworkStateTracker;
@@ -81,6 +82,7 @@
import com.android.internal.app.IBatteryStats;
import android.app.backup.IBackupManager;
import com.android.server.am.BatteryStatsService;
+import com.android.internal.R;
/**
* WifiService handles remote WiFi operation requests by implementing
@@ -94,6 +96,8 @@
private static final boolean DBG = false;
private static final Pattern scanResultPattern = Pattern.compile("\t+");
private final WifiStateTracker mWifiStateTracker;
+ /* TODO: fetch a configurable interface */
+ private static final String SOFTAP_IFACE = "wl0.1";
private Context mContext;
private int mWifiApState;
@@ -105,6 +109,8 @@
private boolean mDeviceIdle;
private int mPluggedType;
+ private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD};
+
// true if the user enabled Wifi while in airplane mode
private boolean mAirplaneModeOverwridden;
@@ -226,18 +232,6 @@
PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
- mWifiStateTracker.setReleaseWakeLockCallback(
- new Runnable() {
- public void run() {
- mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
- synchronized (sDriverStopWakeLock) {
- if (sDriverStopWakeLock.isHeld()) {
- sDriverStopWakeLock.release();
- }
- }
- }
- }
- );
mContext.registerReceiver(
new BroadcastReceiver() {
@@ -270,12 +264,11 @@
* if needed
*/
public void startWifi() {
- boolean wifiEnabled = getPersistedWifiEnabled();
- boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled();
+ /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
+ boolean wifiEnabled = getPersistedWifiEnabled() || testAndClearWifiSavedState();
Slog.i(TAG, "WifiService starting up with Wi-Fi " +
(wifiEnabled ? "enabled" : "disabled"));
- setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
- setWifiApEnabledBlocking(wifiAPEnabled, true, Process.myUid(), null);
+ setWifiEnabledBlocking(wifiEnabled, true, Process.myUid());
}
private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
@@ -297,27 +290,25 @@
try {
ifcg = service.getInterfaceConfig(intf);
if (ifcg != null) {
- /* IP/netmask: 169.254.2.1/255.255.255.0 */
- ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
+ /* IP/netmask: 192.168.43.1/255.255.255.0 */
+ ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1;
ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
ifcg.interfaceFlags = "up";
service.setInterfaceConfig(intf, ifcg);
}
} catch (Exception e) {
- /**
- * TODO: Add broadcast to indicate tether failed
- */
Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
return;
}
- /**
- * TODO: Add broadcast to indicate tether failed
- */
- if(mCm.tether(intf) == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- Slog.d(TAG, "Tethered "+intf);
- } else {
+ if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
Slog.e(TAG, "Error tethering "+intf);
}
break;
@@ -326,6 +317,19 @@
}
}
+ private boolean testAndClearWifiSavedState() {
+ final ContentResolver cr = mContext.getContentResolver();
+ int wifiSavedState = 0;
+ try {
+ wifiSavedState = Settings.Secure.getInt(cr, Settings.Secure.WIFI_SAVED_STATE);
+ if(wifiSavedState == 1)
+ Settings.Secure.putInt(cr, Settings.Secure.WIFI_SAVED_STATE, 0);
+ } catch (Settings.SettingNotFoundException e) {
+ ;
+ }
+ return (wifiSavedState == 1);
+ }
+
private boolean getPersistedWifiEnabled() {
final ContentResolver cr = mContext.getContentResolver();
try {
@@ -431,12 +435,18 @@
return false;
}
- setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
-
+ /**
+ * Fail Wifi if AP is enabled
+ * TODO: Deprecate WIFI_STATE_UNKNOWN and rename it
+ * WIFI_STATE_FAILED
+ */
if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
- setWifiApEnabledBlocking(false, true, Process.myUid(), null);
+ setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
+ return false;
}
+ setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
+
if (enable) {
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed to load Wi-Fi driver.");
@@ -581,25 +591,11 @@
return mWifiStateTracker.reassociate();
}
- private boolean getPersistedWifiApEnabled() {
- final ContentResolver cr = mContext.getContentResolver();
- try {
- return Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_ON) == 1;
- } catch (Settings.SettingNotFoundException e) {
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, 0);
- return false;
- }
- }
-
- private void persistWifiApEnabled(boolean enabled) {
- final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, enabled ? 1 : 0);
- }
-
/**
- * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)}
+ * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
* @param wifiConfig SSID, security and channel details as
* part of WifiConfiguration
+ * @param enabled, true to enable and false to disable
* @return {@code true} if the start operation was
* started or is already in the queue.
*/
@@ -621,46 +617,108 @@
return true;
}
+ public WifiConfiguration getWifiApConfiguration() {
+ final ContentResolver cr = mContext.getContentResolver();
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ int authType;
+ try {
+ wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID);
+ if (wifiConfig.SSID == null)
+ return null;
+ authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY);
+ wifiConfig.allowedKeyManagement.set(authType);
+ wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD);
+ return wifiConfig;
+ } catch (Settings.SettingNotFoundException e) {
+ Slog.e(TAG,"AP settings not found, returning");
+ return null;
+ }
+ }
+
+ private void persistApConfiguration(WifiConfiguration wifiConfig) {
+ final ContentResolver cr = mContext.getContentResolver();
+ boolean isWpa;
+ if (wifiConfig == null)
+ return;
+ Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID);
+ isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK);
+ Settings.Secure.putInt(cr,
+ Settings.Secure.WIFI_AP_SECURITY,
+ isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE);
+ if (isWpa)
+ Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey);
+ }
+
/**
* Enables/disables Wi-Fi AP synchronously. The driver is loaded
* and soft access point configured as a single operation.
* @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
- * @param persist {@code true} if the setting should be persisted.
* @param uid The UID of the process making the request.
- * @param config The WifiConfiguration for AP
+ * @param wifiConfig The WifiConfiguration for AP
* @return {@code true} if the operation succeeds (or if the existing state
* is the same as the requested state)
*/
- /**
- * TODO: persist needs to go away in WifiService
- * This will affect all persist related functions
- * for Access Point
- */
private boolean setWifiApEnabledBlocking(boolean enable,
- boolean persist, int uid, WifiConfiguration wifiConfig) {
+ int uid, WifiConfiguration wifiConfig) {
final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
if (mWifiApState == eventualWifiApState) {
- return true;
+ /* Configuration changed on a running access point */
+ if(enable && (wifiConfig != null)) {
+ try {
+ persistApConfiguration(wifiConfig);
+ nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
+ SOFTAP_IFACE);
+ return true;
+ } catch(Exception e) {
+ Slog.e(TAG, "Exception in nwService during AP restart");
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+ return false;
+ }
+ } else {
+ return true;
+ }
}
- setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : WIFI_AP_STATE_DISABLING, uid);
-
- if (enable && (mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED)) {
- setWifiEnabledBlocking(false, true, Process.myUid());
+ /**
+ * Fail AP if Wifi is enabled
+ */
+ if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) && enable) {
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
+ return false;
}
+ setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING :
+ WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD);
+
if (enable) {
+
+ /* Use default config if there is no existing config */
+ if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
+ wifiConfig = new WifiConfiguration();
+ wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
+ wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+ }
+ persistApConfiguration(wifiConfig);
+
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
return false;
}
try {
- nwService.startAccessPoint();
+ nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
+ SOFTAP_IFACE);
} catch(Exception e) {
Slog.e(TAG, "Exception in startAccessPoint()");
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+ return false;
}
} else {
@@ -669,20 +727,18 @@
nwService.stopAccessPoint();
} catch(Exception e) {
Slog.e(TAG, "Exception in stopAccessPoint()");
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+ return false;
}
if (!mWifiStateTracker.unloadDriver()) {
Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+ setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
return false;
}
}
- // Success!
- if (persist) {
- persistWifiApEnabled(enable);
- }
- setWifiApEnabledState(eventualWifiApState, uid);
+ setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD);
return true;
}
@@ -699,9 +755,16 @@
return mWifiApState;
}
- private void setWifiApEnabledState(int wifiAPState, int uid) {
+ private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) {
final int previousWifiApState = mWifiApState;
+ /**
+ * Unload the driver if going to a failed state
+ */
+ if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) {
+ mWifiStateTracker.unloadDriver();
+ }
+
long ident = Binder.clearCallingIdentity();
try {
if (wifiAPState == WIFI_AP_STATE_ENABLED) {
@@ -959,8 +1022,7 @@
*/
int netId = config.networkId;
boolean newNetwork = netId == -1;
- boolean doReconfig;
- int currentPriority;
+ boolean doReconfig = false;
// networkId of -1 means we want to create a new network
synchronized (mWifiStateTracker) {
if (newNetwork) {
@@ -972,17 +1034,6 @@
return -1;
}
doReconfig = true;
- } else {
- String priorityVal = mWifiStateTracker.getNetworkVariable(
- netId, WifiConfiguration.priorityVarName);
- currentPriority = -1;
- if (!TextUtils.isEmpty(priorityVal)) {
- try {
- currentPriority = Integer.parseInt(priorityVal);
- } catch (NumberFormatException ignore) {
- }
- }
- doReconfig = currentPriority != config.priority;
}
mNeedReconfig = mNeedReconfig || doReconfig;
}
@@ -1706,7 +1757,7 @@
private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
Message.obtain(mWifiHandler,
(enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
- 0, uid, wifiConfig).sendToTarget();
+ uid, 0, wifiConfig).sendToTarget();
}
private void updateWifiState() {
@@ -1737,20 +1788,16 @@
sendEnableMessage(true, false, mLastEnableUid);
sWakeLock.acquire();
sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
- } else {
+ } else if (!mWifiStateTracker.isDriverStopped()) {
int wakeLockTimeout =
Settings.Secure.getInt(
mContext.getContentResolver(),
Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
DEFAULT_WAKELOCK_TIMEOUT);
/*
- * The following wakelock is held in order to ensure
- * that the connectivity manager has time to fail over
- * to the mobile data network. The connectivity manager
- * releases it once mobile data connectivity has been
- * established. If connectivity cannot be established,
- * the wakelock is released after wakeLockTimeout
- * milliseconds have elapsed.
+ * We are assuming that ConnectivityService can make
+ * a transition to cellular data within wakeLockTimeout time.
+ * The wakelock is released by the delayed message.
*/
sDriverStopWakeLock.acquire();
mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
@@ -1844,24 +1891,19 @@
break;
case MESSAGE_RELEASE_WAKELOCK:
- synchronized (sDriverStopWakeLock) {
- if (sDriverStopWakeLock.isHeld()) {
- sDriverStopWakeLock.release();
- }
- }
+ sDriverStopWakeLock.release();
break;
case MESSAGE_START_ACCESS_POINT:
setWifiApEnabledBlocking(true,
- msg.arg1 == 1,
- msg.arg2,
+ msg.arg1,
(WifiConfiguration) msg.obj);
+ sWakeLock.release();
break;
case MESSAGE_STOP_ACCESS_POINT:
setWifiApEnabledBlocking(false,
- msg.arg1 == 1,
- msg.arg2,
+ msg.arg1,
(WifiConfiguration) msg.obj);
sWakeLock.release();
break;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b27736a..0e4b38d 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -143,6 +143,7 @@
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
+ static final boolean DEBUG_CONFIGURATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
@@ -2317,7 +2318,7 @@
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
- Surface outSurface) {
+ Configuration outConfig, Surface outSurface) {
boolean displayed = false;
boolean inTouchMode;
boolean configChanged;
@@ -2390,16 +2391,30 @@
if (oldVisibility == View.GONE) {
win.mEnterAnimationPending = true;
}
- if (displayed && win.mSurface != null && !win.mDrawPending
- && !win.mCommitDrawPending && !mDisplayFrozen
- && mPolicy.isScreenOn()) {
- applyEnterAnimationLocked(win);
- }
- if (displayed && (win.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
- if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Relayout window turning screen on: " + win);
- win.mTurnOnScreen = true;
+ if (displayed) {
+ if (win.mSurface != null && !win.mDrawPending
+ && !win.mCommitDrawPending && !mDisplayFrozen
+ && mPolicy.isScreenOn()) {
+ applyEnterAnimationLocked(win);
+ }
+ if ((win.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG,
+ "Relayout window turning screen on: " + win);
+ win.mTurnOnScreen = true;
+ }
+ int diff = 0;
+ if (win.mConfiguration != mCurConfiguration
+ && (win.mConfiguration == null
+ || (diff=mCurConfiguration.diff(win.mConfiguration)) != 0)) {
+ win.mConfiguration = mCurConfiguration;
+ if (DEBUG_CONFIGURATION) {
+ Slog.i(TAG, "Window " + win + " visible with new config: "
+ + win.mConfiguration + " / 0x"
+ + Integer.toHexString(diff));
+ }
+ outConfig.setTo(mCurConfiguration);
+ }
}
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
// To change the format, we need to re-build the surface.
@@ -6694,10 +6709,10 @@
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
boolean insetsPending, Rect outFrame, Rect outContentInsets,
- Rect outVisibleInsets, Surface outSurface) {
+ Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
return relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, insetsPending,
- outFrame, outContentInsets, outVisibleInsets, outSurface);
+ outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
}
public void setTransparentRegion(IWindow window, Region region) {
@@ -7047,6 +7062,12 @@
// Is this window now (or just being) removed?
boolean mRemoved;
+ // For debugging, this is the last information given to the surface flinger.
+ boolean mSurfaceShown;
+ int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+ int mSurfaceLayer;
+ float mSurfaceAlpha;
+
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -7337,6 +7358,13 @@
if (w <= 0) w = 1;
if (h <= 0) h = 1;
+ mSurfaceShown = false;
+ mSurfaceLayer = 0;
+ mSurfaceAlpha = 1;
+ mSurfaceX = 0;
+ mSurfaceY = 0;
+ mSurfaceW = w;
+ mSurfaceH = h;
try {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
@@ -7372,9 +7400,12 @@
Surface.openTransaction();
try {
try {
- mSurface.setPosition(mFrame.left + mXOffset,
- mFrame.top + mYOffset);
+ mSurfaceX = mFrame.left + mXOffset;
+ mSurfaceY = mFrame.top + mYOffset;
+ mSurface.setPosition(mSurfaceX, mSurfaceY);
+ mSurfaceLayer = mAnimLayer;
mSurface.setLayer(mAnimLayer);
+ mSurfaceShown = false;
mSurface.hide();
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null);
@@ -7457,6 +7488,7 @@
+ ": " + e.toString());
}
+ mSurfaceShown = false;
mSurface = null;
}
}
@@ -7716,6 +7748,7 @@
mDestroySurface.add(this);
mDestroying = true;
if (SHOW_TRANSACTIONS) logSurface(this, "HIDE (finishExit)", null);
+ mSurfaceShown = false;
try {
mSurface.hide();
} catch (RuntimeException e) {
@@ -8181,6 +8214,13 @@
pw.print(" mLastLayer="); pw.println(mLastLayer);
if (mSurface != null) {
pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+ pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
+ pw.print(" layer="); pw.print(mSurfaceLayer);
+ pw.print(" alpha="); pw.print(mSurfaceAlpha);
+ pw.print(" rect=("); pw.print(mSurfaceX);
+ pw.print(","); pw.print(mSurfaceY);
+ pw.print(") "); pw.print(mSurfaceW);
+ pw.print(" x "); pw.println(mSurfaceH);
}
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
@@ -9945,6 +9985,8 @@
}
}
+ int adjResult = 0;
+
if (!animating && mAppTransitionRunning) {
// We have finished the animation of an app transition. To do
// this, we have delayed a lot of operations like showing and
@@ -9958,6 +10000,7 @@
rebuildAppWindowListLocked();
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ adjResult |= ADJUST_WALLPAPER_LAYERS_CHANGED;
moveInputMethodWindowsIfNeededLocked(false);
wallpaperMayChange = true;
// Since the window list has been rebuilt, focus might
@@ -9966,8 +10009,6 @@
mFocusMayChange = true;
}
- int adjResult = 0;
-
if (wallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
// At this point, there was a window with a wallpaper that
// was force hiding other windows behind it, but now it
@@ -9992,7 +10033,7 @@
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
}
}
- adjResult = adjustWallpaperWindowsLocked();
+ adjResult |= adjustWallpaperWindowsLocked();
wallpaperMayChange = false;
wallpaperForceHidingChanged = false;
if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
@@ -10024,7 +10065,7 @@
if (wallpaperMayChange) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper may change! Adjusting");
- adjResult = adjustWallpaperWindowsLocked();
+ adjResult |= adjustWallpaperWindowsLocked();
}
if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
@@ -10112,6 +10153,8 @@
if (SHOW_TRANSACTIONS) logSurface(w,
"POS " + w.mShownFrame.left
+ ", " + w.mShownFrame.top, null);
+ w.mSurfaceX = w.mShownFrame.left;
+ w.mSurfaceY = w.mShownFrame.top;
w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface in " + w, e);
@@ -10137,7 +10180,11 @@
+ w.mShownFrame.width() + "x"
+ w.mShownFrame.height(), null);
w.mSurfaceResized = true;
+ w.mSurfaceW = width;
+ w.mSurfaceH = height;
w.mSurface.setSize(width, height);
+ w.mSurfaceX = w.mShownFrame.left;
+ w.mSurfaceY = w.mShownFrame.top;
w.mSurface.setPosition(w.mShownFrame.left,
w.mShownFrame.top);
} catch (RuntimeException e) {
@@ -10163,6 +10210,10 @@
w.mConfiguration != mCurConfiguration
&& (w.mConfiguration == null
|| mCurConfiguration.diff(w.mConfiguration) != 0);
+ if (DEBUG_CONFIGURATION && configChanged) {
+ Slog.v(TAG, "Win " + w + " config changed: "
+ + mCurConfiguration);
+ }
if (localLOGV) Slog.v(TAG, "Resizing " + w
+ ": configChanged=" + configChanged
+ " last=" + w.mLastFrame + " frame=" + w.mFrame);
@@ -10226,6 +10277,7 @@
if (SHOW_TRANSACTIONS) logSurface(w,
"HIDE (performLayout)", null);
if (w.mSurface != null) {
+ w.mSurfaceShown = false;
try {
w.mSurface.hide();
} catch (RuntimeException e) {
@@ -10271,7 +10323,9 @@
+ "," + (w.mDtDy*w.mVScale) + "]", null);
if (w.mSurface != null) {
try {
+ w.mSurfaceAlpha = w.mShownAlpha;
w.mSurface.setAlpha(w.mShownAlpha);
+ w.mSurfaceLayer = w.mAnimLayer;
w.mSurface.setLayer(w.mAnimLayer);
w.mSurface.setMatrix(
w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
@@ -10408,14 +10462,13 @@
mDimAnimator = new DimAnimator(mFxSession);
}
mDimAnimator.show(dw, dh);
+ mDimAnimator.updateParameters(w, currentTime);
}
- mDimAnimator.updateParameters(w, currentTime);
}
if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
if (!blurring) {
//Slog.i(TAG, "BLUR BEHIND: " + w);
blurring = true;
- mBlurShown = true;
if (mBlurSurface == null) {
if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR "
+ mBlurSurface + ": CREATE");
@@ -10429,20 +10482,25 @@
Slog.e(TAG, "Exception creating Blur surface", e);
}
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR "
- + mBlurSurface + ": SHOW pos=(0,0) (" +
- dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
if (mBlurSurface != null) {
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR "
+ + mBlurSurface + ": pos=(0,0) (" +
+ dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
mBlurSurface.setPosition(0, 0);
mBlurSurface.setSize(dw, dh);
- try {
- mBlurSurface.show();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure showing blur surface", e);
+ mBlurSurface.setLayer(w.mAnimLayer-2);
+ if (!mBlurShown) {
+ try {
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, " BLUR "
+ + mBlurSurface + ": SHOW");
+ mBlurSurface.show();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure showing blur surface", e);
+ }
+ mBlurShown = true;
}
}
}
- mBlurSurface.setLayer(w.mAnimLayer-2);
}
}
}
@@ -10507,16 +10565,19 @@
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
"Reporting new frame to " + win + ": " + win.mFrame);
+ int diff = 0;
boolean configChanged =
win.mConfiguration != mCurConfiguration
&& (win.mConfiguration == null
- || mCurConfiguration.diff(win.mConfiguration) != 0);
- win.mConfiguration = mCurConfiguration;
- if ((DEBUG_RESIZE || DEBUG_ORIENTATION) && configChanged) {
+ || (diff=mCurConfiguration.diff(win.mConfiguration)) != 0);
+ if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
+ && configChanged) {
Slog.i(TAG, "Sending new config to window " + win + ": "
+ win.mFrame.width() + "x" + win.mFrame.height()
- + " / " + win.mConfiguration);
+ + " / " + mCurConfiguration + " / 0x"
+ + Integer.toHexString(diff));
}
+ win.mConfiguration = mCurConfiguration;
win.mClient.resized(win.mFrame.width(),
win.mFrame.height(), win.mLastContentInsets,
win.mLastVisibleInsets, win.mDrawPending,
@@ -10591,6 +10652,7 @@
mAppTransitionRunning = false;
needRelayout = true;
rebuildAppWindowListLocked();
+ assignLayersLocked();
// Clear information about apps that were moving.
mToBottomApps.clear();
}
@@ -10655,6 +10717,7 @@
boolean showSurfaceRobustlyLocked(WindowState win) {
try {
if (win.mSurface != null) {
+ win.mSurfaceShown = true;
win.mSurface.show();
if (win.mTurnOnScreen) {
if (DEBUG_VISIBILITY) Slog.v(TAG,
@@ -10701,6 +10764,7 @@
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
ws.mSurface.destroy();
+ ws.mSurfaceShown = false;
ws.mSurface = null;
mForceRemoves.add(ws);
i--;
@@ -10711,6 +10775,7 @@
+ ws + " surface=" + ws.mSurface
+ " token=" + win.mAppToken);
ws.mSurface.destroy();
+ ws.mSurfaceShown = false;
ws.mSurface = null;
leakedSurface = true;
}
@@ -10733,7 +10798,7 @@
pids[i] = pidCandidates.keyAt(i);
}
try {
- if (mActivityManager.killPidsForMemory(pids)) {
+ if (mActivityManager.killPids(pids, "Free memory")) {
killedApps = true;
}
} catch (RemoteException e) {
@@ -10747,6 +10812,7 @@
Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
if (surface != null) {
surface.destroy();
+ win.mSurfaceShown = false;
win.mSurface = null;
}
@@ -11223,15 +11289,17 @@
* Show the dim surface.
*/
void show(int dw, int dh) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
- dw + "x" + dh + ")");
- mDimShown = true;
- try {
- mDimSurface.setPosition(0, 0);
- mDimSurface.setSize(dw, dh);
- mDimSurface.show();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure showing dim surface", e);
+ if (!mDimShown) {
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
+ dw + "x" + dh + ")");
+ mDimShown = true;
+ try {
+ mDimSurface.setPosition(0, 0);
+ mDimSurface.setSize(dw, dh);
+ mDimSurface.show();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure showing dim surface", e);
+ }
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a437d95..cd2d3e3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -122,6 +122,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor {
static final String TAG = "ActivityManager";
@@ -781,6 +782,12 @@
int mConfigurationSeq = 0;
/**
+ * Set when we know we are going to be calling updateConfiguration()
+ * soon, so want to skip intermediate config checks.
+ */
+ boolean mConfigWillChange;
+
+ /**
* Hardware-reported OpenGLES version.
*/
final int GL_ES_VERSION;
@@ -3612,7 +3619,7 @@
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult) {
+ boolean debug, WaitResult outResult, Configuration config) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3661,11 +3668,26 @@
} else {
callingPid = callingUid = -1;
}
+
+ mConfigWillChange = config != null && mConfiguration.diff(config) != 0;
+
final long origId = Binder.clearCallingIdentity();
+
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
onlyIfNeeded, componentSpecified);
+
+ if (config != null && mConfigWillChange) {
+ // If the caller also wants to switch to a new configuration,
+ // do so now. This allows a clean switch, as we are waiting
+ // for the current activity to pause (so we will not destroy
+ // it), and have not yet started the next activity.
+ enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "updateConfiguration()");
+ updateConfigurationLocked(config, null);
+ }
+
Binder.restoreCallingIdentity(origId);
if (outResult != null) {
@@ -3707,8 +3729,9 @@
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions,
- grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, null);
+ return startActivityMayWait(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, resultTo, resultWho,
+ requestCode, onlyIfNeeded, debug, null, null);
}
public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -3717,11 +3740,22 @@
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions,
- grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, res);
+ startActivityMayWait(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, resultTo, resultWho,
+ requestCode, onlyIfNeeded, debug, res, null);
return res;
}
+ public final int startActivityWithConfig(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, Configuration config) {
+ return startActivityMayWait(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, resultTo, resultWho,
+ requestCode, onlyIfNeeded, debug, null, config);
+ }
+
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -8359,11 +8393,11 @@
}
}
- public boolean killPidsForMemory(int[] pids) {
+ public boolean killPids(int[] pids, String pReason) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("killPidsForMemory only available to the system");
+ throw new SecurityException("killPids only available to the system");
}
-
+ String reason = (pReason == null) ? "Unknown" : pReason;
// XXX Note: don't acquire main activity lock here, because the window
// manager calls in with its locks held.
@@ -8387,7 +8421,7 @@
if (worstType < EMPTY_APP_ADJ && worstType > HIDDEN_APP_MIN_ADJ) {
worstType = HIDDEN_APP_MIN_ADJ;
}
- Slog.w(TAG, "Killing processes for memory at adjustment " + worstType);
+ Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
for (int i=0; i<pids.length; i++) {
ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
if (proc == null) {
@@ -8395,10 +8429,10 @@
}
int adj = proc.setAdj;
if (adj >= worstType) {
- Slog.w(TAG, "Killing for memory: " + proc + " (adj "
+ Slog.w(TAG, "Killing " + reason + " : " + proc + " (adj "
+ adj + ")");
- EventLog.writeEvent(EventLogTags.AM_KILL_FOR_MEMORY, proc.pid,
- proc.processName, adj);
+ EventLog.writeEvent(EventLogTags.AM_KILL, proc.pid,
+ proc.processName, adj, reason);
killed = true;
Process.killProcess(pids[i]);
}
@@ -9331,6 +9365,32 @@
return runList;
}
+ public List<ApplicationInfo> getRunningExternalApplications() {
+ List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses();
+ List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>();
+ if (runningApps != null && runningApps.size() > 0) {
+ Set<String> extList = new HashSet<String>();
+ for (ActivityManager.RunningAppProcessInfo app : runningApps) {
+ if (app.pkgList != null) {
+ for (String pkg : app.pkgList) {
+ extList.add(pkg);
+ }
+ }
+ }
+ IPackageManager pm = ActivityThread.getPackageManager();
+ for (String pkg : extList) {
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
+ if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ retList.add(info);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ return retList;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (checkCallingPermission(android.Manifest.permission.DUMP)
@@ -9356,7 +9416,7 @@
dumpAll = true;
} else if ("-h".equals(opt)) {
pw.println("Activity manager dump options:");
- pw.println(" [-a] [h- [cmd] ...");
+ pw.println(" [-a] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
pw.println(" activities: activity stack state");
pw.println(" broadcasts: broadcast state");
@@ -9656,6 +9716,7 @@
pw.println(" ");
pw.println(" mHomeProcess: " + mHomeProcess);
pw.println(" mConfiguration: " + mConfiguration);
+ pw.println(" mConfigWillChange: " + mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -9756,7 +9817,7 @@
pw.println(" ");
pw.println("Receiver Resolver Table:");
- mReceiverResolver.dump(pw, " ");
+ mReceiverResolver.dump(pw, null, " ", null);
needSep = true;
}
@@ -13403,6 +13464,12 @@
*/
private final boolean ensureActivityConfigurationLocked(HistoryRecord r,
int globalChanges) {
+ if (!mConfigWillChange) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Skipping config check (will change): " + r);
+ return true;
+ }
+
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Ensuring correct configuration: " + r);
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index cdf4e95..33bbc13 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -158,9 +158,7 @@
public void noteInputEvent() {
enforceCallingPermission();
- synchronized (mStats) {
- mStats.noteInputEventLocked();
- }
+ mStats.noteInputEventAtomic();
}
public void noteUserActivity(int uid, int event) {
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 0ddcc247..aadd37d 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -58,7 +58,7 @@
# The activity's onResume has been called.
30022 am_on_resume_called (Component Name|3)
# Kill a process to reclaim memory.
-30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5)
+30023 am_kill (PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
# Discard an undelivered serialized broadcast (timeout/ANR/crash)
30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index d170b02..1b9e1c7 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -383,9 +383,13 @@
File backupFile = null;
if (mFile != null && mFile.exists()) {
backupFile = new File(mFile.getPath() + ".bak");
- if (!mFile.renameTo(backupFile)) {
- Slog.w(TAG, "Failed to persist new stats");
- return;
+ if (!backupFile.exists()) {
+ if (!mFile.renameTo(backupFile)) {
+ Slog.w(TAG, "Failed to persist new stats");
+ return;
+ }
+ } else {
+ mFile.delete();
}
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index ccb5fc6..f335287 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -31,6 +31,7 @@
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
+import android.net.NetworkUtils;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Environment;
@@ -80,20 +81,22 @@
private BroadcastReceiver mStateReceiver;
- private static final String USB_NEAR_IFACE_ADDR = "169.254.2.1";
+ private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
+ private static final String USB_NETMASK = "255.255.255.0";
+
+ // FYI - the default wifi is 192.168.43.1 and 255.255.255.0
private String[] mDhcpRange;
- private static final String DHCP_DEFAULT_RANGE_START = "169.254.2.10";
- private static final String DHCP_DEFAULT_RANGE_STOP = "169.254.2.64";
+ private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
+ private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254";
+ private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
+ private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254";
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
private boolean mDunRequired; // configuration info - must use DUN apn on 3g
- private boolean mUseHiPri;
-
- private String mUpstreamIfaceName;
private HierarchicalStateMachine mTetherMasterSM;
@@ -141,15 +144,12 @@
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
- if (mDhcpRange.length == 0) {
- mDhcpRange = new String[2];
- mDhcpRange[0] = DHCP_DEFAULT_RANGE_START;
- mDhcpRange[1] = DHCP_DEFAULT_RANGE_STOP;
- } else if(mDhcpRange.length == 1) {
- String[] tmp = new String[2];
- tmp[0] = mDhcpRange[0];
- tmp[1] = new String("");
- mDhcpRange = tmp;
+ if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
+ mDhcpRange = new String[4];
+ mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
+ mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
+ mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
+ mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
}
mDunRequired = context.getResources().getBoolean(
com.android.internal.R.bool.config_tether_dun_required);
@@ -189,7 +189,7 @@
}
} else {
if (sm != null) {
- sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN));
+ sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
mIfaces.remove(iface);
}
}
@@ -247,7 +247,7 @@
Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
- sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN));
+ sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
mIfaces.remove(iface);
}
}
@@ -266,7 +266,7 @@
Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
- sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED));
+ sm.sendMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED);
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
@@ -284,7 +284,7 @@
Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
- sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED));
+ sm.sendMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED);
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
@@ -367,6 +367,13 @@
return;
}
+ if (mTetheredNotification != null) {
+ if (mTetheredNotification.icon == icon) {
+ return;
+ }
+ notificationManager.cancel(mTetheredNotification.icon);
+ }
+
Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
@@ -423,20 +430,8 @@
mUsbMassStorageOff = true;
updateUsbStatus();
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- try {
- int netType = (mUseHiPri ? ConnectivityManager.TYPE_MOBILE_HIPRI:
- ConnectivityManager.TYPE_MOBILE_DUN);
- NetworkInfo info = service.getNetworkInfo(netType);
- int msg;
- if (info != null && info.isConnected() == true) {
- msg = TetherMasterSM.CMD_CELL_DUN_ENABLED;
- } else {
- msg = TetherMasterSM.CMD_CELL_DUN_DISABLED;
- }
- mTetherMasterSM.sendMessage(mTetherMasterSM.obtainMessage(msg));
- } catch (RemoteException e) {}
+ Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBooted = true;
updateUsbStatus();
@@ -512,8 +507,16 @@
try {
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
- ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
- ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
+ String[] addr = USB_NEAR_IFACE_ADDR.split("\\.");
+ ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
+ (Integer.parseInt(addr[1]) << 16) +
+ (Integer.parseInt(addr[2]) << 8) +
+ (Integer.parseInt(addr[3]));
+ addr = USB_NETMASK.split("\\.");
+ ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
+ (Integer.parseInt(addr[1]) << 16) +
+ (Integer.parseInt(addr[2]) << 8) +
+ (Integer.parseInt(addr[3]));
if (enabled) {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {
@@ -605,32 +608,30 @@
class TetherInterfaceSM extends HierarchicalStateMachine {
- // notification from the master SM that it's in tether mode
- static final int CMD_TETHER_MODE_ALIVE = 1;
// notification from the master SM that it's not in tether mode
- static final int CMD_TETHER_MODE_DEAD = 2;
+ static final int CMD_TETHER_MODE_DEAD = 1;
// request from the user that it wants to tether
- static final int CMD_TETHER_REQUESTED = 3;
+ static final int CMD_TETHER_REQUESTED = 2;
// request from the user that it wants to untether
- static final int CMD_TETHER_UNREQUESTED = 4;
+ static final int CMD_TETHER_UNREQUESTED = 3;
// notification that this interface is down
- static final int CMD_INTERFACE_DOWN = 5;
+ static final int CMD_INTERFACE_DOWN = 4;
// notification that this interface is up
- static final int CMD_INTERFACE_UP = 6;
+ static final int CMD_INTERFACE_UP = 5;
// notification from the master SM that it had an error turning on cellular dun
- static final int CMD_CELL_DUN_ERROR = 10;
+ static final int CMD_CELL_DUN_ERROR = 6;
// notification from the master SM that it had trouble enabling IP Forwarding
- static final int CMD_IP_FORWARDING_ENABLE_ERROR = 11;
+ static final int CMD_IP_FORWARDING_ENABLE_ERROR = 7;
// notification from the master SM that it had trouble disabling IP Forwarding
- static final int CMD_IP_FORWARDING_DISABLE_ERROR = 12;
+ static final int CMD_IP_FORWARDING_DISABLE_ERROR = 8;
// notification from the master SM that it had trouble staring tethering
- static final int CMD_START_TETHERING_ERROR = 13;
+ static final int CMD_START_TETHERING_ERROR = 9;
// notification from the master SM that it had trouble stopping tethering
- static final int CMD_STOP_TETHERING_ERROR = 14;
+ static final int CMD_STOP_TETHERING_ERROR = 10;
// notification from the master SM that it had trouble setting the DNS forwarders
- static final int CMD_SET_DNS_FORWARDERS_ERROR = 15;
- // a mechanism to transition self to another state from an enter function
- static final int CMD_TRANSITION_TO_STATE = 16;
+ static final int CMD_SET_DNS_FORWARDERS_ERROR = 11;
+ // the upstream connection has changed
+ static final int CMD_TETHER_CONNECTION_CHANGED = 12;
private HierarchicalState mDefaultState;
@@ -645,6 +646,8 @@
int mLastError;
String mIfaceName;
+ String mMyUpstreamIfaceName; // may change over time
+
boolean mUsb;
TetherInterfaceSM(String name, Looper looper, boolean usb) {
@@ -733,10 +736,8 @@
switch (message.what) {
case CMD_TETHER_REQUESTED:
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
- Message m = mTetherMasterSM.obtainMessage(
- TetherMasterSM.CMD_TETHER_MODE_REQUESTED);
- m.obj = TetherInterfaceSM.this;
- mTetherMasterSM.sendMessage(m);
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED,
+ TetherInterfaceSM.this);
transitionTo(mStartingState);
break;
case CMD_INTERFACE_DOWN:
@@ -756,20 +757,18 @@
setAvailable(false);
if (mUsb) {
if (!Tethering.this.configureUsbIface(true)) {
- Message m = mTetherMasterSM.obtainMessage(
- TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
- m.obj = TetherInterfaceSM.this;
- mTetherMasterSM.sendMessage(m);
-
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
+ TetherInterfaceSM.this);
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
- m = obtainMessage(CMD_TRANSITION_TO_STATE);
- m.obj = mInitialState;
- sendMessageAtFrontOfQueue(m);
+ transitionTo(mInitialState);
return;
}
}
sendTetherStateChangedBroadcast();
+
+ // Skipping StartingState
+ transitionTo(mTetheredState);
}
@Override
public boolean processMessage(Message message) {
@@ -778,10 +777,8 @@
switch (message.what) {
// maybe a parent class?
case CMD_TETHER_UNREQUESTED:
- Message m = mTetherMasterSM.obtainMessage(
- TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
- m.obj = TetherInterfaceSM.this;
- mTetherMasterSM.sendMessage(m);
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
+ TetherInterfaceSM.this);
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
setLastErrorAndTransitionToInitialState(
@@ -791,9 +788,6 @@
}
transitionTo(mInitialState);
break;
- case CMD_TETHER_MODE_ALIVE:
- transitionTo(mTetheredState);
- break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -804,16 +798,10 @@
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
case CMD_INTERFACE_DOWN:
- m = mTetherMasterSM.obtainMessage(
- TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
- m.obj = TetherInterfaceSM.this;
- mTetherMasterSM.sendMessage(m);
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
+ TetherInterfaceSM.this);
transitionTo(mUnavailableState);
break;
- case CMD_TRANSITION_TO_STATE:
- HierarchicalState s = (HierarchicalState)(message.obj);
- transitionTo(s);
- break;
default:
retValue = false;
}
@@ -832,23 +820,7 @@
} catch (Exception e) {
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
- Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
- m.obj = mInitialState;
- sendMessageAtFrontOfQueue(m);
- return;
- }
- try {
- service.enableNat(mIfaceName, mUpstreamIfaceName);
- } catch (Exception e) {
- try {
- service.untetherInterface(mIfaceName);
- } catch (Exception ee) {}
-
- setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
-
- Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
- m.obj = mInitialState;
- sendMessageAtFrontOfQueue(m);
+ transitionTo(mInitialState);
return;
}
if (mUsb) Tethering.this.enableUsbRndis(true);
@@ -872,16 +844,19 @@
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
- try {
- service.disableNat(mIfaceName, mUpstreamIfaceName);
- } catch (Exception e) {
+ if (mMyUpstreamIfaceName != null) {
try {
- service.untetherInterface(mIfaceName);
- } catch (Exception ee) {}
+ service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mMyUpstreamIfaceName = null;
+ } catch (Exception e) {
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
- break;
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
+ break;
+ }
}
try {
service.untetherInterface(mIfaceName);
@@ -890,10 +865,8 @@
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
- Message m = mTetherMasterSM.obtainMessage(
- TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
- m.obj = TetherInterfaceSM.this;
- mTetherMasterSM.sendMessage(m);
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED,
+ TetherInterfaceSM.this);
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
@@ -907,6 +880,40 @@
}
Log.d(TAG, "Untethered " + mIfaceName);
break;
+ case CMD_TETHER_CONNECTION_CHANGED:
+ String newUpstreamIfaceName = (String)(message.obj);
+ b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ service = INetworkManagementService.Stub.asInterface(b);
+
+ if (mMyUpstreamIfaceName != null) {
+ try {
+ service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mMyUpstreamIfaceName = null;
+ } catch (Exception e) {
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
+
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
+ break;
+ }
+ }
+ if (newUpstreamIfaceName != null) {
+ try {
+ service.enableNat(mIfaceName, newUpstreamIfaceName);
+ } catch (Exception e) {
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
+
+ setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
+ transitionTo(mInitialState);
+ return true;
+ }
+ }
+ mMyUpstreamIfaceName = newUpstreamIfaceName;
+ break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -918,16 +925,19 @@
case CMD_TETHER_MODE_DEAD:
b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
service = INetworkManagementService.Stub.asInterface(b);
- try {
- service.disableNat(mIfaceName, mUpstreamIfaceName);
- } catch (Exception e) {
+ if (mMyUpstreamIfaceName != null) {
try {
- service.untetherInterface(mIfaceName);
- } catch (Exception ee) {}
+ service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mMyUpstreamIfaceName = null;
+ } catch (Exception e) {
+ try {
+ service.untetherInterface(mIfaceName);
+ } catch (Exception ee) {}
- setLastErrorAndTransitionToInitialState(
- ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
- break;
+ setLastErrorAndTransitionToInitialState(
+ ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
+ break;
+ }
}
try {
service.untetherInterface(mIfaceName);
@@ -950,10 +960,6 @@
}
transitionTo(mInitialState);
break;
- case CMD_TRANSITION_TO_STATE:
- HierarchicalState s = (HierarchicalState)(message.obj);
- transitionTo(s);
- break;
default:
retValue = false;
break;
@@ -997,14 +1003,12 @@
static final int CMD_TETHER_MODE_REQUESTED = 1;
// an interface SM has unrequested Tethering
static final int CMD_TETHER_MODE_UNREQUESTED = 2;
+ // upstream connection change - do the right thing
+ static final int CMD_UPSTREAM_CHANGED = 3;
// we received notice that the cellular DUN connection is up
- static final int CMD_CELL_DUN_ENABLED = 3;
- // we received notice that the cellular DUN connection is down
- static final int CMD_CELL_DUN_DISABLED = 4;
- // we timed out on a cellular DUN toggle
- static final int CMD_CELL_DUN_TIMEOUT = 5;
- // it's time to renew our cellular DUN reservation
- static final int CMD_CELL_DUN_RENEW = 6;
+ static final int CMD_CELL_CONNECTION_RENEW = 4;
+ // we don't have a valid upstream conn, check again after a delay
+ static final int CMD_RETRY_UPSTREAM = 5;
// This indicates what a timeout event relates to. A state that
// sends itself a delayed timeout event and handles incoming timeout events
@@ -1013,11 +1017,8 @@
private int mSequenceNumber;
private HierarchicalState mInitialState;
- private HierarchicalState mCellDunRequestedState;
- private HierarchicalState mCellDunAliveState;
private HierarchicalState mTetherModeAliveState;
- private HierarchicalState mCellDunErrorState;
private HierarchicalState mSetIpForwardingEnabledErrorState;
private HierarchicalState mSetIpForwardingDisabledErrorState;
private HierarchicalState mStartTetheringErrorState;
@@ -1026,10 +1027,12 @@
private ArrayList mNotifyList;
+ private boolean mConnectionRequested = false;
- private static final int CELL_DUN_TIMEOUT_MS = 45000;
- private static final int CELL_DISABLE_DUN_TIMEOUT_MS = 3000;
- private static final int CELL_DUN_RENEW_MS = 40000;
+ private String mUpstreamIfaceName = null;
+
+ private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
+ private static final int CELL_CONNECTION_RENEW_MS = 40000;
TetherMasterSM(String name, Looper looper) {
super(name, looper);
@@ -1037,15 +1040,9 @@
//Add states
mInitialState = new InitialState();
addState(mInitialState);
- mCellDunRequestedState = new CellDunRequestedState();
- addState(mCellDunRequestedState);
- mCellDunAliveState = new CellDunAliveState();
- addState(mCellDunAliveState);
mTetherModeAliveState = new TetherModeAliveState();
addState(mTetherModeAliveState);
- mCellDunErrorState = new CellDunErrorState();
- addState(mCellDunErrorState);
mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState();
addState(mSetIpForwardingEnabledErrorState);
mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState();
@@ -1058,40 +1055,58 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList();
-
setInitialState(mInitialState);
}
class TetherMasterUtilState extends HierarchicalState {
+ protected final static boolean TRY_TO_SETUP_MOBILE_CONNECTION = true;
+ protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE = false;
+
@Override
public boolean processMessage(Message m) {
return false;
}
- public int turnOnMobileDun() {
+ protected int turnOnMobileConnection() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
int retValue = Phone.APN_REQUEST_FAILED;
try {
retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- (mUseHiPri ? Phone.FEATURE_ENABLE_HIPRI : Phone.FEATURE_ENABLE_DUN),
+ (mDunRequired ? Phone.FEATURE_ENABLE_DUN : Phone.FEATURE_ENABLE_HIPRI),
new Binder());
} catch (Exception e) {
}
+ switch (retValue) {
+ case Phone.APN_ALREADY_ACTIVE:
+ case Phone.APN_REQUEST_STARTED:
+ sendMessageDelayed(CMD_CELL_CONNECTION_RENEW, CELL_CONNECTION_RENEW_MS);
+ mConnectionRequested = true;
+ break;
+ case Phone.APN_REQUEST_FAILED:
+ default:
+ mConnectionRequested = false;
+ break;
+ }
+
return retValue;
}
- public boolean turnOffMobileDun() {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service =
- IConnectivityManager.Stub.asInterface(b);
- try {
- service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- (mUseHiPri ? Phone.FEATURE_ENABLE_HIPRI : Phone.FEATURE_ENABLE_DUN));
- } catch (Exception e) {
- return false;
+ protected boolean turnOffMobileConnection() {
+ if (mConnectionRequested) {
+ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service =
+ IConnectivityManager.Stub.asInterface(b);
+ try {
+ service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ (mDunRequired? Phone.FEATURE_ENABLE_DUN :
+ Phone.FEATURE_ENABLE_HIPRI));
+ } catch (Exception e) {
+ return false;
+ }
+ mConnectionRequested = false;
}
return true;
}
- public boolean turnOnMasterTetherSettings() {
+ protected boolean turnOnMasterTetherSettings() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
@@ -1102,7 +1117,7 @@
return false;
}
try {
- service.startTethering(mDhcpRange[0], mDhcpRange[1]);
+ service.startTethering(mDhcpRange);
} catch (Exception e) {
transitionTo(mStartTetheringErrorState);
return false;
@@ -1113,10 +1128,9 @@
transitionTo(mSetDnsForwardersErrorState);
return false;
}
- transitionTo(mTetherModeAliveState);
return true;
}
- public boolean turnOffMasterTetherSettings() {
+ protected boolean turnOffMasterTetherSettings() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
@@ -1135,7 +1149,7 @@
transitionTo(mInitialState);
return true;
}
- public String findActiveUpstreamIface() {
+ protected String findActiveUpstreamIface() {
// check for what iface we can use - if none found switch to error.
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -1147,6 +1161,7 @@
Log.e(TAG, "Error listing Interfaces :" + e);
return null;
}
+
for (String iface : ifaces) {
for (String regex : mUpstreamIfaceRegexs) {
if (iface.matches(regex)) {
@@ -1167,12 +1182,74 @@
}
return null;
}
+ protected void chooseUpstreamType(boolean tryCell) {
+ // decide if the current upstream is good or not and if not
+ // do something about it (start up DUN if required or HiPri if not)
+ String iface = findActiveUpstreamIface();
+ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
+ mConnectionRequested = false;
+ Log.d(TAG, "chooseUpstreamType(" + tryCell + "), dunRequired ="
+ + mDunRequired + ", iface=" + iface);
+ if (iface != null) {
+ try {
+ if (mDunRequired) {
+ // check if Dun is on - we can use that
+ NetworkInfo info = cm.getNetworkInfo(
+ ConnectivityManager.TYPE_MOBILE_DUN);
+ if (info.isConnected()) {
+ Log.d(TAG, "setting dun ifacename =" + iface);
+ // even if we're already connected - it may be somebody else's
+ // refcount, so add our own
+ turnOnMobileConnection();
+ } else {
+ // verify the iface is not the default mobile - can't use that!
+ info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ if (info.isConnected()) {
+ iface = null; // can't accept this one
+ }
+ }
+ } else {
+ Log.d(TAG, "checking if hipri brought us this connection");
+ NetworkInfo info = cm.getNetworkInfo(
+ ConnectivityManager.TYPE_MOBILE_HIPRI);
+ if (info.isConnected()) {
+ Log.d(TAG, "yes - hipri in use");
+ // even if we're already connected - it may be sombody else's
+ // refcount, so add our own
+ turnOnMobileConnection();
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling ConnectivityManager " + e);
+ iface = null;
+ }
+ }
+ // may have been set to null in the if above
+ if (iface == null ) {
+ if (tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) {
+ turnOnMobileConnection();
+ }
+ // wait for things to settle and retry
+ sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
+ }
+ notifyTetheredOfNewUpstreamIface(iface);
+ }
+ protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
+ Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+ mUpstreamIfaceName = ifaceName;
+ for (Object o : mNotifyList) {
+ TetherInterfaceSM sm = (TetherInterfaceSM)o;
+ sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
+ ifaceName);
+ }
+ }
}
class InitialState extends TetherMasterUtilState {
@Override
public void enter() {
- mUseHiPri = false;
+ mConnectionRequested = false;
}
@Override
public boolean processMessage(Message message) {
@@ -1183,7 +1260,7 @@
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
Log.d(TAG, "Tether Mode requested by " + who.toString());
mNotifyList.add(who);
- transitionTo(mCellDunRequestedState);
+ transitionTo(mTetherModeAliveState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
@@ -1193,130 +1270,6 @@
mNotifyList.remove(who);
}
break;
- case CMD_CELL_DUN_ENABLED:
- transitionTo(mCellDunAliveState);
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
- class CellDunRequestedState extends TetherMasterUtilState {
- @Override
- public void enter() {
- mUseHiPri = (findActiveUpstreamIface() == null && !mDunRequired);
- if (mDunRequired || mUseHiPri) {
- ++mSequenceNumber;
- int result = turnOnMobileDun();
- switch (result) {
- case Phone.APN_ALREADY_ACTIVE:
- Log.d(TAG, "Dun already active");
- sendMessage(obtainMessage(CMD_CELL_DUN_ENABLED));
- break;
- case Phone.APN_REQUEST_FAILED:
- case Phone.APN_TYPE_NOT_AVAILABLE:
- Log.d(TAG, "Error bringing up Dun connection");
- Message m = obtainMessage(CMD_CELL_DUN_TIMEOUT);
- m.arg1 = mSequenceNumber;
- sendMessage(m);
- break;
- case Phone.APN_REQUEST_STARTED:
- Log.d(TAG, "Started bringing up Dun connection");
- m = obtainMessage(CMD_CELL_DUN_TIMEOUT);
- m.arg1 = mSequenceNumber;
- sendMessageDelayed(m, CELL_DUN_TIMEOUT_MS);
- break;
- default:
- Log.e(TAG, "Unknown return value from startUsingNetworkFeature " +
- result);
- }
- } else {
- Log.d(TAG, "no Dun Required. Skipping to Active");
- sendMessage(obtainMessage(CMD_CELL_DUN_ENABLED));
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- Log.d(TAG, "CellDunRequestedState.processMessage what=" + message.what);
- boolean retValue = true;
- switch (message.what) {
- case CMD_TETHER_MODE_REQUESTED:
- TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- mNotifyList.add(who);
- break;
- case CMD_TETHER_MODE_UNREQUESTED:
- who = (TetherInterfaceSM)message.obj;
- int index = mNotifyList.indexOf(who);
- if (index != -1) {
- mNotifyList.remove(index);
- if (mNotifyList.isEmpty()) {
- if (mDunRequired || mUseHiPri) {
- turnOffMobileDun();
- }
- transitionTo(mInitialState);
- }
- }
- break;
- case CMD_CELL_DUN_ENABLED:
- turnOnMasterTetherSettings();
- break;
- case CMD_CELL_DUN_DISABLED:
- break;
- case CMD_CELL_DUN_TIMEOUT:
- if (message.arg1 == mSequenceNumber) {
- transitionTo(mCellDunErrorState);
- }
- break;
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
-
- class CellDunAliveState extends TetherMasterUtilState {
- @Override
- public void enter() {
- Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
- sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
- }
-
- @Override
- public boolean processMessage(Message message) {
- Log.d(TAG, "CellDunAliveState.processMessage what=" + message.what);
- boolean retValue = true;
- switch (message.what) {
- case CMD_TETHER_MODE_REQUESTED:
- TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- mNotifyList.add(who);
- turnOnMasterTetherSettings();
- break;
- case CMD_TETHER_MODE_UNREQUESTED:
- who = (TetherInterfaceSM)message.obj;
- int index = mNotifyList.indexOf(who);
- if (index != -1) {
- mNotifyList.remove(index);
- if (mNotifyList.isEmpty()) {
- if (mDunRequired || mUseHiPri) {
- turnOffMobileDun();
- }
- transitionTo(mInitialState);
- }
- }
- break;
- case CMD_CELL_DUN_DISABLED:
- transitionTo(mInitialState);
- break;
- case CMD_CELL_DUN_RENEW:
- Log.d(TAG, "renewing dun connection - requeuing for another " +
- CELL_DUN_RENEW_MS + "ms");
- turnOnMobileDun();
- sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
- break;
default:
retValue = false;
break;
@@ -1326,22 +1279,18 @@
}
class TetherModeAliveState extends TetherMasterUtilState {
+ boolean mTryCell = WAIT_FOR_NETWORK_TO_SETTLE;
@Override
public void enter() {
- if (mDunRequired || mUseHiPri) {
- Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
- sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
- }
- mUpstreamIfaceName = findActiveUpstreamIface();
- if (mUpstreamIfaceName == null) {
- Log.d(TAG, "Erroring our of tether - no upstream ifaces available");
- sendMessage(obtainMessage(CMD_CELL_DUN_DISABLED));
- } else {
- for (Object o : mNotifyList) {
- TetherInterfaceSM sm = (TetherInterfaceSM)o;
- sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
- }
- }
+ mTryCell = WAIT_FOR_NETWORK_TO_SETTLE; // first pass lets just see what we have.
+ chooseUpstreamType(mTryCell);
+ mTryCell = !mTryCell;
+ turnOnMasterTetherSettings(); // may transition us out
+ }
+ @Override
+ public void exit() {
+ turnOffMobileConnection();
+ notifyTetheredOfNewUpstreamIface(null);
}
@Override
public boolean processMessage(Message message) {
@@ -1351,7 +1300,8 @@
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
mNotifyList.add(who);
- who.sendMessage(who.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
+ who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
+ mUpstreamIfaceName);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
@@ -1359,32 +1309,29 @@
if (index != -1) {
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
- if (mDunRequired || mUseHiPri) {
- turnOffMobileDun();
- }
turnOffMasterTetherSettings(); // transitions appropriately
- mUpstreamIfaceName = null;
}
}
break;
- case CMD_CELL_DUN_DISABLED:
- int size = mNotifyList.size();
- for (int i = 0; i < size; i++) {
- TetherInterfaceSM sm = (TetherInterfaceSM)mNotifyList.get(i);
- mNotifyList.remove(i);
- sm.sendMessage(sm.obtainMessage(
- TetherInterfaceSM.CMD_TETHER_MODE_DEAD));
+ case CMD_UPSTREAM_CHANGED:
+ mTryCell = WAIT_FOR_NETWORK_TO_SETTLE;
+ chooseUpstreamType(mTryCell);
+ mTryCell = !mTryCell;
+ break;
+ case CMD_CELL_CONNECTION_RENEW:
+ // make sure we're still using a requested connection - may have found
+ // wifi or something since then.
+ if (mConnectionRequested) {
+ Log.d(TAG, "renewing mobile connection - requeuing for another " +
+ CELL_CONNECTION_RENEW_MS + "ms");
+ turnOnMobileConnection();
}
- turnOffMasterTetherSettings(); // transitions appropriately
- mUpstreamIfaceName = null;
break;
- case CMD_CELL_DUN_RENEW:
- Log.d(TAG, "renewing dun connection - requeuing for another " +
- CELL_DUN_RENEW_MS + "ms");
- turnOnMobileDun();
- sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
- break;
- default:
+ case CMD_RETRY_UPSTREAM:
+ chooseUpstreamType(mTryCell);
+ mTryCell = !mTryCell;
+ break;
+ default:
retValue = false;
break;
}
@@ -1400,7 +1347,7 @@
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- who.sendMessage(who.obtainMessage(mErrorNotification));
+ who.sendMessage(mErrorNotification);
break;
default:
retValue = false;
@@ -1411,19 +1358,11 @@
mErrorNotification = msgType;
for (Object o : mNotifyList) {
TetherInterfaceSM sm = (TetherInterfaceSM)o;
- sm.sendMessage(sm.obtainMessage(msgType));
+ sm.sendMessage(msgType);
}
}
}
- class CellDunErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error trying to enable Cell DUN");
- notify(TetherInterfaceSM.CMD_CELL_DUN_ERROR);
- }
- }
-
class SetIpForwardingEnabledErrorState extends ErrorState {
@Override
public void enter() {
diff --git a/location/java/com/android/internal/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
similarity index 98%
rename from location/java/com/android/internal/location/GeocoderProxy.java
rename to services/java/com/android/server/location/GeocoderProxy.java
index b06297b..3c05da2 100644
--- a/location/java/com/android/internal/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
import android.content.ComponentName;
import android.content.Context;
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
similarity index 81%
rename from location/java/com/android/internal/location/GpsLocationProvider.java
rename to services/java/com/android/server/location/GpsLocationProvider.java
index 44a5a6b..daa198f 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -30,13 +30,16 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -59,6 +62,7 @@
import java.util.Date;
import java.util.Properties;
import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
/**
* A GPS implementation of LocationProvider used by LocationManager.
@@ -71,37 +75,6 @@
private static final boolean DEBUG = false;
private static final boolean VERBOSE = false;
-
- /**
- * Broadcast intent action indicating that the GPS has either been
- * enabled or disabled. An intent extra provides this state as a boolean,
- * where {@code true} means enabled.
- * @see #EXTRA_ENABLED
- *
- * {@hide}
- */
- public static final String GPS_ENABLED_CHANGE_ACTION =
- "android.location.GPS_ENABLED_CHANGE";
-
- /**
- * Broadcast intent action indicating that the GPS has either started or
- * stopped receiving GPS fixes. An intent extra provides this state as a
- * boolean, where {@code true} means that the GPS is actively receiving fixes.
- * @see #EXTRA_ENABLED
- *
- * {@hide}
- */
- public static final String GPS_FIX_CHANGE_ACTION =
- "android.location.GPS_FIX_CHANGE";
-
- /**
- * The lookup key for a boolean that indicates whether GPS is enabled or
- * disabled. {@code true} means GPS is enabled. Retrieve it with
- * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
- *
- * {@hide}
- */
- public static final String EXTRA_ENABLED = "enabled";
// these need to match GpsPositionMode enum in gps.h
private static final int GPS_POSITION_MODE_STANDALONE = 0;
@@ -155,6 +128,17 @@
private static final int AGPS_DATA_CONNECTION_OPENING = 1;
private static final int AGPS_DATA_CONNECTION_OPEN = 2;
+ // Handler messages
+ private static final int CHECK_LOCATION = 1;
+ private static final int ENABLE = 2;
+ private static final int ENABLE_TRACKING = 3;
+ private static final int UPDATE_NETWORK_STATE = 4;
+ private static final int INJECT_NTP_TIME = 5;
+ private static final int DOWNLOAD_XTRA_DATA = 6;
+ private static final int UPDATE_LOCATION = 7;
+ private static final int ADD_LISTENER = 8;
+ private static final int REMOVE_LISTENER = 9;
+
private static final String PROPERTIES_FILE = "/etc/gps.conf";
private int mLocationFlags = LOCATION_INVALID;
@@ -175,11 +159,16 @@
private static final int NO_FIX_TIMEOUT = 60;
// true if we are enabled
- private boolean mEnabled;
+ private volatile boolean mEnabled;
// true if we have network connectivity
private boolean mNetworkAvailable;
+ // flags to trigger NTP or XTRA data download when network becomes available
+ // initialized to true so we do NTP and XTRA when the network comes up after booting
+ private boolean mInjectNtpTimePending = true;
+ private boolean mDownloadXtraDataPending = true;
+
// true if GPS is navigating
private boolean mNavigating;
@@ -215,9 +204,15 @@
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
- private GpsEventThread mEventThread;
- private GpsNetworkThread mNetworkThread;
- private Object mNetworkThreadLock = new Object();
+
+ // GpsLocationProvider's handler thread
+ private final Thread mThread;
+ // Handler for processing events in mThread.
+ private Handler mHandler;
+ // Used to signal when our main thread has initialized everything
+ private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
+ // Thread for receiving events from the native code
+ private Thread mEventThread;
private String mAGpsApn;
private int mAGpsDataConnectionState;
@@ -321,7 +316,7 @@
public GpsLocationProvider(Context context, ILocationManager locationManager) {
mContext = context;
mLocationManager = locationManager;
- mNIHandler = new GpsNetInitiatedHandler(context, this);
+ mNIHandler = new GpsNetInitiatedHandler(context);
mLocation.setExtras(mLocationExtras);
@@ -333,11 +328,6 @@
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ALARM_WAKEUP);
- intentFilter.addAction(ALARM_TIMEOUT);
- context.registerReceiver(mBroadcastReciever, intentFilter);
-
mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
// Battery statistics service to be notified when GPS turns on or off
@@ -373,6 +363,26 @@
} catch (IOException e) {
Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
}
+
+ // wait until we are fully initialized before returning
+ mThread = new GpsLocationProviderThread();
+ mThread.start();
+ while (true) {
+ try {
+ mInitializedLatch.await();
+ break;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void initialize() {
+ // register our receiver on our thread rather than the main thread
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ALARM_WAKEUP);
+ intentFilter.addAction(ALARM_TIMEOUT);
+ mContext.registerReceiver(mBroadcastReciever, intentFilter);
}
/**
@@ -391,6 +401,14 @@
}
public void updateNetworkState(int state, NetworkInfo info) {
+ mHandler.removeMessages(UPDATE_NETWORK_STATE);
+ Message m = Message.obtain(mHandler, UPDATE_NETWORK_STATE);
+ m.arg1 = state;
+ m.obj = info;
+ mHandler.sendMessage(m);
+ }
+
+ private void handleUpdateNetworkState(int state, NetworkInfo info) {
mNetworkAvailable = (state == LocationProvider.AVAILABLE);
if (DEBUG) {
@@ -414,10 +432,84 @@
}
}
- if (mNetworkAvailable && mNetworkThread != null && mEnabled) {
- // signal the network thread when the network becomes available
- mNetworkThread.signal();
- }
+ if (mNetworkAvailable) {
+ if (mInjectNtpTimePending) {
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME));
+ }
+ if (mDownloadXtraDataPending) {
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA));
+ }
+ }
+ }
+
+ private void handleInjectNtpTime() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mInjectNtpTimePending = true;
+ return;
+ }
+ mInjectNtpTimePending = false;
+
+ SntpClient client = new SntpClient();
+ long delay;
+
+ if (client.requestTime(mNtpServer, 10000)) {
+ long time = client.getNtpTime();
+ long timeReference = client.getNtpTimeReference();
+ int certainty = (int)(client.getRoundTripTime()/2);
+ long now = System.currentTimeMillis();
+ long systemTimeOffset = time - now;
+
+ Log.d(TAG, "NTP server returned: "
+ + time + " (" + new Date(time)
+ + ") reference: " + timeReference
+ + " certainty: " + certainty
+ + " system time offset: " + systemTimeOffset);
+
+ // sanity check NTP time and do not use if it is too far from system time
+ if (systemTimeOffset < 0) {
+ systemTimeOffset = -systemTimeOffset;
+ }
+ if (systemTimeOffset < MAX_NTP_SYSTEM_TIME_OFFSET) {
+ native_inject_time(time, timeReference, certainty);
+ } else {
+ Log.e(TAG, "NTP time differs from system time by " + systemTimeOffset
+ + "ms. Ignoring.");
+ }
+ delay = NTP_INTERVAL;
+ } else {
+ if (DEBUG) Log.d(TAG, "requestTime failed");
+ delay = RETRY_INTERVAL;
+ }
+
+ // send delayed message for next NTP injection
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ }
+
+ private void handleDownloadXtraData() {
+ if (!mDownloadXtraDataPending) {
+ // try again when network is up
+ mDownloadXtraDataPending = true;
+ return;
+ }
+ mDownloadXtraDataPending = false;
+
+
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) {
+ Log.d(TAG, "calling native_inject_xtra_data");
+ }
+ native_inject_xtra_data(data, data.length);
+ } else {
+ // try again later
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ }
}
/**
@@ -425,6 +517,13 @@
* Someday we might use this for network location injection to aid the GPS
*/
public void updateLocation(Location location) {
+ mHandler.removeMessages(UPDATE_LOCATION);
+ Message m = Message.obtain(mHandler, UPDATE_LOCATION);
+ m.obj = location;
+ mHandler.sendMessage(m);
+ }
+
+ private void handleUpdateLocation(Location location) {
if (location.hasAccuracy()) {
native_inject_location(location.getLatitude(), location.getLongitude(),
location.getAccuracy());
@@ -513,8 +612,17 @@
* must be handled. Hardware may be started up
* when the provider is enabled.
*/
- public synchronized void enable() {
- if (DEBUG) Log.d(TAG, "enable");
+ public void enable() {
+ synchronized (mHandler) {
+ mHandler.removeMessages(ENABLE);
+ Message m = Message.obtain(mHandler, ENABLE);
+ m.arg1 = 1;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleEnable() {
+ if (DEBUG) Log.d(TAG, "handleEnable");
if (mEnabled) return;
mEnabled = native_init();
@@ -529,16 +637,6 @@
// run event listener thread while we are enabled
mEventThread = new GpsEventThread();
mEventThread.start();
-
- if (requiresNetwork()) {
- // run network thread for NTP and XTRA support
- if (mNetworkThread == null) {
- mNetworkThread = new GpsNetworkThread();
- mNetworkThread.start();
- } else {
- mNetworkThread.signal();
- }
- }
} else {
Log.w(TAG, "Failed to enable location provider");
}
@@ -549,8 +647,17 @@
* need not be handled. Hardware may be shut
* down while the provider is disabled.
*/
- public synchronized void disable() {
- if (DEBUG) Log.d(TAG, "disable");
+ public void disable() {
+ synchronized (mHandler) {
+ mHandler.removeMessages(ENABLE);
+ Message m = Message.obtain(mHandler, ENABLE);
+ m.arg1 = 0;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleDisable() {
+ if (DEBUG) Log.d(TAG, "handleDisable");
if (!mEnabled) return;
mEnabled = false;
@@ -567,11 +674,6 @@
mEventThread = null;
}
- if (mNetworkThread != null) {
- mNetworkThread.setDone();
- mNetworkThread = null;
- }
-
// do this before releasing wakelock
native_cleanup();
@@ -610,6 +712,15 @@
}
public void enableLocationTracking(boolean enable) {
+ synchronized (mHandler) {
+ mHandler.removeMessages(ENABLE_TRACKING);
+ Message m = Message.obtain(mHandler, ENABLE_TRACKING);
+ m.arg1 = (enable ? 1 : 0);
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleEnableLocationTracking(boolean enable) {
if (enable) {
mTTFF = 0;
mLastFixTime = 0;
@@ -659,6 +770,12 @@
}
public void addListener(int uid) {
+ Message m = Message.obtain(mHandler, ADD_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+
+ private void handleAddListener(int uid) {
synchronized(mListeners) {
if (mClientUids.indexOfKey(uid) >= 0) {
// Shouldn't be here -- already have this uid.
@@ -677,6 +794,12 @@
}
public void removeListener(int uid) {
+ Message m = Message.obtain(mHandler, REMOVE_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+
+ private void handleRemoveListener(int uid) {
synchronized(mListeners) {
if (mClientUids.indexOfKey(uid) < 0) {
// Shouldn't be here -- don't have this uid.
@@ -700,10 +823,12 @@
return deleteAidingData(extras);
}
if ("force_time_injection".equals(command)) {
- return forceTimeInjection();
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME));
+ return true;
}
if ("force_xtra_injection".equals(command)) {
- if (native_supports_xtra() && mNetworkThread != null) {
+ if (native_supports_xtra()) {
xtraDownloadRequest();
return true;
}
@@ -744,16 +869,7 @@
return false;
}
- private boolean forceTimeInjection() {
- if (DEBUG) Log.d(TAG, "forceTimeInjection");
- if (mNetworkThread != null) {
- mNetworkThread.timeInjectRequest();
- return true;
- }
- return false;
- }
-
- public void startNavigating() {
+ private void startNavigating() {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
@@ -784,7 +900,7 @@
}
}
- public void stopNavigating() {
+ private void stopNavigating() {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
@@ -883,8 +999,8 @@
}
// send an intent to notify that the GPS is receiving fixes.
- Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
- intent.putExtra(EXTRA_ENABLED, true);
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
mContext.sendBroadcast(intent);
updateStatus(LocationProvider.AVAILABLE, mSvCount);
}
@@ -960,8 +1076,8 @@
}
// send an intent to notify that the GPS has been enabled or disabled.
- Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION);
- intent.putExtra(EXTRA_ENABLED, mNavigating);
+ Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
mContext.sendBroadcast(intent);
}
@@ -1017,8 +1133,8 @@
if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT * 1000) {
// send an intent to notify that the GPS is no longer receiving fixes.
- Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
- intent.putExtra(EXTRA_ENABLED, false);
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
mContext.sendBroadcast(intent);
updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
}
@@ -1092,11 +1208,13 @@
}
}
+ /**
+ * called from native code to request XTRA data
+ */
private void xtraDownloadRequest() {
if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
- if (mNetworkThread != null) {
- mNetworkThread.xtraDownloadRequest();
- }
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA));
}
//=============================================================
@@ -1189,7 +1307,11 @@
mNIHandler.handleNiNotification(notification);
}
- private class GpsEventThread extends Thread {
+ // this thread is used to receive events from the native code.
+ // native_wait_for_event() will callback to us via reportLocation(), reportStatus(), etc.
+ // this is necessary because native code cannot call Java on a thread that the JVM does
+ // not know about.
+ private final class GpsEventThread extends Thread {
public GpsEventThread() {
super("GpsEventThread");
@@ -1207,156 +1329,59 @@
}
}
- private class GpsNetworkThread extends Thread {
+ private final class ProviderHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg)
+ {
+ switch (msg.what) {
+ case ENABLE:
+ if (msg.arg1 == 1) {
+ handleEnable();
+ } else {
+ handleDisable();
+ }
+ break;
+ case ENABLE_TRACKING:
+ handleEnableLocationTracking(msg.arg1 == 1);
+ break;
+ case UPDATE_NETWORK_STATE:
+ handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
+ break;
+ case INJECT_NTP_TIME:
+ handleInjectNtpTime();
+ break;
+ case DOWNLOAD_XTRA_DATA:
+ if (native_supports_xtra()) {
+ handleDownloadXtraData();
+ }
+ break;
+ case UPDATE_LOCATION:
+ handleUpdateLocation((Location)msg.obj);
+ break;
+ case ADD_LISTENER:
+ handleAddListener(msg.arg1);
+ break;
+ case REMOVE_LISTENER:
+ handleRemoveListener(msg.arg1);
+ break;
+ }
+ }
+ };
- private long mNextNtpTime = 0;
- private long mNextXtraTime = 0;
- private boolean mTimeInjectRequested = false;
- private boolean mXtraDownloadRequested = false;
- private boolean mDone = false;
+ private final class GpsLocationProviderThread extends Thread {
- public GpsNetworkThread() {
- super("GpsNetworkThread");
+ public GpsLocationProviderThread() {
+ super("GpsLocationProvider");
}
public void run() {
- synchronized (mNetworkThreadLock) {
- if (!mDone) {
- runLocked();
- }
- }
- }
-
- public void runLocked() {
- if (DEBUG) Log.d(TAG, "NetworkThread starting");
-
- SntpClient client = new SntpClient();
- GpsXtraDownloader xtraDownloader = null;
-
- if (native_supports_xtra()) {
- xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
- }
-
- // thread exits after disable() is called
- while (!mDone) {
- long waitTime = getWaitTime();
- do {
- synchronized (this) {
- try {
- if (!mNetworkAvailable) {
- if (DEBUG) Log.d(TAG, "NetworkThread wait for network");
- wait();
- } else if (waitTime > 0) {
- if (DEBUG) {
- Log.d(TAG, "NetworkThread wait for " +
- waitTime + "ms");
- }
- wait(waitTime);
- }
- } catch (InterruptedException e) {
- if (DEBUG) {
- Log.d(TAG, "InterruptedException in GpsNetworkThread");
- }
- }
- }
- waitTime = getWaitTime();
- } while (!mDone && ((!mXtraDownloadRequested &&
- !mTimeInjectRequested && waitTime > 0)
- || !mNetworkAvailable));
- if (DEBUG) Log.d(TAG, "NetworkThread out of wake loop");
- if (!mDone) {
- if (mNtpServer != null &&
- (mTimeInjectRequested || mNextNtpTime <= System.currentTimeMillis())) {
- if (DEBUG) {
- Log.d(TAG, "Requesting time from NTP server " + mNtpServer);
- }
- mTimeInjectRequested = false;
- if (client.requestTime(mNtpServer, 10000)) {
- long time = client.getNtpTime();
- long timeReference = client.getNtpTimeReference();
- int certainty = (int)(client.getRoundTripTime()/2);
- long now = System.currentTimeMillis();
- long systemTimeOffset = time - now;
-
- Log.d(TAG, "NTP server returned: "
- + time + " (" + new Date(time)
- + ") reference: " + timeReference
- + " certainty: " + certainty
- + " system time offset: " + systemTimeOffset);
-
- // sanity check NTP time and do not use if it is too far from system time
- if (systemTimeOffset < 0) {
- systemTimeOffset = -systemTimeOffset;
- }
- if (systemTimeOffset < MAX_NTP_SYSTEM_TIME_OFFSET) {
- native_inject_time(time, timeReference, certainty);
- } else {
- Log.e(TAG, "NTP time differs from system time by " + systemTimeOffset
- + "ms. Ignoring.");
- }
- mNextNtpTime = now + NTP_INTERVAL;
- } else {
- if (DEBUG) Log.d(TAG, "requestTime failed");
- mNextNtpTime = System.currentTimeMillis() + RETRY_INTERVAL;
- }
- }
-
- if ((mXtraDownloadRequested ||
- (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis()))
- && xtraDownloader != null) {
- mXtraDownloadRequested = false;
- byte[] data = xtraDownloader.downloadXtraData();
- if (data != null) {
- if (DEBUG) {
- Log.d(TAG, "calling native_inject_xtra_data");
- }
- native_inject_xtra_data(data, data.length);
- mNextXtraTime = 0;
- } else {
- mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL;
- }
- }
- }
- }
- if (DEBUG) Log.d(TAG, "NetworkThread exiting");
- }
-
- synchronized void xtraDownloadRequest() {
- mXtraDownloadRequested = true;
- notify();
- }
-
- synchronized void timeInjectRequest() {
- mTimeInjectRequested = true;
- notify();
- }
-
- synchronized void signal() {
- notify();
- }
-
- synchronized void setDone() {
- if (DEBUG) Log.d(TAG, "stopping NetworkThread");
- mDone = true;
- notify();
- }
-
- private long getWaitTime() {
- long now = System.currentTimeMillis();
- long waitTime = Long.MAX_VALUE;
- if (mNtpServer != null) {
- waitTime = mNextNtpTime - now;
- }
- if (mNextXtraTime != 0) {
- long xtraWaitTime = mNextXtraTime - now;
- if (xtraWaitTime < waitTime) {
- waitTime = xtraWaitTime;
- }
- }
- if (waitTime < 0) {
- waitTime = 0;
- }
- return waitTime;
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ initialize();
+ Looper.prepare();
+ mHandler = new ProviderHandler();
+ // signal when we are initialized and ready to go
+ mInitializedLatch.countDown();
+ Looper.loop();
}
}
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
similarity index 98%
rename from location/java/com/android/internal/location/GpsXtraDownloader.java
rename to services/java/com/android/server/location/GpsXtraDownloader.java
index 978bda2..bc96980 100644
--- a/location/java/com/android/internal/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Config;
+import android.util.Log;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
@@ -30,14 +36,6 @@
import java.util.Properties;
import java.util.Random;
-import android.content.Context;
-import android.net.Proxy;
-import android.net.http.AndroidHttpClient;
-import android.util.Config;
-import android.util.Log;
-
-
-
/**
* A class for downloading GPS XTRA data.
*
diff --git a/location/java/android/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
similarity index 97%
rename from location/java/android/location/LocationProviderInterface.java
rename to services/java/com/android/server/location/LocationProviderInterface.java
index 5ffe15c3..a472143 100644
--- a/location/java/android/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.location;
+package com.android.server.location;
import android.location.Location;
import android.net.NetworkInfo;
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
similarity index 98%
rename from location/java/com/android/internal/location/LocationProviderProxy.java
rename to services/java/com/android/server/location/LocationProviderProxy.java
index 31ec09a..3e118f9 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
import android.content.ComponentName;
import android.content.Context;
@@ -22,7 +22,6 @@
import android.content.ServiceConnection;
import android.location.ILocationProvider;
import android.location.Location;
-import android.location.LocationProviderInterface;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
@@ -31,6 +30,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.location.DummyLocationProvider;
+
/**
* A class for proxying location providers implemented as services.
*
diff --git a/location/java/com/android/internal/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
similarity index 97%
rename from location/java/com/android/internal/location/MockProvider.java
rename to services/java/com/android/server/location/MockProvider.java
index d912740..e3f33469 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
diff --git a/location/java/com/android/internal/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
similarity index 96%
rename from location/java/com/android/internal/location/PassiveProvider.java
rename to services/java/com/android/server/location/PassiveProvider.java
index ab90937..5ed1558 100644
--- a/location/java/com/android/internal/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.location;
+package com.android.server.location;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index 1598b68..1bb56a7 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -173,6 +173,12 @@
}
void add(StatusBarNotification notification) {
+ if (StatusBarService.SPEW) {
+ Slog.d(StatusBarService.TAG, "before add NotificationViewList"
+ + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
+ dump(notification);
+ }
+
ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
long when = notification.data.when;
final int N = list.size();
@@ -187,20 +193,25 @@
list.add(index, notification);
if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "NotificationViewList index=" + index);
+ Slog.d(StatusBarService.TAG, "after add NotificationViewList index=" + index);
dump(notification);
}
}
void dump(StatusBarNotification notification) {
if (StatusBarService.SPEW) {
+ boolean showTime = false;
String s = "";
for (int i=0; i<mOngoing.size(); i++) {
StatusBarNotification that = mOngoing.get(i);
if (that.key == notification.key) {
s += "[";
}
- s += that.data.when;
+ if (showTime) {
+ s += that.data.when;
+ } else {
+ s += that.data.pkg + "/" + that.data.id + "/" + that.view;
+ }
if (that.key == notification.key) {
s += "]";
}
@@ -214,7 +225,11 @@
if (that.key == notification.key) {
s += "[";
}
- s += that.data.when;
+ if (showTime) {
+ s += that.data.when;
+ } else {
+ s += that.data.pkg + "/" + that.data.id + "/" + that.view;
+ }
if (that.key == notification.key) {
s += "]";
}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 44e0dad..efb1a06 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -30,6 +30,7 @@
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -62,7 +63,6 @@
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.cdma.EriInfo;
@@ -387,8 +387,8 @@
action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
updateWifi(intent);
}
- else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
- action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
+ else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+ action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
updateGps(intent);
}
else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
@@ -533,8 +533,8 @@
filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
@@ -991,10 +991,10 @@
// asu = 0 (-113dB or less) is very weak
// signal, its better to show 0 bars to the user in such cases.
// asu = 99 is a special case, where the signal strength is unknown.
- if (asu <= 0 || asu == 99) iconLevel = 0;
- else if (asu >= 16) iconLevel = 4;
+ if (asu <= 2 || asu == 99) iconLevel = 0;
+ else if (asu >= 12) iconLevel = 4;
else if (asu >= 8) iconLevel = 3;
- else if (asu >= 4) iconLevel = 2;
+ else if (asu >= 5) iconLevel = 2;
else iconLevel = 1;
// Though mPhone is a Manager, this call is not an IPC
@@ -1286,13 +1286,13 @@
private final void updateGps(Intent intent) {
final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
+ final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
- if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
+ if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
// GPS is getting fixes
mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
mService.setIconVisibility(mGpsIcon, true);
- } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+ } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
// GPS is off
mService.setIconVisibility(mGpsIcon, false);
} else {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index d5f2c1d..bcaf0a4 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -557,15 +557,17 @@
doRevealAnimation();
return;
}
+ boolean expand = false;
+ boolean doExpand = false;
+ boolean doDisable = false;
+ int disableWhat = 0;
+
synchronized (mQueue) {
boolean wasExpanded = mExpanded;
// for each one in the queue, find all of the ones with the same key
// and collapse that down into a final op and/or call to setVisibility, etc
- boolean expand = wasExpanded;
- boolean doExpand = false;
- boolean doDisable = false;
- int disableWhat = 0;
+ expand = wasExpanded;
int N = mQueue.size();
while (N > 0) {
PendingOp op = mQueue.get(0);
@@ -639,17 +641,20 @@
if (mQueue.size() != 0) {
throw new RuntimeException("Assertion failed: mQueue.size=" + mQueue.size());
}
- if (doExpand) {
- // this is last so that we capture all of the pending changes before doing it
- if (expand) {
- animateExpand();
- } else {
- animateCollapse();
- }
+ }
+ // This must be done outside the synchronized block above to prevent a deadlock where
+ // we call into the NotificationManagerService and it is in turn attempting to post a
+ // message to our queue.
+ if (doExpand) {
+ // this is last so that we capture all of the pending changes before doing it
+ if (expand) {
+ animateExpand();
+ } else {
+ animateCollapse();
}
- if (doDisable) {
- performDisableActions(disableWhat);
- }
+ }
+ if (doDisable) {
+ performDisableActions(disableWhat);
}
}
}
@@ -888,6 +893,8 @@
void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
NotificationData n = notification.data;
if (oldData != null && n != null
+ && n.when == oldData.when
+ && n.ongoingEvent == oldData.ongoingEvent
&& n.contentView != null && oldData.contentView != null
&& n.contentView.getPackage() != null
&& oldData.contentView.getPackage() != null
@@ -1502,7 +1509,7 @@
Drawable bg;
/// ---------- Tracking View --------------
- pixelFormat = PixelFormat.TRANSLUCENT;
+ pixelFormat = PixelFormat.RGBX_8888;
bg = mTrackingView.getBackground();
if (bg != null) {
pixelFormat = bg.getOpacity();
diff --git a/services/java/com/android/server/status/StatusBarView.java b/services/java/com/android/server/status/StatusBarView.java
index 2dd564e..5e1f572 100644
--- a/services/java/com/android/server/status/StatusBarView.java
+++ b/services/java/com/android/server/status/StatusBarView.java
@@ -17,9 +17,10 @@
package com.android.server.status;
import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,8 @@
public class StatusBarView extends FrameLayout {
private static final String TAG = "StatusBarView";
+ static final int DIM_ANIM_TIME = 400;
+
StatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
@@ -38,6 +41,10 @@
ViewGroup mStatusIcons;
View mDate;
FixedSizeDrawable mBackground;
+
+ boolean mNightMode = false;
+ int mStartAlpha = 0, mEndAlpha = 0;
+ long mEndTime = 0;
public StatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -60,7 +67,30 @@
super.onAttachedToWindow();
mService.onBarViewAttached();
}
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ if (mNightMode != nightMode) {
+ mNightMode = nightMode;
+ mStartAlpha = getCurAlpha();
+ mEndAlpha = mNightMode ? 0x80 : 0x00;
+ mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME;
+ invalidate();
+ }
+ }
+ int getCurAlpha() {
+ long time = SystemClock.uptimeMillis();
+ if (time > mEndTime) {
+ return mEndAlpha;
+ }
+ return mEndAlpha
+ - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME));
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -97,6 +127,18 @@
mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t));
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ int alpha = getCurAlpha();
+ if (alpha != 0) {
+ canvas.drawARGB(alpha, 0, 0, 0);
+ }
+ if (alpha != mEndAlpha) {
+ invalidate();
+ }
+ }
+
/**
* Gets the left position of v in this view. Throws if v is not
* a child of this.
diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java
index c1c8c22..e8631c53 100644
--- a/services/java/com/android/server/status/UsbStorageActivity.java
+++ b/services/java/com/android/server/status/UsbStorageActivity.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -26,8 +27,12 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
import android.os.IBinder;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
@@ -36,10 +41,14 @@
import android.os.ServiceManager;
import android.widget.ImageView;
import android.widget.Button;
+import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.View;
+import android.view.Window;
import android.util.Log;
+import java.util.List;
+
/**
* This activity is shown to the user for him/her to enable USB mass storage
* on-demand (that is, when the USB cable is connected). It uses the alert
@@ -48,8 +57,10 @@
public class UsbStorageActivity extends Activity
implements View.OnClickListener, OnCancelListener {
private static final String TAG = "UsbStorageActivity";
+
private Button mMountButton;
private Button mUnmountButton;
+ private ProgressBar mProgressBar;
private TextView mBanner;
private TextView mMessage;
private ImageView mIcon;
@@ -71,11 +82,8 @@
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onStorageStateChanged(String path, String oldState, String newState) {
- if (newState.equals(Environment.MEDIA_SHARED)) {
- switchDisplay(true);
- } else {
- switchDisplay(false);
- }
+ final boolean on = newState.equals(Environment.MEDIA_SHARED);
+ switchDisplay(on);
}
};
@@ -90,6 +98,9 @@
}
}
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setProgressBarIndeterminateVisibility(true);
+
setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
setContentView(com.android.internal.R.layout.usb_storage_activity);
@@ -102,16 +113,19 @@
mMountButton.setOnClickListener(this);
mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
mUnmountButton.setOnClickListener(this);
+ mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
}
private void switchDisplay(boolean usbStorageInUse) {
if (usbStorageInUse) {
+ mProgressBar.setVisibility(View.GONE);
mUnmountButton.setVisibility(View.VISIBLE);
mMountButton.setVisibility(View.GONE);
mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected);
mBanner.setText(com.android.internal.R.string.usb_storage_stop_title);
mMessage.setText(com.android.internal.R.string.usb_storage_stop_message);
} else {
+ mProgressBar.setVisibility(View.GONE);
mUnmountButton.setVisibility(View.GONE);
mMountButton.setVisibility(View.VISIBLE);
mIcon.setImageResource(com.android.internal.R.drawable.usb_android);
@@ -167,7 +181,7 @@
.setTitle(R.string.dlg_confirm_kill_storage_users_title)
.setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- mStorageManager.enableUsbMassStorage();
+ switchUsbMassStorageAsync(true);
}})
.setNegativeButton(R.string.cancel, null)
.setMessage(R.string.dlg_confirm_kill_storage_users_text)
@@ -189,37 +203,65 @@
showDialog(id);
}
+ private void switchUsbMassStorageAsync(boolean on) {
+ mUnmountButton.setVisibility(View.GONE);
+ mMountButton.setVisibility(View.GONE);
+
+ mProgressBar.setVisibility(View.VISIBLE);
+ // will be hidden once USB mass storage kicks in (or fails)
+
+ final boolean _on = on;
+ new Thread() {
+ public void run() {
+ if (_on) {
+ mStorageManager.enableUsbMassStorage();
+ } else {
+ mStorageManager.disableUsbMassStorage();
+ }
+ }
+ }.start();
+ }
+
private void checkStorageUsers() {
IMountService ims = getMountService();
if (ims == null) {
// Display error dialog
showDialogInner(DLG_ERROR_SHARING);
}
- String path = Environment.getExternalStorageDirectory().getPath();
- int stUsers[] = null;
+ String extStoragePath = Environment.getExternalStorageDirectory().toString();
+ boolean showDialog = false;
try {
- if (localLOGV) Log.i(TAG, "Checking getStorageUsers");
- stUsers = ims.getStorageUsers(path);
+ int[] stUsers = ims.getStorageUsers(extStoragePath);
+ if (stUsers != null && stUsers.length > 0) {
+ showDialog = true;
+ } else {
+ // List of applications on sdcard.
+ ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ List<ApplicationInfo> infoList = am.getRunningExternalApplications();
+ if (infoList != null && infoList.size() > 0) {
+ showDialog = true;
+ }
+ }
} catch (RemoteException e) {
+ // Display error dialog
showDialogInner(DLG_ERROR_SHARING);
}
- if (stUsers != null && stUsers.length > 0) {
+ if (showDialog) {
// Display dialog to user
showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS);
} else {
if (localLOGV) Log.i(TAG, "Enabling UMS");
- mStorageManager.enableUsbMassStorage();
+ switchUsbMassStorageAsync(true);
}
}
public void onClick(View v) {
- Log.i(TAG, "Clicked button");
if (v == mMountButton) {
// Check for list of storage users and display dialog if needed.
checkStorageUsers();
} else if (v == mUnmountButton) {
if (localLOGV) Log.i(TAG, "Disabling UMS");
- mStorageManager.disableUsbMassStorage();
+ switchUsbMassStorageAsync(false);
}
}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 9d2760e..b90e327 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -9,6 +9,7 @@
com_android_server_SensorService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_VibratorService.cpp \
+ com_android_server_location_GpsLocationProvider.cpp \
onload.cpp
LOCAL_C_INCLUDES += \
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
similarity index 98%
rename from core/jni/android_location_GpsLocationProvider.cpp
rename to services/jni/com_android_server_location_GpsLocationProvider.cpp
index a3be309..003f109 100755
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -523,9 +523,9 @@
{"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
};
-int register_android_location_GpsLocationProvider(JNIEnv* env)
+int register_android_server_location_GpsLocationProvider(JNIEnv* env)
{
- return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods));
+ return jniRegisterNativeMethods(env, "com/android/server/location/GpsLocationProvider", sMethods, NELEM(sMethods));
}
} /* namespace android */
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index c16fdb8..d11e7e1 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -11,6 +11,7 @@
int register_android_server_SensorService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_location_GpsLocationProvider(JNIEnv* env);
};
using namespace android;
@@ -33,6 +34,7 @@
register_android_server_SensorService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
+ register_android_server_location_GpsLocationProvider(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 3842d45..78a90fb 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -39,6 +39,7 @@
public void tearDown() throws Exception {
ContentResolver cr = getContext().getContentResolver();
Settings.Secure.putString(cr, Settings.Secure.DROPBOX_AGE_SECONDS, "");
+ Settings.Secure.putString(cr, Settings.Secure.DROPBOX_MAX_FILES, "");
Settings.Secure.putString(cr, Settings.Secure.DROPBOX_QUOTA_KB, "");
Settings.Secure.putString(cr, Settings.Secure.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
}
@@ -457,6 +458,55 @@
e0.close();
}
+ public void testFileCountLimits() throws Exception {
+ File dir = getEmptyDir("testFileCountLimits");
+
+ DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
+ DropBoxManager dropbox = new DropBoxManager(service);
+ dropbox.addText("DropBoxTest", "TEST0");
+ dropbox.addText("DropBoxTest", "TEST1");
+ dropbox.addText("DropBoxTest", "TEST2");
+ dropbox.addText("DropBoxTest", "TEST3");
+ dropbox.addText("DropBoxTest", "TEST4");
+ dropbox.addText("DropBoxTest", "TEST5");
+
+ // Verify 6 files added
+ DropBoxManager.Entry e0 = dropbox.getNextEntry(null, 0);
+ DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
+ DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
+ DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
+ DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis());
+ DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis());
+ assertTrue(null == dropbox.getNextEntry(null, e5.getTimeMillis()));
+ assertEquals("TEST0", e0.getText(80));
+ assertEquals("TEST5", e5.getText(80));
+
+ e0.close();
+ e1.close();
+ e2.close();
+ e3.close();
+ e4.close();
+ e5.close();
+
+ // Limit to 3 files and add one more entry
+ ContentResolver cr = getContext().getContentResolver();
+ Settings.Secure.putString(cr, Settings.Secure.DROPBOX_MAX_FILES, "3");
+ dropbox.addText("DropBoxTest", "TEST6");
+
+ // Verify only 3 files left
+ DropBoxManager.Entry f0 = dropbox.getNextEntry(null, 0);
+ DropBoxManager.Entry f1 = dropbox.getNextEntry(null, f0.getTimeMillis());
+ DropBoxManager.Entry f2 = dropbox.getNextEntry(null, f1.getTimeMillis());
+ assertTrue(null == dropbox.getNextEntry(null, f2.getTimeMillis()));
+ assertEquals("TEST4", f0.getText(80));
+ assertEquals("TEST5", f1.getText(80));
+ assertEquals("TEST6", f2.getText(80));
+
+ f0.close();
+ f1.close();
+ f2.close();
+ }
+
public void testCreateDropBoxManagerWithInvalidDirectory() throws Exception {
// If created with an invalid directory, the DropBoxManager should suffer quietly
// and fail all operations (this is how it survives a full disk).
diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
new file mode 100644
index 0000000..636ba21
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+/**
+ * Tests for {@link com.android.server.EntropyService}
+ */
+public class EntropyServiceTest extends AndroidTestCase {
+
+ public void testInitialWrite() throws Exception {
+ File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
+ File file = File.createTempFile("testInitialWrite", "dat", dir);
+ file.deleteOnExit();
+ assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
+
+ // The constructor has the side effect of writing to file
+ new EntropyService("/dev/null", file.getCanonicalPath());
+
+ assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
+ }
+}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index b8ea4c0..a284ea5 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -237,8 +237,8 @@
/*
* TODO(cleanup): It would make some sense if the result of
- * preprocessing a message to determine the proper encoding (ie
- * the resulting datastructure from calculateLength) could be
+ * preprocessing a message to determine the proper encoding (i.e.
+ * the resulting data structure from calculateLength) could be
* passed as an argument to the actual final encoding function.
* This would better ensure that the logic behind size calculation
* actually matched the encoding.
@@ -427,7 +427,7 @@
* @param destinationAddress the address of the destination for the message
* @param destinationPort the port to deliver the message to at the
* destination
- * @param data the dat for the message
+ * @param data the data for the message
* @return a <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message.
* Returns null on encode error.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ae5b1de..f018d107 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -46,7 +46,7 @@
* {@link android.content.Context#getSystemService
* Context.getSystemService(Context.TELEPHONY_SERVICE)}.
* <p>
- * Note that acess to some telephony information is
+ * Note that access to some telephony information is
* permission-protected. Your application cannot access the protected
* information unless it has the appropriate permissions declared in
* its manifest file. Where permissions apply, they are noted in the
@@ -356,7 +356,7 @@
}
/**
- * Returns the ISO country code equivilent of the current registered
+ * Returns the ISO country code equivalent of the current registered
* operator's MCC (Mobile Country Code).
* <p>
* Availability: Only when user is registered to a network. Result may be
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 6eea46e..d3a34ec 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -202,7 +202,7 @@
/**
* Supply the ICC PIN to the ICC
*
- * When the operation is complete, onComplete will be sent to it's
+ * When the operation is complete, onComplete will be sent to its
* Handler.
*
* onComplete.obj will be an AsyncResult
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 358af95..a8f4143 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -585,7 +585,7 @@
/**
* Utility code to set the system locale if it's not set already
- * @param langauge Two character language code desired
+ * @param language Two character language code desired
* @param country Two character country code desired
*
* {@hide}
@@ -694,22 +694,22 @@
public void setTTYMode(int ttyMode, Message onComplete) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("setTTYMode");
}
public void queryTTYMode(Message onComplete) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("queryTTYMode");
}
public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("enableEnhancedVoicePrivacy");
}
public void getEnhancedVoicePrivacy(Message onComplete) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("getEnhancedVoicePrivacy");
}
public void setBandMode(int bandMode, Message response) {
@@ -754,7 +754,7 @@
* Returns the CDMA ERI icon index to display
*/
public int getCdmaEriIconIndex() {
- Log.e(LOG_TAG, "Error! getCdmaEriIconIndex should never be executed in GSM mode");
+ logUnexpectedCdmaMethodCall("getCdmaEriIconIndex");
return -1;
}
@@ -764,7 +764,7 @@
* 1 - FLASHING
*/
public int getCdmaEriIconMode() {
- Log.e(LOG_TAG, "Error! getCdmaEriIconMode should never be executed in GSM mode");
+ logUnexpectedCdmaMethodCall("getCdmaEriIconMode");
return -1;
}
@@ -772,82 +772,82 @@
* Returns the CDMA ERI text,
*/
public String getCdmaEriText() {
- Log.e(LOG_TAG, "Error! getCdmaEriText should never be executed in GSM mode");
+ logUnexpectedCdmaMethodCall("getCdmaEriText");
return "GSM nw, no ERI";
}
public String getCdmaMin() {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("getCdmaMin");
return null;
}
public boolean isMinInfoReady() {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("isMinInfoReady");
return false;
}
public String getCdmaPrlVersion(){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("getCdmaPrlVersion");
return null;
}
public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("sendBurstDtmf");
}
public void exitEmergencyCallbackMode() {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("exitEmergencyCallbackMode");
}
public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("registerForCdmaOtaStatusChange");
}
public void unregisterForCdmaOtaStatusChange(Handler h) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("unregisterForCdmaOtaStatusChange");
}
public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("registerForSubscriptionInfoReady");
}
public void unregisterForSubscriptionInfoReady(Handler h) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("unregisterForSubscriptionInfoReady");
}
public boolean isOtaSpNumber(String dialStr) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("isOtaSpNumber");
return false;
}
public void registerForCallWaiting(Handler h, int what, Object obj){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("registerForCallWaiting");
}
public void unregisterForCallWaiting(Handler h){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("unregisterForCallWaiting");
}
public void registerForEcmTimerReset(Handler h, int what, Object obj) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("registerForEcmTimerReset");
}
public void unregisterForEcmTimerReset(Handler h) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("unregisterForEcmTimerReset");
}
public void registerForSignalInfo(Handler h, int what, Object obj) {
@@ -908,12 +908,12 @@
public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("setOnEcbModeExitResponse");
}
public void unsetOnEcbModeExitResponse(Handler h){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
- Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
}
public String getInterfaceName(String apnType) {
@@ -984,7 +984,7 @@
}
/**
- * Notifiy registrants of a new ringing Connection.
+ * Notify registrants of a new ringing Connection.
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
@@ -1017,4 +1017,13 @@
+ " mCallRingContinueToken=" + mCallRingContinueToken);
}
}
+
+ /**
+ * Common error logger method for unexpected calls to CDMA-only methods.
+ */
+ private void logUnexpectedCdmaMethodCall(String name)
+ {
+ Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
+ "called, CDMAPhone inactive.");
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 4a4282fc..764d12e 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -109,6 +109,12 @@
/** Stop the sending */
static final protected int EVENT_STOP_SENDING = 10;
+ /** Memory status reporting is acknowledged by RIL */
+ static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11;
+
+ /** Radio is ON */
+ static final protected int EVENT_RADIO_ON = 12;
+
protected Phone mPhone;
protected Context mContext;
protected ContentResolver mResolver;
@@ -152,6 +158,7 @@
private SmsMessageBase.SubmitPduBase mSubmitPduBase;
protected boolean mStorageAvailable = true;
+ protected boolean mReportMemoryStatusPending = false;
protected static int getNextConcatenatedRef() {
sConcatenatedRef += 1;
@@ -235,6 +242,7 @@
mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);
mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
+ mCm.registerForOn(this, EVENT_RADIO_ON, null);
// Don't always start message ref at 0.
sConcatenatedRef = new Random().nextInt(256);
@@ -253,6 +261,7 @@
mCm.unSetOnNewSMS(this);
mCm.unSetOnSmsStatus(this);
mCm.unSetOnIccSmsFull(this);
+ mCm.unregisterForOn(this);
}
protected void finalize() {
@@ -370,6 +379,26 @@
removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
}
break;
+
+ case EVENT_REPORT_MEMORY_STATUS_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception != null) {
+ mReportMemoryStatusPending = true;
+ Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
+ + mStorageAvailable);
+ } else {
+ mReportMemoryStatusPending = false;
+ }
+ break;
+
+ case EVENT_RADIO_ON:
+ if (mReportMemoryStatusPending) {
+ Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
+ + mStorageAvailable);
+ mCm.reportSmsMemoryStatus(mStorageAvailable,
+ obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+ }
+ break;
}
}
@@ -940,10 +969,10 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_LOW)) {
mStorageAvailable = false;
- mCm.reportSmsMemoryStatus(false, null);
+ mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
} else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_OK)) {
mStorageAvailable = true;
- mCm.reportSmsMemoryStatus(true, null);
+ mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
} else {
// Assume the intent is one of the SMS receive intents that
// was sent as an ordered broadcast. Check result and ACK.
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 6c20204..95cb1c6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -20,6 +20,8 @@
import android.util.Log;
import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.gsm.ApnSetting;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
/**
@@ -71,12 +73,20 @@
createTime = -1;
lastFailTime = -1;
lastFailCause = FailCause.NONE;
+ int dataProfile;
+ if ((cp.apn != null) && (cp.apn.types.length > 0) && (cp.apn.types[0] != null) &&
+ (cp.apn.types[0].equals(Phone.APN_TYPE_DUN))) {
+ if (DBG) log("CdmaDataConnection using DUN");
+ dataProfile = RILConstants.DATA_PROFILE_TETHERED;
+ } else {
+ dataProfile = RILConstants.DATA_PROFILE_DEFAULT;
+ }
// msg.obj will be returned in AsyncResult.userObj;
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_CDMA),
- Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), null, null,
+ Integer.toString(dataProfile), null, null,
null, Integer.toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP), msg);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index af9c652..2f801ccc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -48,6 +48,7 @@
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.DataConnectionTracker;
import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.gsm.ApnSetting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.ServiceStateTracker;
@@ -77,9 +78,6 @@
/** Currently active CdmaDataConnection */
private CdmaDataConnection mActiveDataConnection;
- /** mimic of GSM's mActiveApn */
- private boolean mIsApnActive = false;
-
private boolean mPendingRestartRadio = false;
private static final int TIME_DELAYED_TO_RESTART_RADIO =
SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000);
@@ -108,6 +106,14 @@
Phone.APN_TYPE_DUN,
Phone.APN_TYPE_HIPRI };
+ private static final String[] mDefaultApnTypes = {
+ Phone.APN_TYPE_DEFAULT,
+ Phone.APN_TYPE_MMS,
+ Phone.APN_TYPE_HIPRI };
+
+ // if we have no active Apn this is null
+ protected ApnSetting mActiveApn;
+
// Possibly promoate to base class, the only difference is
// the INTENT_RECONNECT_ALARM action is a different string.
// Do consider technology changes if it is promoted.
@@ -250,7 +256,7 @@
@Override
protected boolean isApnTypeActive(String type) {
- return (mIsApnActive && isApnTypeAvailable(type));
+ return mActiveApn != null && mActiveApn.canHandleType(type);
}
@Override
@@ -265,10 +271,9 @@
protected String[] getActiveApnTypes() {
String[] result;
- if (mIsApnActive) {
- result = mSupportedApnTypes.clone();
+ if (mActiveApn != null) {
+ result = mActiveApn.types;
} else {
- // TODO - should this return an empty array? See GSM too.
result = new String[1];
result[0] = Phone.APN_TYPE_DEFAULT;
}
@@ -414,7 +419,6 @@
}
private boolean setupData(String reason) {
-
CdmaDataConnection conn = findFreeDataConnection();
if (conn == null) {
@@ -423,12 +427,19 @@
}
mActiveDataConnection = conn;
- mIsApnActive = true;
+ String[] types;
+ if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
+ types = new String[1];
+ types[0] = Phone.APN_TYPE_DUN;
+ } else {
+ types = mDefaultApnTypes;
+ }
+ mActiveApn = new ApnSetting(0, "", "", "", "", "", "", "", "", "", "", 0, types);
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
msg.obj = reason;
- conn.connect(msg);
+ conn.connect(msg, mActiveApn);
setState(State.INITING);
phone.notifyDataConnection(reason);
@@ -627,7 +638,7 @@
if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
setState(State.IDLE);
phone.notifyDataConnection(reason);
- mIsApnActive = false;
+ mActiveApn = null;
}
protected void onRecordsLoaded() {
@@ -649,8 +660,7 @@
*/
@Override
protected void onEnableNewApn() {
- // for cdma we only use this when default data is enabled..
- onTrySetupData(Phone.REASON_DATA_ENABLED);
+ cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
}
/**
@@ -763,7 +773,7 @@
}
phone.notifyDataConnection(reason);
- mIsApnActive = false;
+ mActiveApn = null;
if (retryAfterDisconnected(reason)) {
trySetupData(reason);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 8d807fd..4cbfc87 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -32,12 +32,12 @@
String user;
String password;
int authType;
- String[] types;
+ public String[] types;
int id;
String numeric;
- ApnSetting(int id, String numeric, String carrier, String apn, String proxy, String port,
+ public ApnSetting(int id, String numeric, String carrier, String apn, String proxy, String port,
String mmsc, String mmsProxy, String mmsPort,
String user, String password, int authType, String[] types) {
this.id = id;
@@ -73,7 +73,7 @@
return sb.toString();
}
- boolean canHandleType(String type) {
+ public boolean canHandleType(String type) {
for (String t : types) {
// DEFAULT handles all, and HIPRI is handled by DEFAULT
if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL) ||
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index ee6b89c..63d50c7 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -121,6 +121,10 @@
* -e class com.android.foo.FooTest,com.android.foo.TooTest
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
+ * <b>Running all tests in a java package:</b> adb shell am instrument -w
+ * -e package com.android.foo.subpkg
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
* <b>Including performance tests:</b> adb shell am instrument -w
* -e perf true
* com.android.foo/android.test.InstrumentationTestRunner
diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index 0d29c35..c22547f 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -16,4 +16,6 @@
include $(BUILD_PACKAGE)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+LOCAL_STORED_PATH:= $(LOCAL_PATH)
+include $(call all-makefiles-under,$(LOCAL_STORED_PATH))
+include $(call all-makefiles-under,$(LOCAL_STORED_PATH)/apks)
diff --git a/tests/AndroidTests/apks/install_decl_perm/Android.mk b/tests/AndroidTests/apks/install_decl_perm/Android.mk
new file mode 100644
index 0000000..9dcf9a0
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AndroidTests_install_decl_perm
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml b/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml
new file mode 100644
index 0000000..4387500
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.unit_tests.install_decl_perm">
+
+ <permission android:name="com.android.unit_tests.NORMAL"
+ android:permissionGroup="android.permission-group.COST_MONEY"
+ android:protectionLevel="normal"
+ android:label="test normal perm" />
+
+ <permission android:name="com.android.unit_tests.DANGEROUS"
+ android:permissionGroup="android.permission-group.COST_MONEY"
+ android:protectionLevel="dangerous"
+ android:label="test dangerous perm" />
+
+ <permission android:name="com.android.unit_tests.SIGNATURE"
+ android:permissionGroup="android.permission-group.COST_MONEY"
+ android:protectionLevel="signature"
+ android:label="test signature perm" />
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml b/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/tests/AndroidTests/apks/install_use_perm_good/Android.mk b/tests/AndroidTests/apks/install_use_perm_good/Android.mk
new file mode 100644
index 0000000..a25a03c
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AndroidTests_install_use_perm_good
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml b/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml
new file mode 100644
index 0000000..6dd3e71
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml
@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.unit_tests.install_use_perm_good">
+
+ <uses-permission android:name="com.android.unit_tests.NORMAL" />
+ <uses-permission android:name="com.android.unit_tests.DANGEROUS" />
+ <uses-permission android:name="com.android.unit_tests.SIGNATURE" />
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml b/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/tests/AndroidTests/res/raw/install_decl_perm b/tests/AndroidTests/res/raw/install_decl_perm
new file mode 100644
index 0000000..6f22321
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_decl_perm
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_use_perm_good b/tests/AndroidTests/res/raw/install_use_perm_good
new file mode 100644
index 0000000..d5216f8
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_use_perm_good
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java
index f2f7743..fa7b9ff 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java
@@ -136,9 +136,7 @@
if(localLOGV || TRACKING) Log.i(TAG, "blks3="+blks3);
verifyTestFiles1(cacheDir, "testtmpdir", 5);
}
-
- // TODO: flaky test
- // @LargeTest
+
public void testFreeApplicationCacheSomeFiles() throws Exception {
StatFs st = new StatFs("/data");
long blks1 = getFreeStorageBlks(st);
diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
index 9aed363..9a75047 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
@@ -259,6 +259,17 @@
}
}
+ public void testNonExistPath() {
+ IMountService ms = getMs();
+ try {
+ String path = ms.getSecureContainerPath("jparks.broke.it");
+ failStr(path);
+ } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
+ failStr(e);
+ }
+ }
+
public void testUnmountBusyContainer() {
IMountService ms = getMs();
try {
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 8c8b00c..449661c 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -45,6 +45,7 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.IPackageManager;
+import android.content.pm.PermissionInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -72,25 +73,43 @@
public class PackageManagerTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG="PackageManagerTests";
- public final long MAX_WAIT_TIME=120*1000;
- public final long WAIT_TIME_INCR=20*1000;
+ public final long MAX_WAIT_TIME = 25*1000;
+ public final long WAIT_TIME_INCR = 5*1000;
private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO;
private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL;
private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
+ private boolean mOrigState;
void failStr(String errMsg) {
Log.w(TAG, "errMsg="+errMsg);
fail(errMsg);
}
void failStr(Exception e) {
- Log.w(TAG, "e.getMessage="+e.getMessage());
- Log.w(TAG, "e="+e);
+ failStr(e.getMessage());
}
@Override
protected void setUp() throws Exception {
super.setUp();
+ mOrigState = getMediaState();
+ if (!mountMedia()) {
+ Log.i(TAG, "sdcard not mounted? Some of these tests might fail");
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Restore media state.
+ boolean newState = getMediaState();
+ if (newState != mOrigState) {
+ if (mOrigState) {
+ getMs().mountVolume(Environment.getExternalStorageDirectory().getPath());
+ } else {
+ getMs().unmountVolume(Environment.getExternalStorageDirectory().getPath(), true);
+ }
+ }
+ super.tearDown();
}
private class PackageInstallObserver extends IPackageInstallObserver.Stub {
@@ -254,21 +273,73 @@
packageParser = null;
return pkg;
}
- private boolean getInstallLoc(int flags, int expInstallLocation) {
+ private boolean checkSd(long pkgLen) {
+ String status = Environment.getExternalStorageState();
+ if (!status.equals(Environment.MEDIA_MOUNTED)) {
+ return false;
+ }
+ long sdSize = -1;
+ StatFs sdStats = new StatFs(
+ Environment.getExternalStorageDirectory().getPath());
+ sdSize = (long)sdStats.getAvailableBlocks() *
+ (long)sdStats.getBlockSize();
+ // TODO check for thesholds here
+ return pkgLen <= sdSize;
+
+ }
+ private boolean checkInt(long pkgLen) {
+ StatFs intStats = new StatFs(Environment.getDataDirectory().getPath());
+ long intSize = (long)intStats.getBlockCount() *
+ (long)intStats.getBlockSize();
+ long iSize = (long)intStats.getAvailableBlocks() *
+ (long)intStats.getBlockSize();
+ // TODO check for thresholds here?
+ return pkgLen <= iSize;
+ }
+ private static final int INSTALL_LOC_INT = 1;
+ private static final int INSTALL_LOC_SD = 2;
+ private static final int INSTALL_LOC_ERR = -1;
+ private int checkDefaultPolicy(long pkgLen) {
+ // Check for free memory internally
+ if (checkInt(pkgLen)) {
+ return INSTALL_LOC_INT;
+ }
+ // Check for free memory externally
+ if (checkSd(pkgLen)) {
+ return INSTALL_LOC_SD;
+ }
+ return INSTALL_LOC_ERR;
+ }
+ private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
// Flags explicitly over ride everything else.
if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) {
- return false;
+ return INSTALL_LOC_INT;
} else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0 ) {
- return true;
+ return INSTALL_LOC_SD;
+ } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+ return INSTALL_LOC_INT;
}
// Manifest option takes precedence next
if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- return true;
+ // TODO fitsonSd check
+ if (checkSd(pkgLen)) {
+ return INSTALL_LOC_SD;
+ }
+ if (checkInt(pkgLen)) {
+ return INSTALL_LOC_INT;
+ }
+ return INSTALL_LOC_ERR;
}
if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- return false;
+ if (checkInt(pkgLen)) {
+ return INSTALL_LOC_INT;
+ }
+ return INSTALL_LOC_ERR;
}
- // TODO Out of memory checks here.
+ if (expInstallLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ return checkDefaultPolicy(pkgLen);
+ }
+ // Check for settings preference.
boolean checkSd = false;
int setLoc = 0;
try {
@@ -284,50 +355,59 @@
failStr(e);
}
if (userPref == APP_INSTALL_DEVICE) {
- checkSd = false;
+ if (checkInt(pkgLen)) {
+ return INSTALL_LOC_INT;
+ }
+ return INSTALL_LOC_ERR;
} else if (userPref == APP_INSTALL_SDCARD) {
- checkSd = true;
- } else if (userPref == APP_INSTALL_AUTO) {
- // Might be determined dynamically. TODO fix this
- checkSd = false;
+ if (checkSd(pkgLen)) {
+ return INSTALL_LOC_SD;
+ }
+ return INSTALL_LOC_ERR;
}
}
- return checkSd;
+ return checkDefaultPolicy(pkgLen);
}
+
private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) {
try {
String pkgName = pkg.packageName;
- ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
- assertNotNull(info);
- assertEquals(pkgName, info.packageName);
- File dataDir = Environment.getDataDirectory();
- String appInstallPath = new File(dataDir, "app").getPath();
- String drmInstallPath = new File(dataDir, "app-private").getPath();
- File srcDir = new File(info.sourceDir);
- String srcPath = srcDir.getParent();
- File publicSrcDir = new File(info.publicSourceDir);
- String publicSrcPath = publicSrcDir.getParent();
+ ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+ assertNotNull(info);
+ assertEquals(pkgName, info.packageName);
+ File dataDir = Environment.getDataDirectory();
+ String appInstallPath = new File(dataDir, "app").getPath();
+ String drmInstallPath = new File(dataDir, "app-private").getPath();
+ File srcDir = new File(info.sourceDir);
+ String srcPath = srcDir.getParent();
+ File publicSrcDir = new File(info.publicSourceDir);
+ String publicSrcPath = publicSrcDir.getParent();
+ long pkgLen = new File(info.sourceDir).length();
- if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
- assertEquals(srcPath, drmInstallPath);
- assertEquals(publicSrcPath, appInstallPath);
- } else {
- assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
- if (!getInstallLoc(flags, expInstallLocation)) {
- assertEquals(srcPath, appInstallPath);
+ if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ assertEquals(srcPath, drmInstallPath);
assertEquals(publicSrcPath, appInstallPath);
- assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
} else {
- assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
- assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
- assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
+ assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
+ if (rLoc == INSTALL_LOC_INT) {
+ assertEquals(srcPath, appInstallPath);
+ assertEquals(publicSrcPath, appInstallPath);
+ assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+ } else if (rLoc == INSTALL_LOC_SD){
+ assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+ assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
+ assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
+ } else {
+ // TODO handle error. Install should have failed.
+ }
}
- }
} catch (NameNotFoundException e) {
failStr("failed with exception : " + e);
}
}
+
private void assertNotInstalled(String pkgName) {
try {
ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
@@ -349,25 +429,96 @@
private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) {
return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
- false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+ false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
- public void clearSecureContainersForPkg(String pkgName) {
- IMountService ms = getMs();
- try {
- String list[] = ms.getSecureContainerList();
- if (list != null) {
- for (String cid : list) {
- boolean delete = false;
- // STOPSHIP issues with rename should be fixed.
- if (cid.contains(pkgName) ||
- cid.contains("smdltmp")) {
- Log.i(TAG, "Destroying container " + cid);
- ms.destroySecureContainer(cid, true);
+ static final String PERM_PACKAGE = "package";
+ static final String PERM_DEFINED = "defined";
+ static final String PERM_UNDEFINED = "undefined";
+ static final String PERM_USED = "used";
+ static final String PERM_NOTUSED = "notused";
+
+ private void assertPermissions(String[] cmds) {
+ final PackageManager pm = getPm();
+ String pkg = null;
+ PackageInfo pkgInfo = null;
+ String mode = PERM_DEFINED;
+ int i = 0;
+ while (i < cmds.length) {
+ String cmd = cmds[i++];
+ if (cmd == PERM_PACKAGE) {
+ pkg = cmds[i++];
+ try {
+ pkgInfo = pm.getPackageInfo(pkg,
+ PackageManager.GET_PERMISSIONS
+ | PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ pkgInfo = null;
+ }
+ } else if (cmd == PERM_DEFINED || cmd == PERM_UNDEFINED
+ || cmd == PERM_USED || cmd == PERM_NOTUSED) {
+ mode = cmds[i++];
+ } else {
+ if (mode == PERM_DEFINED) {
+ try {
+ PermissionInfo pi = pm.getPermissionInfo(cmd, 0);
+ assertNotNull(pi);
+ assertEquals(pi.packageName, pkg);
+ assertEquals(pi.name, cmd);
+ assertNotNull(pkgInfo);
+ boolean found = false;
+ for (int j=0; j<pkgInfo.permissions.length && !found; j++) {
+ if (pkgInfo.permissions[j].name.equals(cmd)) {
+ found = true;
+ }
+ }
+ if (!found) {
+ fail("Permission not found: " + cmd);
+ }
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ } else if (mode == PERM_UNDEFINED) {
+ try {
+ pm.getPermissionInfo(cmd, 0);
+ throw new RuntimeException("Permission exists: " + cmd);
+ } catch (NameNotFoundException e) {
+ }
+ if (pkgInfo != null) {
+ boolean found = false;
+ for (int j=0; j<pkgInfo.permissions.length && !found; j++) {
+ if (pkgInfo.permissions[j].name.equals(cmd)) {
+ found = true;
+ }
+ }
+ if (found) {
+ fail("Permission still exists: " + cmd);
+ }
+ }
+ } else if (mode == PERM_USED || mode == PERM_NOTUSED) {
+ boolean found = false;
+ for (int j=0; j<pkgInfo.requestedPermissions.length && !found; j++) {
+ if (pkgInfo.requestedPermissions[j].equals(cmd)) {
+ found = true;
+ }
+ }
+ if (!found) {
+ fail("Permission not requested: " + cmd);
+ }
+ if (mode == PERM_USED) {
+ if (pm.checkPermission(cmd, pkg)
+ != PackageManager.PERMISSION_GRANTED) {
+ fail("Permission not granted: " + cmd);
+ }
+ } else {
+ if (pm.checkPermission(cmd, pkg)
+ != PackageManager.PERMISSION_DENIED) {
+ fail("Permission granted: " + cmd);
+ }
}
}
}
- } catch (RemoteException e) {}
+ }
}
/*
@@ -378,39 +529,48 @@
private InstallParams installFromRawResource(String outFileName,
int rawResId, int flags, boolean cleanUp, boolean fail, int result,
int expInstallLocation) {
+ PackageManager pm = mContext.getPackageManager();
File filesDir = mContext.getFilesDir();
File outFile = new File(filesDir, outFileName);
Uri packageURI = getInstallablePackage(rawResId, outFile);
PackageParser.Package pkg = parsePackage(packageURI);
assertNotNull(pkg);
- InstallParams ip = null;
- // Make sure the package doesn't exist
- getPm().deletePackage(pkg.packageName, null, 0);
- // Clean up the containers as well
- clearSecureContainersForPkg(pkg.packageName);
- try {
+ if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
+ // Make sure the package doesn't exist
try {
- if (fail) {
- assertTrue(invokeInstallPackageFail(packageURI, flags,
- pkg.packageName, result));
- assertNotInstalled(pkg.packageName);
- } else {
- InstallReceiver receiver = new InstallReceiver(pkg.packageName);
- assertTrue(invokeInstallPackage(packageURI, flags,
- pkg.packageName, receiver));
- // Verify installed information
- assertInstall(pkg, flags, expInstallLocation);
- ip = new InstallParams(pkg, outFileName, packageURI);
- }
+ ApplicationInfo appInfo = pm.getApplicationInfo(pkg.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ GenericReceiver receiver = new DeleteReceiver(pkg.packageName);
+ invokeDeletePackage(packageURI, 0,
+ pkg.packageName, receiver);
+ } catch (NameNotFoundException e1) {
} catch (Exception e) {
- failStr("Failed with exception : " + e);
+ failStr(e);
+ }
+ }
+ InstallParams ip = null;
+ try {
+ if (fail) {
+ assertTrue(invokeInstallPackageFail(packageURI, flags,
+ pkg.packageName, result));
+ assertNotInstalled(pkg.packageName);
+ } else {
+ InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+ assertTrue(invokeInstallPackage(packageURI, flags,
+ pkg.packageName, receiver));
+ // Verify installed information
+ assertInstall(pkg, flags, expInstallLocation);
+ ip = new InstallParams(pkg, outFileName, packageURI);
}
return ip;
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
} finally {
if (cleanUp) {
cleanUpInstall(ip);
}
}
+ return ip;
}
@MediumTest
@@ -614,7 +774,7 @@
waitTime += WAIT_TIME_INCR;
}
if(!receiver.isDone()) {
- throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+ throw new Exception("Timed out waiting for PACKAGE_REMOVED notification");
}
return receiver.received;
}
@@ -820,7 +980,7 @@
try {
// Wait on observer
synchronized(observer) {
- getMs().unmountVolume(path, false);
+ getMs().unmountVolume(path, true);
long waitTime = 0;
while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
observer.wait(WAIT_TIME_INCR);
@@ -931,7 +1091,7 @@
public void testManifestInstallLocationUnspecified() {
installFromRawResource("install.apk", R.raw.install_loc_unspecified,
- 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+ 0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
public void testManifestInstallLocationFwdLockedFlagSdcard() {
@@ -939,7 +1099,7 @@
PackageManager.INSTALL_FORWARD_LOCK |
PackageManager.INSTALL_EXTERNAL, true, true,
PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- PackageInfo.INSTALL_LOCATION_AUTO);
+ PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
}
public void testManifestInstallLocationFwdLockedSdcard() {
@@ -949,14 +1109,21 @@
PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
}
- private void replaceManifestLocation(int iFlags, int rFlags) {
+ /*
+ * Install a package on internal flash via PackageManager install flag. Replace
+ * the package via flag to install on sdcard. Make sure the new flag overrides
+ * the old install location.
+ */
+ public void testReplaceFlagInternalSdcard() {
+ int iFlags = 0;
+ int rFlags = PackageManager.INSTALL_EXTERNAL;
InstallParams ip = sampleInstallFromRawResource(iFlags, false);
GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags,
ip.pkg.packageName, receiver), true);
- assertInstall(ip.pkg, replaceFlags, ip.pkg.installLocation);
+ assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
} catch (Exception e) {
failStr("Failed with exception : " + e);
} finally {
@@ -964,27 +1131,41 @@
}
}
- public void testReplaceFlagInternalSdcard() {
- replaceManifestLocation(0, PackageManager.INSTALL_EXTERNAL);
- }
-
+ /*
+ * Install a package on sdcard via PackageManager install flag. Replace
+ * the package with no flags or manifest option and make sure the old
+ * install location is retained.
+ */
public void testReplaceFlagSdcardInternal() {
- replaceManifestLocation(PackageManager.INSTALL_EXTERNAL, 0);
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = 0;
+ InstallParams ip = sampleInstallFromRawResource(iFlags, false);
+ GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
+ int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+ try {
+ assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags,
+ ip.pkg.packageName, receiver), true);
+ assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ } finally {
+ cleanUpInstall(ip);
+ }
}
public void testManifestInstallLocationReplaceInternalSdcard() {
int iFlags = 0;
- int iApk = R.raw.install_loc_unspecified;
+ int iApk = R.raw.install_loc_internal;
int rFlags = 0;
int rApk = R.raw.install_loc_sdcard;
InstallParams ip = installFromRawResource("install.apk", iApk,
iFlags, false,
- false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
InstallParams rp = installFromRawResource("install.apk", rApk,
- rFlags, false,
+ replaceFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
assertInstall(rp.pkg, replaceFlags, rp.pkg.installLocation);
} catch (Exception e) {
@@ -1002,11 +1183,10 @@
InstallParams ip = installFromRawResource("install.apk", iApk,
iFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
try {
InstallParams rp = installFromRawResource("install.apk", rApk,
- rFlags, false,
+ replaceFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
assertInstall(rp.pkg, replaceFlags, ip.pkg.installLocation);
} catch (Exception e) {
@@ -1016,19 +1196,6 @@
}
}
- public void xxxtestClearAllSecureContainers() {
- IMountService ms = getMs();
- try {
- String list[] = ms.getSecureContainerList();
- if (list != null) {
- for (String cid : list) {
- Log.i(TAG, "Destroying container " + cid);
- ms.destroySecureContainer(cid, false);
- }
- }
- } catch (RemoteException e) {}
- }
-
class MoveReceiver extends GenericReceiver {
String pkgName;
final static int INVALID = -1;
@@ -1154,7 +1321,7 @@
public void moveFromRawResource(int installFlags, int moveFlags,
int expRetCode) {
int origDefaultLoc = getInstallLoc();
- setInstallLoc(PackageInfo.INSTALL_LOCATION_AUTO);
+ setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
// Install first
InstallParams ip = sampleInstallFromRawResource(installFlags, false);
ApplicationInfo oldAppInfo = null;
@@ -1211,6 +1378,840 @@
moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_INTERNAL,
PackageManager.MOVE_SUCCEEDED);
}
+
+ /*
+ * Test that an install error code is returned when media is unmounted
+ * and package installed on sdcard via package manager flag.
+ */
+ public void testInstallSdcardUnmount() {
+ boolean origState = getMediaState();
+ try {
+ // Unmount sdcard
+ assertTrue(unmountMedia());
+ // Try to install and make sure an error code is returned.
+ assertNull(installFromRawResource("install.apk", R.raw.install,
+ PackageManager.INSTALL_EXTERNAL, false,
+ true, PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ PackageInfo.INSTALL_LOCATION_AUTO));
+ } finally {
+ // Restore original media state
+ if (origState) {
+ mountMedia();
+ } else {
+ unmountMedia();
+ }
+ }
+ }
+
+ /*
+ * Unmount sdcard. Try installing an app with manifest option to install
+ * on sdcard. Make sure it gets installed on internal flash.
+ */
+ public void testInstallManifestSdcardUnmount() {
+ boolean origState = getMediaState();
+ try {
+ // Unmount sdcard
+ assertTrue(unmountMedia());
+ // Try to install and make sure an error code is returned.
+ assertNotNull(installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ 0, false,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY));
+ } finally {
+ // Restore original media state
+ if (origState) {
+ mountMedia();
+ } else {
+ unmountMedia();
+ }
+ }
+ }
+
+ /*---------- Recommended install location tests ----*/
+ /* Precedence: FlagManifestExistingUser
+ * PrecedenceSuffixes:
+ * Flag : FlagI, FlagE, FlagF
+ * I - internal, E - external, F - forward locked, Flag suffix absent if not using any option.
+ * Manifest: ManifestI, ManifestE, ManifestA, Manifest suffix absent if not using any option.
+ * Existing: Existing suffix absent if not existing.
+ * User: UserI, UserE, UserA, User suffix absent if not existing.
+ *
+ */
+ /*
+ * Install an app on internal flash
+ */
+ public void testFlagI() {
+ sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, true);
+ }
+ /*
+ * Install an app on sdcard.
+ */
+ public void testFlagE() {
+ sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
+ }
+
+ /*
+ * Install an app on sdcard.
+ */
+ public void testFlagF() {
+ sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
+ }
+ /*
+ * Install an app with both internal and external flags set. should fail
+ */
+ public void testFlagIE() {
+ installFromRawResource("install.apk", R.raw.install,
+ PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_INTERNAL,
+ false,
+ true, PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+
+ /*
+ * Install an app with both internal and external flags set. should fail
+ */
+ public void testFlagIF() {
+ sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK |
+ PackageManager.INSTALL_INTERNAL, true);
+ }
+ /*
+ * Install an app with both internal and external flags set. should fail
+ */
+ public void testFlagEF() {
+ installFromRawResource("install.apk", R.raw.install,
+ PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_EXTERNAL,
+ false,
+ true, PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+ /*
+ * Install an app with both internal and external flags set. should fail
+ */
+ public void testFlagIEF() {
+ installFromRawResource("install.apk", R.raw.install,
+ PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_INTERNAL |
+ PackageManager.INSTALL_EXTERNAL,
+ false,
+ true, PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+ /*
+ * Install an app with both internal and manifest option set.
+ * should install on internal.
+ */
+ public void testFlagIManifestI() {
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ PackageManager.INSTALL_INTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ /*
+ * Install an app with both internal and manifest preference for
+ * preferExternal. Should install on internal.
+ */
+ public void testFlagIManifestE() {
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ PackageManager.INSTALL_INTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ /*
+ * Install an app with both internal and manifest preference for
+ * auto. should install internal.
+ */
+ public void testFlagIManifestA() {
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ PackageManager.INSTALL_INTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ /*
+ * Install an app with both external and manifest option set.
+ * should install externally.
+ */
+ public void testFlagEManifestI() {
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * Install an app with both external and manifest preference for
+ * preferExternal. Should install externally.
+ */
+ public void testFlagEManifestE() {
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * Install an app with both external and manifest preference for
+ * auto. should install on external media.
+ */
+ public void testFlagEManifestA() {
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * Install an app with fwd locked flag set and install location set to
+ * internal. should install internally.
+ */
+ public void testFlagFManifestI() {
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * Install an app with fwd locked flag set and install location set to
+ * preferExternal. should install internally.
+ */
+ public void testFlagFManifestE() {
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * Install an app with fwd locked flag set and install location set to
+ * auto. should install internally.
+ */
+ public void testFlagFManifestA() {
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ PackageManager.INSTALL_EXTERNAL,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /* The following test functions verify install location for existing apps.
+ * ie existing app can be installed internally or externally. If install
+ * flag is explicitly set it should override current location. If manifest location
+ * is set, that should over ride current location too. if not the existing install
+ * location should be honoured.
+ * testFlagI/E/F/ExistingI/E -
+ */
+ public void testFlagIExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ public void testFlagIExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ public void testFlagEExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ public void testFlagEExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ public void testFlagFExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ public void testFlagFExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ -1);
+ }
+ /*
+ * The following set of tests verify the installation of apps with
+ * install location attribute set to internalOnly, preferExternal and auto.
+ * The manifest option should dictate the install location.
+ * public void testManifestI/E/A
+ * TODO out of memory fall back behaviour.
+ */
+ public void testManifestI() {
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ 0,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testManifestE() {
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ 0,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ public void testManifestA() {
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ 0,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ /*
+ * The following set of tests verify the installation of apps
+ * with install location attribute set to internalOnly, preferExternal and auto
+ * for already existing apps. The manifest option should take precedence.
+ * TODO add out of memory fall back behaviour.
+ * testManifestI/E/AExistingI/E
+ */
+ public void testManifestIExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testManifestIExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_internal,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testManifestEExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ public void testManifestEExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ public void testManifestAExistingI() {
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+ public void testManifestAExistingE() {
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ -1);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install_loc_auto,
+ rFlags,
+ true,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+ /*
+ * The following set of tests check install location for existing
+ * application based on user setting.
+ */
+ private int getExpectedInstallLocation(int userSetting) {
+ int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ boolean enable = getUserSettingSetInstallLocation();
+ if (enable) {
+ if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
+ iloc = PackageInfo.INSTALL_LOCATION_AUTO;
+ } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
+ iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
+ } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
+ iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+ }
+ }
+ return iloc;
+ }
+ private void setExistingXUserX(int userSetting, int iFlags, int iloc) {
+ int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+ // First install.
+ installFromRawResource("install.apk", R.raw.install,
+ iFlags,
+ false,
+ false, -1,
+ PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+ int origSetting = getInstallLoc();
+ try {
+ // Set user setting
+ setInstallLoc(userSetting);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ rFlags,
+ true,
+ false, -1,
+ iloc);
+ } finally {
+ setInstallLoc(origSetting);
+ }
+ }
+ public void testExistingIUserI() {
+ int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testExistingIUserE() {
+ int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testExistingIUserA() {
+ int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ }
+ public void testExistingEUserI() {
+ int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ public void testExistingEUserE() {
+ int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ public void testExistingEUserA() {
+ int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int iFlags = PackageManager.INSTALL_EXTERNAL;
+ setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+ /*
+ * The following set of tests verify that the user setting defines
+ * the install location.
+ *
+ */
+ private boolean getUserSettingSetInstallLocation() {
+ try {
+ return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION) != 0;
+
+ } catch (SettingNotFoundException e1) {
+ }
+ return false;
+ }
+
+ private void setUserSettingSetInstallLocation(boolean value) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SET_INSTALL_LOCATION, value ? 1 : 0);
+ }
+ private void setUserX(boolean enable, int userSetting, int iloc) {
+ boolean origUserSetting = getUserSettingSetInstallLocation();
+ int origSetting = getInstallLoc();
+ try {
+ setUserSettingSetInstallLocation(enable);
+ // Set user setting
+ setInstallLoc(userSetting);
+ // Replace now
+ installFromRawResource("install.apk", R.raw.install,
+ 0,
+ true,
+ false, -1,
+ iloc);
+ } finally {
+ // Restore original setting
+ setUserSettingSetInstallLocation(origUserSetting);
+ setInstallLoc(origSetting);
+ }
+ }
+ public void testUserI() {
+ int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int iloc = getExpectedInstallLocation(userSetting);
+ setUserX(true, userSetting, iloc);
+ }
+ public void testUserE() {
+ int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int iloc = getExpectedInstallLocation(userSetting);
+ setUserX(true, userSetting, iloc);
+ }
+ public void testUserA() {
+ int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int iloc = getExpectedInstallLocation(userSetting);
+ setUserX(true, userSetting, iloc);
+ }
+ /*
+ * The following set of tests turn on/off the basic
+ * user setting for turning on install location.
+ */
+ public void testUserPrefOffUserI() {
+ int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ setUserX(false, userSetting, iloc);
+ }
+ public void testUserPrefOffUserE() {
+ int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ setUserX(false, userSetting, iloc);
+ }
+ public void testUserPrefOffA() {
+ int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ setUserX(false, userSetting, iloc);
+ }
+
+ static final String BASE_PERMISSIONS_DEFINED[] = new String[] {
+ PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
+ PERM_DEFINED,
+ "com.android.unit_tests.NORMAL",
+ "com.android.unit_tests.DANGEROUS",
+ "com.android.unit_tests.SIGNATURE",
+ };
+
+ static final String BASE_PERMISSIONS_UNDEFINED[] = new String[] {
+ PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
+ PERM_UNDEFINED,
+ "com.android.unit_tests.NORMAL",
+ "com.android.unit_tests.DANGEROUS",
+ "com.android.unit_tests.SIGNATURE",
+ };
+
+ static final String BASE_PERMISSIONS_USED[] = new String[] {
+ PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+ PERM_USED,
+ "com.android.unit_tests.NORMAL",
+ "com.android.unit_tests.DANGEROUS",
+ "com.android.unit_tests.SIGNATURE",
+ };
+
+ static final String BASE_PERMISSIONS_NOTUSED[] = new String[] {
+ PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+ PERM_NOTUSED,
+ "com.android.unit_tests.NORMAL",
+ "com.android.unit_tests.DANGEROUS",
+ "com.android.unit_tests.SIGNATURE",
+ };
+
+ static final String BASE_PERMISSIONS_SIGUSED[] = new String[] {
+ PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+ PERM_USED,
+ "com.android.unit_tests.SIGNATURE",
+ PERM_NOTUSED,
+ "com.android.unit_tests.NORMAL",
+ "com.android.unit_tests.DANGEROUS",
+ };
+
+ /*
+ * Ensure that permissions are properly declared.
+ */
+ public void testInstallDeclaresPermissions() {
+ InstallParams ip = null;
+ InstallParams ip2 = null;
+ try {
+ // **: Upon installing a package, are its declared permissions published?
+
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int iApk = R.raw.install_decl_perm;
+ ip = installFromRawResource("install.apk", iApk,
+ iFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+
+ // **: Upon installing package, are its permissions granted?
+
+ int i2Flags = PackageManager.INSTALL_INTERNAL;
+ int i2Apk = R.raw.install_use_perm_good;
+ ip2 = installFromRawResource("install2.apk", i2Apk,
+ i2Flags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_USED);
+
+ // **: Upon removing but not deleting, are permissions retained?
+
+ GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+
+ try {
+ invokeDeletePackage(ip.packageURI, PackageManager.DONT_DELETE_DATA,
+ ip.pkg.packageName, receiver);
+ } catch (Exception e) {
+ failStr(e);
+ }
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+ assertPermissions(BASE_PERMISSIONS_USED);
+
+ // **: Upon re-installing, are permissions retained?
+
+ ip = installFromRawResource("install.apk", iApk,
+ iFlags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+ assertPermissions(BASE_PERMISSIONS_USED);
+
+ // **: Upon deleting package, are all permissions removed?
+
+ try {
+ invokeDeletePackage(ip.packageURI, 0,
+ ip.pkg.packageName, receiver);
+ ip = null;
+ } catch (Exception e) {
+ failStr(e);
+ }
+ assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+ assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+ // **: Delete package using permissions; nothing to check here.
+
+ GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.packageName);
+ try {
+ invokeDeletePackage(ip2.packageURI, 0,
+ ip2.pkg.packageName, receiver);
+ ip2 = null;
+ } catch (Exception e) {
+ failStr(e);
+ }
+
+ // **: Re-install package using permissions; no permissions can be granted.
+
+ ip2 = installFromRawResource("install2.apk", i2Apk,
+ i2Flags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+ // **: Upon installing declaring package, are sig permissions granted
+ // to other apps (but not other perms)?
+
+ ip = installFromRawResource("install.apk", iApk,
+ iFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+ assertPermissions(BASE_PERMISSIONS_SIGUSED);
+
+ // **: Re-install package using permissions; are all permissions granted?
+
+ ip2 = installFromRawResource("install2.apk", i2Apk,
+ i2Flags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+ // **: Upon deleting package, are all permissions removed?
+
+ try {
+ invokeDeletePackage(ip.packageURI, 0,
+ ip.pkg.packageName, receiver);
+ ip = null;
+ } catch (Exception e) {
+ failStr(e);
+ }
+ assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+ assertPermissions(BASE_PERMISSIONS_NOTUSED);
+
+ // **: Delete package using permissions; nothing to check here.
+
+ try {
+ invokeDeletePackage(ip2.packageURI, 0,
+ ip2.pkg.packageName, receiver);
+ ip2 = null;
+ } catch (Exception e) {
+ failStr(e);
+ }
+
+ } finally {
+ if (ip2 != null) {
+ cleanUpInstall(ip2);
+ }
+ if (ip != null) {
+ cleanUpInstall(ip);
+ }
+ }
+ }
+
+ /*
+ * Ensure that permissions are properly declared.
+ */
+ public void testInstallOnSdPermissionsUnmount() {
+ InstallParams ip = null;
+ boolean origMediaState = getMediaState();
+ try {
+ // **: Upon installing a package, are its declared permissions published?
+ int iFlags = PackageManager.INSTALL_INTERNAL;
+ int iApk = R.raw.install_decl_perm;
+ ip = installFromRawResource("install.apk", iApk,
+ iFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+ // Unmount media here
+ assertTrue(unmountMedia());
+ // Mount media again
+ mountMedia();
+ //Check permissions now
+ assertPermissions(BASE_PERMISSIONS_DEFINED);
+ } finally {
+ if (ip != null) {
+ cleanUpInstall(ip);
+ }
+ }
+ }
+
+ /* This test creates a stale container via MountService and then installs
+ * a package and verifies that the stale container is cleaned up and install
+ * is successful.
+ * Please note that this test is very closely tied to the framework's
+ * naming convention for secure containers.
+ */
+ public void testInstallSdcardStaleContainer() {
+ boolean origMediaState = getMediaState();
+ try {
+ String outFileName = "install.apk";
+ int rawResId = R.raw.install;
+ PackageManager pm = mContext.getPackageManager();
+ File filesDir = mContext.getFilesDir();
+ File outFile = new File(filesDir, outFileName);
+ Uri packageURI = getInstallablePackage(rawResId, outFile);
+ PackageParser.Package pkg = parsePackage(packageURI);
+ assertNotNull(pkg);
+ // Install an app on sdcard.
+ installFromRawResource(outFileName, rawResId,
+ PackageManager.INSTALL_EXTERNAL, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+ // Unmount sdcard
+ unmountMedia();
+ // Delete the app on sdcard to leave a stale container on sdcard.
+ GenericReceiver receiver = new DeleteReceiver(pkg.packageName);
+ assertTrue(invokeDeletePackage(packageURI, 0, pkg.packageName, receiver));
+ mountMedia();
+ // Reinstall the app and make sure it gets installed.
+ installFromRawResource(outFileName, rawResId,
+ PackageManager.INSTALL_EXTERNAL, true,
+ false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+ } catch (Exception e) {
+ failStr(e.getMessage());
+ } finally {
+ if (origMediaState) {
+ mountMedia();
+ } else {
+ unmountMedia();
+ }
+
+ }
+ }
+ /*---------- Recommended install location tests ----*/
/*
* TODO's
* check version numbers for upgrades
diff --git a/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java
index 8e1ff0b..71a8ea7 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/internal/util/HanziToPinyinTest.java
@@ -17,6 +17,7 @@
package com.android.unit_tests.internal.util;
import java.text.Collator;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Locale;
@@ -37,6 +38,9 @@
@SmallTest
public void testGetToken() throws Exception {
+ if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
+ return;
+ }
ArrayList<Token> tokens = HanziToPinyin.getInstance().get(ONE_HANZI);
assertEquals(tokens.size(), 1);
assertEquals(tokens.get(0).type, Token.PINYIN);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index ce1bf8d..5780c43 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -73,6 +73,7 @@
private static final int LAYOUT_DUMP_DATABASE_CALLBACKS = 41;
private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
private static final int SET_GEOLOCATION_PERMISSION = 43;
+ private static final int OVERRIDE_PREFERENCE = 44;
CallbackProxy(EventSender eventSender,
LayoutTestController layoutTestController) {
@@ -266,6 +267,12 @@
mLayoutTestController.setGeolocationPermission(
msg.arg1 == 1 ? true : false);
break;
+
+ case OVERRIDE_PREFERENCE:
+ String key = msg.getData().getString("key");
+ boolean value = msg.getData().getBoolean("value");
+ mLayoutTestController.overridePreference(key, value);
+ break;
}
}
@@ -461,6 +468,10 @@
WebStorage.getInstance().setQuotaForOrigin("file://", quota);
}
+ public void setAppCacheMaximumSize(long size) {
+ WebStorage.getInstance().setAppCacheMaximumSize(size);
+ }
+
public void setCanOpenWindows() {
obtainMessage(LAYOUT_SET_CAN_OPEN_WINDOWS).sendToTarget();
}
@@ -480,4 +491,11 @@
public void setGeolocationPermission(boolean allow) {
obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
}
+
+ public void overridePreference(String key, boolean value) {
+ Message message = obtainMessage(OVERRIDE_PREFERENCE);
+ message.getData().putString("key", key);
+ message.getData().putBoolean("value", value);
+ message.sendToTarget();
+ }
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 297b963..d10e382 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -82,40 +82,24 @@
// This first block of tests are for HTML5 features, for which Android
// should pass all tests. They are skipped only temporarily.
// TODO: Fix these failing tests and remove them from this list.
- ignoreResultList.add("http/tests/appcache/auth.html"); // file not found
- ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found
- ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found
- ignoreResultList.add("http/tests/appcache/destroyed-frame.html"); // file not found
- ignoreResultList.add("http/tests/appcache/detached-iframe.html"); // file not found
- ignoreResultList.add("http/tests/appcache/different-scheme.html"); // file not found
- ignoreResultList.add("http/tests/appcache/disabled.html"); // not found
+ ignoreResultList.add("http/tests/appcache/auth.html"); // DumpRenderTree throws exception when authentication fails
ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
- ignoreResultList.add("http/tests/appcache/local-content.html"); // text diff
- ignoreResultList.add("http/tests/appcache/max-size.html"); // no layoutTestController.setAppCacheMaximumSize
ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
- ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found
- ignoreResultList.add("storage/database-lock-after-reload.html"); // failure
+ ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
+ ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
// Will always fail
ignoreResultList.add("dom/svg/level3/xpath"); // XPath not supported
- ignoreResultList.add("fast/workers/shared-worker-constructor.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-context-gc.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-event-listener.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-exception.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-frame-lifecycle.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-gc.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-lifecycle.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-load-error.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-location.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-name.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-navigator.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-replace-global-constructor.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-replace-self.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-script-error.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-shared.html"); // shared workers not supported
- ignoreResultList.add("fast/workers/shared-worker-simple.html"); // shared workers not supported
+ ignoreResultList.add("fast/workers"); // workers not supported
ignoreResultList.add("fast/xpath"); // XPath not supported
+ ignoreResultList.add("http/tests/eventsource/workers"); // workers not supported
+ ignoreResultList.add("http/tests/workers"); // workers not supported
+ ignoreResultList.add("http/tests/xmlhttprequest/workers"); // workers not supported
+ ignoreResultList.add("storage/domstorage/localstorage/private-browsing-affects-storage.html"); // private browsing not supported
+ ignoreResultList.add("storage/domstorage/sessionstorage/private-browsing-affects-storage.html"); // private browsing not supported
+ ignoreResultList.add("storage/private-browsing-readonly.html"); // private browsing not supported
+ ignoreResultList.add("websocket/tests/workers"); // workers not supported
// TODO: These need to be triaged
ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707
@@ -189,9 +173,6 @@
ignoreResultList.add("fast/replaced/image-map.html"); // requires eventSender.mouseDown(),mouseUp()
ignoreResultList.add("fast/text/plain-text-line-breaks.html"); // extra spacing because iFrames rendered next to each other on Apple
ignoreResultList.add("profiler"); // profiler is not supported
- ignoreResultList.add("storage/domstorage/localstorage/private-browsing-affects-storage.html"); // No notion of private browsing.
- ignoreResultList.add("storage/domstorage/sessionstorage/private-browsing-affects-storage.html"); // No notion of private browsing.
- ignoreResultList.add("storage/private-browsing-readonly.html"); // No notion of private browsing.
ignoreResultList.add("svg"); // svg is not supported
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index f535ed7..9236345 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -65,4 +65,6 @@
// For Geolocation tests
public void setGeolocationPermission(boolean allow);
+
+ public void overridePreference(String key, boolean value);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 02a7046..24f58b2 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -61,6 +61,9 @@
static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
+ // String constants for use with layoutTestController.overridePreferences
+ private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled";
+
public class AsyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
@@ -459,6 +462,16 @@
mGeolocationPermission = allow;
}
+ public void overridePreference(String key, boolean value) {
+ // TODO: We should look up the correct WebView for the frame which
+ // called the layoutTestController method. Currently, we just use the
+ // WebView for the main frame. EventSender suffers from the same
+ // problem.
+ if (key.equals(WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
+ mWebView.getSettings().setAppCacheEnabled(value);
+ }
+ }
+
private final WebViewClient mViewClient = new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
index 5b75653..e2332bf 100644
--- a/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
@@ -193,11 +193,11 @@
}
private boolean doDebugLogging() {
- return getPreferences().getBoolean(DEBUG_PREF, true);
+ return getPreferences().getBoolean(DEBUG_PREF, false);
}
private boolean trackSignalStrength() {
- return getPreferences().getBoolean(SIGNAL_PREF, true);
+ return getPreferences().getBoolean(SIGNAL_PREF, false);
}
private float getLocationMinDistance() {
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 08530a0..3308a35 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -37,12 +37,12 @@
mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
mUpdate(false), mExtending(false),
mRequireLocalization(false), mPseudolocalize(false),
- mUTF8(false), mEncodingSpecified(false), mValues(false),
+ mWantUTF16(false), mValues(false),
mCompressionMethod(0), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
- mRClassDir(NULL), mResourceIntermediatesDir(NULL),
+ mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
mArgc(0), mArgv(NULL)
@@ -77,10 +77,8 @@
void setRequireLocalization(bool val) { mRequireLocalization = val; }
bool getPseudolocalize(void) const { return mPseudolocalize; }
void setPseudolocalize(bool val) { mPseudolocalize = val; }
- bool getUTF8(void) const { return mUTF8; }
- void setUTF8(bool val) { mUTF8 = val; }
- bool getEncodingSpecified(void) const { return mEncodingSpecified; }
- void setEncodingSpecified(bool val) { mEncodingSpecified = val; }
+ bool getWantUTF16(void) const { return mWantUTF16; }
+ void setWantUTF16(bool val) { mWantUTF16 = val; }
bool getValues(void) const { return mValues; }
void setValues(bool val) { mValues = val; }
int getCompressionMethod(void) const { return mCompressionMethod; }
@@ -122,13 +120,10 @@
const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; }
void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
+ const char* getManifestMinSdkVersion() const { return mManifestMinSdkVersion; }
+ void setManifestMinSdkVersion(const char* val) { mManifestMinSdkVersion = val; }
const char* getMinSdkVersion() const { return mMinSdkVersion; }
- void setMinSdkVersion(const char* val) {
- mMinSdkVersion = val;
- if (!mEncodingSpecified) {
- setUTF8(isUTF8Available());
- }
- }
+ void setMinSdkVersion(const char* val) { mMinSdkVersion = val; }
const char* getTargetSdkVersion() const { return mTargetSdkVersion; }
void setTargetSdkVersion(const char* val) { mTargetSdkVersion = val; }
const char* getMaxSdkVersion() const { return mMaxSdkVersion; }
@@ -167,6 +162,35 @@
void setPackageCount(int val) { mPackageCount = val; }
#endif
+ /* Certain features may only be available on a specific SDK level or
+ * above. SDK levels that have a non-numeric identifier are assumed
+ * to be newer than any SDK level that has a number designated.
+ */
+ bool isMinSdkAtLeast(int desired) {
+ /* If the application specifies a minSdkVersion in the manifest
+ * then use that. Otherwise, check what the user specified on
+ * the command line. If neither, it's not available since
+ * the minimum SDK version is assumed to be 1.
+ */
+ const char *minVer;
+ if (mManifestMinSdkVersion != NULL) {
+ minVer = mManifestMinSdkVersion;
+ } else if (mMinSdkVersion != NULL) {
+ minVer = mMinSdkVersion;
+ } else {
+ return false;
+ }
+
+ char *end;
+ int minSdkNum = (int)strtol(minVer, &end, 0);
+ if (*end == '\0') {
+ if (minSdkNum < desired) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private:
/* commands & modifiers */
Command mCmd;
@@ -179,8 +203,7 @@
bool mExtending;
bool mRequireLocalization;
bool mPseudolocalize;
- bool mUTF8;
- bool mEncodingSpecified;
+ bool mWantUTF16;
bool mValues;
int mCompressionMethod;
bool mJunkPath;
@@ -200,6 +223,7 @@
android::Vector<const char*> mNoCompressExtensions;
android::Vector<const char*> mResourceSourceDirs;
+ const char* mManifestMinSdkVersion;
const char* mMinSdkVersion;
const char* mTargetSdkVersion;
const char* mMaxSdkVersion;
@@ -216,19 +240,6 @@
int mPackageCount;
#endif
- /* UTF-8 is only available on APIs 7 or above or
- * SDK levels that have code names.
- */
- bool isUTF8Available() {
- char *end;
- int minSdkNum = (int)strtol(mMinSdkVersion, &end, 0);
- if (*end == '\0') {
- if (minSdkNum < 7) {
- return false;
- }
- }
- return true;
- }
};
#endif // __BUNDLE_H
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index dd98c85..b0c6e39 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -446,8 +446,7 @@
}
bundle.setCustomPackage(argv[0]);
} else if (strcmp(cp, "-utf16") == 0) {
- bundle.setEncodingSpecified(true);
- bundle.setUTF8(false);
+ bundle.setWantUTF16(true);
} else if (strcmp(cp, "-rename-manifest-package") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index c0ebb59..cafd635 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -226,7 +226,7 @@
if (minSdkIndex >= 0) {
const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
const char* minSdk8 = strdup(String8(minSdk16).string());
- bundle->setMinSdkVersion(minSdk8);
+ bundle->setManifestMinSdkVersion(minSdk8);
}
}
}
@@ -611,7 +611,8 @@
const String16 attr(attr8);
if (node->getAttribute(ns, attr) != NULL) {
- fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n",
+ fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
+ " using existing value in manifest.\n",
String8(attr).string(), String8(ns).string());
return;
}
@@ -768,7 +769,13 @@
// Standard flags for compiled XML and optional UTF-8 encoding
int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
- if (bundle->getUTF8()) {
+
+ /* Only enable UTF-8 if the caller of aapt didn't specifically
+ * request UTF-16 encoding and the parameters of this package
+ * allow UTF-8 to be used.
+ */
+ if (!bundle->getWantUTF16()
+ && bundle->isMinSdkAtLeast(SDK_FROYO)) {
xmlFlags |= XML_COMPILE_UTF8;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index ab5e937..a2f085a 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2527,9 +2527,11 @@
const size_t N = mOrderedPackages.size();
size_t pi;
+ bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
+
// Iterate through all data, collecting all values (strings,
// references, etc).
- StringPool valueStrings = StringPool(false, bundle->getUTF8());
+ StringPool valueStrings = StringPool(false, useUTF8);
for (pi=0; pi<N; pi++) {
sp<Package> p = mOrderedPackages.itemAt(pi);
if (p->getTypes().size() == 0) {
@@ -2537,8 +2539,8 @@
continue;
}
- StringPool typeStrings = StringPool(false, bundle->getUTF8());
- StringPool keyStrings = StringPool(false, bundle->getUTF8());
+ StringPool typeStrings = StringPool(false, useUTF8);
+ StringPool keyStrings = StringPool(false, useUTF8);
const size_t N = p->getOrderedTypes().size();
for (size_t ti=0; ti<N; ti++) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 8e9e75f..4201e80 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -1032,7 +1032,7 @@
@SuppressWarnings("unused")
public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4,
- boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Surface arg8)
+ boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
throws RemoteException {
// pass for now.
return 0;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index d833e33..5fd44b1 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -81,5 +81,7 @@
boolean setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable);
int getWifiApEnabledState();
+
+ WifiConfiguration getWifiApConfiguration();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9ef8ba1..970d5fc 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -810,6 +810,20 @@
}
/**
+ * Gets the Wi-Fi AP Configuration.
+ * @return AP details in WifiConfiguration
+ *
+ * @hide Dont open yet
+ */
+ public WifiConfiguration getWifiApConfiguration() {
+ try {
+ return mService.getWifiApConfiguration();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
* Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 9339428..4f84aab 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -54,10 +54,11 @@
import android.database.ContentObserver;
import com.android.internal.app.IBatteryStats;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Set;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Track the state of Wifi connectivity. All event handling is done here,
@@ -282,8 +283,13 @@
* {@link WifiManager#WIFI_STATE_ENABLED},
* {@link WifiManager#WIFI_STATE_ENABLING},
* {@link WifiManager#WIFI_STATE_UNKNOWN}
+ *
+ * getWifiState() is not synchronized to make sure it's always fast,
+ * even when the instance lock is held on other slow operations.
+ * Use a atomic variable for state.
*/
- private int mWifiState;
+ private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
+
// Wi-Fi run states:
private static final int RUN_STATE_STARTING = 1;
private static final int RUN_STATE_RUNNING = 2;
@@ -307,8 +313,6 @@
private String mInterfaceName;
private static String LS = System.getProperty("line.separator");
- private Runnable mReleaseWakeLockCallback;
-
private static String[] sDnsPropNames;
/**
@@ -609,7 +613,15 @@
}
}
- private synchronized boolean isDriverStopped() {
+ /**
+ * TODO: mRunState is not synchronized in some places
+ * address this as part of re-architect.
+ *
+ * TODO: We are exposing an additional public synchronized call
+ * for a wakelock optimization in WifiService. Remove it
+ * when we handle the wakelock in ConnectivityService.
+ */
+ public synchronized boolean isDriverStopped() {
return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
}
@@ -668,15 +680,20 @@
}
}
+ /**
+ * We release the wakelock in WifiService
+ * using a timer.
+ *
+ * TODO:
+ * Releasing wakelock using both timer and
+ * a call from ConnectivityService requires
+ * a rethink. We had problems where WifiService
+ * could keep a wakelock forever if we delete
+ * messages in the asynchronous call
+ * from ConnectivityService
+ */
@Override
public void releaseWakeLock() {
- if (mReleaseWakeLockCallback != null) {
- mReleaseWakeLockCallback.run();
- }
- }
-
- public void setReleaseWakeLockCallback(Runnable callback) {
- mReleaseWakeLockCallback = callback;
}
/**
@@ -785,8 +802,8 @@
case EVENT_SUPPLICANT_DISCONNECT:
mRunState = RUN_STATE_STOPPED;
noteRunState();
- boolean died = mWifiState != WIFI_STATE_DISABLED &&
- mWifiState != WIFI_STATE_DISABLING;
+ boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
+ mWifiState.get() != WIFI_STATE_DISABLING;
if (died) {
if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
} else {
@@ -1074,7 +1091,11 @@
break;
case EVENT_DEFERRED_RECONNECT:
- String BSSID = msg.obj.toString();
+ /**
+ * mLastBssid can be null when there is a reconnect
+ * request on the first BSSID we connect to
+ */
+ String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
/**
* If we've exceeded the maximum number of retries for reconnecting
* to a given network, blacklist the BSSID to allow a connection attempt on
@@ -1471,20 +1492,6 @@
} else {
return disconnect();
}
- } else {
- /*
- * The "driver-stop" wake lock normally is released from the
- * connectivity manager after the mobile data connection has
- * been established, or after a timeout period, if that never
- * happens. Because WifiService.updateWifiState() can get called
- * multiple times, we can end up acquiring the wake lock and calling
- * disconnectAndStop() even when a disconnect or stop operation
- * is already in progress. In that case, we want to ignore the
- * disconnectAndStop request and release the (ref-counted) wake
- * lock, so that eventually, when the mobile data connection is
- * established, the ref count will drop to zero.
- */
- releaseWakeLock();
}
return true;
}
@@ -1500,12 +1507,12 @@
return true;
}
- public synchronized int getWifiState() {
- return mWifiState;
+ public int getWifiState() {
+ return mWifiState.get();
}
- public synchronized void setWifiState(int wifiState) {
- mWifiState = wifiState;
+ public void setWifiState(int wifiState) {
+ mWifiState.set(wifiState);
}
/**
@@ -1584,7 +1591,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean ping() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.pingCommand();
@@ -1597,7 +1604,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean scan(boolean forceActive) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.scanCommand(forceActive);
@@ -1613,7 +1620,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean setScanResultHandling(int mode) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.setScanResultHandlingCommand(mode);
@@ -1627,7 +1634,7 @@
* 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
*/
public synchronized String scanResults() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return null;
}
return WifiNative.scanResultsCommand();
@@ -1639,7 +1646,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean setScanMode(boolean isScanModeActive) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
if (mIsScanModeActive != isScanModeActive) {
@@ -1654,7 +1661,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean disconnect() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.disconnectCommand();
@@ -1666,7 +1673,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean reconnectCommand() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.reconnectCommand();
@@ -1678,7 +1685,7 @@
* @return network id of the new network
*/
public synchronized int addNetwork() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return -1;
}
return WifiNative.addNetworkCommand();
@@ -1691,7 +1698,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean removeNetwork(int networkId) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
@@ -1705,7 +1712,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.enableNetworkCommand(netId, disableOthers);
@@ -1718,7 +1725,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean disableNetwork(int netId) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.disableNetworkCommand(netId);
@@ -1730,7 +1737,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean reassociate() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.reassociateCommand();
@@ -1744,7 +1751,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean addToBlacklist(String bssid) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.addToBlacklistCommand(bssid);
@@ -1756,7 +1763,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean clearBlacklist() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.clearBlacklistCommand();
@@ -1768,7 +1775,7 @@
* @return list of networks or null on failure
*/
public synchronized String listNetworks() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return null;
}
return WifiNative.listNetworksCommand();
@@ -1782,7 +1789,7 @@
* @return value corresponding to key
*/
public synchronized String getNetworkVariable(int netId, String name) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return null;
}
return WifiNative.getNetworkVariableCommand(netId, name);
@@ -1797,7 +1804,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean setNetworkVariable(int netId, String name, String value) {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.setNetworkVariableCommand(netId, name, value);
@@ -1817,7 +1824,7 @@
* ip_address=X.X.X.X
*/
public synchronized String status() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return null;
}
return WifiNative.statusCommand();
@@ -1829,7 +1836,7 @@
* @return RSSI value, -1 on failure
*/
public synchronized int getRssi() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return -1;
}
return WifiNative.getRssiApproxCommand();
@@ -1841,7 +1848,7 @@
* @return RSSI value, -1 on failure
*/
public synchronized int getRssiApprox() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return -1;
}
return WifiNative.getRssiApproxCommand();
@@ -1853,7 +1860,7 @@
* @return link speed, -1 on failure
*/
public synchronized int getLinkSpeed() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return -1;
}
return WifiNative.getLinkSpeedCommand();
@@ -1865,7 +1872,7 @@
* @return MAC address, null on failure
*/
public synchronized String getMacAddress() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return null;
}
return WifiNative.getMacAddressCommand();
@@ -1877,7 +1884,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean startDriver() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.startDriverCommand();
@@ -1889,7 +1896,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean stopDriver() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.stopDriverCommand();
@@ -1901,7 +1908,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean startPacketFiltering() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.startPacketFiltering();
@@ -1913,7 +1920,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean stopPacketFiltering() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.stopPacketFiltering();
@@ -1927,7 +1934,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean setPowerMode(int mode) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.setPowerModeCommand(mode);
@@ -1940,7 +1947,7 @@
* the number of channels is invalid.
*/
public synchronized boolean setNumAllowedChannels() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
try {
@@ -1965,7 +1972,7 @@
* {@code numChannels} is outside the valid range.
*/
public synchronized boolean setNumAllowedChannels(int numChannels) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
mNumAllowedChannels = numChannels;
@@ -1978,7 +1985,7 @@
* @return channel count, -1 on failure
*/
public synchronized int getNumAllowedChannels() {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return -1;
}
return WifiNative.getNumAllowedChannelsCommand();
@@ -1994,7 +2001,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean setBluetoothCoexistenceMode(int mode) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return false;
}
return WifiNative.setBluetoothCoexistenceModeCommand(mode);
@@ -2008,7 +2015,7 @@
* @param isBluetoothPlaying whether to enable or disable this mode
*/
public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
- if (mWifiState != WIFI_STATE_ENABLED && !isDriverStopped()) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
return;
}
WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
@@ -2020,7 +2027,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean saveConfig() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.saveConfigCommand();
@@ -2032,7 +2039,7 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public synchronized boolean reloadConfig() {
- if (mWifiState != WIFI_STATE_ENABLED) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
return WifiNative.reloadConfigCommand();