Merge "Add setRadioState to MockRilController and the corresponding test case. Add test case for setRadioState command in Mock Ril"
diff --git a/Android.mk b/Android.mk
index 7c29c73..39bbbddc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -123,6 +123,7 @@
core/java/android/os/storage/IMountService.aidl \
core/java/android/os/storage/IMountServiceListener.aidl \
core/java/android/os/storage/IMountShutdownObserver.aidl \
+ core/java/android/os/storage/IObbActionListener.aidl \
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/INetStatService.aidl \
core/java/android/os/IPermissionController.aidl \
@@ -368,7 +369,7 @@
-since ./frameworks/base/api/7.xml 7 \
-since ./frameworks/base/api/8.xml 8 \
-since ./frameworks/base/api/current.xml HC \
- -error 1 -error 2 -warning 3 -error 4 -error 6 -error 8 \
+ -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 \
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
@@ -379,6 +380,8 @@
# (see development/build/sdk.atree)
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
+ -samplecode $(sample_dir)/AccessibilityService \
+ resources/samples/AccessibilityService "Accessibility Service" \
-samplecode $(sample_dir)/ApiDemos \
resources/samples/ApiDemos "API Demos" \
-samplecode $(sample_dir)/BackupRestore \
diff --git a/api/9.xml b/api/9.xml
index f151a16..7a4aed0 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -74123,7 +74123,7 @@
type="float"
transient="false"
volatile="false"
- value="0.0010f"
+ value="0.001f"
static="true"
final="true"
deprecated="not deprecated"
@@ -195395,6 +195395,39 @@
<parameter name="defStyle" type="int">
</parameter>
</constructor>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<method name="afterTextChanged"
return="void"
abstract="false"
@@ -203851,39 +203884,6 @@
<parameter name="y" type="int">
</parameter>
</method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<class name="ListView.FixedViewInfo"
extends="java.lang.Object"
diff --git a/api/current.xml b/api/current.xml
index 61c1cac..7b3b9e5 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1482,6 +1482,28 @@
visibility="public"
>
</field>
+<field name="animator_fade_in"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432609"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="animator_fade_out"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432610"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="anticipate_interpolator"
type="int"
transient="false"
@@ -4310,6 +4332,50 @@
visibility="public"
>
</field>
+<field name="fragmentCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843561"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843562"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843559"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843560"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="freezesText"
type="int"
transient="false"
@@ -4904,6 +4970,39 @@
visibility="public"
>
</field>
+<field name="imeSubtypeExtraValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843566"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="imeSubtypeLocale"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843564"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="imeSubtypeMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843565"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="immersive"
type="int"
transient="false"
@@ -10580,6 +10679,28 @@
visibility="public"
>
</field>
+<field name="windowActionBarOverlay"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843558"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="windowActionBarSize"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843563"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="windowActionBarStyle"
type="int"
transient="false"
@@ -19991,6 +20112,17 @@
visibility="public"
>
</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="removeUpdateListener"
return="void"
abstract="false"
@@ -20004,6 +20136,17 @@
<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener">
</parameter>
</method>
+<method name="reverse"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="setCurrentPlayTime"
return="void"
abstract="false"
@@ -20795,6 +20938,19 @@
<parameter name="sequenceItems" type="android.animation.Animatable...">
</parameter>
</method>
+<method name="setTarget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+</method>
</class>
<class name="Sequencer.Builder"
extends="java.lang.Object"
@@ -20936,6 +21092,17 @@
visibility="public"
>
</method>
+<method name="getHeight"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNavigationMode"
return="int"
abstract="true"
@@ -20980,6 +21147,17 @@
visibility="public"
>
</method>
+<method name="hide"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="insertTab"
return="void"
abstract="true"
@@ -20995,6 +21173,17 @@
<parameter name="position" type="int">
</parameter>
</method>
+<method name="isShowing"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="newTab"
return="android.app.ActionBar.Tab"
abstract="true"
@@ -21287,6 +21476,17 @@
<parameter name="resId" type="int">
</parameter>
</method>
+<method name="show"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<field name="DISPLAY_HIDE_HOME"
type="int"
transient="false"
@@ -21651,7 +21851,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="id" type="int">
@@ -21664,7 +21864,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="tag" type="java.lang.String">
@@ -21812,6 +22012,17 @@
visibility="public"
>
</method>
+<method name="getFragmentManager"
+ return="android.app.FragmentManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getInstanceCount"
return="long"
abstract="false"
@@ -22850,7 +23061,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -22887,7 +23098,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -22898,7 +23109,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="name" type="java.lang.String">
@@ -22913,7 +23124,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="id" type="int">
@@ -26353,6 +26564,17 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="getActionBar"
+ return="android.app.ActionBar"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getContext"
return="android.content.Context"
abstract="false"
@@ -26430,6 +26652,17 @@
visibility="public"
>
</method>
+<method name="invalidateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isShowing"
return="boolean"
abstract="false"
@@ -27232,18 +27465,6 @@
visibility="public"
>
</constructor>
-<constructor name="DialogFragment"
- type="android.app.DialogFragment"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="style" type="int">
-</parameter>
-<parameter name="theme" type="int">
-</parameter>
-</constructor>
<method name="dismiss"
return="void"
abstract="false"
@@ -27340,6 +27561,21 @@
<parameter name="cancelable" type="boolean">
</parameter>
</method>
+<method name="setStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="style" type="int">
+</parameter>
+<parameter name="theme" type="int">
+</parameter>
+</method>
<method name="show"
return="void"
abstract="false"
@@ -27620,6 +27856,28 @@
visibility="public"
>
</method>
+<method name="getArguments"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFragmentManager"
+ return="android.app.FragmentManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getId"
return="int"
abstract="false"
@@ -27664,6 +27922,28 @@
visibility="public"
>
</method>
+<method name="getTargetFragment"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTargetRequestCode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getView"
return="android.view.View"
abstract="false"
@@ -27700,18 +27980,23 @@
</parameter>
<parameter name="fname" type="java.lang.String">
</parameter>
-<exception name="ClassNotFoundException" type="java.lang.ClassNotFoundException">
-</exception>
-<exception name="IllegalAccessException" type="java.lang.IllegalAccessException">
-</exception>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="InstantiationException" type="java.lang.InstantiationException">
-</exception>
-<exception name="InvocationTargetException" type="java.lang.reflect.InvocationTargetException">
-</exception>
-<exception name="NoSuchMethodException" type="java.lang.NoSuchMethodException">
-</exception>
+</method>
+<method name="instantiate"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="fname" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
</method>
<method name="isAdded"
return="boolean"
@@ -27839,8 +28124,8 @@
<parameter name="savedInstanceState" type="android.os.Bundle">
</parameter>
</method>
-<method name="onCreateAnimation"
- return="android.view.animation.Animation"
+<method name="onCreateAnimatable"
+ return="android.animation.Animatable"
abstract="false"
native="false"
synchronized="false"
@@ -27961,8 +28246,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="activity" type="android.app.Activity">
-</parameter>
<parameter name="attrs" type="android.util.AttributeSet">
</parameter>
<parameter name="savedInstanceState" type="android.os.Bundle">
@@ -28088,6 +28371,19 @@
<parameter name="view" type="android.view.View">
</parameter>
</method>
+<method name="setArguments"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
<method name="setHasOptionsMenu"
return="void"
abstract="false"
@@ -28114,6 +28410,21 @@
<parameter name="retain" type="boolean">
</parameter>
</method>
+<method name="setTargetFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="false"
@@ -28156,6 +28467,156 @@
</parameter>
</method>
</class>
+<class name="Fragment.InstantiationException"
+ extends="android.util.AndroidRuntimeException"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Fragment.InstantiationException"
+ type="android.app.Fragment.InstantiationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="msg" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Exception">
+</parameter>
+</constructor>
+</class>
+<interface name="FragmentManager"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="findFragmentById"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="findFragmentByTag"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="getFragment"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bundle" type="android.os.Bundle">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="openTransaction"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="putFragment"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bundle" type="android.os.Bundle">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<field name="POP_BACK_STACK_INCLUSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
<interface name="FragmentTransaction"
abstract="true"
static="false"
@@ -28346,39 +28807,6 @@
<parameter name="fragment" type="android.app.Fragment">
</parameter>
</method>
-<field name="TRANSIT_ACTIVITY_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8199"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_ACTIVITY_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4102"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_ENTER"
- type="int"
- transient="false"
- volatile="false"
- value="4097"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TRANSIT_ENTER_MASK"
type="int"
transient="false"
@@ -28390,17 +28818,6 @@
visibility="public"
>
</field>
-<field name="TRANSIT_EXIT"
- type="int"
- transient="false"
- volatile="false"
- value="8194"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TRANSIT_EXIT_MASK"
type="int"
transient="false"
@@ -28412,11 +28829,22 @@
visibility="public"
>
</field>
-<field name="TRANSIT_HIDE"
+<field name="TRANSIT_FRAGMENT_CLOSE"
type="int"
transient="false"
volatile="false"
- value="8196"
+ value="8194"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_FRAGMENT_OPEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4097"
static="true"
final="true"
deprecated="not deprecated"
@@ -28434,72 +28862,6 @@
visibility="public"
>
</field>
-<field name="TRANSIT_PREVIEW_DONE"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_SHOW"
- type="int"
- transient="false"
- volatile="false"
- value="4099"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8201"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4104"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_TO_BACK"
- type="int"
- transient="false"
- volatile="false"
- value="8203"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_TO_FRONT"
- type="int"
- transient="false"
- volatile="false"
- value="4106"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TRANSIT_UNSET"
type="int"
transient="false"
@@ -28511,50 +28873,6 @@
visibility="public"
>
</field>
-<field name="TRANSIT_WALLPAPER_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8204"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_INTRA_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8207"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_INTRA_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4110"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4109"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</interface>
<class name="Instrumentation"
extends="java.lang.Object"
@@ -30658,6 +30976,17 @@
<parameter name="holder" type="android.view.SurfaceHolder">
</parameter>
</method>
+<field name="KEY_NATIVE_SAVED_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android:native_state""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="META_DATA_LIB_NAME"
type="java.lang.String"
transient="false"
@@ -35882,8 +36211,6 @@
>
<parameter name="appWidgetIds" type="int[]">
</parameter>
-<parameter name="views" type="android.widget.RemoteViews">
-</parameter>
<parameter name="viewId" type="int">
</parameter>
</method>
@@ -35899,9 +36226,37 @@
>
<parameter name="appWidgetId" type="int">
</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetIds" type="int[]">
+</parameter>
<parameter name="views" type="android.widget.RemoteViews">
</parameter>
-<parameter name="viewId" type="int">
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetId" type="int">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
</parameter>
</method>
<method name="updateAppWidget"
@@ -61912,6 +62267,21 @@
<parameter name="sqlString" type="java.lang.String">
</parameter>
</method>
+<method name="appendSelectionArgs"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalValues" type="java.lang.String[]">
+</parameter>
+<parameter name="newValues" type="java.lang.String[]">
+</parameter>
+</method>
<method name="appendValueToSql"
return="void"
abstract="false"
@@ -61944,6 +62314,38 @@
<parameter name="value" type="java.lang.Object">
</parameter>
</method>
+<method name="blobFileDescriptorForQuery"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="query" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="blobFileDescriptorForQuery"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prog" type="android.database.sqlite.SQLiteStatement">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
<method name="createDbFromSqlStatements"
return="void"
abstract="false"
@@ -65759,6 +66161,17 @@
visibility="public"
>
</method>
+<method name="simpleQueryForBlobFileDescriptor"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="simpleQueryForLong"
return="long"
abstract="false"
@@ -73750,7 +74163,7 @@
<method name="setShadowLayer"
return="void"
abstract="false"
- native="true"
+ native="false"
synchronized="false"
static="false"
final="false"
@@ -84869,7 +85282,7 @@
type="float"
transient="false"
volatile="false"
- value="0.0010f"
+ value="0.001f"
static="true"
final="true"
deprecated="not deprecated"
@@ -86751,6 +87164,24 @@
</parameter>
<parameter name="modeId" type="int">
</parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</constructor>
+<constructor name="Keyboard"
+ type="android.inputmethodservice.Keyboard"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="xmlLayoutResId" type="int">
+</parameter>
+<parameter name="modeId" type="int">
+</parameter>
</constructor>
<constructor name="Keyboard"
type="android.inputmethodservice.Keyboard"
@@ -91791,7 +92222,7 @@
type="int"
transient="false"
volatile="false"
- value="1"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
@@ -91802,7 +92233,7 @@
type="int"
transient="false"
volatile="false"
- value="0"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
@@ -102793,6 +103224,17 @@
visibility="public"
>
</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ERROR_DEVICE_NOT_FOUND"
type="int"
transient="false"
@@ -104563,6 +105005,21 @@
visibility="public"
>
</method>
+<method name="getBooleanQueryParameter"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="boolean">
+</parameter>
+</method>
<method name="getEncodedAuthority"
return="java.lang.String"
abstract="true"
@@ -127839,6 +128296,16 @@
visibility="public"
>
</field>
+<field name="SERIAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TAGS"
type="java.lang.String"
transient="false"
@@ -134154,6 +134621,23 @@
visibility="public"
>
</method>
+<method name="fromData"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
<method name="fromSocket"
return="android.os.ParcelFileDescriptor"
abstract="false"
@@ -136133,6 +136617,8 @@
>
<parameter name="filename" type="java.lang.String">
</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
</method>
<method name="isUsbMassStorageConnected"
return="boolean"
@@ -136198,6 +136684,8 @@
</parameter>
<parameter name="force" type="boolean">
</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
</method>
<method name="unregisterListener"
return="void"
@@ -137372,6 +137860,17 @@
visibility="public"
>
</method>
+<method name="getExtras"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getFragment"
return="java.lang.String"
abstract="false"
@@ -137815,6 +138314,17 @@
<parameter name="defaultValue" type="java.lang.Object">
</parameter>
</method>
+<method name="peekExtras"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="persistBoolean"
return="boolean"
abstract="false"
@@ -138347,6 +138857,39 @@
visibility="public"
>
</method>
+<method name="hasHeaders"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="invalidateHeaders"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isMultiPane"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="loadHeadersFromResource"
return="void"
abstract="false"
@@ -138375,8 +138918,8 @@
<parameter name="target" type="java.util.List<android.preference.PreferenceActivity.Header>">
</parameter>
</method>
-<method name="onGetInitialFragment"
- return="java.lang.String"
+<method name="onGetInitialHeader"
+ return="android.preference.PreferenceActivity.Header"
abstract="false"
native="false"
synchronized="false"
@@ -138478,6 +139021,8 @@
>
<parameter name="fragmentName" type="java.lang.String">
</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
</method>
<method name="switchToHeader"
return="void"
@@ -138491,7 +139036,42 @@
>
<parameter name="fragmentName" type="java.lang.String">
</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
</method>
+<field name="EXTRA_NO_HEADERS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="":android:no_headers""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_SHOW_FRAGMENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="":android:show_fragment""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_SHOW_FRAGMENT_ARGUMENTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="":android:show_fragment_args""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="PreferenceActivity.Header"
extends="java.lang.Object"
@@ -138509,6 +139089,66 @@
visibility="public"
>
</constructor>
+<field name="fragment"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentArguments"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="icon"
+ type="android.graphics.drawable.Drawable"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="iconRes"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="summary"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="title"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="PreferenceCategory"
extends="android.preference.PreferenceGroup"
@@ -147119,11 +147759,11 @@
visibility="public"
>
</field>
-<field name="CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY"
+<field name="CAPABILITY_HAS_VIDEO"
type="int"
transient="false"
volatile="false"
- value="1"
+ value="2"
static="true"
final="true"
deprecated="not deprecated"
@@ -147134,7 +147774,7 @@
type="int"
transient="false"
volatile="false"
- value="2"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
@@ -179126,6 +179766,18 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AndroidException"
+ type="android.util.AndroidException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="cause" type="java.lang.Exception">
</parameter>
</constructor>
@@ -179163,6 +179815,18 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AndroidRuntimeException"
+ type="android.util.AndroidRuntimeException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="cause" type="java.lang.Exception">
</parameter>
</constructor>
@@ -193670,6 +194334,28 @@
visibility="public"
>
</method>
+<method name="getRotationX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRotationY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getScaleX"
return="float"
abstract="false"
@@ -195840,6 +196526,32 @@
<parameter name="rotation" type="float">
</parameter>
</method>
+<method name="setRotationX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationX" type="float">
+</parameter>
+</method>
+<method name="setRotationY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationY" type="float">
+</parameter>
+</method>
<method name="setSaveEnabled"
return="void"
abstract="false"
@@ -201044,7 +201756,7 @@
visibility="public"
>
</field>
-<field name="FEATURE_ACTION_MODE_OVERLAY"
+<field name="FEATURE_ACTION_BAR_OVERLAY"
type="int"
transient="false"
volatile="false"
@@ -201055,6 +201767,17 @@
visibility="public"
>
</field>
+<field name="FEATURE_ACTION_MODE_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FEATURE_CONTEXT_MENU"
type="int"
transient="false"
@@ -201081,7 +201804,7 @@
type="int"
transient="false"
volatile="false"
- value="10"
+ value="11"
static="true"
final="true"
deprecated="not deprecated"
@@ -208140,6 +208863,17 @@
visibility="public"
>
</method>
+<method name="getSubtypes"
+ return="java.util.ArrayList<android.view.inputmethod.InputMethodInfo.InputMethodSubtype>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="loadIcon"
return="android.graphics.drawable.Drawable"
abstract="false"
@@ -208192,6 +208926,108 @@
>
</field>
</class>
+<class name="InputMethodInfo.InputMethodSubtype"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExtraValue"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIconId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMode"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="InputMethodManager"
extends="java.lang.Object"
abstract="false"
@@ -213236,17 +214072,6 @@
visibility="public"
>
</method>
-<method name="getVisibleTitleHeight"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getZoomControls"
return="android.view.View"
abstract="false"
@@ -214668,6 +215493,17 @@
<parameter name="after" type="int">
</parameter>
</method>
+<method name="clearChoices"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="clearTextFilter"
return="void"
abstract="false"
@@ -214690,6 +215526,61 @@
visibility="public"
>
</method>
+<method name="getCheckedItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemIds"
+ return="long[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPositions"
+ return="android.util.SparseBooleanArray"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getChoiceMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getListPaddingBottom"
return="int"
abstract="false"
@@ -214833,6 +215724,19 @@
visibility="protected"
>
</method>
+<method name="isItemChecked"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="isScrollingCacheEnabled"
return="boolean"
abstract="false"
@@ -215033,6 +215937,19 @@
<parameter name="views" type="java.util.List<android.view.View>">
</parameter>
</method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
<method name="setCacheColorHint"
return="void"
abstract="false"
@@ -215046,6 +215963,19 @@
<parameter name="color" type="int">
</parameter>
</method>
+<method name="setChoiceMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="choiceMode" type="int">
+</parameter>
+</method>
<method name="setDrawSelectorOnTop"
return="void"
abstract="false"
@@ -215085,6 +216015,34 @@
<parameter name="filterText" type="java.lang.String">
</parameter>
</method>
+<method name="setItemChecked"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="value" type="boolean">
+</parameter>
+</method>
+<method name="setMultiChoiceModeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.AbsListView.MultiChoiceModeListener">
+</parameter>
+</method>
<method name="setOnScrollListener"
return="void"
abstract="false"
@@ -215301,6 +216259,50 @@
<parameter name="dr" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_MULTIPLE_MODAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TRANSCRIPT_MODE_ALWAYS_SCROLL"
type="int"
transient="false"
@@ -215392,6 +216394,35 @@
</parameter>
</constructor>
</class>
+<interface name="AbsListView.MultiChoiceModeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.ActionMode.Callback">
+</implements>
+<method name="onItemCheckedStateChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="id" type="long">
+</parameter>
+<parameter name="checked" type="boolean">
+</parameter>
+</method>
+</interface>
<interface name="AbsListView.OnScrollListener"
abstract="true"
static="true"
@@ -216570,7 +217601,7 @@
</interface>
<class name="AdapterViewAnimator"
extends="android.widget.AdapterView"
- abstract="false"
+ abstract="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -216620,28 +217651,6 @@
visibility="public"
>
</method>
-<method name="getDefaultInAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDefaultOutAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getDisplayedChild"
return="int"
abstract="false"
@@ -216708,6 +217717,30 @@
visibility="public"
>
</method>
+<method name="onRestoreInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="android.os.Parcelable">
+</parameter>
+</method>
+<method name="onSaveInstanceState"
+ return="android.os.Parcelable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="setAdapter"
return="void"
abstract="false"
@@ -216840,23 +217873,6 @@
visibility="public"
>
</method>
-<method name="showOnly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="childIndex" type="int">
-</parameter>
-<parameter name="animate" type="boolean">
-</parameter>
-<parameter name="onLayout" type="boolean">
-</parameter>
-</method>
<method name="showPrevious"
return="void"
abstract="false"
@@ -221878,19 +222894,6 @@
visibility="public"
>
</method>
-<method name="setAdapter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
-</method>
<method name="setColumnWidth"
return="void"
abstract="false"
@@ -224193,17 +225196,6 @@
<parameter name="v" type="android.view.View">
</parameter>
</method>
-<method name="clearChoices"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="findViewTraversal"
return="android.view.View"
abstract="false"
@@ -224252,61 +225244,6 @@
visibility="public"
>
</method>
-<method name="getCheckedItemCount"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemIds"
- return="long[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPosition"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPositions"
- return="android.util.SparseBooleanArray"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getChoiceMode"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getDivider"
return="android.graphics.drawable.Drawable"
abstract="false"
@@ -224373,19 +225310,6 @@
visibility="public"
>
</method>
-<method name="isItemChecked"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-</method>
<method name="removeFooterView"
return="boolean"
abstract="false"
@@ -224412,32 +225336,6 @@
<parameter name="v" type="android.view.View">
</parameter>
</method>
-<method name="setAdapter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
-</method>
-<method name="setChoiceMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="choiceMode" type="int">
-</parameter>
-</method>
<method name="setDivider"
return="void"
abstract="false"
@@ -224490,21 +225388,6 @@
<parameter name="headerDividersEnabled" type="boolean">
</parameter>
</method>
-<method name="setItemChecked"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-<parameter name="value" type="boolean">
-</parameter>
-</method>
<method name="setItemsCanFocus"
return="void"
abstract="false"
@@ -224518,19 +225401,6 @@
<parameter name="itemsCanFocus" type="boolean">
</parameter>
</method>
-<method name="setMultiChoiceModeListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.widget.ListView.MultiChoiceModeListener">
-</parameter>
-</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -224583,50 +225453,6 @@
<parameter name="offset" type="int">
</parameter>
</method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_MULTIPLE_MODAL"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<class name="ListView.FixedViewInfo"
extends="java.lang.Object"
@@ -224675,35 +225501,6 @@
>
</field>
</class>
-<interface name="ListView.MultiChoiceModeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.view.ActionMode.Callback">
-</implements>
-<method name="onItemCheckedStateChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mode" type="android.view.ActionMode">
-</parameter>
-<parameter name="position" type="int">
-</parameter>
-<parameter name="id" type="long">
-</parameter>
-<parameter name="checked" type="boolean">
-</parameter>
-</method>
-</interface>
<class name="MediaController"
extends="android.widget.FrameLayout"
abstract="false"
@@ -227785,6 +228582,32 @@
<parameter name="visibility" type="int">
</parameter>
</method>
+<method name="showNext"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="showPrevious"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
<method name="writeToParcel"
return="void"
abstract="false"
@@ -227982,6 +228805,17 @@
visibility="public"
>
</method>
+<method name="onDataSetChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onDestroy"
return="void"
abstract="true"
@@ -228032,6 +228866,22 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</constructor>
+<constructor name="ResourceCursorAdapter"
+ type="android.widget.ResourceCursorAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
<method name="newView"
return="android.view.View"
abstract="false"
@@ -230162,6 +231012,37 @@
</parameter>
</method>
</interface>
+<class name="StackView"
+ extends="android.widget.AdapterViewAnimator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+</class>
<class name="TabHost"
extends="android.widget.FrameLayout"
abstract="false"
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 7a7f8ed..9fe1fb8 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -49,6 +49,9 @@
#include "BootAnimation.h"
+#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
+#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
+
namespace android {
// ---------------------------------------------------------------------------
@@ -244,12 +247,12 @@
mFlingerSurfaceControl = control;
mFlingerSurface = s;
- mAndroidAnimation = false;
- status_t err = mZip.open("/data/local/bootanimation.zip");
- if (err != NO_ERROR) {
- err = mZip.open("/system/media/bootanimation.zip");
- if (err != NO_ERROR) {
- mAndroidAnimation = true;
+ mAndroidAnimation = true;
+ if ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) ||
+ (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0)) {
+ if ((mZip.open(USER_BOOTANIMATION_FILE) != NO_ERROR) ||
+ (mZip.open(SYSTEM_BOOTANIMATION_FILE) != NO_ERROR)) {
+ mAndroidAnimation = false;
}
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 1013e01..cd6531b 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -422,7 +422,6 @@
mEvaluator = (mValueType == int.class) ? sIntEvaluator :
(mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
}
- mPlayingBackwards = false;
mCurrentIteration = 0;
mInitialized = true;
}
@@ -790,7 +789,8 @@
}
}
- public void start() {
+ private void start(boolean playBackwards) {
+ mPlayingBackwards = playBackwards;
mPlayingState = STOPPED;
sPendingAnimations.add(this);
if (sAnimationHandler == null) {
@@ -801,6 +801,12 @@
sAnimationHandler.sendEmptyMessage(ANIMATION_START);
}
+ @Override
+ public void start() {
+ start(false);
+ }
+
+ @Override
public void cancel() {
if (mListeners != null) {
ArrayList<AnimatableListener> tmpListeners =
@@ -814,6 +820,7 @@
mPlayingState = CANCELED;
}
+ @Override
public void end() {
// Just set the ENDED flag - this causes the animation to end the next time a frame
// is processed.
@@ -821,6 +828,33 @@
}
/**
+ * Returns whether this Animator is currently running (having been started and not yet ended).
+ * @return Wehther the Animator is running.
+ */
+ public boolean isRunning() {
+ return mPlayingState == RUNNING;
+ }
+
+ /**
+ * Plays the Animator in reverse. If the animation is already running,
+ * it will stop itself and play backwards from the point reached when reverse was called.
+ * If the animation is not currently running, then it will start from the end and
+ * play backwards. This behavior is only set for the current animation; future playing
+ * of the animation will use the default behavior of playing forward.
+ */
+ public void reverse() {
+ mPlayingBackwards = !mPlayingBackwards;
+ if (mPlayingState == RUNNING) {
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ long currentPlayTime = currentTime - mStartTime;
+ long timeLeft = mDuration - currentPlayTime;
+ mStartTime = currentTime - timeLeft;
+ } else {
+ start(true);
+ }
+ }
+
+ /**
* Called internally to end an animation by removing it from the animations list. Must be
* called on the UI thread.
*/
@@ -892,7 +926,6 @@
* <code>repeatCount</code> has been exceeded and the animation should be ended.
*/
private boolean animationFrame(long currentTime) {
-
boolean done = false;
if (mPlayingState == STOPPED) {
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 3278a3e..2406d8a 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -140,6 +140,24 @@
}
/**
+ * Sets the target object for all current {@link #getChildAnimations() child animations}
+ * of this Sequencer that take targets ({@link android.animation.PropertyAnimator} and
+ * Sequencer).
+ *
+ * @param target The object being animated
+ */
+ public void setTarget(Object target) {
+ for (Node node : mNodes) {
+ Animatable animation = node.animation;
+ if (animation instanceof Sequencer) {
+ ((Sequencer)animation).setTarget(target);
+ } else if (animation instanceof PropertyAnimator) {
+ ((PropertyAnimator)animation).setTarget(target);
+ }
+ }
+ }
+
+ /**
* This method creates a <code>Builder</code> object, which is used to
* set up playing constraints. This initial <code>play()</code> method
* tells the <code>Builder</code> the animation that is the dependency for
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index d33494b..38086f0 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -18,6 +18,7 @@
import android.graphics.drawable.Drawable;
import android.view.View;
+import android.view.Window;
import android.widget.SpinnerAdapter;
/**
@@ -383,6 +384,34 @@
public abstract void selectTab(Tab tab);
/**
+ * Retrieve the current height of the ActionBar.
+ *
+ * @return The ActionBar's height
+ */
+ public abstract int getHeight();
+
+ /**
+ * Show the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void show();
+
+ /**
+ * Hide the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void hide();
+
+ /**
+ * @return <code>true</code> if the ActionBar is showing, <code>false</code> otherwise.
+ */
+ public abstract boolean isShowing();
+
+ /**
* Callback interface for ActionBar navigation events.
*/
public interface NavigationCallback {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d49adc2..bf660ea9 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -56,7 +56,6 @@
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
-import android.view.InflateException;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -114,6 +113,7 @@
*
* <p>Topics covered here:
* <ol>
+ * <li><a href="#Fragments">Fragments</a>
* <li><a href="#ActivityLifecycle">Activity Lifecycle</a>
* <li><a href="#ConfigurationChanges">Configuration Changes</a>
* <li><a href="#StartingActivities">Starting Activities and Getting Results</a>
@@ -122,6 +122,14 @@
* <li><a href="#ProcessLifecycle">Process Lifecycle</a>
* </ol>
*
+ * <a name="Fragments"></a>
+ * <h3>Fragments</h3>
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
+ * implementations can make use of the {@link Fragment} class to better
+ * modularize their code, build more sophisticated user interfaces for larger
+ * screens, and help scale their application between small and large screens.
+ *
* <a name="ActivityLifecycle"></a>
* <h3>Activity Lifecycle</h3>
*
@@ -667,7 +675,7 @@
private CharSequence mTitle;
private int mTitleColor = 0;
- final FragmentManager mFragments = new FragmentManager();
+ final FragmentManagerImpl mFragments = new FragmentManagerImpl();
SparseArray<LoaderManagerImpl> mAllLoaderManagers;
LoaderManagerImpl mLoaderManager;
@@ -1485,6 +1493,11 @@
* {@link #getLastNonConfigurationInstance()} in the new activity
* instance.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using a {@link Fragment} with
+ * {@link Fragment#setRetainInstance(boolean)
+ * Fragment.setRetainInstance(boolean}.</em>
+ *
* <p>This function is called purely as an optimization, and you must
* not rely on it being called. When it is called, a number of guarantees
* will be made to help optimize configuration switching:
@@ -1580,11 +1593,21 @@
}
/**
+ * Return the FragmentManager for interacting with fragments associated
+ * with this activity.
+ */
+ public FragmentManager getFragmentManager() {
+ return mFragments;
+ }
+
+ /**
* Start a series of edit operations on the Fragments associated with
* this activity.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public FragmentTransaction openFragmentTransaction() {
- return new BackStackEntry(mFragments);
+ return mFragments.openTransaction();
}
void invalidateFragmentIndex(int index) {
@@ -1613,6 +1636,10 @@
* {@link #startManagingCursor} so that the activity will manage its
* lifecycle for you.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using {@link LoaderManager} instead, available
+ * via {@link #getLoaderManager()}.</em>
+ *
* @param uri The URI of the content provider to query.
* @param projection List of columns to return.
* @param selection SQL WHERE clause.
@@ -1624,11 +1651,8 @@
* @see #startManagingCursor
* @hide
*/
- public final Cursor managedQuery(Uri uri,
- String[] projection,
- String selection,
- String sortOrder)
- {
+ public final Cursor managedQuery(Uri uri, String[] projection, String selection,
+ String sortOrder) {
Cursor c = getContentResolver().query(uri, projection, selection, null, sortOrder);
if (c != null) {
startManagingCursor(c);
@@ -1643,6 +1667,10 @@
* {@link #startManagingCursor} so that the activity will manage its
* lifecycle for you.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using {@link LoaderManager} instead, available
+ * via {@link #getLoaderManager()}.</em>
+ *
* @param uri The URI of the content provider to query.
* @param projection List of columns to return.
* @param selection SQL WHERE clause.
@@ -1654,12 +1682,8 @@
* @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
* @see #startManagingCursor
*/
- public final Cursor managedQuery(Uri uri,
- String[] projection,
- String selection,
- String[] selectionArgs,
- String sortOrder)
- {
+ public final Cursor managedQuery(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
if (c != null) {
startManagingCursor(c);
@@ -1675,6 +1699,10 @@
* it will call {@link Cursor#requery} for you. When the activity is
* destroyed, all managed Cursors will be closed automatically.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using {@link LoaderManager} instead, available
+ * via {@link #getLoaderManager()}.</em>
+ *
* @param c The Cursor to be managed.
*
* @see #managedQuery(android.net.Uri , String[], String, String[], String)
@@ -1748,13 +1776,11 @@
/**
* Retrieve a reference to this activity's ActionBar.
- *
- * <p><em>Note:</em> The ActionBar is initialized when a content view
- * is set. This function will return null if called before {@link #setContentView}
- * or {@link #addContentView}.
+ *
* @return The Activity's ActionBar, or null if it does not have one.
*/
public ActionBar getActionBar() {
+ initActionBar();
return mActionBar;
}
@@ -1764,7 +1790,7 @@
*/
private void initActionBar() {
Window window = getWindow();
- if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
+ if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
@@ -1776,7 +1802,9 @@
* from XML or as the container ID when added in a transaction. This only
* returns fragments that are currently added to the activity's content.
* @return The fragment if found or null otherwise.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public Fragment findFragmentById(int id) {
return mFragments.findFragmentById(id);
}
@@ -1786,7 +1814,9 @@
* from XML or as supplied when added in a transaction. This only
* returns fragments that are currently added to the activity's content.
* @return The fragment if found or null otherwise.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public Fragment findFragmentByTag(String tag) {
return mFragments.findFragmentByTag(tag);
}
@@ -2072,9 +2102,11 @@
/**
* Pop the top state off the back stack. Returns true if there was one
* to pop, else false.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public boolean popBackStack() {
- return popBackStack(null, -1);
+ return mFragments.popBackStack();
}
/**
@@ -2085,9 +2117,11 @@
* {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
* the named state itself is popped. If null, only the top state is popped.
* @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public boolean popBackStack(String name, int flags) {
- return mFragments.popBackStackState(mHandler, name, flags);
+ return mFragments.popBackStack(name, flags);
}
/**
@@ -2099,9 +2133,11 @@
* {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
* the named state itself is popped.
* @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ * @deprecated use {@link #getFragmentManager}.
*/
+ @Deprecated
public boolean popBackStack(int id, int flags) {
- return mFragments.popBackStackState(mHandler, id, flags);
+ return mFragments.popBackStack(id, flags);
}
/**
@@ -2110,7 +2146,7 @@
* but you can override this to do whatever you want.
*/
public void onBackPressed() {
- if (!popBackStack()) {
+ if (!mFragments.popBackStack()) {
finish();
}
}
@@ -2671,6 +2707,9 @@
* by the activity. The default implementation calls through to
* {@link #onCreateDialog(int)} for compatibility.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using a {@link DialogFragment} instead.</em>
+ *
* <p>If you use {@link #showDialog(int)}, the activity will call through to
* this method the first time, and hang onto it thereafter. Any dialog
* that is created by this method will automatically be saved and restored
@@ -2743,6 +2782,9 @@
* will be made with the same id the first time this is called for a given
* id. From thereafter, the dialog will be automatically saved and restored.
*
+ * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * or later, consider instead using a {@link DialogFragment} instead.</em>
+ *
* <p>Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will
* be made to provide an opportunity to do any timely preparation.
*
@@ -3989,9 +4031,12 @@
return null;
}
+ String fname = attrs.getAttributeValue(null, "class");
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
- String fname = a.getString(com.android.internal.R.styleable.Fragment_name);
+ if (fname == null) {
+ fname = a.getString(com.android.internal.R.styleable.Fragment_name);
+ }
int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, 0);
String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
a.recycle();
@@ -4001,43 +4046,36 @@
+ ": Must specify unique android:id for " + fname);
}
- try {
- // If we restored from a previous state, we may already have
- // instantiated this fragment from the state and should use
- // that instance instead of making a new one.
- Fragment fragment = mFragments.findFragmentById(id);
- if (FragmentManager.DEBUG) Log.v(TAG, "onCreateView: id=0x"
- + Integer.toHexString(id) + " fname=" + fname
- + " existing=" + fragment);
- if (fragment == null) {
- fragment = Fragment.instantiate(this, fname);
- fragment.mFromLayout = true;
- fragment.mFragmentId = id;
- fragment.mTag = tag;
- fragment.mImmediateActivity = this;
- mFragments.addFragment(fragment, true);
- }
+ // If we restored from a previous state, we may already have
+ // instantiated this fragment from the state and should use
+ // that instance instead of making a new one.
+ Fragment fragment = mFragments.findFragmentById(id);
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
+ + Integer.toHexString(id) + " fname=" + fname
+ + " existing=" + fragment);
+ if (fragment == null) {
+ fragment = Fragment.instantiate(this, fname);
+ fragment.mFromLayout = true;
+ fragment.mFragmentId = id;
+ fragment.mTag = tag;
+ fragment.mImmediateActivity = this;
// If this fragment is newly instantiated (either right now, or
// from last saved state), then give it the attributes to
// initialize itself.
if (!fragment.mRetaining) {
- fragment.onInflate(this, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(attrs, fragment.mSavedFragmentState);
}
- if (fragment.mView == null) {
- throw new IllegalStateException("Fragment " + fname
- + " did not create a view.");
- }
- fragment.mView.setId(id);
- if (fragment.mView.getTag() == null) {
- fragment.mView.setTag(tag);
- }
- return fragment.mView;
- } catch (Exception e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating fragment " + fname);
- ie.initCause(e);
- throw ie;
+ mFragments.addFragment(fragment, true);
}
+ if (fragment.mView == null) {
+ throw new IllegalStateException("Fragment " + fname
+ + " did not create a view.");
+ }
+ fragment.mView.setId(id);
+ if (fragment.mView.getTag() == null) {
+ fragment.mView.setTag(tag);
+ }
+ return fragment.mView;
}
/**
@@ -4093,6 +4131,7 @@
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
+ initActionBar();
if (mActionBar != null) {
return mActionBar.startActionMode(callback);
}
@@ -4294,6 +4333,7 @@
}
final void performDestroy() {
+ mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
if (mLoaderManager != null) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d66e98b..d5741fc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -365,7 +365,8 @@
/**
* The time when the service was first made active, either by someone
- * starting or binding to it.
+ * starting or binding to it. This
+ * is in units of {@link android.os.SystemClock#elapsedRealtime()}.
*/
public long activeSince;
@@ -387,7 +388,8 @@
/**
* The time when there was last activity in the service (either
- * explicit requests to start it or clients binding to it).
+ * explicit requests to start it or clients binding to it). This
+ * is in units of {@link android.os.SystemClock#uptimeMillis()}.
*/
public long lastActivityTime;
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index d63b862..520e4fd 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -29,7 +29,7 @@
final String mName;
final int mIndex;
- public BackStackState(FragmentManager fm, BackStackEntry bse) {
+ public BackStackState(FragmentManagerImpl fm, BackStackEntry bse) {
int numRemoved = 0;
BackStackEntry.Op op = bse.mHead;
while (op != null) {
@@ -38,6 +38,10 @@
}
mOps = new int[bse.mNumOp*5 + numRemoved];
+ if (!bse.mAddToBackStack) {
+ throw new IllegalStateException("Not on back stack");
+ }
+
op = bse.mHead;
int pos = 0;
while (op != null) {
@@ -70,14 +74,15 @@
mIndex = in.readInt();
}
- public BackStackEntry instantiate(FragmentManager fm) {
+ public BackStackEntry instantiate(FragmentManagerImpl fm) {
BackStackEntry bse = new BackStackEntry(fm);
int pos = 0;
while (pos < mOps.length) {
BackStackEntry.Op op = new BackStackEntry.Op();
op.cmd = mOps[pos++];
+ if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+ "BSE " + bse + " set base fragment #" + mOps[pos]);
Fragment f = fm.mActive.get(mOps[pos++]);
- f.mBackStackNesting++;
op.fragment = f;
op.enterAnim = mOps[pos++];
op.exitAnim = mOps[pos++];
@@ -85,7 +90,10 @@
if (N > 0) {
op.removed = new ArrayList<Fragment>(N);
for (int i=0; i<N; i++) {
- op.removed.add(fm.mActive.get(mOps[pos++]));
+ if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+ "BSE " + bse + " set remove fragment #" + mOps[pos]);
+ Fragment r = fm.mActive.get(mOps[pos++]);
+ op.removed.add(r);
}
}
bse.addOp(op);
@@ -94,6 +102,8 @@
bse.mTransitionStyle = mTransitionStyle;
bse.mName = mName;
bse.mIndex = mIndex;
+ bse.mAddToBackStack = true;
+ bse.bumpBackStackNesting(1);
return bse;
}
@@ -127,7 +137,7 @@
final class BackStackEntry implements FragmentTransaction, Runnable {
static final String TAG = "BackStackEntry";
- final FragmentManager mManager;
+ final FragmentManagerImpl mManager;
static final int OP_NULL = 0;
static final int OP_ADD = 1;
@@ -158,7 +168,7 @@
boolean mCommitted;
int mIndex;
- public BackStackEntry(FragmentManager manager) {
+ public BackStackEntry(FragmentManagerImpl manager) {
mManager = manager;
}
@@ -295,9 +305,32 @@
return this;
}
+ void bumpBackStackNesting(int amt) {
+ if (!mAddToBackStack) {
+ return;
+ }
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
+ + " by " + amt);
+ Op op = mHead;
+ while (op != null) {
+ op.fragment.mBackStackNesting += amt;
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ + op.fragment + " to " + op.fragment.mBackStackNesting);
+ if (op.removed != null) {
+ for (int i=op.removed.size()-1; i>=0; i--) {
+ Fragment r = op.removed.get(i);
+ r.mBackStackNesting += amt;
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ + r + " to " + r.mBackStackNesting);
+ }
+ }
+ op = op.next;
+ }
+ }
+
public int commit() {
if (mCommitted) throw new IllegalStateException("commit already called");
- if (FragmentManager.DEBUG) Log.v(TAG, "Commit: " + this);
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this);
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
@@ -309,7 +342,7 @@
}
public void run() {
- if (FragmentManager.DEBUG) Log.v(TAG, "Run: " + this);
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
if (mAddToBackStack) {
if (mIndex < 0) {
@@ -317,14 +350,13 @@
}
}
+ bumpBackStackNesting(1);
+
Op op = mHead;
while (op != null) {
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
} break;
@@ -333,48 +365,38 @@
if (mManager.mAdded != null) {
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
- if (FragmentManager.DEBUG) Log.v(TAG,
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
if (old.mContainerId == f.mContainerId) {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old);
- if (mAddToBackStack) {
- old.mBackStackNesting++;
- }
old.mNextAnim = op.exitAnim;
+ if (mAddToBackStack) {
+ old.mBackStackNesting += 1;
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ + old + " to " + old.mBackStackNesting);
+ }
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
}
}
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
f.mNextAnim = op.exitAnim;
mManager.removeFragment(f, mTransition, mTransitionStyle);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
f.mNextAnim = op.exitAnim;
mManager.hideFragment(f, mTransition, mTransitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting++;
- }
f.mNextAnim = op.enterAnim;
mManager.showFragment(f, mTransition, mTransitionStyle);
} break;
@@ -399,36 +421,29 @@
}
public void popFromBackStack() {
- if (FragmentManager.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
+ if (FragmentManagerImpl.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
+
+ bumpBackStackNesting(-1);
Op op = mTail;
while (op != null) {
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
f.mImmediateActivity = null;
mManager.removeFragment(f,
- FragmentManager.reverseTransit(mTransition),
+ FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
} break;
case OP_REPLACE: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
f.mImmediateActivity = null;
mManager.removeFragment(f,
- FragmentManager.reverseTransit(mTransition),
+ FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
if (op.removed != null) {
for (int i=0; i<op.removed.size(); i++) {
Fragment old = op.removed.get(i);
- if (mAddToBackStack) {
- old.mBackStackNesting--;
- }
f.mImmediateActivity = mManager.mActivity;
mManager.addFragment(old, false);
}
@@ -436,27 +451,18 @@
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
f.mImmediateActivity = mManager.mActivity;
mManager.addFragment(f, false);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
mManager.showFragment(f,
- FragmentManager.reverseTransit(mTransition), mTransitionStyle);
+ FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
- if (mAddToBackStack) {
- f.mBackStackNesting--;
- }
mManager.hideFragment(f,
- FragmentManager.reverseTransit(mTransition), mTransitionStyle);
+ FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
} break;
default: {
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
@@ -467,7 +473,7 @@
}
mManager.moveToState(mManager.mCurState,
- FragmentManager.reverseTransit(mTransition), mTransitionStyle, true);
+ FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
if (mManager.mNeedMenuInvalidate && mManager.mActivity != null) {
mManager.mActivity.invalidateOptionsMenu();
mManager.mNeedMenuInvalidate = false;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a9420b4e..274a266 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,7 @@
package android.app;
+import com.android.internal.app.ActionBarImpl;
import com.android.internal.policy.PolicyManager;
import android.content.ComponentName;
@@ -27,9 +28,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.ActionMode;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -77,6 +78,7 @@
final WindowManager mWindowManager;
Window mWindow;
View mDecor;
+ private ActionBarImpl mActionBar;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -178,6 +180,15 @@
}
/**
+ * Retrieve the {@link ActionBar} attached to this dialog, if present.
+ *
+ * @return The ActionBar attached to the dialog or null if no ActionBar is present.
+ */
+ public ActionBar getActionBar() {
+ return mActionBar;
+ }
+
+ /**
* Sets the Activity that owns this dialog. An example use: This Dialog will
* use the suggested volume control stream of the Activity.
*
@@ -217,6 +228,9 @@
public void show() {
if (mShowing) {
if (mDecor != null) {
+ if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
+ }
mDecor.setVisibility(View.VISIBLE);
}
return;
@@ -228,6 +242,11 @@
onStart();
mDecor = mWindow.getDecorView();
+
+ if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mActionBar = new ActionBarImpl(this);
+ }
+
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
@@ -776,6 +795,13 @@
}
/**
+ * @see Activity#invalidateOptionsMenu()
+ */
+ public void invalidateOptionsMenu() {
+ mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ /**
* @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
*/
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
@@ -834,12 +860,14 @@
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
- // TODO Support context modes in dialogs
+ if (mActionBar != null) {
+ return mActionBar.startActionMode(callback);
+ }
return null;
}
/**
- * @return The activity associated with this dialog, or null if there is no assocaited activity.
+ * @return The activity associated with this dialog, or null if there is no associated activity.
*/
private ComponentName getAssociatedActivity() {
Activity activity = mOwnerActivity;
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 391f672..50e7421 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -41,26 +41,26 @@
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
/**
- * Style for {@link #DialogFragment(int, int)} constructor: a basic,
+ * Style for {@link #setStyle(int, int)}: a basic,
* normal dialog.
*/
public static final int STYLE_NORMAL = 0;
/**
- * Style for {@link #DialogFragment(int, int)} constructor: don't include
+ * Style for {@link #setStyle(int, int)}: don't include
* a title area.
*/
public static final int STYLE_NO_TITLE = 1;
/**
- * Style for {@link #DialogFragment(int, int)} constructor: don't draw
+ * Style for {@link #setStyle(int, int)}: don't draw
* any frame at all; the view hierarchy returned by {@link #onCreateView}
* is entirely responsible for drawing the dialog.
*/
public static final int STYLE_NO_FRAME = 2;
/**
- * Style for {@link #DialogFragment(int, int)} constructor: like
+ * Style for {@link #setStyle(int, int)}: like
* {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
* The user can not touch it, and its window will not receive input focus.
*/
@@ -79,16 +79,18 @@
Dialog mDialog;
boolean mDestroyed;
+ boolean mRemoved;
public DialogFragment() {
}
/**
- * Constructor to customize the basic appearance and behavior of the
+ * Call to customize the basic appearance and behavior of the
* fragment's dialog. This can be used for some common dialog behaviors,
* taking care of selecting flags, theme, and other options for you. The
* same effect can be achieve by manually setting Dialog and Window
- * attributes yourself.
+ * attributes yourself. Calling this after the fragment's Dialog is
+ * created will have no effect.
*
* @param style Selects a standard style: may be {@link #STYLE_NORMAL},
* {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
@@ -96,7 +98,7 @@
* @param theme Optional custom theme. If 0, an appropriate theme (based
* on the style) will be selected for you.
*/
- public DialogFragment(int style, int theme) {
+ public void setStyle(int style, int theme) {
mStyle = style;
if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
mTheme = android.R.style.Theme_Dialog_NoFrame;
@@ -135,6 +137,7 @@
*/
public int show(Activity activity, FragmentTransaction transaction, String tag) {
transaction.add(this, tag);
+ mRemoved = false;
mBackStackId = transaction.commit();
return mBackStackId;
}
@@ -148,12 +151,15 @@
public void dismiss() {
if (mDialog != null) {
mDialog.dismiss();
+ mDialog = null;
}
+ mRemoved = true;
if (mBackStackId >= 0) {
- getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
+ getFragmentManager().popBackStack(mBackStackId,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1;
} else {
- FragmentTransaction ft = getActivity().openFragmentTransaction();
+ FragmentTransaction ft = getFragmentManager().openTransaction();
ft.remove(this);
ft.commit();
}
@@ -192,15 +198,12 @@
}
public void onCancel(DialogInterface dialog) {
- if (mBackStackId >= 0) {
- // If this fragment is part of the back stack, then cancelling
- // the dialog means popping off the back stack.
- getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE);
- mBackStackId = -1;
- }
}
public void onDismiss(DialogInterface dialog) {
+ if (!mRemoved) {
+ dismiss();
+ }
}
@Override
@@ -240,7 +243,10 @@
@Override
public void onStart() {
super.onStart();
- mDialog.show();
+ if (mDialog != null) {
+ mRemoved = false;
+ mDialog.show();
+ }
}
@Override
@@ -261,17 +267,25 @@
@Override
public void onStop() {
super.onStop();
- mDialog.hide();
+ if (mDialog != null) {
+ mDialog.hide();
+ }
}
/**
- * Detach from list view.
+ * Remove dialog.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
mDestroyed = true;
- mDialog.dismiss();
- mDialog = null;
+ if (mDialog != null) {
+ // Set removed here because this dismissal is just to hide
+ // the dialog -- we don't want this to cause the fragment to
+ // actually be removed.
+ mRemoved = true;
+ mDialog.dismiss();
+ mDialog = null;
+ }
}
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 0bb200c..b10a8a8 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -16,6 +16,7 @@
package android.app;
+import android.animation.Animatable;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
@@ -23,6 +24,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.AndroidRuntimeException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -35,15 +37,11 @@
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
-import android.view.animation.Animation;
import android.widget.AdapterView;
-import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
final class FragmentState implements Parcelable {
- static final String VIEW_STATE_TAG = "android:view_state";
-
final String mClassName;
final int mIndex;
final boolean mFromLayout;
@@ -51,6 +49,7 @@
final int mContainerId;
final String mTag;
final boolean mRetainInstance;
+ final Bundle mArguments;
Bundle mSavedFragmentState;
@@ -64,6 +63,7 @@
mContainerId = frag.mContainerId;
mTag = frag.mTag;
mRetainInstance = frag.mRetainInstance;
+ mArguments = frag.mArguments;
}
public FragmentState(Parcel in) {
@@ -74,6 +74,7 @@
mContainerId = in.readInt();
mTag = in.readString();
mRetainInstance = in.readInt() != 0;
+ mArguments = in.readBundle();
mSavedFragmentState = in.readBundle();
}
@@ -82,17 +83,11 @@
return mInstance;
}
- try {
- mInstance = Fragment.instantiate(activity, mClassName);
- } catch (Exception e) {
- throw new RuntimeException("Unable to restore fragment " + mClassName, e);
- }
+ mInstance = Fragment.instantiate(activity, mClassName, mArguments);
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(activity.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
- mInstance.mSavedViewState
- = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
}
mInstance.setIndex(mIndex);
mInstance.mFromLayout = mFromLayout;
@@ -116,6 +111,7 @@
dest.writeInt(mContainerId);
dest.writeString(mTag);
dest.writeInt(mRetainInstance ? 1 : 0);
+ dest.writeBundle(mArguments);
dest.writeBundle(mSavedFragmentState);
}
@@ -133,7 +129,152 @@
/**
* A Fragment is a piece of an application's user interface or behavior
- * that can be placed in an {@link Activity}.
+ * that can be placed in an {@link Activity}. Interaction with fragments
+ * is done through {@link FragmentManager}, which can be obtained via
+ * {@link Activity#getFragmentManager() Activity.getFragmentManager()} and
+ * {@link Fragment#getFragmentManager() Fragment.getFragmentManager()}.
+ *
+ * <p>The Fragment class can be used many ways to achieve a wide variety of
+ * results. It is core, it represents a particular operation or interface
+ * that is running within a larger {@link Activity}. A Fragment is closely
+ * tied to the Activity it is in, and can not be used apart from one. Though
+ * Fragment defines its own lifecycle, that lifecycle is dependent on its
+ * activity: if the activity is stopped, no fragments inside of it can be
+ * started; when the activity is destroyed, all fragments will be destroyed.
+ *
+ * <p>All subclasses of Fragment must include a public empty constructor.
+ * The framework will often re-instantiate a fragment class when needed,
+ * in particular during state restore, and needs to be able to find this
+ * constructor to instantiate it. If the empty constructor is not available,
+ * a runtime exception will occur in some cases during state restore.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#Layout">Layout</a>
+ * <li><a href="#BackStack">Back Stack</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>Though a Fragment's lifecycle is tied to its owning activity, it has
+ * its own wrinkle on the standard activity lifecycle. It includes basic
+ * activity lifecycle methods such as {@link #onResume}, but also important
+ * are methods related to interactions with the activity and UI generation.
+ *
+ * <p>The core series of lifecycle methods that are called to bring a fragment
+ * up to resumed state (interacting with the user) are:
+ *
+ * <ol>
+ * <li> {@link #onAttach} called once the fragment is associated with its activity.
+ * <li> {@link #onCreate} called to do initial creation of the fragment.
+ * <li> {@link #onCreateView} creates and returns the view hierarchy associated
+ * with the fragment.
+ * <li> {@link #onActivityCreated} tells the fragment that its activity has
+ * completed its own {@link Activity#onCreate Activity.onCreaate}.
+ * <li> {@link #onStart} makes the fragment visible to the user (based on its
+ * containing activity being started).
+ * <li> {@link #onResume} makes the fragment interacting with the user (based on its
+ * containing activity being resumed).
+ * </ol>
+ *
+ * <p>As a fragment is no longer being used, it goes through a reverse
+ * series of callbacks:
+ *
+ * <ol>
+ * <li> {@link #onPause} fragment is no longer interacting with the user either
+ * because its activity is being paused or a fragment operation is modifying it
+ * in the activity.
+ * <li> {@link #onStop} fragment is no longer visible to the user either
+ * because its activity is being stopped or a fragment operation is modifying it
+ * in the activity.
+ * <li> {@link #onDestroyView} allows the fragment to clean up resources
+ * associated with its View.
+ * <li> {@link #onDestroy} called to do final cleanup of the fragment's state.
+ * <li> {@link #onDetach} called immediately prior to the fragment no longer
+ * being associated with its activity.
+ * </ol>
+ *
+ * <a name="Layout"></a>
+ * <h3>Layout</h3>
+ *
+ * <p>Fragments can be used as part of your application's layout, allowing
+ * you to better modularize your code and more easily adjust your user
+ * interface to the screen it is running on. As an example, we can look
+ * at a simple program consisting of a list of items, and display of the
+ * details of each item.</p>
+ *
+ * <p>An activity's layout XML can include <code><fragment></code> tags
+ * to embed fragment instances inside of the layout. For example, here is
+ * a simply layout that embeds one fragment:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout}
+ *
+ * <p>The layout is installed in the activity in the normal way:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ * main}
+ *
+ * <p>The titles fragment, showing a list of titles, is very simple, relying
+ * on {@link ListFragment} for most of its work. Note the implementation of
+ * clicking an item, which can either update
+ * the content of the details fragment or start a new activity show the
+ * details depending on whether the current activity's layout can show the
+ * details.</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ * titles}
+ *
+ * <p>The details fragment showing the contents of selected item here just
+ * displays a string of text based on an index of a string array built in to
+ * the app:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ * details}
+ *
+ * <p>In this case when the user clicks on a title, there is no details
+ * fragment in the current activity, so the title title fragment's click code will
+ * launch a new activity to display the details fragment:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ * details_activity}
+ *
+ * <p>However the screen may be large enough to show both the list of titles
+ * and details about the currently selected title. To use such a layout on
+ * a landscape screen, this alternative layout can be placed under layout-land:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout}
+ *
+ * <p>Note how the prior code will adjust to this alternative UI flow: the
+ * titles fragment will now show its text inside of its activity, and the
+ * details activity will finish of it finds itself running in a configuration
+ * where the details can be shown inline.
+ *
+ * <a name="BackStack"></a>
+ * <h3>Back Stack</h3>
+ *
+ * <p>The transaction in which fragments are modified can be placed on an
+ * internal back-stack of the owning activity. When the user presses back
+ * in the activity, any transactions on the back stack are popped off before
+ * the activity itself is finished.
+ *
+ * <p>For example, consider this simple fragment that is instantiated with
+ * an integer argument and displays that in a TextView in its UI:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+ * fragment}
+ *
+ * <p>A function that creates a new instance of the fragment, replacing
+ * whatever current fragment instance is being shown and pushing that change
+ * on to the back stack could be written as:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+ * add_stack}
+ *
+ * <p>After each call to this function, a new entry is on the stack, and
+ * pressing back will pop it to return the user to whatever previous state
+ * the activity UI was in.
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
private static final HashMap<String, Class<?>> sClassMap =
@@ -157,6 +298,15 @@
// Internal unique name for this fragment;
String mWho;
+ // Construction arguments;
+ Bundle mArguments;
+
+ // Target fragment.
+ Fragment mTarget;
+
+ // Target request code.
+ int mTargetRequestCode;
+
// True if the fragment is in the list of added fragments.
boolean mAdded;
@@ -220,46 +370,84 @@
boolean mCheckedForLoaderManager;
/**
- * Default constructor. <strong>Every</string> fragment must have an
+ * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
+ * there is an instantiation failure.
+ */
+ static public class InstantiationException extends AndroidRuntimeException {
+ public InstantiationException(String msg, Exception cause) {
+ super(msg, cause);
+ }
+ }
+
+ /**
+ * Default constructor. <strong>Every</strong> fragment must have an
* empty constructor, so it can be instantiated when restoring its
* activity's state. It is strongly recommended that subclasses do not
* have other constructors with parameters, since these constructors
* will not be called when the fragment is re-instantiated; instead,
- * retrieve such parameters from the activity in {@link #onAttach(Activity)}.
+ * arguments can be supplied by the caller with {@link #setArguments}
+ * and later retrieved by the Fragment with {@link #getArguments}.
+ *
+ * <p>Applications should generally not implement a constructor. The
+ * first place application code an run where the fragment is ready to
+ * be used is in {@link #onAttach(Activity)}, the point where the fragment
+ * is actually associated with its activity. Some applications may also
+ * want to implement {@link #onInflate} to retrieve attributes from a
+ * layout resource, though should take care here because this happens for
+ * the fragment is attached to its activity.
*/
public Fragment() {
}
/**
+ * Like {@link #instantiate(Context, String, Bundle)} but with a null
+ * argument Bundle.
+ */
+ public static Fragment instantiate(Context context, String fname) {
+ return instantiate(context, fname, null);
+ }
+
+ /**
* Create a new instance of a Fragment with the given class name. This is
* the same as calling its empty constructor.
*
* @param context The calling context being used to instantiate the fragment.
* This is currently just used to get its ClassLoader.
* @param fname The class name of the fragment to instantiate.
+ * @param args Bundle of arguments to supply to the fragment, which it
+ * can retrieve with {@link #getArguments()}. May be null.
* @return Returns a new fragment instance.
- * @throws NoSuchMethodException The fragment does not have an empty constructor.
- * @throws ClassNotFoundException The fragment class does not exist.
- * @throws IllegalArgumentException Bad arguments supplied to fragment class
- * constructor (should not happen).
- * @throws InstantiationException Caller does not have permission to instantiate
- * the fragment (for example its constructor is not public).
- * @throws IllegalAccessException Caller does not have permission to access
- * the given fragment class.
- * @throws InvocationTargetException Failure running the fragment's constructor.
+ * @throws InstantiationException If there is a failure in instantiating
+ * the given fragment class. This is a runtime exception; it is not
+ * normally expected to happen.
*/
- public static Fragment instantiate(Context context, String fname)
- throws NoSuchMethodException, ClassNotFoundException,
- IllegalArgumentException, InstantiationException,
- IllegalAccessException, InvocationTargetException {
- Class<?> clazz = sClassMap.get(fname);
-
- if (clazz == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = context.getClassLoader().loadClass(fname);
- sClassMap.put(fname, clazz);
+ public static Fragment instantiate(Context context, String fname, Bundle args) {
+ try {
+ Class<?> clazz = sClassMap.get(fname);
+ if (clazz == null) {
+ // Class not found in the cache, see if it's real, and try to add it
+ clazz = context.getClassLoader().loadClass(fname);
+ sClassMap.put(fname, clazz);
+ }
+ Fragment f = (Fragment)clazz.newInstance();
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.mArguments = args;
+ }
+ return f;
+ } catch (ClassNotFoundException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (java.lang.InstantiationException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (IllegalAccessException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
}
- return (Fragment)clazz.newInstance();
}
void restoreViewState() {
@@ -331,6 +519,58 @@
}
/**
+ * Supply the construction arguments for this fragment. This can only
+ * be called before the fragment has been attached to its activity; that
+ * is, you should call it immediately after constructing the fragment. The
+ * arguments supplied here will be retained across fragment destroy and
+ * creation.
+ */
+ public void setArguments(Bundle args) {
+ if (mIndex >= 0) {
+ throw new IllegalStateException("Fragment already active");
+ }
+ mArguments = args;
+ }
+
+ /**
+ * Return the arguments supplied when the fragment was instantiated,
+ * if any.
+ */
+ final public Bundle getArguments() {
+ return mArguments;
+ }
+
+ /**
+ * Optional target for this fragment. This may be used, for example,
+ * if this fragment is being started by another, and when done wants to
+ * give a result back to the first. The target set here is retained
+ * across instances via {@link FragmentManager#putFragment
+ * FragmentManager.putFragment()}.
+ *
+ * @param fragment The fragment that is the target of this one.
+ * @param requestCode Optional request code, for convenience if you
+ * are going to call back with {@link #onActivityResult(int, int, Intent)}.
+ */
+ public void setTargetFragment(Fragment fragment, int requestCode) {
+ mTarget = fragment;
+ mTargetRequestCode = requestCode;
+ }
+
+ /**
+ * Return the target fragment set by {@link #setTargetFragment}.
+ */
+ final public Fragment getTargetFragment() {
+ return mTarget;
+ }
+
+ /**
+ * Return the target request code set by {@link #setTargetFragment}.
+ */
+ final public int getTargetRequestCode() {
+ return mTargetRequestCode;
+ }
+
+ /**
* Return the Activity this fragment is currently associated with.
*/
final public Activity getActivity() {
@@ -338,6 +578,14 @@
}
/**
+ * Return the FragmentManager for interacting with fragments associated
+ * with this fragment's activity.
+ */
+ final public FragmentManager getFragmentManager() {
+ return mActivity.mFragments;
+ }
+
+ /**
* Return true if the fragment is currently added to its activity.
*/
final public boolean isAdded() {
@@ -469,21 +717,30 @@
/**
* Called when a fragment is being created as part of a view layout
* inflation, typically from setting the content view of an activity. This
- * will be called both the first time the fragment is created, as well
- * later when it is being re-created from its saved state (which is also
- * given here).
+ * will be called immediately after the fragment is created from a <fragment>
+ * tag in a layout file. Note this is <em>before</em> the fragment's
+ * {@link #onAttach(Activity)} has been called; all you should do here is
+ * parse the attributes and save them away. A convenient thing to do is
+ * simply copy them into a Bundle that is given to {@link #setArguments(Bundle)}.
*
- * XXX This is kind-of yucky... maybe we could just supply the
- * AttributeSet to onCreate()?
+ * <p>This is called every time the fragment is inflated, even if it is
+ * being inflated into a new instance with saved state. Because a fragment's
+ * arguments are retained across instances, it may make no sense to re-parse
+ * the attributes into new arguments. You may want to first check
+ * {@link #getArguments()} and only parse the attributes if it returns null,
+ * the assumption being that if it is non-null those are the same arguments
+ * from the first time the fragment was inflated. (That said, you may want
+ * to have layouts change for different configurations such as landscape
+ * and portrait, which can have different attributes. If so, you will need
+ * to re-parse the attributes each time this is called to generate new
+ * arguments.)</p>
*
- * @param activity The Activity that is inflating the fragment.
* @param attrs The attributes at the tag where the fragment is
* being created.
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
- public void onInflate(Activity activity, AttributeSet attrs,
- Bundle savedInstanceState) {
+ public void onInflate(AttributeSet attrs, Bundle savedInstanceState) {
mCalled = true;
}
@@ -495,7 +752,10 @@
mCalled = true;
}
- public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+ /**
+ * Called when a fragment loads an animation.
+ */
+ public Animatable onCreateAnimatable(int transit, boolean enter, int nextAnim) {
return null;
}
@@ -622,8 +882,10 @@
* Called when the view previously created by {@link #onCreateView} has
* been detached from the fragment. The next time the fragment needs
* to be displayed, a new view will be created. This is called
- * after {@link #onStop()} and before {@link #onDestroy()}; it is only
- * called if {@link #onCreateView} returns a non-null View.
+ * after {@link #onStop()} and before {@link #onDestroy()}. It is called
+ * <em>regardless</em> of whether {@link #onCreateView} returned a
+ * non-null view. Internally it is called after the view's state has
+ * been saved but before it has been removed from its parent.
*/
public void onDestroyView() {
mCalled = true;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 35b4610..f190d0d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -16,6 +16,9 @@
package android.app;
+import android.animation.Animatable;
+import android.animation.PropertyAnimator;
+import android.animation.Sequencer;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
@@ -28,11 +31,104 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.util.ArrayList;
+/**
+ * Interface for interacting with {@link Fragment} objects inside of an
+ * {@link Activity}
+ */
+public interface FragmentManager {
+ /**
+ * Start a series of edit operations on the Fragments associated with
+ * this FragmentManager.
+ */
+ public FragmentTransaction openTransaction();
+
+ /**
+ * Finds a fragment that was identified by the given id either when inflated
+ * from XML or as the container ID when added in a transaction. This first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack associated with this ID are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentById(int id);
+
+ /**
+ * Finds a fragment that was identified by the given tag either when inflated
+ * from XML or as supplied when added in a transaction. This first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentByTag(String tag);
+
+ /**
+ * Flag for {@link #popBackStack(String, int)}
+ * and {@link #popBackStack(int, int)}: If set, and the name or ID of
+ * a back stack entry has been supplied, then all matching entries will
+ * be consumed until one that doesn't match is found or the bottom of
+ * the stack is reached. Otherwise, all entries up to but not including that entry
+ * will be removed.
+ */
+ public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+
+ /**
+ * Pop the top state off the back stack. Returns true if there was one
+ * to pop, else false.
+ */
+ public boolean popBackStack();
+
+ /**
+ * Pop the last fragment transition from the manager's fragment
+ * back stack. If there is nothing to pop, false is returned.
+ * @param name If non-null, this is the name of a previous back state
+ * to look for; if found, all states up to that state will be popped. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped. If null, only the top state is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(String name, int flags);
+
+ /**
+ * Pop all back stack states up to the one with the given identifier.
+ * @param id Identifier of the stated to be popped. If no identifier exists,
+ * false is returned.
+ * The identifier is the number returned by
+ * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(int id, int flags);
+
+ /**
+ * Put a reference to a fragment in a Bundle. This Bundle can be
+ * persisted as saved state, and when later restoring
+ * {@link #getFragment(Bundle, String)} will return the current
+ * instance of the same fragment.
+ *
+ * @param bundle The bundle in which to put the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @param fragment The Fragment whose reference is to be stored.
+ */
+ public void putFragment(Bundle bundle, String key, Fragment fragment);
+
+ /**
+ * Retrieve the current Fragment instance for a reference previously
+ * placed with {@link #putFragment(Bundle, String, Fragment)}.
+ *
+ * @param bundle The bundle from which to retrieve the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @return Returns the current Fragment instance that is associated with
+ * the given reference.
+ */
+ public Fragment getFragment(Bundle bundle, String key);
+}
+
final class FragmentManagerState implements Parcelable {
FragmentState[] mActive;
int[] mAdded;
@@ -70,13 +166,16 @@
}
/**
- * @hide
* Container for fragments associated with an activity.
*/
-public class FragmentManager {
+final class FragmentManagerImpl implements FragmentManager {
static final boolean DEBUG = true;
static final String TAG = "FragmentManager";
+ static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
+ static final String TARGET_STATE_TAG = "android:target_state";
+ static final String VIEW_STATE_TAG = "android:view_state";
+
ArrayList<Runnable> mPendingActions;
Runnable[] mTmpActions;
boolean mExecutingActions;
@@ -105,17 +204,60 @@
execPendingActions();
}
};
-
- Animation loadAnimation(Fragment fragment, int transit, boolean enter,
+ public FragmentTransaction openTransaction() {
+ return new BackStackEntry(this);
+ }
+
+ public boolean popBackStack() {
+ return popBackStackState(mActivity.mHandler, null, -1, 0);
+ }
+
+ public boolean popBackStack(String name, int flags) {
+ return popBackStackState(mActivity.mHandler, name, -1, flags);
+ }
+
+ public boolean popBackStack(int id, int flags) {
+ if (id < 0) {
+ throw new IllegalArgumentException("Bad id: " + id);
+ }
+ return popBackStackState(mActivity.mHandler, null, id, flags);
+ }
+
+ public void putFragment(Bundle bundle, String key, Fragment fragment) {
+ if (fragment.mIndex < 0) {
+ throw new IllegalStateException("Fragment " + fragment
+ + " is not currently in the FragmentManager");
+ }
+ bundle.putInt(key, fragment.mIndex);
+ }
+
+ public Fragment getFragment(Bundle bundle, String key) {
+ int index = bundle.getInt(key, -1);
+ if (index == -1) {
+ return null;
+ }
+ if (index >= mActive.size()) {
+ throw new IllegalStateException("Fragement no longer exists for key "
+ + key + ": index " + index);
+ }
+ Fragment f = mActive.get(index);
+ if (f == null) {
+ throw new IllegalStateException("Fragement no longer exists for key "
+ + key + ": index " + index);
+ }
+ return f;
+ }
+
+ Animatable loadAnimatable(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
- Animation animObj = fragment.onCreateAnimation(transitionStyle, enter,
+ Animatable animObj = fragment.onCreateAnimatable(transit, enter,
fragment.mNextAnim);
if (animObj != null) {
return animObj;
}
if (fragment.mNextAnim != 0) {
- Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim);
+ Animatable anim = AnimationUtils.loadAnimator(mActivity, fragment.mNextAnim);
if (anim != null) {
return anim;
}
@@ -138,7 +280,7 @@
}
TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
- com.android.internal.R.styleable.WindowAnimation);
+ com.android.internal.R.styleable.FragmentAnimation);
int anim = attrs.getResourceId(styleIndex, 0);
attrs.recycle();
@@ -146,7 +288,7 @@
return null;
}
- return AnimationUtils.loadAnimation(mActivity, anim);
+ return AnimationUtils.loadAnimator(mActivity, anim);
}
void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
@@ -159,6 +301,16 @@
switch (f.mState) {
case Fragment.INITIALIZING:
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
+ if (f.mSavedFragmentState != null) {
+ f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
+ FragmentManagerImpl.VIEW_STATE_TAG);
+ f.mTarget = getFragment(f.mSavedFragmentState,
+ FragmentManagerImpl.TARGET_STATE_TAG);
+ if (f.mTarget != null) {
+ f.mTargetRequestCode = f.mSavedFragmentState.getInt(
+ FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
+ }
+ }
f.mActivity = mActivity;
f.mCalled = false;
f.onAttach(mActivity);
@@ -208,10 +360,15 @@
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
- Animation anim = loadAnimation(f, transit, true,
+ Animatable anim = loadAnimatable(f, transit, true,
transitionStyle);
if (anim != null) {
- f.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(f.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(f.mView);
+ }
+ anim.start();
}
container.addView(f.mView);
f.restoreViewState();
@@ -277,27 +434,32 @@
if (newState < Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
if (f.mView != null) {
- f.mCalled = false;
- f.onDestroyView();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onDestroyedView()");
- }
// Need to save the current view state if not
// done already.
if (!mActivity.isFinishing() && f.mSavedFragmentState == null) {
saveFragmentViewState(f);
}
- if (f.mContainer != null) {
- if (mCurState > Fragment.INITIALIZING) {
- Animation anim = loadAnimation(f, transit, false,
- transitionStyle);
- if (anim != null) {
- f.mView.setAnimation(anim);
+ }
+ f.mCalled = false;
+ f.onDestroyView();
+ if (!f.mCalled) {
+ throw new SuperNotCalledException("Fragment " + f
+ + " did not call through to super.onDestroyedView()");
+ }
+ if (f.mView != null && f.mContainer != null) {
+ if (mCurState > Fragment.INITIALIZING) {
+ Animatable anim = loadAnimatable(f, transit, true,
+ transitionStyle);
+ if (anim != null) {
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(f.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(f.mView);
}
+ anim.start();
}
- f.mContainer.removeView(f.mView);
}
+ f.mContainer.removeView(f.mView);
}
f.mContainer = null;
f.mView = null;
@@ -375,6 +537,7 @@
return;
}
+ if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
mActive.set(f.mIndex, null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList<Integer>();
@@ -420,10 +583,15 @@
if (!fragment.mHidden) {
fragment.mHidden = true;
if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, false,
+ Animatable anim = loadAnimatable(fragment, transition, true,
transitionStyle);
if (anim != null) {
- fragment.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(fragment.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(fragment.mView);
+ }
+ anim.start();
}
fragment.mView.setVisibility(View.GONE);
}
@@ -439,10 +607,15 @@
if (fragment.mHidden) {
fragment.mHidden = false;
if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, true,
+ Animatable anim = loadAnimatable(fragment, transition, true,
transitionStyle);
if (anim != null) {
- fragment.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(fragment.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(fragment.mView);
+ }
+ anim.start();
}
fragment.mView.setVisibility(View.VISIBLE);
}
@@ -614,17 +787,6 @@
mBackStack.add(state);
}
- public boolean popBackStackState(Handler handler, String name, int flags) {
- return popBackStackState(handler, name, -1, flags);
- }
-
- public boolean popBackStackState(Handler handler, int id, int flags) {
- if (id < 0) {
- return false;
- }
- return popBackStackState(handler, null, id, flags);
- }
-
boolean popBackStackState(Handler handler, String name, int id, int flags) {
if (mBackStack == null) {
return false;
@@ -761,14 +923,30 @@
fs.mSavedFragmentState = new Bundle();
}
fs.mSavedFragmentState.putSparseParcelableArray(
- FragmentState.VIEW_STATE_TAG, f.mSavedViewState);
+ FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
+ }
+ }
+
+ if (f.mTarget != null) {
+ if (fs.mSavedFragmentState == null) {
+ fs.mSavedFragmentState = new Bundle();
+ }
+ putFragment(fs.mSavedFragmentState,
+ FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
+ if (f.mTargetRequestCode != 0) {
+ fs.mSavedFragmentState.putInt(
+ FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
+ f.mTargetRequestCode);
}
}
+ if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
+ + fs.mSavedFragmentState);
}
}
if (!haveFragments) {
+ if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
return null;
}
@@ -781,6 +959,8 @@
added = new int[N];
for (int i=0; i<N; i++) {
added[i] = mAdded.get(i).mIndex;
+ if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+ + ": " + mAdded.get(i));
}
}
@@ -791,6 +971,8 @@
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
backStack[i] = new BackStackState(this, mBackStack.get(i));
+ if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ + ": " + mBackStack.get(i));
}
}
}
@@ -814,6 +996,7 @@
if (nonConfig != null) {
for (int i=0; i<nonConfig.size(); i++) {
Fragment f = nonConfig.get(i);
+ if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
FragmentState fs = fms.mActive[f.mIndex];
fs.mInstance = f;
f.mSavedViewState = null;
@@ -821,7 +1004,7 @@
f.mAdded = false;
if (fs.mSavedFragmentState != null) {
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
- FragmentState.VIEW_STATE_TAG);
+ FragmentManagerImpl.VIEW_STATE_TAG);
}
}
}
@@ -835,12 +1018,16 @@
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
- mActive.add(fs.instantiate(mActivity));
+ Fragment f = fs.instantiate(mActivity);
+ if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f);
+ mActive.add(f);
} else {
+ if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)");
mActive.add(null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList<Integer>();
}
+ if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i);
mAvailIndices.add(i);
}
}
@@ -856,6 +1043,7 @@
}
f.mAdded = true;
f.mImmediateActivity = mActivity;
+ if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f);
mAdded.add(f);
}
} else {
@@ -867,6 +1055,8 @@
mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length);
for (int i=0; i<fms.mBackStack.length; i++) {
BackStackEntry bse = fms.mBackStack[i].instantiate(this);
+ if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i
+ + " (index " + bse.mIndex + "): " + bse);
mBackStack.add(bse);
if (bse.mIndex >= 0) {
setBackStackIndex(bse.mIndex, bse);
@@ -981,47 +1171,11 @@
public static int reverseTransit(int transit) {
int rev = 0;
switch (transit) {
- case FragmentTransaction.TRANSIT_ENTER:
- rev = FragmentTransaction.TRANSIT_EXIT;
+ case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
+ rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
break;
- case FragmentTransaction.TRANSIT_EXIT:
- rev = FragmentTransaction.TRANSIT_ENTER;
- break;
- case FragmentTransaction.TRANSIT_SHOW:
- rev = FragmentTransaction.TRANSIT_HIDE;
- break;
- case FragmentTransaction.TRANSIT_HIDE:
- rev = FragmentTransaction.TRANSIT_SHOW;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
- rev = FragmentTransaction.TRANSIT_ACTIVITY_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
- rev = FragmentTransaction.TRANSIT_ACTIVITY_OPEN;
- break;
- case FragmentTransaction.TRANSIT_TASK_OPEN:
- rev = FragmentTransaction.TRANSIT_TASK_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_TASK_CLOSE:
- rev = FragmentTransaction.TRANSIT_TASK_OPEN;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
- rev = FragmentTransaction.TRANSIT_TASK_TO_BACK;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_BACK:
- rev = FragmentTransaction.TRANSIT_TASK_TO_FRONT;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_OPEN;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN;
+ case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
+ rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
break;
}
return rev;
@@ -1031,67 +1185,15 @@
public static int transitToStyleIndex(int transit, boolean enter) {
int animAttr = -1;
switch (transit) {
- case FragmentTransaction.TRANSIT_ENTER:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
- break;
- case FragmentTransaction.TRANSIT_EXIT:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_SHOW:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
- break;
- case FragmentTransaction.TRANSIT_HIDE:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
+ case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
+ ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
+ : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
break;
- case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
+ case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_BACK:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+ ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
+ : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
break;
}
return animAttr;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 65cf85c..04598a3 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -100,47 +100,11 @@
public final int TRANSIT_UNSET = -1;
/** No animation for transition. */
public final int TRANSIT_NONE = 0;
- /** Window has been added to the screen. */
- public final int TRANSIT_ENTER = 1 | TRANSIT_ENTER_MASK;
- /** Window has been removed from the screen. */
- public final int TRANSIT_EXIT = 2 | TRANSIT_EXIT_MASK;
- /** Window has been made visible. */
- public final int TRANSIT_SHOW = 3 | TRANSIT_ENTER_MASK;
- /** Window has been made invisible. */
- public final int TRANSIT_HIDE = 4 | TRANSIT_EXIT_MASK;
- /** The "application starting" preview window is no longer needed, and will
- * animate away to show the real window. */
- public final int TRANSIT_PREVIEW_DONE = 5;
- /** A window in a new activity is being opened on top of an existing one
- * in the same task. */
- public final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity in the same task. */
- public final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK;
- /** A window in a new task is being opened on top of an existing one
- * in another activity's task. */
- public final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK;
- /** A window in the top-most activity is being closed to reveal the
- * previous activity in a different task. */
- public final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK;
- /** A window in an existing task is being displayed on top of an existing one
- * in another activity's task. */
- public final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK;
- /** A window in an existing task is being put below all other tasks. */
- public final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK;
- /** A window in a new activity that doesn't have a wallpaper is being
- * opened on top of one that does, effectively closing the wallpaper. */
- public final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK;
- /** A window in a new activity that does have a wallpaper is being
- * opened on one that didn't, effectively opening the wallpaper. */
- public final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK;
- /** A window in a new activity is being opened on top of an existing one,
- * and both are on top of the wallpaper. */
- public final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity, and both are on top of he wallpaper. */
- public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK;
-
+ /** Fragment is being added */
+ public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK;
+ /** Fragment is being removed */
+ public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
+
public FragmentTransaction setCustomAnimations(int enter, int exit);
public FragmentTransaction setTransition(int transit);
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index e7bdd8b..28abcaa 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -185,11 +185,14 @@
void stop() {
if (DEBUG) Log.v(TAG, " Stopping: " + this);
mStarted = false;
- if (mLoader != null && mListenerRegistered) {
- // Let the loader know we're done with it
- mListenerRegistered = false;
- mLoader.unregisterListener(this);
- mLoader.stopLoading();
+ if (!mRetaining) {
+ if (mLoader != null && mListenerRegistered) {
+ // Let the loader know we're done with it
+ mListenerRegistered = false;
+ mLoader.unregisterListener(this);
+ mLoader.stopLoading();
+ }
+ mData = null;
}
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index eaf0675..4dc88b3 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -10,6 +10,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
@@ -32,12 +33,27 @@
/**
* Convenience for implementing an activity that will be implemented
- * purely in native code. That is, a game (or game-like thing).
+ * purely in native code. That is, a game (or game-like thing). There
+ * is no need to derive from this class; you can simply declare it in your
+ * manifest, and use the NDK APIs from there.
+ *
+ * <p>A typical manifest would look like:
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml
+ * manifest}
+ *
+ * <p>A very simple example of native code that is run by NativeActivity
+ * follows. This reads input events from the user and uses OpenGLES to
+ * draw into the native activity's window.
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
*/
public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
InputQueue.Callback, OnGlobalLayoutListener {
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
+ public static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
+
private NativeContentView mNativeContentView;
private InputMethodManager mIMM;
private InputMethodCallback mInputMethodCallback;
@@ -59,14 +75,15 @@
private native int loadNativeCode(String path, MessageQueue queue,
String internalDataPath, String externalDataPath, int sdkVersion,
- AssetManager assetMgr);
+ AssetManager assetMgr, byte[] savedState);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
private native void onResumeNative(int handle);
- private native void onSaveInstanceStateNative(int handle);
+ private native byte[] onSaveInstanceStateNative(int handle);
private native void onPauseNative(int handle);
private native void onStopNative(int handle);
+ private native void onConfigurationChangedNative(int handle);
private native void onLowMemoryNative(int handle);
private native void onWindowFocusChangedNative(int handle, boolean focused);
private native void onSurfaceCreatedNative(int handle, Surface surface);
@@ -165,10 +182,13 @@
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
+ byte[] nativeSavedState = savedInstanceState != null
+ ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
+
mNativeHandle = loadNativeCode(path, Looper.myQueue(),
getFilesDir().toString(),
Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
- Build.VERSION.SDK_INT, getAssets());
+ Build.VERSION.SDK_INT, getAssets(), nativeSavedState);
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
@@ -206,7 +226,10 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- onSaveInstanceStateNative(mNativeHandle);
+ byte[] state = onSaveInstanceStateNative(mNativeHandle);
+ if (state != null) {
+ outState.putByteArray(KEY_NATIVE_SAVED_STATE, state);
+ }
}
@Override
@@ -222,6 +245,14 @@
}
@Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (!mDestroyed) {
+ onConfigurationChangedNative(mNativeHandle);
+ }
+ }
+
+ @Override
public void onLowMemory() {
super.onLowMemory();
if (!mDestroyed) {
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 6011eec..7730942 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -62,11 +62,10 @@
msg.sendToTarget();
}
- public void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ public void viewDataChanged(int appWidgetId, int viewId) {
Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
msg.arg1 = appWidgetId;
msg.arg2 = viewId;
- msg.obj = views;
msg.sendToTarget();
}
}
@@ -87,7 +86,7 @@
break;
}
case HANDLE_VIEW_DATA_CHANGED: {
- viewDataChanged(msg.arg1, (RemoteViews) msg.obj, msg.arg2);
+ viewDataChanged(msg.arg1, msg.arg2);
break;
}
}
@@ -264,13 +263,13 @@
}
}
- void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ void viewDataChanged(int appWidgetId, int viewId) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
}
if (v != null) {
- v.viewDataChanged(views, viewId);
+ v.viewDataChanged(viewId);
}
}
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 22f4266..4f8ee93 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -264,7 +264,7 @@
* Process data-changed notifications for the specified view in the specified
* set of {@link RemoteViews} views.
*/
- void viewDataChanged(RemoteViews remoteViews, int viewId) {
+ void viewDataChanged(int viewId) {
View v = findViewById(viewId);
if ((v != null) && (v instanceof AdapterView<?>)) {
AdapterView<?> adapterView = (AdapterView<?>) v;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 5ee721f..2a583c1 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -233,6 +233,10 @@
/**
* Set the RemoteViews to use for the specified appWidgetIds.
*
+ * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+ * contain a complete representation of the widget. For performing partial widget updates, see
+ * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
+ *
* <p>
* It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
* and outside of the handler.
@@ -253,6 +257,10 @@
/**
* Set the RemoteViews to use for the specified appWidgetId.
*
+ * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+ * contain a complete representation of the widget. For performing partial widget updates, see
+ * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
+ *
* <p>
* It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
* and outside of the handler.
@@ -266,6 +274,59 @@
}
/**
+ * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
+ *
+ * This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
+ * RemoteViews object which is passed is understood to be an incomplete representation of the
+ * widget, and hence is not cached by the AppWidgetService. Note that because these updates are
+ * not cached, any state that they modify that is not restored by restoreInstanceState will not
+ * persist in the case that the widgets are restored using the cached version in
+ * AppWidgetService.
+ *
+ * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+ * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+ *
+ * <p>
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
+ * @param views The RemoteViews object containing the incremental update / command.
+ */
+ public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
+ try {
+ sService.partiallyUpdateAppWidgetIds(appWidgetIds, views);
+ } catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Perform an incremental update or command on the widget specified by appWidgetId.
+ *
+ * This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
+ * object which is passed is understood to be an incomplete representation of the widget, and
+ * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
+ * any state that they modify that is not restored by restoreInstanceState will not persist in
+ * the case that the widgets are restored using the cached version in AppWidgetService.
+ *
+ * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+ * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+ *
+ * <p>
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
+ * @param views The RemoteViews object containing the incremental update / command.
+ */
+ public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
+ partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
+ }
+
+ /**
* Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
*
* <p>
@@ -292,12 +353,11 @@
* to invalidate their currently data.
*
* @param appWidgetIds The AppWidget instances for which to notify of view data changes.
- * @param views The RemoteViews which contains the view referenced at viewId.
* @param viewId The collection view id.
*/
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
try {
- sService.notifyAppWidgetViewDataChanged(appWidgetIds, views, viewId);
+ sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -309,11 +369,10 @@
* to invalidate it's currently data.
*
* @param appWidgetId The AppWidget instance for which to notify of view data changes.
- * @param views The RemoteViews which contains the view referenced at viewId.
* @param viewId The collection view id.
*/
- public void notifyAppWidgetViewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
- notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, views, viewId);
+ public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
+ notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8eda844..03bcadc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -468,12 +468,17 @@
* <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
* many remote devices can only display the first 40 characters, and some
* may be limited to just 20.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param name a valid Bluetooth name
* @return true if the name was set, false otherwise
*/
public boolean setName(String name) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setName(name);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -488,11 +493,16 @@
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return scan mode
*/
public int getScanMode() {
+ if (getState() != STATE_ON) return SCAN_MODE_NONE;
try {
return mService.getScanMode();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -511,6 +521,10 @@
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
* <p>Applications cannot set the scan mode. They should use
* <code>startActivityForResult(
@@ -524,6 +538,7 @@
* @hide
*/
public boolean setScanMode(int mode, int duration) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setScanMode(mode, duration);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -532,11 +547,13 @@
/** @hide */
public boolean setScanMode(int mode) {
+ if (getState() != STATE_ON) return false;
return setScanMode(mode, 120);
}
/** @hide */
public int getDiscoverableTimeout() {
+ if (getState() != STATE_ON) return -1;
try {
return mService.getDiscoverableTimeout();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -545,6 +562,7 @@
/** @hide */
public void setDiscoverableTimeout(int timeout) {
+ if (getState() != STATE_ON) return;
try {
mService.setDiscoverableTimeout(timeout);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -572,11 +590,16 @@
* <p>Device discovery will only find remote devices that are currently
* <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
* not discoverable by default, and need to be entered into a special mode.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @return true on success, false on error
*/
public boolean startDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
return mService.startDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -593,10 +616,15 @@
* the Activity, but is run as a system service, so an application should
* always call cancel discovery even if it did not directly request a
* discovery, just to be sure.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
*
* @return true on success, false on error
*/
public boolean cancelDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
mService.cancelDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -614,11 +642,16 @@
* <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
* or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
* starts or completes.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return true if discovering
*/
public boolean isDiscovering() {
+ if (getState() != STATE_ON) return false;
try {
return mService.isDiscovering();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -628,11 +661,18 @@
/**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return an empty set. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return unmodifiable set of {@link BluetoothDevice}, or null on error
*/
public Set<BluetoothDevice> getBondedDevices() {
+ if (getState() != STATE_ON) {
+ return toDeviceSet(new String[0]);
+ }
try {
return toDeviceSet(mService.listBonds());
} catch (RemoteException e) {Log.e(TAG, "", e);}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 1163add..22bce05 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -802,7 +802,7 @@
* @param uri The data in the content provider being queried.
* @param mimeTypeFilter The type of data the client desires. May be
* a pattern, such as *\/* to retrieve all possible data types.
- * @returns Returns null if there are no possible data streams for the
+ * @return Returns null if there are no possible data streams for the
* given mimeTypeFilter. Otherwise returns an array of all available
* concrete MIME types.
*
@@ -851,8 +851,14 @@
*/
public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
+ if ("*/*".equals(mimeTypeFilter)) {
+ // If they can take anything, the untyped open call is good enough.
+ return openAssetFile(uri, "r");
+ }
String baseType = getType(uri);
if (baseType != null && compareMimeTypes(baseType, mimeTypeFilter)) {
+ // Use old untyped open call if this provider has a type for this
+ // URI and it matches the request.
return openAssetFile(uri, "r");
}
throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
@@ -1001,7 +1007,7 @@
/**
* @hide -- until interface has proven itself
*
- * Call an provider-defined method. This can be used to implement
+ * Call a provider-defined method. This can be used to implement
* interfaces that are cheaper than using a Cursor.
*
* @param method Method name to call. Opaque to framework.
@@ -1013,29 +1019,26 @@
}
/**
- * Shuts down this instance of the ContentProvider. It is useful when writing tests that use
- * the ContentProvider.
+ * Implement this to shut down the ContentProvider instance. You can then
+ * invoke this method in unit tests.
+ *
* <p>
- * If a unittest starts the ContentProvider in its test(..() methods, it could run into sqlite
- * errors "disk I/O error" or "corruption" in the following scenario:
- * <ul>
- * <li>Say, there are 2 test methods in the unittest</li>
- * <li>test1() (or setUp()) causes ContentProvider object to be initialized and
- * assume it opens a database connection to "foo.db"</li>
- * <li>est1() completes and test2() starts</li>
- * <li>During the execution of test2() there will be 2 connections to "foo.db"</li>
- * <li>Different threads in the ContentProvider may have one of these two connection
- * handles. This is not a problem per se</li>
- * <li>But if the two threads with 2 database connections don't interact correctly,
- * there could be unexpected errors from sqlite</li>
- * <li>Some of those unexpected errros are "disk I/O error" or "corruption" error</li>
- * <li>Common practice in tearDown() is to delete test directory (and the database files)</li>
- * <li>If this is done while some threads are still holding unclosed database connections,
- * sqlite quite easily gets into corruption and disk I/O errors</li>
- * </ul>
+ * Android normally handles ContentProvider startup and shutdown
+ * automatically. You do not need to start up or shut down a
+ * ContentProvider. When you invoke a test method on a ContentProvider,
+ * however, a ContentProvider instance is started and keeps running after
+ * the test finishes, even if a succeeding test instantiates another
+ * ContentProvider. A conflict develops because the two instances are
+ * usually running against the same underlying data source (for example, an
+ * sqlite database).
+ * </p>
* <p>
- * tearDown() in the unittests should call this method to have ContentProvider gracefully
- * shutdown all database connections.
+ * Implementing shutDown() avoids this conflict by providing a way to
+ * terminate the ContentProvider. This method can also prevent memory leaks
+ * from multiple instantiations of the ContentProvider, and it can ensure
+ * unit test isolation by allowing you to completely clean up the test
+ * fixture before moving on to the next test.
+ * </p>
*/
public void shutdown() {
Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " +
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 850ff7f..42599ed 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -100,8 +100,8 @@
public void stopLoading() {
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
- mCursor = null;
}
+ mCursor = null;
// Attempt to cancel the current load task if possible.
cancelLoad();
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index ccb8605..01ae1da 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -25,8 +25,6 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.FileChannel;
/**
* File descriptor of an entry in the AssetManager. This provides your own
@@ -51,7 +49,7 @@
* @param startOffset The location within the file that the asset starts.
* This must be 0 if length is UNKNOWN_LENGTH.
* @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length) {
@@ -125,17 +123,6 @@
public void close() throws IOException {
mFd.close();
}
-
- /**
- * Checks whether this file descriptor is for a memory file.
- */
- private boolean isMemoryFile() {
- try {
- return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
- } catch (IOException e) {
- return false;
- }
- }
/**
* Create and return a new auto-close input stream for this asset. This
@@ -146,12 +133,6 @@
* should only call this once for a particular asset.
*/
public FileInputStream createInputStream() throws IOException {
- if (isMemoryFile()) {
- if (mLength > Integer.MAX_VALUE) {
- throw new IOException("File length too large for a memory file: " + mLength);
- }
- return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
- }
if (mLength < 0) {
return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
}
@@ -280,66 +261,6 @@
super.reset();
}
}
-
- /**
- * An input stream that reads from a MemoryFile and closes it when the stream is closed.
- * This extends FileInputStream just because {@link #createInputStream} returns
- * a FileInputStream. All the FileInputStream methods are
- * overridden to use the MemoryFile instead.
- */
- private static class AutoCloseMemoryFileInputStream extends FileInputStream {
- private ParcelFileDescriptor mParcelFd;
- private MemoryFile mFile;
- private InputStream mStream;
-
- public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
- throws IOException {
- super(fd.getFileDescriptor());
- mParcelFd = fd;
- mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
- mStream = mFile.getInputStream();
- }
-
- @Override
- public int available() throws IOException {
- return mStream.available();
- }
-
- @Override
- public void close() throws IOException {
- mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor,
- // since it could be a subclass of ParcelFileDescriptor.
- // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
- // a content provider
- mFile.close(); // to unmap the memory file from the address space.
- mStream.close(); // doesn't actually do anything
- }
-
- @Override
- public FileChannel getChannel() {
- return null;
- }
-
- @Override
- public int read() throws IOException {
- return mStream.read();
- }
-
- @Override
- public int read(byte[] buffer, int offset, int count) throws IOException {
- return mStream.read(buffer, offset, count);
- }
-
- @Override
- public int read(byte[] buffer) throws IOException {
- return mStream.read(buffer);
- }
-
- @Override
- public long skip(long count) throws IOException {
- return mStream.skip(count);
- }
- }
/**
* An OutputStream you can create on a ParcelFileDescriptor, which will
@@ -426,15 +347,4 @@
}
};
- /**
- * Creates an AssetFileDescriptor from a memory file.
- *
- * @hide
- */
- public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
- throws IOException {
- ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
- return new AssetFileDescriptor(fd, 0, memoryFile.length());
- }
-
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ba1b3a9..9b23c1e 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1333,7 +1333,7 @@
height = mMetrics.widthPixels;
}
int keyboardHidden = mConfiguration.keyboardHidden;
- if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
&& mConfiguration.hardKeyboardHidden
== Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
@@ -1559,7 +1559,7 @@
/**
* Parse a series of {@link android.R.styleable#Extra <extra>} tags from
* an XML file. You call this when you are at the parent tag of the
- * extra tags, and it return once all of the child tags have been parsed.
+ * extra tags, and it will return once all of the child tags have been parsed.
* This will call {@link #parseBundleExtra} for each extra tag encountered.
*
* @param parser The parser from which to retrieve the extras.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 9ac45d8..03aa968 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -31,6 +31,7 @@
import android.database.sqlite.SQLiteProgram;
import android.database.sqlite.SQLiteStatement;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
@@ -705,6 +706,34 @@
}
/**
+ * Utility method to run the query on the db and return the blob value in the
+ * first column of the first row.
+ *
+ * @return A read-only file descriptor for a copy of the blob value.
+ */
+ public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteDatabase db,
+ String query, String[] selectionArgs) {
+ SQLiteStatement prog = db.compileStatement(query);
+ try {
+ return blobFileDescriptorForQuery(prog, selectionArgs);
+ } finally {
+ prog.close();
+ }
+ }
+
+ /**
+ * Utility method to run the pre-compiled query and return the blob value in the
+ * first column of the first row.
+ *
+ * @return A read-only file descriptor for a copy of the blob value.
+ */
+ public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteStatement prog,
+ String[] selectionArgs) {
+ prog.bindAllArgsAsStrings(selectionArgs);
+ return prog.simpleQueryForBlobFileDescriptor();
+ }
+
+ /**
* Reads a String out of a column in a Cursor and writes it to a ContentValues.
* Adds nothing to the ContentValues if the column isn't present or if its value is null.
*
@@ -1203,4 +1232,18 @@
}
return STATEMENT_OTHER;
}
+
+ /**
+ * Appends one set of selection args to another. This is useful when adding a selection
+ * argument to a user provided set.
+ */
+ public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
+ if (originalValues == null || originalValues.length == 0) {
+ return newValues;
+ }
+ String[] result = new String[originalValues.length + newValues.length ];
+ System.arraycopy(originalValues, 0, result, 0, originalValues.length);
+ System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
+ return result;
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java
deleted file mode 100644
index 2800d86..0000000
--- a/core/java/android/database/sqlite/SQLiteContentHelper.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.os.MemoryFile;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-/**
- * Some helper functions for using SQLite database to implement content providers.
- *
- * @hide
- */
-public class SQLiteContentHelper {
-
- /**
- * Runs an SQLite query and returns an AssetFileDescriptor for the
- * blob in column 0 of the first row. If the first column does
- * not contain a blob, an unspecified exception is thrown.
- *
- * @param db Handle to a readable database.
- * @param sql SQL query, possibly with query arguments.
- * @param selectionArgs Query argument values, or {@code null} for no argument.
- * @return If no exception is thrown, a non-null AssetFileDescriptor is returned.
- * @throws FileNotFoundException If the query returns no results or the
- * value of column 0 is NULL, or if there is an error creating the
- * asset file descriptor.
- */
- public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
- String[] selectionArgs) throws FileNotFoundException {
- try {
- MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
- if (file == null) {
- throw new FileNotFoundException("No results.");
- }
- return AssetFileDescriptor.fromMemoryFile(file);
- } catch (IOException ex) {
- throw new FileNotFoundException(ex.toString());
- }
- }
-
- /**
- * Runs an SQLite query and returns a MemoryFile for the
- * blob in column 0 of the first row. If the first column does
- * not contain a blob, an unspecified exception is thrown.
- *
- * @return A memory file, or {@code null} if the query returns no results
- * or the value column 0 is NULL.
- * @throws IOException If there is an error creating the memory file.
- */
- // TODO: make this native and use the SQLite blob API to reduce copying
- private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,
- String[] selectionArgs) throws IOException {
- Cursor cursor = db.rawQuery(sql, selectionArgs);
- if (cursor == null) {
- return null;
- }
- try {
- if (!cursor.moveToFirst()) {
- return null;
- }
- byte[] bytes = cursor.getBlob(0);
- if (bytes == null) {
- return null;
- }
- MemoryFile file = new MemoryFile(null, bytes.length);
- file.writeBytes(bytes, 0, 0, bytes.length);
- file.deactivate();
- return file;
- } finally {
- cursor.close();
- }
- }
-
-}
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 97e39d6..14de60f 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -17,7 +17,11 @@
package android.database.sqlite;
import android.database.DatabaseUtils;
+import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
import dalvik.system.BlockGuard;
@@ -33,6 +37,9 @@
@SuppressWarnings("deprecation")
public class SQLiteStatement extends SQLiteProgram
{
+
+ private static final String TAG = "SQLiteStatement";
+
private static final boolean READ = true;
private static final boolean WRITE = false;
@@ -150,6 +157,30 @@
}
/**
+ * Executes a statement that returns a 1 by 1 table with a blob value.
+ *
+ * @return A read-only file descriptor for a copy of the blob value, or {@code null}
+ * if the value is null or could not be read for some reason.
+ *
+ * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
+ */
+ public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
+ synchronized(this) {
+ long timeStart = acquireAndLock(READ);
+ try {
+ ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
+ mDatabase.logTimeStat(mSql, timeStart);
+ return retValue;
+ } catch (IOException ex) {
+ Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
+ return null;
+ } finally {
+ releaseAndUnlock();
+ }
+ }
+ }
+
+ /**
* Called before every method in this class before executing a SQL statement,
* this method does the following:
* <ul>
@@ -244,4 +275,5 @@
private final native long native_executeInsert();
private final native long native_1x1_long();
private final native String native_1x1_string();
+ private final native ParcelFileDescriptor native_1x1_blob_ashmem() throws IOException;
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 19e578f..f72de67 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1166,6 +1166,14 @@
*/
public static final String FOCUS_MODE_EDOF = "edof";
+ /**
+ * Continuous auto focus mode. The camera continuously tries to focus.
+ * This is ideal for shooting video or shooting photo of moving object.
+ * Auto focus starts when the parameter is set. Applications should not
+ * call {@link #autoFocus(AutoFocusCallback)} in this mode.
+ */
+ public static final String FOCUS_MODE_CONTINUOUS = "continuous";
+
// Indices for focus distance array.
/**
* The array index of near focus distance for use with
@@ -1186,16 +1194,6 @@
public static final int FOCUS_DISTANCE_FAR_INDEX = 2;
/**
- * Continuous focus mode. The camera continuously tries to focus. This
- * is ideal for shooting video or shooting photo of moving object.
- * Continuous focus starts when {@link #autoFocus(AutoFocusCallback)} is
- * called. Continuous focus stops when {@link #cancelAutoFocus()} is
- * called. AutoFocusCallback will be only called once as soon as the
- * picture is in focus.
- */
- public static final String FOCUS_MODE_CONTINUOUS = "continuous";
-
- /**
* The camera determines the exposure by giving more weight to the
* central part of the scene.
*/
@@ -1942,15 +1940,15 @@
/**
* Gets the current focus mode setting.
*
- * @return current focus mode. If the camera does not support
- * auto-focus, this should return {@link #FOCUS_MODE_FIXED}. If
- * the focus mode is not FOCUS_MODE_FIXED or {@link
- * #FOCUS_MODE_INFINITY}, applications should call {@link
- * #autoFocus(AutoFocusCallback)} to start the focus.
+ * @return current focus mode. This method will always return a non-null
+ * value. Applications should call {@link
+ * #autoFocus(AutoFocusCallback)} to start the focus if focus
+ * mode is FOCUS_MODE_AUTO or FOCUS_MODE_MACRO.
* @see #FOCUS_MODE_AUTO
* @see #FOCUS_MODE_INFINITY
* @see #FOCUS_MODE_MACRO
* @see #FOCUS_MODE_FIXED
+ * @see #FOCUS_MODE_CONTINUOUS
*/
public String getFocusMode() {
return get(KEY_FOCUS_MODE);
@@ -2152,8 +2150,14 @@
* #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
* #startPreview()}. Applications can call {@link #getParameters()}
* and this method anytime to get the latest focus distances. If the
- * focus mode is FOCUS_MODE_CONTINUOUS and autofocus has started, focus
- * distances may change from time to time.
+ * focus mode is FOCUS_MODE_CONTINUOUS, focus distances may change from
+ * time to time.
+ *
+ * This method is intended to estimate the distance between the camera
+ * and the subject. After autofocus, the subject distance may be within
+ * near and far focus distance. However, the precision depends on the
+ * camera hardware, autofocus algorithm, the focus area, and the scene.
+ * The error can be large and it should be only used as a reference.
*
* Far focus distance >= optimal focus distance >= near focus distance.
* If the focus distance is infinity, the value will be
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 4814b0a..885a6b8 100755
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -500,7 +500,30 @@
public Keyboard(Context context, int xmlLayoutResId) {
this(context, xmlLayoutResId, 0);
}
-
+
+ /**
+ * Creates a keyboard from the given xml key layout file. Weeds out rows
+ * that have a keyboard mode defined but don't match the specified mode.
+ * @param context the application or service context
+ * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
+ * @param modeId keyboard mode identifier
+ * @param width sets width of keyboard
+ * @param height sets height of keyboard
+ */
+ public Keyboard(Context context, int xmlLayoutResId, int modeId, int width, int height) {
+ mDisplayWidth = width;
+ mDisplayHeight = height;
+
+ mDefaultHorizontalGap = 0;
+ mDefaultWidth = mDisplayWidth / 10;
+ mDefaultVerticalGap = 0;
+ mDefaultHeight = mDefaultWidth;
+ mKeys = new ArrayList<Key>();
+ mModifierKeys = new ArrayList<Key>();
+ mKeyboardMode = modeId;
+ loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
+ }
+
/**
* Creates a keyboard from the given xml key layout file. Weeds out rows
* that have a keyboard mode defined but don't match the specified mode.
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e69c324..447e642 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -185,6 +185,12 @@
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
/**
+ * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+ * resume the download.
+ */
+ public final static int ERROR_CANNOT_RESUME = 1008;
+
+ /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -715,7 +721,8 @@
if (translateStatus(status) != STATUS_FAILED) {
return 0; // arbitrary value when status is not an error
}
- if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+ if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+ || (500 <= status && status < 600)) {
// HTTP status code
return status;
}
@@ -740,6 +747,9 @@
case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
+ case Downloads.Impl.STATUS_CANNOT_RESUME:
+ return ERROR_CANNOT_RESUME;
+
default:
return ERROR_UNKNOWN;
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 11cd526..965af8b 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -67,15 +67,10 @@
/**
* Create a new MobileDataStateTracker
- * @param context the application context of the caller
- * @param target a message handler for getting callbacks about state changes
* @param netType the ConnectivityManager network type
- * @param apnType the Phone apnType
* @param tag the name of this network
*/
- public MobileDataStateTracker(Context context, Handler target, int netType, String tag) {
- mTarget = target;
- mContext = context;
+ public MobileDataStateTracker(int netType, String tag) {
mNetworkInfo = new NetworkInfo(netType,
TelephonyManager.getDefault().getNetworkType(), tag,
TelephonyManager.getDefault().getNetworkTypeName());
@@ -101,6 +96,25 @@
}
/**
+ * Begin monitoring data connectivity.
+ *
+ * @param context is the current Android context
+ * @param target is the Hander to which to return the events.
+ */
+ public void startMonitoring(Context context, Handler target) {
+ mTarget = target;
+ mContext = context;
+
+ IntentFilter filter =
+ new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
+ filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+
+ mContext.registerReceiver(new MobileDataStateReceiver(), filter);
+ mMobileDataState = Phone.DataState.DISCONNECTED;
+ }
+
+ /**
* Return the IP addresses of the DNS servers available for the mobile data
* network interface.
* @return a list of DNS addresses, with no holes.
@@ -139,45 +153,6 @@
public void releaseWakeLock() {
}
- /**
- * Begin monitoring mobile data connectivity.
- */
- public void startMonitoring() {
- IntentFilter filter =
- new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
- filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
-
- mContext.registerReceiver(new MobileDataStateReceiver(), filter);
- mMobileDataState = Phone.DataState.DISCONNECTED;
- }
-
- /**
- * Record the roaming status of the device, and if it is a change from the previous
- * status, send a notification to any listeners.
- * @param isRoaming {@code true} if the device is now roaming, {@code false}
- * if it is no longer roaming.
- */
- private void setRoamingStatus(boolean isRoaming) {
- if (isRoaming != mNetworkInfo.isRoaming()) {
- mNetworkInfo.setRoaming(isRoaming);
- Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
-
- private void setSubtype(int subtype, String subtypeName) {
- if (mNetworkInfo.isConnected()) {
- int oldSubtype = mNetworkInfo.getSubtype();
- if (subtype != oldSubtype) {
- mNetworkInfo.setSubtype(subtype, subtypeName);
- Message msg = mTarget.obtainMessage(
- EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
- msg.sendToTarget();
- }
- }
- }
-
private class MobileDataStateReceiver extends BroadcastReceiver {
IConnectivityManager mConnectivityManager;
@@ -279,8 +254,6 @@
setDetailedState(DetailedState.FAILED, reason, apnName);
}
TelephonyManager tm = TelephonyManager.getDefault();
- setRoamingStatus(tm.isNetworkRoaming());
- setSubtype(tm.getNetworkType(), tm.getNetworkTypeName());
}
}
@@ -526,12 +499,6 @@
return -1;
}
- /**
- * This is not supported.
- */
- public void interpretScanResultsAvailable() {
- }
-
@Override
public String toString() {
StringBuffer sb = new StringBuffer("Mobile data state: ");
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 44215e7..0048a2e 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -16,29 +16,74 @@
package android.net;
+import android.content.Context;
+import android.os.Handler;
+
/**
- * Interface for connectivity service to act on a network interface.
- * All state information for a network should be kept in a Tracker class.
- * This interface defines network-type-independent functions that should
- * be implemented by the Tracker class.
+ * Interface provides the {@link com.android.server.ConnectivityService}
+ * with three services. Events to the ConnectivityService when
+ * changes occur, an API for controlling the network and storage
+ * for network specific information.
+ *
+ * The Connectivity will call startMonitoring before any other
+ * method is called.
*
* {@hide}
*/
public interface NetworkStateTracker {
- public static final int EVENT_STATE_CHANGED = 1;
- public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
/**
- * arg1: 1 to show, 0 to hide
- * arg2: ID of the notification
- * obj: Notification (if showing)
+ * -------------------------------------------------------------
+ * Event Interface back to ConnectivityService.
+ *
+ * The events that are to be sent back to the Handler passed
+ * to startMonitoring when the particular event occurs.
+ * -------------------------------------------------------------
*/
- public static final int EVENT_NOTIFICATION_CHANGED = 3;
- public static final int EVENT_CONFIGURATION_CHANGED = 4;
- public static final int EVENT_ROAMING_CHANGED = 5;
- public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
- public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
- public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
+
+ /**
+ * The network state has changed and the NetworkInfo object
+ * contains the new state.
+ *
+ * msg.what = EVENT_STATE_CHANGED
+ * msg.obj = NetworkInfo object
+ */
+ public static final int EVENT_STATE_CHANGED = 1;
+
+ /**
+ * msg.what = EVENT_CONFIGURATION_CHANGED
+ * msg.obj = NetworkInfo object
+ */
+ public static final int EVENT_CONFIGURATION_CHANGED = 3;
+
+ /**
+ * msg.what = EVENT_RESTORE_DEFAULT_NETWORK
+ * msg.obj = FeatureUser object
+ */
+ public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
+
+ /**
+ * USED by ConnectivityService only
+ *
+ * msg.what = EVENT_CLEAR_NET_TRANSITION_WAKELOCK
+ * msg.arg1 = mNetTransitionWakeLockSerialNumber
+ */
+ public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7;
+
+ /**
+ * -------------------------------------------------------------
+ * Control Interface
+ * -------------------------------------------------------------
+ */
+ /**
+ * Begin monitoring data connectivity.
+ *
+ * This is the first method called when this interface is used.
+ *
+ * @param context is the current Android context
+ * @param target is the Hander to which to return the events.
+ */
+ public void startMonitoring(Context context, Handler target);
/**
* Fetch NetworkInfo for the network
@@ -57,43 +102,6 @@
public String getTcpBufferSizesPropName();
/**
- * Check if private DNS route is set for the network
- */
- public boolean isPrivateDnsRouteSet();
-
- /**
- * Set a flag indicating private DNS route is set
- */
- public void privateDnsRouteSet(boolean enabled);
-
- /**
- * Fetch default gateway address for the network
- */
- public int getDefaultGatewayAddr();
-
- /**
- * Check if default route is set
- */
- public boolean isDefaultRouteSet();
-
- /**
- * Set a flag indicating default route is set for the network
- */
- public void defaultRouteSet(boolean enabled);
-
- /**
- * Indicate tear down requested from connectivity
- */
- public void setTeardownRequested(boolean isRequested);
-
- /**
- * Check if tear down was requested
- */
- public boolean isTeardownRequested();
-
- public void startMonitoring();
-
- /**
* Disable connectivity to a network
* @return {@code true} if a teardown occurred, {@code false} if the
* teardown did not occur.
@@ -120,6 +128,11 @@
public boolean isAvailable();
/**
+ * Fetch default gateway address for the network
+ */
+ public int getDefaultGatewayAddr();
+
+ /**
* Tells the underlying networking system that the caller wants to
* begin using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
@@ -148,9 +161,40 @@
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
/**
- * Interprets scan results. This will be called at a safe time for
- * processing, and from a safe thread.
+ * -------------------------------------------------------------
+ * Storage API used by ConnectivityService for saving
+ * Network specific information.
+ * -------------------------------------------------------------
*/
- public void interpretScanResultsAvailable();
+
+ /**
+ * Check if private DNS route is set for the network
+ */
+ public boolean isPrivateDnsRouteSet();
+
+ /**
+ * Set a flag indicating private DNS route is set
+ */
+ public void privateDnsRouteSet(boolean enabled);
+
+ /**
+ * Check if default route is set
+ */
+ public boolean isDefaultRouteSet();
+
+ /**
+ * Set a flag indicating default route is set for the network
+ */
+ public void defaultRouteSet(boolean enabled);
+
+ /**
+ * Check if tear down was requested
+ */
+ public boolean isTeardownRequested();
+
+ /**
+ * Indicate tear down requested from connectivity
+ */
+ public void setTeardownRequested(boolean isRequested);
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 47faaba..63adcd0 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1568,7 +1568,7 @@
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
if (key == null) {
- throw new NullPointerException("key");
+ throw new NullPointerException("key");
}
final String query = getEncodedQuery();
@@ -1608,6 +1608,24 @@
return null;
}
+ /**
+ * Searches the query string for the first value with the given key and interprets it
+ * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
+ * else is interpreted as <code>true</code>.
+ *
+ * @param key which will be decoded
+ * @param defaultValue the default value to return if there is no query parameter for key
+ * @return the boolean interpretation of the query parameter key
+ */
+ public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
+ String flag = getQueryParameter(key);
+ if (flag == null) {
+ return defaultValue;
+ }
+ flag = flag.toLowerCase();
+ return (!"false".equals(flag) && !"0".equals(flag));
+ }
+
/** Identifies a null parcelled Uri. */
private static final int NULL_TYPE_ID = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a699388..95f217f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.TimeUtils;
/**
* A class providing access to battery usage statistics, including information on
@@ -290,6 +291,11 @@
*/
public static abstract class Proc {
+ public static class ExcessiveWake {
+ public long overTime;
+ public long usedTime;
+ }
+
/**
* Returns the total time (in 1/100 sec) spent executing in user code.
*
@@ -326,6 +332,10 @@
* @see BatteryStats#getCpuSpeedSteps()
*/
public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
+
+ public abstract int countExcessiveWakes();
+
+ public abstract ExcessiveWake getExcessiveWake(int i);
}
/**
@@ -421,6 +431,8 @@
public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
public static final int STATE_AUDIO_ON_FLAG = 1<<19;
public static final int STATE_VIDEO_ON_FLAG = 1<<18;
+ public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
+ public static final int STATE_SENSOR_ON_FLAG = 1<<16;
public int states;
@@ -470,6 +482,16 @@
batteryVoltage = o.batteryVoltage;
states = o.states;
}
+
+ public boolean same(HistoryItem o) {
+ return batteryLevel == o.batteryLevel
+ && batteryStatus == o.batteryStatus
+ && batteryHealth == o.batteryHealth
+ && batteryPlugType == o.batteryPlugType
+ && batteryTemperature == o.batteryTemperature
+ && batteryVoltage == o.batteryVoltage
+ && states == o.states;
+ }
}
public static final class BitDescription {
@@ -633,6 +655,8 @@
new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+ new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock"),
+ new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor"),
new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
SCREEN_BRIGHTNESS_NAMES),
@@ -1376,7 +1400,6 @@
pw.println(getDischargeStartLevel());
pw.print(prefix); pw.print(" Discharge cycle current level: ");
pw.println(getDischargeCurrentLevel());
- } else {
pw.print(prefix); pw.println(" Device is currently plugged into power");
pw.print(prefix); pw.print(" Last discharge cycle start level: ");
pw.println(getDischargeStartLevel());
@@ -1384,6 +1407,13 @@
pw.println(getDischargeCurrentLevel());
}
pw.println(" ");
+ } else {
+ pw.print(prefix); pw.println(" Device battery use since last full charge");
+ pw.print(prefix); pw.print(" Amount discharged (lower bound): ");
+ pw.println(getLowDischargeAmountSinceCharge());
+ pw.print(prefix); pw.print(" Amount discharged (upper bound): ");
+ pw.println(getHighDischargeAmountSinceCharge());
+ pw.println(" ");
}
@@ -1524,12 +1554,16 @@
long userTime;
long systemTime;
int starts;
+ int numExcessive;
userTime = ps.getUserTime(which);
systemTime = ps.getSystemTime(which);
starts = ps.getStarts(which);
+ numExcessive = which == STATS_SINCE_CHARGED
+ ? ps.countExcessiveWakes() : 0;
- if (userTime != 0 || systemTime != 0 || starts != 0) {
+ if (userTime != 0 || systemTime != 0 || starts != 0
+ || numExcessive != 0) {
sb.setLength(0);
sb.append(prefix); sb.append(" Proc ");
sb.append(ent.getKey()); sb.append(":\n");
@@ -1539,6 +1573,18 @@
sb.append(prefix); sb.append(" "); sb.append(starts);
sb.append(" proc starts");
pw.println(sb.toString());
+ for (int e=0; e<numExcessive; e++) {
+ Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
+ if (ew != null) {
+ pw.print(prefix); pw.print(" * Killed for wake lock use: ");
+ TimeUtils.formatDuration(ew.usedTime, pw);
+ pw.print(" over ");
+ TimeUtils.formatDuration(ew.overTime, pw);
+ pw.print(" (");
+ pw.print((ew.usedTime*100)/ew.overTime);
+ pw.println("%)");
+ }
+ }
uidActivity = true;
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ec638..8624467 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -62,6 +62,9 @@
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
+ /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
+ public static final String SERIAL = getString("ro.serialno");
+
/** Various version strings. */
public static class VERSION {
/**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 69b3540..d360140 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -180,6 +180,11 @@
return mThread;
}
+ /** @hide */
+ public MessageQueue getQueue() {
+ return mQueue;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + this);
pw.println(prefix + "mRun=" + mRun);
@@ -187,10 +192,11 @@
pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
if (mQueue != null) {
synchronized (mQueue) {
+ long now = SystemClock.uptimeMillis();
Message msg = mQueue.mMessages;
int n = 0;
while (msg != null) {
- pw.println(prefix + " Message " + n + ": " + msg);
+ pw.println(prefix + " Message " + n + ": " + msg.toString(now));
n++;
msg = msg.next;
}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index a81e16b..f82702a 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -58,7 +58,6 @@
private int mAddress; // address of ashmem memory
private int mLength; // total length of our ashmem region
private boolean mAllowPurging = false; // true if our ashmem region is unpinned
- private final boolean mOwnsRegion; // false if this is a ref to an existing ashmem region
/**
* Allocates a new ashmem region. The region is initially not purgable.
@@ -70,38 +69,11 @@
public MemoryFile(String name, int length) throws IOException {
mLength = length;
mFD = native_open(name, length);
- mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
- mOwnsRegion = true;
- }
-
- /**
- * Creates a reference to an existing memory file. Changes to the original file
- * will be available through this reference.
- * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.
- *
- * @param fd File descriptor for an existing memory file, as returned by
- * {@link #getFileDescriptor()}. This file descriptor will be closed
- * by {@link #close()}.
- * @param length Length of the memory file in bytes.
- * @param mode File mode. Currently only "r" for read-only access is supported.
- * @throws NullPointerException if <code>fd</code> is null.
- * @throws IOException If <code>fd</code> does not refer to an existing memory file,
- * or if the file mode of the existing memory file is more restrictive
- * than <code>mode</code>.
- *
- * @hide
- */
- public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
- if (fd == null) {
- throw new NullPointerException("File descriptor is null.");
+ if (length > 0) {
+ mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
+ } else {
+ mAddress = 0;
}
- if (!isMemoryFile(fd)) {
- throw new IllegalArgumentException("Not a memory file.");
- }
- mLength = length;
- mFD = fd;
- mAddress = native_mmap(mFD, length, modeToProt(mode));
- mOwnsRegion = false;
}
/**
@@ -122,7 +94,7 @@
*
* @hide
*/
- public void deactivate() {
+ void deactivate() {
if (!isDeactivated()) {
try {
native_munmap(mAddress, mLength);
@@ -181,9 +153,6 @@
* @return previous value of allowPurging
*/
synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
- if (!mOwnsRegion) {
- throw new IOException("Only the owner can make ashmem regions purgable.");
- }
boolean oldValue = mAllowPurging;
if (oldValue != allowPurging) {
native_pin(mFD, !allowPurging);
@@ -260,28 +229,7 @@
}
/**
- * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()}
- * for caveats. This must be here to allow classes outside <code>android.os</code< to
- * make ParcelFileDescriptors from MemoryFiles, as
- * {@link ParcelFileDescriptor#ParcelFileDescriptor(FileDescriptor)} is package private.
- *
- *
- * @return The file descriptor owned by this memory file object.
- * The file descriptor is not duplicated.
- * @throws IOException If the memory file has been closed.
- *
- * @hide
- */
- public ParcelFileDescriptor getParcelFileDescriptor() throws IOException {
- FileDescriptor fd = getFileDescriptor();
- return fd != null ? new ParcelFileDescriptor(fd) : null;
- }
-
- /**
- * Gets a FileDescriptor for the memory file. Note that this file descriptor
- * is only safe to pass to {@link #MemoryFile(FileDescriptor,int)}). It
- * should not be used with file descriptor operations that expect a file descriptor
- * for a normal file.
+ * Gets a FileDescriptor for the memory file.
*
* The returned file descriptor is not duplicated.
*
@@ -294,17 +242,6 @@
}
/**
- * Checks whether the given file descriptor refers to a memory file.
- *
- * @throws IOException If <code>fd</code> is not a valid file descriptor.
- *
- * @hide
- */
- public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
- return (native_get_size(fd) >= 0);
- }
-
- /**
* Returns the size of the memory file that the file descriptor refers to,
* or -1 if the file descriptor does not refer to a memory file.
*
@@ -316,20 +253,6 @@
return native_get_size(fd);
}
- /**
- * Converts a file mode string to a <code>prot</code> value as expected by
- * native_mmap().
- *
- * @throws IllegalArgumentException if the file mode is invalid.
- */
- private static int modeToProt(String mode) {
- if ("r".equals(mode)) {
- return PROT_READ;
- } else {
- throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'");
- }
- }
-
private class MemoryInputStream extends InputStream {
private int mMark = 0;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 476da1d..49b72fe 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.TimeUtils;
/**
*
@@ -366,13 +367,17 @@
}
public String toString() {
+ return toString(SystemClock.uptimeMillis());
+ }
+
+ String toString(long now) {
StringBuilder b = new StringBuilder();
b.append("{ what=");
b.append(what);
b.append(" when=");
- b.append(when);
+ TimeUtils.formatDuration(when-now, b);
if (arg1 != 0) {
b.append(" arg1=");
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index d853f13..c1a1809 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -153,6 +153,26 @@
private static native int createPipeNative(FileDescriptor[] outFds);
/**
+ * Gets a file descriptor for a read-only copy of the given data.
+ *
+ * @param data Data to copy.
+ * @param name Name for the shared memory area that may back the file descriptor.
+ * This is purely informative and may be {@code null}.
+ * @return A ParcelFileDescriptor.
+ * @throws IOException if there is an error while creating the shared memory area.
+ */
+ public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
+ if (data == null) return null;
+ MemoryFile file = new MemoryFile(name, data.length);
+ if (data.length > 0) {
+ file.writeBytes(data, 0, 0, data.length);
+ }
+ file.deactivate();
+ FileDescriptor fd = file.getFileDescriptor();
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
+ }
+
+ /**
* Retrieve the actual FileDescriptor associated with this object.
*
* @return Returns the FileDescriptor associated with this object.
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index ca7efe7..5c69214 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -19,6 +19,7 @@
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
* In particular, the ordering of the methods below must match the
@@ -156,14 +157,20 @@
/**
* Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
* allows the calling process's UID access to the contents.
+ *
+ * MountService will call back to the supplied IObbActionListener to inform
+ * it of the terminal state of the call.
*/
- int mountObb(String filename, String key);
+ void mountObb(String filename, String key, IObbActionListener token);
/**
* Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
* program using it will be forcibly killed to unmount the image.
+ *
+ * MountService will call back to the supplied IObbActionListener to inform
+ * it of the terminal state of the call.
*/
- int unmountObb(String filename, boolean force);
+ void unmountObb(String filename, boolean force, IObbActionListener token);
/**
* Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl
new file mode 100644
index 0000000..78d7a9e
--- /dev/null
+++ b/core/java/android/os/storage/IObbActionListener.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.os.storage;
+
+/**
+ * Callback class for receiving events from MountService about
+ * Opaque Binary Blobs (OBBs).
+ *
+ * @hide - Applications should use android.os.storage.StorageManager
+ * to interact with OBBs.
+ */
+interface IObbActionListener {
+ /**
+ * Return from an OBB action result.
+ *
+ * @param filename the path to the OBB the operation was performed on
+ * @param returnCode status of the operation
+ */
+ void onObbResult(String filename, String status);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 61cdace..7c9effa 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,28 +16,14 @@
package android.os.storage;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
import android.util.Log;
-import android.util.SparseArray;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
/**
* StorageManager is the interface to the systems storage service.
@@ -88,6 +74,17 @@
}
/**
+ * Binder listener for OBB action results.
+ */
+ private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
+ private class ObbActionBinderListener extends IObbActionListener.Stub {
+ @Override
+ public void onObbResult(String filename, String status) throws RemoteException {
+ Log.i(TAG, "filename = " + filename + ", result = " + status);
+ }
+ }
+
+ /**
* Private base class for messages sent between the callback thread
* and the target looper handler.
*/
@@ -290,12 +287,23 @@
}
/**
- * Mount an OBB file.
+ * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
+ * specified, it is supplied to the mounting process to be used in any
+ * encryption used in the OBB.
+ * <p>
+ * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+ * file matches a package ID that is owned by the calling program's UID.
+ * That is, shared UID applications can obtain access to any other
+ * application's OBB that shares its UID.
+ *
+ * @param filename the path to the OBB file
+ * @param key decryption key
+ * @return whether the mount call was successfully queued or not
*/
public boolean mountObb(String filename, String key) {
try {
- return mMountService.mountObb(filename, key)
- == StorageResultCode.OperationSucceeded;
+ mMountService.mountObb(filename, key, mObbActionListener);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
}
@@ -304,12 +312,24 @@
}
/**
- * Mount an OBB file.
+ * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
+ * is true, it will kill any application needed to unmount the given OBB.
+ * <p>
+ * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+ * file matches a package ID that is owned by the calling program's UID.
+ * That is, shared UID applications can obtain access to any other
+ * application's OBB that shares its UID.
+ *
+ * @param filename path to the OBB file
+ * @param force whether to kill any programs using this in order to unmount
+ * it
+ * @return whether the unmount call was successfully queued or not
+ * @throws IllegalArgumentException when OBB is not already mounted
*/
- public boolean unmountObb(String filename, boolean force) {
+ public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
try {
- return mMountService.unmountObb(filename, force)
- == StorageResultCode.OperationSucceeded;
+ mMountService.unmountObb(filename, force, mObbActionListener);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
}
@@ -317,7 +337,13 @@
return false;
}
- public boolean isObbMounted(String filename) {
+ /**
+ * Check whether an Opaque Binary Blob (OBB) is mounted or not.
+ *
+ * @param filename path to OBB image
+ * @return true if OBB is mounted; false if not mounted or on error
+ */
+ public boolean isObbMounted(String filename) throws IllegalArgumentException {
try {
return mMountService.isObbMounted(filename);
} catch (RemoteException e) {
@@ -328,13 +354,21 @@
}
/**
- * Check the mounted path of an OBB file.
+ * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
+ * give you the path to where you can obtain access to the internals of the
+ * OBB.
+ *
+ * @param filename path to OBB image
+ * @return absolute path to mounted OBB image data or <code>null</code> if
+ * not mounted or exception encountered trying to read status
*/
public String getMountedObbPath(String filename) {
try {
return mMountService.getMountedObbPath(filename);
} catch (RemoteException e) {
Log.e(TAG, "Failed to find mounted path for OBB", e);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Couldn't read OBB file", e);
}
return null;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 117e507..ffc2862 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -90,6 +90,7 @@
private String mKey;
private Intent mIntent;
private String mFragment;
+ private Bundle mExtras;
private boolean mEnabled = true;
private boolean mSelectable = true;
private boolean mRequiresKey;
@@ -339,6 +340,26 @@
}
/**
+ * Return the extras Bundle object associated with this preference, creating
+ * a new Bundle if there currently isn't one. You can use this to get and
+ * set individual extra key/value pairs.
+ */
+ public Bundle getExtras() {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ return mExtras;
+ }
+
+ /**
+ * Return the extras Bundle object associated with this preference,
+ * returning null if there is not currently one.
+ */
+ public Bundle peekExtras() {
+ return mExtras;
+ }
+
+ /**
* Sets the layout resource that is inflated as the {@link View} to be shown
* for this Preference. In most cases, the default layout is sufficient for
* custom Preference objects and only the widget layout needs to be changed.
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index e13c3e8..70a2b80 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -113,9 +113,28 @@
private static final String PREFERENCES_TAG = "android:preferences";
- private static final String EXTRA_PREFS_SHOW_FRAGMENT = ":android:show_fragment";
+ /**
+ * When starting this activity, the invoking Intent can contain this extra
+ * string to specify which fragment should be initially displayed.
+ */
+ public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment";
- private static final String EXTRA_PREFS_NO_HEADERS = ":android:no_headers";
+ /**
+ * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
+ * this extra can also be specify to supply a Bundle of arguments to pass
+ * to that fragment when it is instantiated during the initial creation
+ * of PreferenceActivity.
+ */
+ public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args";
+
+ /**
+ * When starting this activity, the invoking Intent can contain this extra
+ * boolean that the header list should not be displayed. This is most often
+ * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
+ * the activity to display a specific fragment that the user has navigated
+ * to.
+ */
+ public static final String EXTRA_NO_HEADERS = ":android:no_headers";
private static final String BACK_STACK_PREFS = ":android:prefs";
@@ -125,6 +144,9 @@
// Back will then return RESULT_CANCELED and Next RESULT_OK
private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
+ // add a Skip button?
+ private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
+
// specify custom text for the Back or Next buttons, or cause a button to not appear
// at all by setting it to null
private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
@@ -156,25 +178,29 @@
private static final int FIRST_REQUEST_CODE = 100;
private static final int MSG_BIND_PREFERENCES = 0;
+ private static final int MSG_BUILD_HEADERS = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
-
case MSG_BIND_PREFERENCES:
bindPreferences();
break;
+ case MSG_BUILD_HEADERS:
+ onBuildHeaders(mHeaders);
+ mAdapter.notifyDataSetChanged();
+ break;
}
}
};
- private class HeaderViewHolder {
- ImageView icon;
- TextView title;
- TextView summary;
- }
+ private static class HeaderAdapter extends ArrayAdapter<Header> {
+ private static class HeaderViewHolder {
+ ImageView icon;
+ TextView title;
+ TextView summary;
+ }
- private class HeaderAdapter extends ArrayAdapter<Header> {
private LayoutInflater mInflater;
public HeaderAdapter(Context context, List<Header> objects) {
@@ -191,23 +217,31 @@
view = mInflater.inflate(com.android.internal.R.layout.preference_list_item,
parent, false);
holder = new HeaderViewHolder();
- holder.icon = (ImageView)view.findViewById(
- com.android.internal.R.id.icon);
- holder.title = (TextView)view.findViewById(
- com.android.internal.R.id.title);
- holder.summary = (TextView)view.findViewById(
- com.android.internal.R.id.summary);
+ holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+ holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
+ holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
view.setTag(holder);
} else {
view = convertView;
- holder = (HeaderViewHolder)view.getTag();
+ holder = (HeaderViewHolder) view.getTag();
}
+ // All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
- if (header.icon != null) holder.icon.setImageDrawable(header.icon);
- else if (header.iconRes != 0) holder.icon.setImageResource(header.iconRes);
- if (header.title != null) holder.title.setText(header.title);
- if (header.summary != null) holder.summary.setText(header.summary);
+ if (header.icon == null) {
+ holder.icon.setImageDrawable(null);
+ holder.icon.setImageResource(header.iconRes);
+ } else {
+ holder.icon.setImageResource(0);
+ holder.icon.setImageDrawable(header.icon);
+ }
+ holder.title.setText(header.title);
+ if (TextUtils.isEmpty(header.summary)) {
+ holder.summary.setVisibility(View.GONE);
+ } else {
+ holder.summary.setVisibility(View.VISIBLE);
+ holder.summary.setText(header.summary);
+ }
return view;
}
@@ -221,32 +255,38 @@
* Title of the header that is shown to the user.
* @attr ref android.R.styleable#PreferenceHeader_title
*/
- CharSequence title;
+ public CharSequence title;
/**
* Optional summary describing what this header controls.
* @attr ref android.R.styleable#PreferenceHeader_summary
*/
- CharSequence summary;
+ public CharSequence summary;
/**
* Optional icon resource to show for this header.
* @attr ref android.R.styleable#PreferenceHeader_icon
*/
- int iconRes;
+ public int iconRes;
/**
* Optional icon drawable to show for this header. (If this is non-null,
* the iconRes will be ignored.)
*/
- Drawable icon;
+ public Drawable icon;
/**
* Full class name of the fragment to display when this header is
* selected.
* @attr ref android.R.styleable#PreferenceHeader_fragment
*/
- String fragment;
+ public String fragment;
+
+ /**
+ * Optional arguments to supply to the fragment when it is
+ * instantiated.
+ */
+ public Bundle fragmentArguments;
}
@Override
@@ -258,7 +298,8 @@
mPrefsContainer = findViewById(com.android.internal.R.id.prefs);
boolean hidingHeaders = onIsHidingHeaders();
mSinglePane = hidingHeaders || !onIsMultiPane();
- String initialFragment = getIntent().getStringExtra(EXTRA_PREFS_SHOW_FRAGMENT);
+ String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
+ Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
if (initialFragment != null && mSinglePane) {
// If we are just showing a fragment, we want to run in
@@ -266,7 +307,7 @@
// the headers.
getListView().setVisibility(View.GONE);
mPrefsContainer.setVisibility(View.VISIBLE);
- switchToHeader(initialFragment);
+ switchToHeader(initialFragment, initialArguments);
} else {
// We need to try to build the headers.
@@ -280,8 +321,12 @@
setListAdapter(mAdapter);
if (!mSinglePane) {
mPrefsContainer.setVisibility(View.VISIBLE);
- switchToHeader(initialFragment != null
- ? initialFragment : onGetInitialFragment());
+ if (initialFragment == null) {
+ Header h = onGetInitialHeader();
+ initialFragment = h.fragment;
+ initialArguments = h.fragmentArguments;
+ }
+ switchToHeader(initialFragment, initialArguments);
}
// If there are no headers, we are in the old "just show a screen
@@ -307,6 +352,13 @@
finish();
}
});
+ Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
+ skipButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ });
mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
mNextButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@@ -334,10 +386,29 @@
backButton.setText(buttonText);
}
}
+ if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
+ skipButton.setVisibility(View.VISIBLE);
+ }
}
}
/**
+ * Returns true if this activity is currently showing the header list.
+ */
+ public boolean hasHeaders() {
+ return getListView().getVisibility() == View.VISIBLE
+ && mPreferenceManager == null;
+ }
+
+ /**
+ * Returns true if this activity is showing multiple panes -- the headers
+ * and a preference fragment.
+ */
+ public boolean isMultiPane() {
+ return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE;
+ }
+
+ /**
* Called to determine if the activity should run in multi-pane mode.
* The default implementation returns true if the screen is large
* enough.
@@ -353,20 +424,25 @@
}
/**
- * Called to determine whether the header list should be hidden. The
- * default implementation hides the list if the activity is being re-launched
- * when not in multi-pane mode.
+ * Called to determine whether the header list should be hidden.
+ * The default implementation returns the
+ * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
+ * This is set to false, for example, when the activity is being re-launched
+ * to show a particular preference activity.
*/
public boolean onIsHidingHeaders() {
- return getIntent().getBooleanExtra(EXTRA_PREFS_NO_HEADERS, false);
+ return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
}
/**
- * Called to determine the initial fragment to be shown. The default
- * implementation simply returns the fragment of the first header.
+ * Called to determine the initial header to be shown. The default
+ * implementation simply returns the fragment of the first header. Note
+ * that the returned Header object does not actually need to exist in
+ * your header list -- whatever its fragment is will simply be used to
+ * show for the initial UI.
*/
- public String onGetInitialFragment() {
- return mHeaders.get(0).fragment;
+ public Header onGetInitialHeader() {
+ return mHeaders.get(0);
}
/**
@@ -386,6 +462,16 @@
}
/**
+ * Call when you need to change the headers being displayed. Will result
+ * in onBuildHeaders() later being called to retrieve the new list.
+ */
+ public void invalidateHeaders() {
+ if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
+ mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
+ }
+ }
+
+ /**
* Parse the given XML file as a header description, adding each
* parsed Header into the target list.
*
@@ -404,12 +490,14 @@
}
String nodeName = parser.getName();
- if (!"PreferenceHeaders".equals(nodeName)) {
+ if (!"preference-headers".equals(nodeName)) {
throw new RuntimeException(
- "XML document must start with <PreferenceHeaders> tag; found"
+ "XML document must start with <preference-headers> tag; found"
+ nodeName + " at " + parser.getPositionDescription());
}
+ Bundle curBundle = null;
+
int outerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -418,7 +506,7 @@
}
nodeName = parser.getName();
- if ("Header".equals(nodeName)) {
+ if ("header".equals(nodeName)) {
Header header = new Header();
TypedArray sa = getResources().obtainAttributes(attrs,
@@ -433,9 +521,16 @@
com.android.internal.R.styleable.PreferenceHeader_fragment);
sa.recycle();
- target.add(header);
+ if (curBundle == null) {
+ curBundle = new Bundle();
+ }
+ getResources().parseBundleExtras(parser, curBundle);
+ if (curBundle.size() > 0) {
+ header.fragmentArguments = curBundle;
+ curBundle = null;
+ }
- XmlUtils.skipCurrentTag(parser);
+ target.add(header);
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -531,17 +626,17 @@
/**
* Called when the user selects an item in the header list. The default
- * implementation will call either {@link #startWithFragment(String)}
- * or {@link #switchToHeader(String)} as appropriate.
+ * implementation will call either {@link #startWithFragment(String, Bundle)}
+ * or {@link #switchToHeader(String, Bundle)} as appropriate.
*
* @param header The header that was selected.
* @param position The header's position in the list.
*/
public void onHeaderClick(Header header, int position) {
if (mSinglePane) {
- startWithFragment(header.fragment);
+ startWithFragment(header.fragment, header.fragmentArguments);
} else {
- switchToHeader(header.fragment);
+ switchToHeader(header.fragment, header.fragmentArguments);
}
}
@@ -552,12 +647,14 @@
* and fill the entire activity.
*
* @param fragmentName The name of the fragment to display.
+ * @param args Optional arguments to supply to the fragment.
*/
- public void startWithFragment(String fragmentName) {
+ public void startWithFragment(String fragmentName, Bundle args) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(this, getClass());
- intent.putExtra(EXTRA_PREFS_SHOW_FRAGMENT, fragmentName);
- intent.putExtra(EXTRA_PREFS_NO_HEADERS, true);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+ intent.putExtra(EXTRA_NO_HEADERS, true);
startActivity(intent);
}
@@ -566,30 +663,20 @@
* preference fragment.
*
* @param fragmentName The name of the fragment to display.
+ * @param args Optional arguments to supply to the fragment.
*/
- public void switchToHeader(String fragmentName) {
- popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+ public void switchToHeader(String fragmentName, Bundle args) {
+ getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
- Fragment f;
- try {
- f = Fragment.instantiate(this, fragmentName);
- } catch (Exception e) {
- Log.w(TAG, "Failure instantiating fragment " + fragmentName, e);
- return;
- }
- openFragmentTransaction().replace(com.android.internal.R.id.prefs, f).commit();
+ Fragment f = Fragment.instantiate(this, fragmentName, args);
+ getFragmentManager().openTransaction().replace(
+ com.android.internal.R.id.prefs, f).commit();
}
@Override
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
- Fragment f;
- try {
- f = Fragment.instantiate(this, pref.getFragment());
- } catch (Exception e) {
- Log.w(TAG, "Failure instantiating fragment " + pref.getFragment(), e);
- return false;
- }
- openFragmentTransaction().replace(com.android.internal.R.id.prefs, f)
+ Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
+ getFragmentManager().openTransaction().replace(com.android.internal.R.id.prefs, f)
.addToBackStack(BACK_STACK_PREFS).commit();
return true;
}
diff --git a/core/java/android/preference/PreferenceInflater.java b/core/java/android/preference/PreferenceInflater.java
index 779e746..c21aa18 100644
--- a/core/java/android/preference/PreferenceInflater.java
+++ b/core/java/android/preference/PreferenceInflater.java
@@ -16,6 +16,8 @@
package android.preference;
+import com.android.internal.util.XmlUtils;
+
import java.io.IOException;
import java.util.Map;
@@ -39,6 +41,7 @@
class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
private static final String TAG = "PreferenceInflater";
private static final String INTENT_TAG_NAME = "intent";
+ private static final String EXTRA_TAG_NAME = "extra";
private PreferenceManager mPreferenceManager;
@@ -73,8 +76,10 @@
try {
intent = Intent.parseIntent(getContext().getResources(), parser, attrs);
} catch (IOException e) {
- Log.w(TAG, "Could not parse Intent.");
- Log.w(TAG, e);
+ XmlPullParserException ex = new XmlPullParserException(
+ "Error parsing preference");
+ ex.initCause(e);
+ throw ex;
}
if (intent != null) {
@@ -82,6 +87,18 @@
}
return true;
+ } else if (tag.equals(EXTRA_TAG_NAME)) {
+ getContext().getResources().parseBundleExtra(EXTRA_TAG_NAME, attrs,
+ parentPreference.getExtras());
+ try {
+ XmlUtils.skipCurrentTag(parser);
+ } catch (IOException e) {
+ XmlPullParserException ex = new XmlPullParserException(
+ "Error parsing preference");
+ ex.initCause(e);
+ throw ex;
+ }
+ return true;
}
return false;
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index f34f4a3..c7f8ab2 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -91,7 +91,8 @@
/**
* Returns an adapter that can be attached to a {@link PreferenceActivity}
- * to show the preferences contained in this {@link PreferenceScreen}.
+ * or {@link PreferenceFragment} to show the preferences contained in this
+ * {@link PreferenceScreen}.
* <p>
* This {@link PreferenceScreen} will NOT appear in the returned adapter, instead
* it appears in the hierarchy above this {@link PreferenceScreen}.
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
new file mode 100644
index 0000000..37f1ff2
--- /dev/null
+++ b/core/java/android/provider/BrowserContract.java
@@ -0,0 +1,383 @@
+/*
+ * 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.provider;
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.SyncStateContract;
+import android.util.Pair;
+
+/**
+ * @hide
+ */
+public class BrowserContract {
+ /** The authority for the browser provider */
+ public static final String AUTHORITY = "com.android.browser";
+
+ /** A content:// style uri to the authority for the browser provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * An optional insert, update or delete URI parameter that allows the caller
+ * to specify that it is a sync adapter. The default value is false. If true
+ * the dirty flag is not automatically set and the "syncToNetwork" parameter
+ * is set to false when calling
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+ */
+ public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
+
+ /**
+ * Generic columns for use by sync adapters. The specific functions of
+ * these columns are private to the sync adapter. Other clients of the API
+ * should not attempt to either read or write these columns.
+ */
+ interface BaseSyncColumns {
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "sync4";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC5 = "sync5";
+ }
+
+ /**
+ * Convenience definitions for use in implementing chrome bookmarks sync in the Bookmarks table.
+ */
+ public static final class ChromeSyncColumns {
+ private ChromeSyncColumns() {}
+
+ /** The server unique ID for an item */
+ public static final String SERVER_UNIQUE = BaseSyncColumns.SYNC3;
+
+ public static final String FOLDER_NAME_ROOT = "google_chrome";
+ public static final String FOLDER_NAME_BOOKMARKS = "google_chrome_bookmarks";
+ public static final String FOLDER_NAME_BOOKMARKS_BAR = "bookmark_bar";
+ public static final String FOLDER_NAME_OTHER_BOOKMARKS = "other_bookmarks";
+
+ /** The client unique ID for an item */
+ public static final String CLIENT_UNIQUE = BaseSyncColumns.SYNC4;
+ }
+
+ /**
+ * Columns that appear when each row of a table belongs to a specific
+ * account, including sync information that an account may need.
+ */
+ interface SyncColumns extends BaseSyncColumns {
+ /**
+ * The name of the account instance to which this row belongs, which when paired with
+ * {@link #ACCOUNT_TYPE} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * String that uniquely identifies this row to its source account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String SOURCE_ID = "sourceid";
+
+ /**
+ * Version number that is updated whenever this row or its related data
+ * changes.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String VERSION = "version";
+
+ /**
+ * Flag indicating that {@link #VERSION} has changed, and this row needs
+ * to be synchronized by its owning account.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DIRTY = "dirty";
+ }
+
+ interface BookmarkColumns {
+ /**
+ * The unique ID for a row.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _ID = "_id";
+
+ /**
+ * The URL of the bookmark.
+ * <P>Type: TEXT (URL)</P>
+ */
+ public static final String URL = "url";
+
+ /**
+ * The user visible title of the bookmark.
+ * <P>Type: TEXT</P>
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * The favicon of the bookmark, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String FAVICON = "favicon";
+
+ /**
+ * A thumbnail of the page,may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String THUMBNAIL = "thumbnail";
+
+ /**
+ * The touch icon for the web page, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ * @hide
+ */
+ public static final String TOUCH_ICON = "touch_icon";
+ }
+
+ /**
+ * The bookmarks table, which holds the user's browser bookmarks.
+ */
+ public static final class Bookmarks implements BookmarkColumns, SyncColumns {
+ /**
+ * This utility class cannot be instantiated.
+ */
+ private Bookmarks() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
+
+ /**
+ * The content:// style URI for the default folder
+ */
+ public static final Uri CONTENT_URI_DEFAULT_FOLDER =
+ Uri.withAppendedPath(CONTENT_URI, "folder");
+
+ /**
+ * Builds a URI that points to a specific folder.
+ * @param folderId the ID of the folder to point to
+ */
+ public static final Uri buildFolderUri(long folderId) {
+ return ContentUris.withAppendedId(CONTENT_URI_DEFAULT_FOLDER, folderId);
+ }
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of bookmarks.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single bookmark.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/bookmark";
+
+ /**
+ * Query parameter to use if you want to see deleted bookmarks that are still
+ * around on the device and haven't been synced yet.
+ * @see #IS_DELETED
+ */
+ public static final String QUERY_PARAMETER_SHOW_DELETED = "show_deleted";
+
+ /**
+ * Flag indicating if an item is a folder or bookmark. Non-zero values indicate
+ * a folder and zero indicates a bookmark.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String IS_FOLDER = "folder";
+
+ /**
+ * The ID of the parent folder. ID 0 is the root folder.
+ * <P>Type: INTEGER (reference to item in the same table)</P>
+ */
+ public static final String PARENT = "parent";
+
+ /**
+ * The position of the bookmark in relation to it's siblings that share the same
+ * {@link #PARENT}. May be negative.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String POSITION = "position";
+
+ /**
+ * The item that the bookmark should be inserted after.
+ * May be negative.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String INSERT_AFTER = "insert_after";
+
+ /**
+ * A flag to indicate if an item has been deleted. Queries will not return deleted
+ * entries unless you add the {@link #QUERY_PARAMETER_SHOW_DELETED} query paramter
+ * to the URI when performing your query.
+ * <p>Type: INTEGER (non-zero if the item has been deleted, zero if it hasn't)
+ * @see #QUERY_PARAMETER_SHOW_DELETED
+ */
+ public static final String IS_DELETED = "deleted";
+ }
+
+ /**
+ * The history table, which holds the browsing history.
+ */
+ public static final class History implements BookmarkColumns {
+ /**
+ * This utility class cannot be instantiated.
+ */
+ private History() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "history");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of browser history items.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/browser-history";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single browser history item.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/browser-history";
+
+ /**
+ * The date the item was last visited, in milliseconds since the epoch.
+ * <p>Type: INTEGER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE_LAST_VISITED = "date";
+
+ /**
+ * The date the item created, in milliseconds since the epoch.
+ * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE_CREATED = "created";
+
+ /**
+ * The number of times the item has been visited.
+ * <p>Type: INTEGER</p>
+ */
+ public static final String VISITS = "visits";
+ }
+
+ /**
+ * The search history table.
+ * @hide
+ */
+ public static final class Searches {
+ private Searches() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "searches");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of browser search items.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searches";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single browser search item.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/searches";
+
+ /**
+ * The unique ID for a row.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _ID = "_id";
+
+ /**
+ * The user entered search term.
+ */
+ public static final String SEARCH = "search";
+
+ /**
+ * The date the search was performed, in milliseconds since the epoch.
+ * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE = "date";
+ }
+
+ /**
+ * A table provided for sync adapters to use for storing private sync state data.
+ *
+ * @see SyncStateContract
+ */
+ public static final class SyncState implements SyncStateContract.Columns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private SyncState() {}
+
+ public static final String CONTENT_DIRECTORY =
+ SyncStateContract.Constants.CONTENT_DIRECTORY;
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, CONTENT_DIRECTORY);
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static byte[] get(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#set
+ */
+ public static void set(ContentProviderClient provider, Account account, byte[] data)
+ throws RemoteException {
+ SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#newSetOperation
+ */
+ public static ContentProviderOperation newSetOperation(Account account, byte[] data) {
+ return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
+ }
+ }
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 48d5345..1981780 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -32,6 +32,7 @@
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
+import android.net.Uri.Builder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.DisplayMetrics;
@@ -40,6 +41,7 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.util.ArrayList;
/**
* <p>
@@ -1487,6 +1489,106 @@
* {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI}.
*/
public static final String CONTENT_DIRECTORY = "suggestions";
+
+ /**
+ * Used with {@link Builder#addParameter} to specify what kind of data is
+ * supplied for the suggestion query.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_MATCH_NAME = "name";
+
+ /**
+ * Used with {@link Builder#addParameter} to specify what kind of data is
+ * supplied for the suggestion query.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_MATCH_EMAIL = "email";
+
+ /**
+ * Used with {@link Builder#addParameter} to specify what kind of data is
+ * supplied for the suggestion query.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_MATCH_PHONE = "phone";
+
+ /**
+ * Used with {@link Builder#addParameter} to specify what kind of data is
+ * supplied for the suggestion query.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_MATCH_NICKNAME = "nickname";
+
+ /**
+ * A convenience builder for aggregation suggestion content URIs.
+ *
+ * TODO: change documentation for this class to use the builder.
+ * @hide
+ */
+ public static final class Builder {
+ private long mContactId;
+ private ArrayList<String> mKinds = new ArrayList<String>();
+ private ArrayList<String> mValues = new ArrayList<String>();
+ private int mLimit;
+
+ /**
+ * Optional existing contact ID. If it is not provided, the search
+ * will be based exclusively on the values supplied with {@link #addParameter}.
+ */
+ public Builder setContactId(long contactId) {
+ this.mContactId = contactId;
+ return this;
+ }
+
+ /**
+ * A value that can be used when searching for an aggregation
+ * suggestion.
+ *
+ * @param kind can be one of
+ * {@link AggregationSuggestions#PARAMETER_MATCH_NAME},
+ * {@link AggregationSuggestions#PARAMETER_MATCH_EMAIL},
+ * {@link AggregationSuggestions#PARAMETER_MATCH_NICKNAME},
+ * {@link AggregationSuggestions#PARAMETER_MATCH_PHONE}
+ */
+ public Builder addParameter(String kind, String value) {
+ if (!TextUtils.isEmpty(value)) {
+ mKinds.add(kind);
+ mValues.add(value);
+ }
+ return this;
+ }
+
+ public Builder setLimit(int limit) {
+ mLimit = limit;
+ return this;
+ }
+
+ public Uri build() {
+ android.net.Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();
+ builder.appendEncodedPath(String.valueOf(mContactId));
+ builder.appendPath(Contacts.AggregationSuggestions.CONTENT_DIRECTORY);
+ if (mLimit != 0) {
+ builder.appendQueryParameter("limit", String.valueOf(mLimit));
+ }
+
+ int count = mKinds.size();
+ for (int i = 0; i < count; i++) {
+ builder.appendQueryParameter("query", mKinds.get(i) + ":" + mValues.get(i));
+ }
+
+ return builder.build();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static final Builder builder() {
+ return new Builder();
+ }
}
/**
@@ -2359,18 +2461,19 @@
public static final String CHAT_CAPABILITY = "chat_capability";
/**
- * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device can
+ * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates audio-chat capability (microphone
+ * and speaker)
+ */
+ public static final int CAPABILITY_HAS_VOICE = 1;
+
+ /**
+ * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates that the contact's device can
* display a video feed.
*/
- public static final int CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY = 1;
+ public static final int CAPABILITY_HAS_VIDEO = 2;
/**
- * An allowed value of {@link #CHAT_CAPABILITY}. Indicates audio-chat capability.
- */
- public static final int CAPABILITY_HAS_VOICE = 2;
-
- /**
- * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device has a
+ * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates that the contact's device has a
* camera that can be used for video chat (e.g. a front-facing camera on a phone).
*/
public static final int CAPABILITY_HAS_CAMERA = 4;
@@ -3530,10 +3633,11 @@
* <td>int</td>
* <td>{@link #CHAT_CAPABILITY}</td>
* <td>read/write</td>
- * <td>Contact IM chat compatibility value. The allowed values are:
+ * <td>Contact IM chat compatibility value. The allowed values combinations of the following
+ * flags. If None of these flags is set, the device can only do text messaging.
* <p>
* <ul>
- * <li>{@link #CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY}</li>
+ * <li>{@link #CAPABILITY_HAS_VIDEO}</li>
* <li>{@link #CAPABILITY_HAS_VOICE}</li>
* <li>{@link #CAPABILITY_HAS_CAMERA}</li>
* </ul>
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9b5512..6bf0d5b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -1060,6 +1060,16 @@
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
+ * The lowest-valued error status that is not an actual HTTP status code.
+ */
+ public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+
+ /**
+ * Some possibly transient error occurred, but we can't resume the download.
+ */
+ public static final int STATUS_CANNOT_RESUME = 489;
+
+ /**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index 15f8666..bc764ac 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -308,6 +308,13 @@
public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
public static final int FORMAT_VCARD_2 = 0xBB82;
+ // Object properties we support
+ public static final int PROPERTY_STORAGE_ID = 0xDC01;
+ public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+ public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
+ public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+ public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+
/**
* Object is not protected. It may be modified and deleted, and its properties
* may be modified.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d271e93..fa5cd8b 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -562,15 +562,24 @@
* values:</p>
*
* <ul>
- * <li><em>transactionId (Integer)</em> - The WAP transaction
- * ID</li>
+ * <li><em>transactionId (Integer)</em> - The WAP transaction ID</li>
* <li><em>pduType (Integer)</em> - The WAP PDU type</li>
* <li><em>header (byte[])</em> - The header of the message</li>
* <li><em>data (byte[])</em> - The data payload of the message</li>
+ * <li><em>contentTypeParameters (HashMap<String,String>)</em>
+ * - Any parameters associated with the content type
+ * (decoded from the WSP Content-Type header)</li>
* </ul>
*
* <p>If a BroadcastReceiver encounters an error while processing
* this intent it should set the result code appropriately.</p>
+ *
+ * <p>The contentTypeParameters extra value is map of content parameters keyed by
+ * their names.</p>
+ *
+ * <p>If any unassigned well-known parameters are encountered, the key of the map will
+ * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter. If
+ * a parameter has No-Value the value in the map will be null.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION =
@@ -583,7 +592,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SIM_FULL_ACTION =
- "android.provider.Telephony.SIM_FULL";
+ "android.provider.Telephony.SIM_FULL";
/**
* Broadcast Action: An incoming SMS has been rejected by the
diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java
index a767ea1..dfe00c9b 100644
--- a/core/java/android/util/AndroidException.java
+++ b/core/java/android/util/AndroidException.java
@@ -27,6 +27,10 @@
super(name);
}
+ public AndroidException(String name, Throwable cause) {
+ super(name, cause);
+ }
+
public AndroidException(Exception cause) {
super(cause);
}
diff --git a/core/java/android/util/AndroidRuntimeException.java b/core/java/android/util/AndroidRuntimeException.java
index 4ed17bcd..2b824bf 100644
--- a/core/java/android/util/AndroidRuntimeException.java
+++ b/core/java/android/util/AndroidRuntimeException.java
@@ -27,6 +27,10 @@
super(name);
}
+ public AndroidRuntimeException(String name, Throwable cause) {
+ super(name, cause);
+ }
+
public AndroidRuntimeException(Exception cause) {
super(cause);
}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 0fc70d5..b01a71d 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,6 +24,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.TimeZone;
import java.util.Date;
@@ -130,4 +131,128 @@
public static String getTimeZoneDatabaseVersion() {
return ZoneInfoDB.getVersion();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, StringBuilder builder) {
+ if (duration == 0) {
+ builder.append("0");
+ return;
+ }
+ if (duration > 0) {
+ builder.append("+");
+ } else {
+ builder.append("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ builder.append(days);
+ builder.append('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ builder.append(hours);
+ builder.append('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ builder.append(minutes);
+ builder.append('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ builder.append(seconds);
+ builder.append('s');
+ doall = true;
+ }
+ builder.append(millis);
+ builder.append("ms");
+ }
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, PrintWriter pw) {
+ if (duration == 0) {
+ pw.print("0");
+ return;
+ }
+ if (duration > 0) {
+ pw.print("+");
+ } else {
+ pw.print("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ pw.print(days);
+ pw.print('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ pw.print(hours);
+ pw.print('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ pw.print(minutes);
+ pw.print('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ pw.print(seconds);
+ pw.print('s');
+ doall = true;
+ }
+ pw.print(millis);
+ pw.print("ms");
+ }
+
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long time, long now, PrintWriter pw) {
+ if (time == 0) {
+ pw.print("--");
+ return;
+ }
+ formatDuration(time-now, pw);
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fabe5c8..34d7935 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -27,7 +27,8 @@
/**
- * Use the WindowManager interface to create a Display object.
+ * Use {@link android.view.WindowManager#getDefaultDisplay()
+ * WindowManager.getDefaultDisplay()} to create a Display object.
* Display gives you access to some information about a particular display
* connected to the device.
*/
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 96bd884..4c72e95 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -527,11 +527,18 @@
@Override
public void drawPath(Path path, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ if (path.isSimplePath) {
+ if (path.rects != null) {
+ nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+ }
+ } else {
+ nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ }
if (hasModifier) nResetModifiers(mRenderer);
}
private native void nDrawPath(int renderer, int path, int paint);
+ private native void nDrawRects(int renderer, int region, int paint);
@Override
public void drawPicture(Picture picture) {
@@ -610,9 +617,13 @@
if ((index | count | (index + count) | (text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
@@ -621,20 +632,23 @@
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
- } else {
- char[] buf = TemporaryBuffer.obtain(end - start);
- TextUtils.getChars(text, start, end, buf, 0);
- nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
- TemporaryBuffer.recycle(buf);
+ try {
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawText(this, start, end, x, y,
+ paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
}
- if (hasModifier) nResetModifiers(mRenderer);
}
@Override
@@ -642,9 +656,13 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
@@ -653,8 +671,12 @@
@Override
public void drawText(String text, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
@Override
@@ -671,15 +693,59 @@
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((index | count | text.length - index - count) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+ throw new IllegalArgumentException("Unknown direction: " + dir);
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, char[] text, int index, int count,
+ int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
+
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((start | end | end - start | text.length() - end) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ int flags = dir == 0 ? 0 : 1;
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+ contextEnd, x, y, flags, paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end,
+ contextStart, contextEnd, x, y, flags, paint);
+ } else {
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+ char[] buf = TemporaryBuffer.obtain(contextLen);
+ TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+ nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+ x, y, flags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, String text, int start, int end,
+ int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
+
@Override
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
@@ -690,6 +756,12 @@
private boolean setupModifiers(Paint paint) {
boolean hasModifier = false;
+ if (paint.hasShadow) {
+ nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
+ paint.shadowColor);
+ hasModifier = true;
+ }
+
final Shader shader = paint.getShader();
if (shader != null) {
nSetupShader(mRenderer, shader.native_shader);
@@ -704,7 +776,7 @@
return hasModifier;
}
-
+
private boolean setupColorFilter(Paint paint) {
final ColorFilter filter = paint.getColorFilter();
if (filter != null) {
@@ -716,5 +788,7 @@
private native void nSetupShader(int renderer, int shader);
private native void nSetupColorFilter(int renderer, int colorFilter);
+ private native void nSetupShadow(int renderer, float radius, float dx, float dy, int color);
+
private native void nResetModifiers(int renderer);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 60d495f..ca60a89 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -34,12 +34,23 @@
*
* @hide
*/
-abstract class HardwareRenderer {
+public abstract class HardwareRenderer {
private boolean mEnabled;
private boolean mRequested = true;
private static final String LOG_TAG = "HardwareRenderer";
/**
+ * Indicates whether hardware acceleration is available under any form for
+ * the view hierarchy.
+ *
+ * @return True if the view hierarchy can potentially be hardware accelerated,
+ * false otherwise
+ */
+ public static boolean isAvailable() {
+ return GLES20Canvas.isAvailable();
+ }
+
+ /**
* Destroys the hardware rendering context.
*/
abstract void destroy();
@@ -289,7 +300,6 @@
*/
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
-
}
return mEglContext.getGL();
@@ -363,10 +373,19 @@
attachInfo.mIgnoreDirtyState = true;
view.mPrivateFlags |= View.DRAWN;
+ // TODO: Don't check the current context when we have one per UI thread
+ // TODO: Use a threadlocal flag to know whether the surface has changed
+ if (mEgl.eglGetCurrentContext() != mEglContext ||
+ mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+
onPreDraw();
Canvas canvas = mCanvas;
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ int saveCount = canvas.save();
canvas.translate(0, -yOffset);
try {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d6b9212..e86e3bf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.InputChannel;
/**
* System private interface to the window manager.
@@ -119,6 +120,7 @@
int getKeycodeStateForDevice(int devid, int sw);
int getTrackballKeycodeState(int sw);
int getDPadKeycodeState(int sw);
+ InputChannel monitorInput(String inputChannelName);
// Report whether the hardware supports the given keys; returns true if successful
boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 13d8104..43c957a 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -132,9 +132,9 @@
synchronized (sLock) {
FinishedCallback callback = sRecycleHead;
if (callback != null) {
- callback.mRecycleNext = null;
sRecycleHead = callback.mRecycleNext;
sRecycleCount -= 1;
+ callback.mRecycleNext = null;
} else {
callback = new FinishedCallback();
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9223e17..ed10e41 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -156,7 +156,7 @@
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- private static final int LAST_KEYCODE = KEYCODE_SWITCH_CHARSET;
+ private static final int LAST_KEYCODE = KEYCODE_BUTTON_MODE;
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index a959e0d..d0985d9 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -246,8 +246,9 @@
* - 0: never
* - 1: ifRoom
* - 2: always
+ * - -1: Safe sentinel for "no value".
*/
- private int itemShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+ private int itemShowAsAction;
private String itemListenerMethodName;
@@ -322,7 +323,7 @@
itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
- itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, 0);
+ itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, -1);
itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
a.recycle();
@@ -346,8 +347,11 @@
.setTitleCondensed(itemTitleCondensed)
.setIcon(itemIconResId)
.setAlphabeticShortcut(itemAlphabeticShortcut)
- .setNumericShortcut(itemNumericShortcut)
- .setShowAsAction(itemShowAsAction);
+ .setNumericShortcut(itemNumericShortcut);
+
+ if (itemShowAsAction >= 0) {
+ item.setShowAsAction(itemShowAsAction);
+ }
if (itemListenerMethodName != null) {
if (mContext.isRestricted()) {
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index ff34f4a..0999598 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -312,7 +312,7 @@
* MotionEvent has no getRawX(int) method; simulate it pending future API approval.
*/
private static float getRawX(MotionEvent event, int pointerIndex) {
- float offset = event.getX() - event.getRawX();
+ float offset = event.getRawX() - event.getX();
return event.getX(pointerIndex) + offset;
}
@@ -320,7 +320,7 @@
* MotionEvent has no getRawY(int) method; simulate it pending future API approval.
*/
private static float getRawY(MotionEvent event, int pointerIndex) {
- float offset = event.getY() - event.getRawY();
+ float offset = event.getRawY() - event.getY();
return event.getY(pointerIndex) + offset;
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 068e7b6..fb88c71 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -33,14 +33,15 @@
* and {@link #getXVelocity()}.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
- static final String TAG = "VelocityTracker";
- static final boolean DEBUG = false;
- static final boolean localLOGV = DEBUG || Config.LOGV;
+ private static final String TAG = "VelocityTracker";
+ private static final boolean DEBUG = false;
+ private static final boolean localLOGV = DEBUG || Config.LOGV;
- static final int NUM_PAST = 10;
- static final int MAX_AGE_MILLISECONDS = 200;
+ private static final int NUM_PAST = 10;
+ private static final int MAX_AGE_MILLISECONDS = 200;
+
+ private static final int POINTER_POOL_CAPACITY = 20;
- static final VelocityTracker[] mPool = new VelocityTracker[1];
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -48,16 +49,19 @@
}
public void onAcquired(VelocityTracker element) {
- element.clear();
}
public void onReleased(VelocityTracker element) {
+ element.clear();
}
}, 2));
- private static final int INITIAL_POINTERS = 5;
+ private static Pointer sRecycledPointerListHead;
+ private static int sRecycledPointerCount;
- private static final class PointerData {
+ private static final class Pointer {
+ public Pointer next;
+
public int id;
public float xVelocity;
public float yVelocity;
@@ -65,11 +69,13 @@
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
+
+ public int generation;
}
- private PointerData[] mPointers = new PointerData[INITIAL_POINTERS];
- private int mNumPointers;
+ private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
+ private int mGeneration;
private VelocityTracker mNext;
@@ -115,7 +121,9 @@
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- mNumPointers = 0;
+ releasePointerList(mPointerListHead);
+
+ mPointerListHead = null;
mLastTouchIndex = 0;
}
@@ -134,56 +142,62 @@
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
+ final int generation = mGeneration++;
- if (pointerCount < mNumPointers) {
- final PointerData[] pointers = mPointers;
- int i = mNumPointers;
- while (--i >= 0) {
- final PointerData pointerData = pointers[i];
- if (ev.findPointerIndex(pointerData.id) == -1) {
- // Pointer went up.
- // Shuffle pointers down to fill the hole. Place the old pointer data at
- // the end so we can recycle it if more pointers are added later.
- mNumPointers -= 1;
- final int remaining = mNumPointers - i;
- if (remaining != 0) {
- System.arraycopy(pointers, i + 1, pointers, i, remaining);
- pointers[mNumPointers] = pointerData;
- }
- }
- }
- }
-
+ mLastTouchIndex = finalTouchIndex;
+
+ // Update pointer data.
+ Pointer previousPointer = null;
for (int i = 0; i < pointerCount; i++){
final int pointerId = ev.getPointerId(i);
- PointerData pointerData = getPointerData(pointerId);
- if (pointerData == null) {
- // Pointer went down.
- // Add a new entry. Write a sentinel at the end of the pastTime trace so we
- // will be able to tell where the trace started.
- final PointerData[] oldPointers = mPointers;
- final int newPointerIndex = mNumPointers;
- if (newPointerIndex < oldPointers.length) {
- pointerData = oldPointers[newPointerIndex];
- if (pointerData == null) {
- pointerData = new PointerData();
- oldPointers[newPointerIndex] = pointerData;
- }
- } else {
- final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
- System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
- mPointers = newPointers;
- pointerData = new PointerData();
- newPointers[newPointerIndex] = pointerData;
- }
- pointerData.id = pointerId;
- pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- mNumPointers += 1;
+
+ // Find the pointer data for this pointer id.
+ // This loop is optimized for the common case where pointer ids in the event
+ // are in sorted order. However, we check for this case explicitly and
+ // perform a full linear scan from the start if needed.
+ Pointer nextPointer;
+ if (previousPointer == null || pointerId < previousPointer.id) {
+ previousPointer = null;
+ nextPointer = mPointerListHead;
+ } else {
+ nextPointer = previousPointer.next;
}
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
- final long[] pastTime = pointerData.pastTime;
+ final Pointer pointer;
+ for (;;) {
+ if (nextPointer != null) {
+ final int nextPointerId = nextPointer.id;
+ if (nextPointerId == pointerId) {
+ pointer = nextPointer;
+ break;
+ }
+ if (nextPointerId < pointerId) {
+ nextPointer = nextPointer.next;
+ continue;
+ }
+ }
+
+ // Pointer went down. Add it to the list.
+ // Write a sentinel at the end of the pastTime trace so we will be able to
+ // tell when the trace started.
+ pointer = obtainPointer();
+ pointer.id = pointerId;
+ pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
+ pointer.next = nextPointer;
+ if (previousPointer == null) {
+ mPointerListHead = pointer;
+ } else {
+ previousPointer.next = pointer;
+ }
+ break;
+ }
+
+ pointer.generation = generation;
+ previousPointer = pointer;
+
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
+ final long[] pastTime = pointer.pastTime;
for (int j = 0; j < historySize; j++) {
final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
@@ -196,7 +210,23 @@
pastTime[finalTouchIndex] = ev.getEventTime();
}
- mLastTouchIndex = finalTouchIndex;
+ // Find removed pointers.
+ previousPointer = null;
+ for (Pointer pointer = mPointerListHead; pointer != null; ) {
+ final Pointer nextPointer = pointer.next;
+ if (pointer.generation != generation) {
+ // Pointer went up. Remove it from the list.
+ if (previousPointer == null) {
+ mPointerListHead = nextPointer;
+ } else {
+ previousPointer.next = nextPointer;
+ }
+ releasePointer(pointer);
+ } else {
+ previousPointer = pointer;
+ }
+ pointer = nextPointer;
+ }
}
/**
@@ -223,13 +253,10 @@
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
- final int numPointers = mNumPointers;
- final PointerData[] pointers = mPointers;
final int lastTouchIndex = mLastTouchIndex;
- for (int p = 0; p < numPointers; p++) {
- final PointerData pointerData = pointers[p];
- final long[] pastTime = pointerData.pastTime;
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ final long[] pastTime = pointer.pastTime;
// Search backwards in time for oldest acceptable time.
// Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
@@ -253,8 +280,8 @@
}
// Kind-of stupid.
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
@@ -290,11 +317,11 @@
accumY = maxVelocity;
}
- pointerData.xVelocity = accumX;
- pointerData.yVelocity = accumY;
+ pointer.xVelocity = accumX;
+ pointer.yVelocity = accumY;
if (localLOGV) {
- Log.v(TAG, "[" + p + "] Pointer " + pointerData.id
+ Log.v(TAG, "Pointer " + pointer.id
+ ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
}
}
@@ -307,8 +334,8 @@
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -318,8 +345,8 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.yVelocity : 0;
}
/**
@@ -330,8 +357,8 @@
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -342,19 +369,68 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.yVelocity : 0;
}
- private final PointerData getPointerData(int id) {
- final PointerData[] pointers = mPointers;
- final int numPointers = mNumPointers;
- for (int p = 0; p < numPointers; p++) {
- PointerData pointerData = pointers[p];
- if (pointerData.id == id) {
- return pointerData;
+ private final Pointer getPointer(int id) {
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ if (pointer.id == id) {
+ return pointer;
}
}
return null;
}
+
+ private static final Pointer obtainPointer() {
+ synchronized (sPool) {
+ if (sRecycledPointerCount != 0) {
+ Pointer element = sRecycledPointerListHead;
+ sRecycledPointerCount -= 1;
+ sRecycledPointerListHead = element.next;
+ element.next = null;
+ return element;
+ }
+ }
+ return new Pointer();
+ }
+
+ private static final void releasePointer(Pointer pointer) {
+ synchronized (sPool) {
+ if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
+ pointer.next = sRecycledPointerListHead;
+ sRecycledPointerCount += 1;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
+
+ private static final void releasePointerList(Pointer pointer) {
+ if (pointer != null) {
+ synchronized (sPool) {
+ int count = sRecycledPointerCount;
+ if (count >= POINTER_POOL_CAPACITY) {
+ return;
+ }
+
+ Pointer tail = pointer;
+ for (;;) {
+ count += 1;
+ if (count >= POINTER_POOL_CAPACITY) {
+ break;
+ }
+
+ Pointer next = tail.next;
+ if (next == null) {
+ break;
+ }
+ tail = next;
+ }
+
+ tail.next = sRecycledPointerListHead;
+ sRecycledPointerCount = count;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 735b35a..a0e840d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Camera;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -1374,14 +1375,14 @@
* Width as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredWidth;
/**
* Height as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredHeight;
/**
@@ -1436,8 +1437,8 @@
static final int MEASURED_DIMENSION_SET = 0x00000800;
/** {@hide} */
static final int FORCE_LAYOUT = 0x00001000;
-
- private static final int LAYOUT_REQUIRED = 0x00002000;
+ /** {@hide} */
+ static final int LAYOUT_REQUIRED = 0x00002000;
private static final int PRESSED = 0x00004000;
@@ -1537,6 +1538,14 @@
private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
/**
+ * Indicates that pivotX or pivotY were explicitly set and we should not assume the center
+ * for transform operations
+ *
+ * @hide
+ */
+ private static final int PIVOT_EXPLICITLY_SET = 0x10000000;
+
+ /**
* The parent this view is attached to.
* {@hide}
*
@@ -1627,6 +1636,42 @@
private boolean mMatrixIsIdentity = true;
/**
+ * The Camera object is used to compute a 3D matrix when rotationX or rotationY are set.
+ */
+ private Camera mCamera = null;
+
+ /**
+ * This matrix is used when computing the matrix for 3D rotations.
+ */
+ private Matrix matrix3D = null;
+
+ /**
+ * These prev values are used to recalculate a centered pivot point when necessary. The
+ * pivot point is only used in matrix operations (when rotation, scale, or translation are
+ * set), so thes values are only used then as well.
+ */
+ private int mPrevWidth = -1;
+ private int mPrevHeight = -1;
+
+ /**
+ * Convenience value to check for float values that are close enough to zero to be considered
+ * zero.
+ */
+ private static float NONZERO_EPSILON = .001f;
+
+ /**
+ * The degrees rotation around the vertical axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationY = 0f;
+
+ /**
+ * The degrees rotation around the horizontal axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationX = 0f;
+
+ /**
* The degrees rotation around the pivot point.
*/
@ViewDebug.ExportedProperty
@@ -1684,28 +1729,28 @@
* to the left edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mBottom;
/**
@@ -1713,14 +1758,14 @@
* horizontally.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollY;
/**
@@ -1728,28 +1773,28 @@
* left edge of this view and the left edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingLeft;
/**
* The right padding in pixels, that is the distance in pixels between the
* right edge of this view and the right edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingRight;
/**
* The top padding in pixels, that is the distance in pixels between the
* top edge of this view and the top edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingTop;
/**
* The bottom padding in pixels, that is the distance in pixels between the
* bottom edge of this view and the bottom edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingBottom;
/**
@@ -1760,13 +1805,13 @@
/**
* Cache the paddingRight set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingRight;
/**
* Cache the paddingBottom set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingBottom;
/**
@@ -1873,14 +1918,14 @@
* The minimum height of the view. We'll try our best to have the height
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinHeight;
/**
* The minimum width of the view. We'll try our best to have the width
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinWidth;
/**
@@ -2722,7 +2767,7 @@
*
* @return True if this view has or contains focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean hasFocus() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -2900,7 +2945,7 @@
*
* @return True if this view has focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean isFocused() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -3311,7 +3356,7 @@
*
* @return true if this view has nothing to draw, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotDraw() {
return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
}
@@ -3334,7 +3379,7 @@
*
* @return true if this view does not cache its drawing, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotCacheDrawing() {
return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
}
@@ -3509,7 +3554,7 @@
* @return True if this view can take focus, or false otherwise.
* @attr ref android.R.styleable#View_focusable
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isFocusable() {
return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
}
@@ -4820,7 +4865,7 @@
*
* @return The width of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
@@ -4830,7 +4875,7 @@
*
* @return The height of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
@@ -4888,6 +4933,16 @@
}
/**
+ * Utility function to determine if the value is far enough away from zero to be
+ * considered non-zero.
+ * @param value A floating point value to check for zero-ness
+ * @return whether the passed-in value is far enough away from zero to be considered non-zero
+ */
+ private static boolean nonzero(float value) {
+ return (value < -NONZERO_EPSILON || value > NONZERO_EPSILON);
+ }
+
+ /**
* Recomputes the transform matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
@@ -4896,10 +4951,34 @@
if (mMatrixDirty) {
// transform-related properties have changed since the last time someone
// asked for the matrix; recalculate it with the current values
+
+ // Figure out if we need to update the pivot point
+ if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
+ if ((mRight - mLeft) != mPrevWidth && (mBottom - mTop) != mPrevHeight) {
+ mPrevWidth = mRight - mLeft;
+ mPrevHeight = mBottom - mTop;
+ mPivotX = (float) mPrevWidth / 2f;
+ mPivotY = (float) mPrevHeight / 2f;
+ }
+ }
mMatrix.reset();
mMatrix.setTranslate(mTranslationX, mTranslationY);
mMatrix.preRotate(mRotation, mPivotX, mPivotY);
mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ if (nonzero(mRotationX) || nonzero(mRotationY)) {
+ if (mCamera == null) {
+ mCamera = new Camera();
+ matrix3D = new Matrix();
+ }
+ mCamera.save();
+ mCamera.rotateX(mRotationX);
+ mCamera.rotateY(mRotationY);
+ mCamera.getMatrix(matrix3D);
+ matrix3D.preTranslate(-mPivotX, -mPivotY);
+ matrix3D.postTranslate(mPivotX, mPivotY);
+ mMatrix.postConcat(matrix3D);
+ mCamera.restore();
+ }
mMatrixDirty = false;
mMatrixIsIdentity = mMatrix.isIdentity();
mInverseMatrixDirty = true;
@@ -4955,6 +5034,64 @@
}
/**
+ * The degrees that the view is rotated around the vertical axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of Y rotation.
+ */
+ public float getRotationY() {
+ return mRotationY;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the vertical axis through pivot point.
+ *
+ * @param rotationY The degrees of Y rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationY(float rotationY) {
+ if (mRotationY != rotationY) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationY = rotationY;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The degrees that the view is rotated around the horizontal axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of X rotation.
+ */
+ public float getRotationX() {
+ return mRotationX;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the horizontal axis through pivot point.
+ *
+ * @param rotationX The degrees of X rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationX(float rotationX) {
+ if (mRotationX != rotationX) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationX = rotationX;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
* The amount that the view is scaled in x around the pivot point, as a proportion of
* the view's unscaled width. A value of 1, the default, means that no scaling is applied.
*
@@ -5035,6 +5172,9 @@
/**
* Sets the x location of the point around which the view is
* {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
+ * By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotX The x location of the pivot point.
* @see #getRotation()
@@ -5043,6 +5183,7 @@
* @see #getPivotY()
*/
public void setPivotX(float pivotX) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotX != pivotX) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5069,7 +5210,9 @@
/**
* Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
- * and {@link #setScaleY(float) scaled}.
+ * and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotY The y location of the pivot point.
* @see #getRotation()
@@ -5078,6 +5221,7 @@
* @see #getPivotY()
*/
public void setPivotY(float pivotY) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotY != pivotY) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5312,15 +5456,6 @@
* is still within the view.
*/
private boolean pointInView(float localX, float localY, float slop) {
- if (!hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = localX;
- localXY[1] = localY;
- getInverseMatrix().mapPoints(localXY);
- localX = localXY[0];
- localY = localXY[1];
- }
return localX > -slop && localY > -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
@@ -5781,7 +5916,7 @@
*
* @return True if this View is guaranteed to be fully opaque, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isOpaque() {
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
}
@@ -6495,9 +6630,8 @@
* @see android.widget.ScrollBarDrawable
* @hide
*/
- protected void onDrawHorizontalScrollBar(Canvas canvas,
- Drawable scrollBar,
- int l, int t, int r, int b) {
+ protected void onDrawHorizontalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -6516,9 +6650,8 @@
* @see android.widget.ScrollBarDrawable
* @hide
*/
- protected void onDrawVerticalScrollBar(Canvas canvas,
- Drawable scrollBar,
- int l, int t, int r, int b) {
+ protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -6866,7 +6999,7 @@
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
}
@@ -8730,7 +8863,7 @@
* @return the offset of the baseline within the widget's bounds or -1
* if baseline alignment is not supported
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int getBaseline() {
return -1;
}
@@ -9883,8 +10016,8 @@
public final Interpolator scrollBarInterpolator = new Interpolator(1, 2);
- private final float[] mOpaque = {255.0f};
- private final float[] mTransparent = {0.0f};
+ private final float[] mOpaque = { 255.0f };
+ private final float[] mTransparent = { 0.0f };
/**
* When fading should start. This time moves into the future every time
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 5dd45f9..2ca08ea 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -255,6 +255,14 @@
* @see #deepExport()
*/
String prefix() default "";
+
+ /**
+ * Specifies the category the property falls into, such as measurement,
+ * layout, drawing, etc.
+ *
+ * @return the category as String
+ */
+ String category() default "";
}
/**
@@ -934,65 +942,76 @@
private static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
- final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- forceLayout(view);
- return null;
- }
+ profileViewAndChildren(view, out, true);
+ }
- private 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));
- }
- }
- }
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
- public void run(Void... data) {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
+ long durationMeasure =
+ (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
- public void post(Void... data) {
- }
- });
+ private 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));
+ }
+ }
+ }
- final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- return null;
- }
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
- public void run(Void... data) {
- view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
- }
+ public void post(Void... data) {
+ }
+ })
+ : 0;
+ long durationLayout =
+ (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
- public void post(Void... data) {
- }
- });
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
- final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
- public Object[] pre() {
- final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
- final Bitmap bitmap =
- Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels,
- Bitmap.Config.RGB_565);
- final Canvas canvas = new Canvas(bitmap);
- return new Object[] {
- bitmap, canvas
- };
- }
+ public void post(Void... data) {
+ }
+ }) : 0;
+ long durationDraw =
+ (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+ new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics =
+ view.getResources().getDisplayMetrics();
+ final Bitmap bitmap =
+ Bitmap.createBitmap(metrics.widthPixels,
+ metrics.heightPixels, Bitmap.Config.RGB_565);
+ final Canvas canvas = new Canvas(bitmap);
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
- public void run(Object... data) {
- view.draw((Canvas) data[1]);
- }
+ public void run(Object... data) {
+ view.draw((Canvas) data[1]);
+ }
- public void post(Object... data) {
- ((Bitmap) data[0]).recycle();
- }
- });
-
+ public void post(Object... data) {
+ ((Bitmap) data[0]).recycle();
+ }
+ }) : 0;
out.write(String.valueOf(durationMeasure));
out.write(' ');
out.write(String.valueOf(durationLayout));
@@ -1003,7 +1022,7 @@
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- profileViewAndChildren(group.getChildAt(i), out);
+ profileViewAndChildren(group.getChildAt(i), out, false);
}
}
}
@@ -1033,7 +1052,10 @@
});
try {
- latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
+ if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.w("View", "Could not complete the profiling of the view " + view);
+ return -1;
+ }
} catch (InterruptedException e) {
Log.w("View", "Could not complete the profiling of the view " + view);
Thread.currentThread().interrupt();
@@ -1354,9 +1376,12 @@
// TODO: This should happen on the UI thread
Object methodValue = method.invoke(view, (Object[]) null);
final Class<?> returnType = method.getReturnType();
+ final ExportedProperty property = sAnnotations.get(method);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (returnType == int.class) {
- final ExportedProperty property = sAnnotations.get(method);
+
if (property.resolveId() && context != null) {
final int id = (Integer) methodValue;
methodValue = resolveId(context, id);
@@ -1364,7 +1389,8 @@
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = (Integer) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + method.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1388,21 +1414,22 @@
}
}
} else if (returnType == int[].class) {
- final ExportedProperty property = sAnnotations.get(method);
final int[] array = (int[]) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
final String suffix = "()";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+
+ // Probably want to return here, same as for fields.
+ return;
} else if (!returnType.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(method);
if (property.deepExport()) {
dumpViewProperties(context, methodValue, out, prefix + property.prefix());
continue;
}
}
- writeEntry(out, prefix, method.getName(), "()", methodValue);
+ writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
@@ -1422,9 +1449,12 @@
try {
Object fieldValue = null;
final Class<?> type = field.getType();
+ final ExportedProperty property = sAnnotations.get(field);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (type == int.class) {
- final ExportedProperty property = sAnnotations.get(field);
+
if (property.resolveId() && context != null) {
final int id = field.getInt(view);
fieldValue = resolveId(context, id);
@@ -1432,7 +1462,8 @@
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = field.getInt(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + field.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1454,9 +1485,8 @@
}
}
} else if (type == int[].class) {
- final ExportedProperty property = sAnnotations.get(field);
final int[] array = (int[]) field.get(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
final String suffix = "";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
@@ -1464,10 +1494,9 @@
// We exit here!
return;
} else if (!type.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(field);
if (property.deepExport()) {
- dumpViewProperties(context, field.get(view), out,
- prefix + property.prefix());
+ dumpViewProperties(context, field.get(view), out, prefix
+ + property.prefix());
continue;
}
}
@@ -1476,7 +1505,7 @@
fieldValue = field.get(view);
}
- writeEntry(out, prefix, field.getName(), "", fieldValue);
+ writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
} catch (IllegalAccessException e) {
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9da5637..e2f9c15 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -377,7 +377,7 @@
* @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
* {@link #FOCUS_BLOCK_DESCENDANTS}.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "focus", mapping = {
@ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
@@ -905,19 +905,16 @@
}
// Calculate the offset point into the target's local coordinates
- float xc;
- float yc;
- if (target.hasIdentityMatrix() || mAttachInfo == null) {
- xc = scrolledXFloat - (float) target.mLeft;
- yc = scrolledYFloat - (float) target.mTop;
- } else {
+ float xc = scrolledXFloat - (float) target.mLeft;
+ float yc = scrolledYFloat - (float) target.mTop;
+ if (!target.hasIdentityMatrix() && mAttachInfo != null) {
// non-identity matrix: transform the point into the view's coordinates
final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = scrolledXFloat;
- localXY[1] = scrolledYFloat;
+ localXY[0] = xc;
+ localXY[1] = yc;
target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0] - (float) target.mLeft;
- yc = localXY[1] - (float) target.mTop;
+ xc = localXY[0];
+ yc = localXY[1];
}
// if have a target, see if we're allowed to and want to intercept its
@@ -2835,7 +2832,7 @@
* @see #setChildrenDrawnWithCacheEnabled(boolean)
* @see View#setDrawingCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isAlwaysDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
}
@@ -2870,7 +2867,7 @@
* @see #setAlwaysDrawnWithCacheEnabled(boolean)
* @see #setChildrenDrawnWithCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
}
@@ -2902,7 +2899,7 @@
* @see #setChildrenDrawingOrderEnabled(boolean)
* @see #getChildDrawingOrder(int, int)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawingOrderEnabled() {
return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
}
@@ -2939,7 +2936,7 @@
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
@@ -3572,7 +3569,7 @@
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3583,7 +3580,7 @@
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3708,25 +3705,25 @@
/**
* The left margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int leftMargin;
/**
* The top margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int topMargin;
/**
* The right margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int rightMargin;
/**
* The bottom margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int bottomMargin;
/**
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 8abbf58..d32ccb1 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -33,6 +33,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.EventLog;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
@@ -1362,7 +1363,6 @@
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
@@ -1373,7 +1373,6 @@
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
- canvas.restoreToCount(saveCount);
}
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1719,7 +1718,7 @@
if (LOCAL_LOGV) Log.v(
TAG, "Dispatching key "
+ msg.obj + " to " + mView);
- deliverKeyEvent((KeyEvent)msg.obj, true);
+ deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
break;
case DISPATCH_POINTER: {
MotionEvent event = (MotionEvent) msg.obj;
@@ -1860,9 +1859,14 @@
}
private void finishKeyEvent(KeyEvent event) {
+ if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished");
+
if (mFinishedCallback != null) {
mFinishedCallback.run();
mFinishedCallback = null;
+ } else {
+ Slog.w(TAG, "Attempted to tell the input queue that the current key event "
+ + "is finished but there is no key event actually in progress.");
}
}
@@ -2321,8 +2325,6 @@
boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
if (handled) {
if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
}
return;
@@ -2353,8 +2355,6 @@
if (!handled) {
deliverKeyEventToViewHierarchy(event, sendDone);
} else if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
} else {
Log.w(TAG, "handleFinishedEvent(seq=" + seq
@@ -2428,8 +2428,6 @@
} finally {
if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
}
// Let the exception fall through -- the looper will catch
@@ -2613,9 +2611,14 @@
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ if (mFinishedCallback != null) {
+ Slog.w(TAG, "Received a new key event from the input queue but there is "
+ + "already an unfinished key event in progress.");
+ }
+
mFinishedCallback = finishedCallback;
- dispatchKey(event);
+ dispatchKey(event, true);
}
public void handleMotion(MotionEvent event, Runnable finishedCallback) {
@@ -2626,9 +2629,13 @@
};
public void dispatchKey(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- //noinspection ConstantConditions
- if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
+ dispatchKey(event, false);
+ }
+
+ private void dispatchKey(KeyEvent event, boolean sendDone) {
+ //noinspection ConstantConditions
+ if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
if (DBG) Log.d("keydisp", "===================================================");
if (DBG) Log.d("keydisp", "Focused view Hierarchy is:");
@@ -2640,6 +2647,7 @@
Message msg = obtainMessage(DISPATCH_KEY);
msg.obj = event;
+ msg.arg1 = sendDone ? 1 : 0;
if (LOCAL_LOGV) Log.v(
TAG, "sending key " + event + " to " + mView);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index f32ff77..705af0f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -64,14 +64,24 @@
*/
public static final int FEATURE_ACTION_BAR = 8;
/**
+ * Flag for requesting an Action Bar that overlays window content.
+ * Normally an Action Bar will sit in the space above window content, but if this
+ * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be layered over
+ * the window content itself. This is useful if you would like your app to have more control
+ * over how the Action Bar is displayed, such as letting application content scroll beneath
+ * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
+ * Action Bar over application content.
+ */
+ public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
+ /**
* Flag for specifying the behavior of action modes when an Action Bar is not present.
* If overlay is enabled, the action mode UI will be allowed to cover existing window content.
*/
- public static final int FEATURE_ACTION_MODE_OVERLAY = 9;
+ public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
/**
* Flag for requesting this window to be hardware accelerated, if possible.
*/
- public static final int FEATURE_HARDWARE_ACCELERATED = 10;
+ public static final int FEATURE_HARDWARE_ACCELERATED = 11;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
@@ -120,6 +130,8 @@
private boolean mHasSoftInputMode = false;
+ private boolean mDestroyed;
+
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
@@ -372,6 +384,16 @@
return mHasChildren;
}
+ /** @hide */
+ public final void destroy() {
+ mDestroyed = true;
+ }
+
+ /** @hide */
+ public final boolean isDestroyed() {
+ return mDestroyed;
+ }
+
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 33757f0..659f9cd 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -779,11 +779,6 @@
*/
public void enableScreenAfterBoot();
- /**
- * Called every time the window manager is dispatching a pointer event.
- */
- public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
-
public void setCurrentOrientationLw(int newOrientation);
/**
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 357cb5fe..68e231c 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -37,6 +37,7 @@
import android.util.Xml;
import java.io.IOException;
+import java.util.ArrayList;
/**
* This class is used to specify meta information of an input method.
@@ -68,7 +69,108 @@
* can change based on the configuration (in particular locale).
*/
final int mIsDefaultResId;
-
+
+ /**
+ * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
+ * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
+ * IME switch. The subtype allows the system to call the specified subtype of IME directly.
+ */
+ public static class InputMethodSubtype implements Parcelable {
+ private final String mSubtypeName;
+ private final int mSubtypeIconId;
+ private final String mSubtypeLocale;
+ private final String mSubtypeMode;
+ private final String mSubtypeExtraValue;
+
+ /**
+ * Constructor
+ * @param name The name of the subtype
+ * @param iconId The icon of the subtype
+ * @param locale The locale supported by the subtype
+ * @param mode The mode supported by the subtype
+ * @param extraValue The extra value of the subtype
+ */
+ InputMethodSubtype(String name, int iconId, String locale, String mode,
+ String extraValue) {
+ mSubtypeName = name;
+ mSubtypeIconId = iconId;
+ mSubtypeLocale = locale;
+ mSubtypeMode = mode;
+ mSubtypeExtraValue = extraValue;
+ }
+
+ InputMethodSubtype(Parcel source) {
+ mSubtypeName = source.readString();
+ mSubtypeIconId = source.readInt();
+ mSubtypeLocale = source.readString();
+ mSubtypeMode = source.readString();
+ mSubtypeExtraValue = source.readString();
+ }
+
+ /**
+ * @return the name of the subtype
+ */
+ public String getName() {
+ return mSubtypeName;
+ }
+
+ /**
+ * @return the icon of the subtype
+ */
+ public int getIconId() {
+ return mSubtypeIconId;
+ }
+
+ /**
+ * @return the locale of the subtype
+ */
+ public String getLocale() {
+ return mSubtypeLocale;
+ }
+
+ /**
+ * @return the mode of the subtype
+ */
+ public String getMode() {
+ return mSubtypeMode;
+ }
+
+ /**
+ * @return the extra value of the subtype
+ */
+ public String getExtraValue() {
+ return mSubtypeExtraValue;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(mSubtypeName);
+ dest.writeInt(mSubtypeIconId);
+ dest.writeString(mSubtypeLocale);
+ dest.writeString(mSubtypeMode);
+ dest.writeString(mSubtypeExtraValue);
+ }
+
+ public static final Parcelable.Creator<InputMethodSubtype> CREATOR
+ = new Parcelable.Creator<InputMethodSubtype>() {
+ public InputMethodSubtype createFromParcel(Parcel source) {
+ return new InputMethodSubtype(source);
+ }
+
+ public InputMethodSubtype[] newArray(int size) {
+ return new InputMethodSubtype[size];
+ }
+ };
+ }
+
+ /**
+ * The array of the subtypes.
+ */
+ private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>();
+
/**
* Constructor.
*
@@ -81,11 +183,11 @@
mService = service;
ServiceInfo si = service.serviceInfo;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
-
+
PackageManager pm = context.getPackageManager();
String settingsActivityComponent = null;
int isDefaultResId = 0;
-
+
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA);
@@ -116,13 +218,36 @@
isDefaultResId = sa.getResourceId(
com.android.internal.R.styleable.InputMethod_isDefault, 0);
sa.recycle();
+
+ final int depth = parser.getDepth();
+ // Parse all subtypes
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+ nodeName = parser.getName();
+ if (!"subtype".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data in input-method does not start with subtype tag");
+ }
+ final TypedArray a = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.InputMethod_Subtype);
+ InputMethodSubtype subtype = new InputMethodSubtype(
+ a.getString(com.android.internal.R.styleable.InputMethod_Subtype_label),
+ a.getResourceId(
+ com.android.internal.R.styleable.InputMethod_Subtype_icon, 0),
+ a.getString(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeLocale),
+ a.getString(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeMode),
+ a.getString(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeExtraValue));
+ mSubtypes.add(subtype);
+ }
} catch (NameNotFoundException e) {
throw new XmlPullParserException(
"Unable to create context for: " + si.packageName);
} finally {
if (parser != null) parser.close();
}
-
mSettingsActivityName = settingsActivityComponent;
mIsDefaultResId = isDefaultResId;
}
@@ -132,8 +257,9 @@
mSettingsActivityName = source.readString();
mIsDefaultResId = source.readInt();
mService = ResolveInfo.CREATOR.createFromParcel(source);
+ source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR);
}
-
+
/**
* Temporary API for creating a built-in input method.
*/
@@ -156,7 +282,7 @@
mSettingsActivityName = settingsActivity;
mIsDefaultResId = 0;
}
-
+
/**
* Return a unique ID for this input method. The ID is generated from
* the package and class name implementing the method.
@@ -164,14 +290,14 @@
public String getId() {
return mId;
}
-
+
/**
* Return the .apk package that implements this input method.
*/
public String getPackageName() {
return mService.serviceInfo.packageName;
}
-
+
/**
* Return the class name of the service component that implements
* this input method.
@@ -196,7 +322,7 @@
return new ComponentName(mService.serviceInfo.packageName,
mService.serviceInfo.name);
}
-
+
/**
* Load the user-displayed label for this input method.
*
@@ -206,7 +332,7 @@
public CharSequence loadLabel(PackageManager pm) {
return mService.loadLabel(pm);
}
-
+
/**
* Load the user-displayed icon for this input method.
*
@@ -216,7 +342,7 @@
public Drawable loadIcon(PackageManager pm) {
return mService.loadIcon(pm);
}
-
+
/**
* Return the class name of an activity that provides a settings UI for
* the input method. You can launch this activity be starting it with
@@ -230,7 +356,14 @@
public String getSettingsActivity() {
return mSettingsActivityName;
}
-
+
+ /**
+ * Return the subtypes of Input Method.
+ */
+ public ArrayList<InputMethodSubtype> getSubtypes() {
+ return mSubtypes;
+ }
+
/**
* Return the resource identifier of a resource inside of this input
* method's .apk that determines whether it should be considered a
@@ -239,7 +372,7 @@
public int getIsDefaultResourceId() {
return mIsDefaultResId;
}
-
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName);
@@ -285,12 +418,14 @@
dest.writeString(mSettingsActivityName);
dest.writeInt(mIsDefaultResId);
mService.writeToParcel(dest, flags);
+ dest.writeTypedList(mSubtypes);
}
/**
* Used to make this class parcelable.
*/
- public static final Parcelable.Creator<InputMethodInfo> CREATOR = new Parcelable.Creator<InputMethodInfo>() {
+ public static final Parcelable.Creator<InputMethodInfo> CREATOR
+ = new Parcelable.Creator<InputMethodInfo>() {
public InputMethodInfo createFromParcel(Parcel source) {
return new InputMethodInfo(source);
}
diff --git a/core/java/android/webkit/DeviceOrientationManager.java b/core/java/android/webkit/DeviceOrientationManager.java
new file mode 100644
index 0000000..f65dccf
--- /dev/null
+++ b/core/java/android/webkit/DeviceOrientationManager.java
@@ -0,0 +1,58 @@
+/*
+ * 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.webkit;
+
+/**
+ * This class is simply a container for the methods used to configure WebKit's
+ * mock DeviceOrientationClient for use in LayoutTests.
+ *
+ * This could be part of WebViewCore, but have moved it to its own class to
+ * avoid bloat there.
+ * @hide
+ */
+public final class DeviceOrientationManager {
+ private WebViewCore mWebViewCore;
+
+ public DeviceOrientationManager(WebViewCore webViewCore) {
+ mWebViewCore = webViewCore;
+ }
+
+ /**
+ * Sets whether the Page for this WebViewCore should use a mock DeviceOrientation
+ * client.
+ */
+ public void useMock() {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeUseMock(mWebViewCore);
+ }
+
+ /**
+ * Set the position for the mock DeviceOrientation service for this WebViewCore.
+ */
+ public void setMockOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta,
+ double beta, boolean canProvideGamma, double gamma) {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeSetMockOrientation(mWebViewCore, canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
+ // Native functions
+ private static native void nativeUseMock(WebViewCore webViewCore);
+ private static native void nativeSetMockOrientation(WebViewCore webViewCore,
+ boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+ boolean canProvideGamma, double gamma);
+}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index ecad261..908526f 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -21,6 +21,7 @@
import android.os.Message;
import android.util.Log;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Set;
@@ -46,7 +47,8 @@
// keep track of the main WebView attached to the current window so that we
// can get the proper Context.
- private WebView mCurrentMainWebView;
+ private static WeakReference<WebView> sCurrentMainWebView =
+ new WeakReference<WebView>(null);
/* package */
static final int REFRESH_PLUGINS = 100;
@@ -67,20 +69,20 @@
nativeFinalize();
}
- synchronized void setActiveWebView(WebView webview) {
- if (mCurrentMainWebView != null) {
+ static synchronized void setActiveWebView(WebView webview) {
+ if (sCurrentMainWebView.get() != null) {
// it is possible if there is a sub-WebView. Do nothing.
return;
}
- mCurrentMainWebView = webview;
+ sCurrentMainWebView = new WeakReference<WebView>(webview);
}
- synchronized void removeActiveWebView(WebView webview) {
- if (mCurrentMainWebView != webview) {
+ static synchronized void removeActiveWebView(WebView webview) {
+ if (sCurrentMainWebView.get() != webview) {
// it is possible if there is a sub-WebView. Do nothing.
return;
}
- mCurrentMainWebView = null;
+ sCurrentMainWebView.clear();
}
/**
@@ -261,11 +263,12 @@
synchronized private String getSignedPublicKey(int index, String challenge,
String url) {
- if (mCurrentMainWebView != null) {
+ WebView current = sCurrentMainWebView.get();
+ if (current != null) {
// generateKeyPair expects organizations which we don't have. Ignore
// url.
return CertTool.getSignedPublicKey(
- mCurrentMainWebView.getContext(), index, challenge);
+ current.getContext(), index, challenge);
} else {
Log.e(LOGTAG, "There is no active WebView for getSignedPublicKey");
return "";
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d1b0902..e13e5c7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1817,9 +1817,12 @@
private void clearTextEntry(boolean disableFocusController) {
if (inEditingMode()) {
mWebTextView.remove();
- if (disableFocusController) {
- setFocusControllerInactive();
- }
+ } else {
+ // The keyboard may be open with the WebView as the served view
+ hideSoftKeyboard();
+ }
+ if (disableFocusController) {
+ setFocusControllerInactive();
}
}
@@ -3554,11 +3557,11 @@
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- // bring it back to the default scale so that user can enter text
- boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
+ // bring it back to the reading level scale so that user can enter text
+ boolean zoom = mZoomManager.getScale() < mZoomManager.getReadingLevelScale();
if (zoom) {
mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
- mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
+ mZoomManager.setZoomScale(mZoomManager.getReadingLevelScale(), false);
}
if (isTextView) {
rebuildWebTextView();
@@ -3747,6 +3750,26 @@
}
/**
+ * Called by DRT on UI thread, need to proxy to WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void useMockDeviceOrientation() {
+ mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
+ }
+
+ /**
+ * Called by DRT on WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
+ /**
* Dump the V8 counters to standard output.
* Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
* true. Otherwise, this will do nothing.
@@ -4234,9 +4257,9 @@
public void onWindowFocusChanged(boolean hasWindowFocus) {
setActive(hasWindowFocus);
if (hasWindowFocus) {
- BrowserFrame.sJavaBridge.setActiveWebView(this);
+ JWebCoreJavaBridge.setActiveWebView(this);
} else {
- BrowserFrame.sJavaBridge.removeActiveWebView(this);
+ JWebCoreJavaBridge.removeActiveWebView(this);
}
super.onWindowFocusChanged(hasWindowFocus);
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3c28c94..1e7e6c0 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -33,6 +33,7 @@
import android.view.KeyEvent;
import android.view.SurfaceView;
import android.view.View;
+import android.webkit.DeviceOrientationManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -116,6 +117,8 @@
private int mWebkitScrollX = 0;
private int mWebkitScrollY = 0;
+ private DeviceOrientationManager mDeviceOrientationManager = new DeviceOrientationManager(this);
+
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
/* package */ static final String THREAD_NAME = "WebViewCoreThread";
@@ -878,6 +881,8 @@
// accessibility support
static final int MODIFY_SELECTION = 190;
+ static final int USE_MOCK_DEVICE_ORIENTATION = 191;
+
// private message ids
private static final int DESTROY = 200;
@@ -1409,6 +1414,10 @@
WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
.sendToTarget();
break;
+
+ case USE_MOCK_DEVICE_ORIENTATION:
+ useMockDeviceOrientation();
+ break;
}
}
};
@@ -2481,6 +2490,16 @@
hMode, vMode).sendToTarget();
}
+ private void useMockDeviceOrientation() {
+ mDeviceOrientationManager.useMock();
+ }
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mDeviceOrientationManager.setMockOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index d75d421..d7b4452 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -772,6 +772,9 @@
}
long getCacheTotalSize() {
+ if (mCacheDatabase == null) {
+ return 0;
+ }
long size = 0;
Cursor cursor = null;
final String query = "SELECT SUM(contentlength) as sum FROM cache";
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 7f7f46e..a973d64 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -56,6 +56,13 @@
private ZoomControlExternal mExternalZoomControl;
/*
+ * For large screen devices, the defaultScale usually set to 1.0 and
+ * equal to the overview scale, to differentiate the zoom level for double tapping,
+ * a minimum reading level scale is used.
+ */
+ private static final float MIN_READING_LEVEL_SCALE = 1.5f;
+
+ /*
* The scale factors that determine the upper and lower bounds for the
* default zoom scale.
*/
@@ -245,6 +252,10 @@
return mDefaultScale;
}
+ public final float getReadingLevelScale() {
+ return Math.max(mDefaultScale, MIN_READING_LEVEL_SCALE);
+ }
+
public final float getInvDefaultScale() {
return mInvDefaultScale;
}
@@ -337,9 +348,9 @@
mInitialScrollX = mWebView.getScrollX();
mInitialScrollY = mWebView.getScrollY();
- // snap to DEFAULT_SCALE if it is close
- if (!exceedsMinScaleIncrement(scale, mDefaultScale)) {
- scale = mDefaultScale;
+ // snap to reading level scale if it is close
+ if (!exceedsMinScaleIncrement(scale, getReadingLevelScale())) {
+ scale = getReadingLevelScale();
}
setZoomScale(scale, reflowText);
@@ -535,7 +546,7 @@
} else if (!mInZoomOverview) {
zoomToOverview();
} else {
- zoomToDefaultLevel();
+ zoomToReadingLevel();
}
}
@@ -563,7 +574,8 @@
startZoomAnimation(getZoomOverviewScale(), true);
}
- private void zoomToDefaultLevel() {
+ private void zoomToReadingLevel() {
+ final float readingScale = getReadingLevelScale();
int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
if (left != WebView.NO_LEFTEDGE) {
// add a 5pt padding to the left edge.
@@ -572,13 +584,13 @@
// Re-calculate the zoom center so that the new scroll x will be
// on the left edge.
if (viewLeft > 0) {
- mZoomCenterX = viewLeft * mDefaultScale / (mDefaultScale - mActualScale);
+ mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
} else {
mWebView.scrollBy(viewLeft, 0);
mZoomCenterX = 0;
}
}
- startZoomAnimation(mDefaultScale, true);
+ startZoomAnimation(readingScale, true);
}
public void updateMultiTouchSupport(Context context) {
@@ -772,6 +784,8 @@
&& Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) {
mInitialZoomOverview = false;
setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale));
+ } else {
+ mInZoomOverview = !exceedsMinScaleIncrement(mActualScale, zoomOverviewScale);
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 372cc83..c155dda 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -34,10 +34,16 @@
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.SparseBooleanArray;
+import android.view.ActionMode;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -45,7 +51,6 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -69,6 +74,7 @@
* @attr ref android.R.styleable#AbsListView_cacheColorHint
* @attr ref android.R.styleable#AbsListView_fastScrollEnabled
* @attr ref android.R.styleable#AbsListView_smoothScrollbar
+ * @attr ref android.R.styleable#AbsListView_choiceMode
*/
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
@@ -167,6 +173,57 @@
static final int LAYOUT_MOVE_SELECTION = 6;
/**
+ * Normal list that does not indicate choices
+ */
+ public static final int CHOICE_MODE_NONE = 0;
+
+ /**
+ * The list allows up to one choice
+ */
+ public static final int CHOICE_MODE_SINGLE = 1;
+
+ /**
+ * The list allows multiple choices
+ */
+ public static final int CHOICE_MODE_MULTIPLE = 2;
+
+ /**
+ * The list allows multiple choices in a modal selection mode
+ */
+ public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
+
+ /**
+ * Controls if/how the user may choose/check items in the list
+ */
+ int mChoiceMode = CHOICE_MODE_NONE;
+
+ /**
+ * Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
+ */
+ ActionMode mChoiceActionMode;
+
+ /**
+ * Wrapper for the multiple choice mode callback; AbsListView needs to perform
+ * a few extra actions around what application code does.
+ */
+ MultiChoiceModeWrapper mMultiChoiceModeCallback;
+
+ /**
+ * Running count of how many items are currently checked
+ */
+ int mCheckedItemCount;
+
+ /**
+ * Running state of which positions are currently checked
+ */
+ SparseBooleanArray mCheckStates;
+
+ /**
+ * Running state of which IDs are currently checked
+ */
+ LongSparseArray<Boolean> mCheckedIdStates;
+
+ /**
* Controls how the next layout will happen
*/
int mLayoutMode = LAYOUT_NORMAL;
@@ -577,6 +634,8 @@
});
}
+ setChoiceMode(a.getInt(R.styleable.AbsListView_choiceMode, CHOICE_MODE_NONE));
+
a.recycle();
}
@@ -596,6 +655,311 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ if (adapter != null) {
+ if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
+ mCheckedIdStates == null) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
+ }
+
+ if (mCheckStates != null) {
+ mCheckStates.clear();
+ }
+
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
+ }
+
+ /**
+ * Returns the number of items currently selected. This will only be valid
+ * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
+ *
+ * <p>To determine the specific items that are currently selected, use one of
+ * the <code>getChecked*</code> methods.
+ *
+ * @return The number of items currently selected
+ *
+ * @see #getCheckedItemPosition()
+ * @see #getCheckedItemPositions()
+ * @see #getCheckedItemIds()
+ */
+ public int getCheckedItemCount() {
+ return mCheckedItemCount;
+ }
+
+ /**
+ * Returns the checked state of the specified position. The result is only
+ * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
+ * or {@link #CHOICE_MODE_MULTIPLE}.
+ *
+ * @param position The item whose checked state to return
+ * @return The item's checked state or <code>false</code> if choice mode
+ * is invalid
+ *
+ * @see #setChoiceMode(int)
+ */
+ public boolean isItemChecked(int position) {
+ if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+ return mCheckStates.get(position);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the currently checked item. The result is only valid if the choice
+ * mode has been set to {@link #CHOICE_MODE_SINGLE}.
+ *
+ * @return The position of the currently checked item or
+ * {@link #INVALID_POSITION} if nothing is selected
+ *
+ * @see #setChoiceMode(int)
+ */
+ public int getCheckedItemPosition() {
+ if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
+ return mCheckStates.keyAt(0);
+ }
+
+ return INVALID_POSITION;
+ }
+
+ /**
+ * Returns the set of checked items in the list. The result is only valid if
+ * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
+ *
+ * @return A SparseBooleanArray which will return true for each call to
+ * get(int position) where position is a position in the list,
+ * or <code>null</code> if the choice mode is set to
+ * {@link #CHOICE_MODE_NONE}.
+ */
+ public SparseBooleanArray getCheckedItemPositions() {
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ return mCheckStates;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the set of checked items ids. The result is only valid if the
+ * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
+ * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
+ *
+ * @return A new array which contains the id of each checked item in the
+ * list.
+ */
+ public long[] getCheckedItemIds() {
+ if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
+ return new long[0];
+ }
+
+ final LongSparseArray<Boolean> idStates = mCheckedIdStates;
+ final int count = idStates.size();
+ final long[] ids = new long[count];
+
+ for (int i = 0; i < count; i++) {
+ ids[i] = idStates.keyAt(i);
+ }
+
+ return ids;
+ }
+
+ /**
+ * Clear any choices previously set
+ */
+ public void clearChoices() {
+ if (mCheckStates != null) {
+ mCheckStates.clear();
+ }
+ if (mCheckedIdStates != null) {
+ mCheckedIdStates.clear();
+ }
+ mCheckedItemCount = 0;
+ }
+
+ /**
+ * Sets the checked state of the specified position. The is only valid if
+ * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
+ * {@link #CHOICE_MODE_MULTIPLE}.
+ *
+ * @param position The item whose checked state is to be checked
+ * @param value The new checked state for the item
+ */
+ public void setItemChecked(int position, boolean value) {
+ if (mChoiceMode == CHOICE_MODE_NONE) {
+ return;
+ }
+
+ // Start selection mode if needed. We don't need to if we're unchecking something.
+ if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+ mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+ }
+
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ boolean oldValue = mCheckStates.get(position);
+ mCheckStates.put(position, value);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (value) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
+ if (oldValue != value) {
+ if (value) {
+ mCheckedItemCount++;
+ } else {
+ mCheckedItemCount--;
+ }
+ }
+ if (mChoiceActionMode != null) {
+ final long id = mAdapter.getItemId(position);
+ mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+ position, id, value);
+ }
+ } else {
+ boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
+ // Clear all values if we're checking something, or unchecking the currently
+ // selected item
+ if (value || isItemChecked(position)) {
+ mCheckStates.clear();
+ if (updateIds) {
+ mCheckedIdStates.clear();
+ }
+ }
+ // this may end up selecting the value we just cleared but this way
+ // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
+ if (value) {
+ mCheckStates.put(position, true);
+ if (updateIds) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
+ mCheckedItemCount = 1;
+ } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+ mCheckedItemCount = 0;
+ }
+ }
+
+ // Do not generate a data change while we are in the layout phase
+ if (!mInLayout && !mBlockLayoutRequests) {
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+ }
+ }
+
+ @Override
+ public boolean performItemClick(View view, int position, long id) {
+ boolean handled = false;
+
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ handled = true;
+
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
+ (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
+ boolean newValue = !mCheckStates.get(position, false);
+ mCheckStates.put(position, newValue);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ if (newValue) {
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ } else {
+ mCheckedIdStates.delete(mAdapter.getItemId(position));
+ }
+ }
+ if (newValue) {
+ mCheckedItemCount++;
+ } else {
+ mCheckedItemCount--;
+ }
+ if (mChoiceActionMode != null) {
+ mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+ position, id, newValue);
+ }
+ } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
+ boolean newValue = !mCheckStates.get(position, false);
+ if (newValue) {
+ mCheckStates.clear();
+ mCheckStates.put(position, true);
+ if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates.clear();
+ mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+ }
+ mCheckedItemCount = 1;
+ } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+ mCheckedItemCount = 0;
+ }
+ }
+
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+ }
+
+ handled |= super.performItemClick(view, position, id);
+
+ return handled;
+ }
+
+ /**
+ * @see #setChoiceMode(int)
+ *
+ * @return The current choice mode
+ */
+ public int getChoiceMode() {
+ return mChoiceMode;
+ }
+
+ /**
+ * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
+ * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
+ * List allows up to one item to be in a chosen state. By setting the choiceMode to
+ * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
+ *
+ * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
+ * {@link #CHOICE_MODE_MULTIPLE}
+ */
+ public void setChoiceMode(int choiceMode) {
+ mChoiceMode = choiceMode;
+ if (mChoiceActionMode != null) {
+ mChoiceActionMode.finish();
+ mChoiceActionMode = null;
+ }
+ if (mChoiceMode != CHOICE_MODE_NONE) {
+ if (mCheckStates == null) {
+ mCheckStates = new SparseBooleanArray();
+ }
+ if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
+ mCheckedIdStates = new LongSparseArray<Boolean>();
+ }
+ // Modal multi-choice mode only has choices when the mode is active. Clear them.
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ clearChoices();
+ setLongClickable(true);
+ }
+ }
+ }
+
+ /**
+ * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
+ * selection {@link ActionMode}. Only used when the choice mode is set to
+ * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
+ *
+ * @param listener Listener that will manage the selection mode
+ *
+ * @see #setChoiceMode(int)
+ */
+ public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
+ if (mMultiChoiceModeCallback == null) {
+ mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
+ }
+ mMultiChoiceModeCallback.setWrapped(listener);
+ }
+
+ /**
* Enables fast scrolling by letting the user quickly scroll through lists by
* dragging the fast scroll thumb. The adapter attached to the list may want
* to implement {@link SectionIndexer} if it wishes to display alphabet preview and
@@ -813,6 +1177,8 @@
int position;
int height;
String filter;
+ SparseBooleanArray checkState;
+ LongSparseArray<Boolean> checkIdState;
/**
* Constructor called from {@link AbsListView#onSaveInstanceState()}
@@ -832,6 +1198,13 @@
position = in.readInt();
height = in.readInt();
filter = in.readString();
+ checkState = in.readSparseBooleanArray();
+ long[] idState = in.createLongArray();
+
+ if (idState.length > 0) {
+ checkIdState = new LongSparseArray<Boolean>();
+ checkIdState.setValues(idState, Boolean.TRUE);
+ }
}
@Override
@@ -843,6 +1216,8 @@
out.writeInt(position);
out.writeInt(height);
out.writeString(filter);
+ out.writeSparseBooleanArray(checkState);
+ out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
}
@Override
@@ -854,7 +1229,8 @@
+ " viewTop=" + viewTop
+ " position=" + position
+ " height=" + height
- + " filter=" + filter + "}";
+ + " filter=" + filter
+ + " checkState=" + checkState + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
@@ -918,6 +1294,9 @@
}
}
+ ss.checkState = mCheckStates;
+ ss.checkIdState = mCheckedIdStates;
+
return ss;
}
@@ -949,6 +1328,14 @@
setFilterText(ss.filter);
+ if (ss.checkState != null) {
+ mCheckStates = ss.checkState;
+ }
+
+ if (ss.checkIdState != null) {
+ mCheckedIdStates = ss.checkIdState;
+ }
+
requestLayout();
}
@@ -1774,8 +2161,19 @@
boolean performLongPress(final View child,
final int longPressPosition, final long longPressId) {
- boolean handled = false;
+ // CHOICE_MODE_MULTIPLE_MODAL takes over long press.
+ if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+ if (mChoiceActionMode == null) {
+ mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+ setItemChecked(longPressPosition, true);
+ }
+ // TODO Should we select the long pressed item if we were already in
+ // selection mode? (i.e. treat it like an item click?)
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ return true;
+ }
+ boolean handled = false;
if (mOnItemLongClickListener != null) {
handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
longPressPosition, longPressId);
@@ -3991,6 +4389,75 @@
}
/**
+ * A MultiChoiceModeListener receives events for {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
+ * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
+ * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
+ * selects and deselects list items.
+ */
+ public interface MultiChoiceModeListener extends ActionMode.Callback {
+ /**
+ * Called when an item is checked or unchecked during selection mode.
+ *
+ * @param mode The {@link ActionMode} providing the selection mode
+ * @param position Adapter position of the item that was checked or unchecked
+ * @param id Adapter ID of the item that was checked or unchecked
+ * @param checked <code>true</code> if the item is now checked, <code>false</code>
+ * if the item is now unchecked.
+ */
+ public void onItemCheckedStateChanged(ActionMode mode,
+ int position, long id, boolean checked);
+ }
+
+ class MultiChoiceModeWrapper implements MultiChoiceModeListener {
+ private MultiChoiceModeListener mWrapped;
+
+ public void setWrapped(MultiChoiceModeListener wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ if (mWrapped.onCreateActionMode(mode, menu)) {
+ // Initialize checked graphic state?
+ setLongClickable(false);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ mChoiceActionMode = null;
+
+ // Ending selection mode means deselecting everything.
+ clearChoices();
+
+ mDataChanged = true;
+ rememberSyncState();
+ requestLayout();
+
+ setLongClickable(true);
+ }
+
+ public void onItemCheckedStateChanged(ActionMode mode,
+ int position, long id, boolean checked) {
+ mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
+
+ // If there are no items selected we no longer need the selection mode.
+ if (getCheckedItemCount() == 0) {
+ mode.finish();
+ }
+ }
+ }
+
+ /**
* AbsListView extends LayoutParams to provide a place to hold the view type.
*/
public static class LayoutParams extends ViewGroup.LayoutParams {
@@ -3998,7 +4465,7 @@
* View type for this view, as returned by
* {@link android.widget.Adapter#getItemViewType(int) }
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "list", mapping = {
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"),
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER")
})
@@ -4010,7 +4477,7 @@
* been added to the list view and whether they should be treated as
* recycled views or not.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean recycledHeaderFooter;
/**
@@ -4021,7 +4488,7 @@
* view to be attached to the window rather than just attached to the
* parent.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean forceAdd;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index fe6d91a..10a8729 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -56,7 +56,7 @@
/**
* The position of the first child displayed
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
int mFirstPosition = 0;
/**
@@ -141,7 +141,7 @@
* The position within the adapter's data set of the item to select
* during the next layout.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mNextSelectedPosition = INVALID_POSITION;
/**
@@ -152,7 +152,7 @@
/**
* The position within the adapter's data set of the currently selected item.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mSelectedPosition = INVALID_POSITION;
/**
@@ -168,7 +168,7 @@
/**
* The number of items in the current adapter.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mItemCount;
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index a6d51702..f11c5c6 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -16,14 +16,20 @@
package android.widget;
+import java.util.ArrayList;
+
+import android.animation.PropertyAnimator;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -35,34 +41,110 @@
* @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
* @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
*/
-public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
+public abstract class AdapterViewAnimator extends AdapterView<Adapter>
+ implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
private static final String TAG = "RemoteViewAnimator";
+ /**
+ * The index of the current child, which appears anywhere from the beginning
+ * to the end of the current set of children, as specified by {@link #mActiveOffset}
+ */
int mWhichChild = 0;
- boolean mFirstTime = true;
+
+ /**
+ * Whether or not the first view(s) should be animated in
+ */
boolean mAnimateFirstTime = true;
+ /**
+ * Represents where the in the current window of
+ * views the current <code>mDisplayedChild</code> sits
+ */
+ int mActiveOffset = 0;
+
+ /**
+ * The number of views that the {@link AdapterViewAnimator} keeps as children at any
+ * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
+ */
+ int mNumActiveViews = 1;
+
+ /**
+ * Array of the children of the {@link AdapterViewAnimator}. This array
+ * is accessed in a circular fashion
+ */
+ View[] mActiveViews;
+
+ /**
+ * List of views pending removal from the {@link AdapterViewAnimator}
+ */
+ ArrayList<View> mPreviousViews;
+
+ /**
+ * The index, relative to the adapter, of the beginning of the window of views
+ */
+ int mCurrentWindowStart = 0;
+
+ /**
+ * The index, relative to the adapter, of the end of the window of views
+ */
+ int mCurrentWindowEnd = -1;
+
+ /**
+ * The same as {@link #mCurrentWindowStart}, except when the we have bounded
+ * {@link #mCurrentWindowStart} to be non-negative
+ */
+ int mCurrentWindowStartUnbounded = 0;
+
+ /**
+ * Handler to post events to the main thread
+ */
+ Handler mMainQueue;
+
+ /**
+ * Listens for data changes from the adapter
+ */
AdapterDataSetObserver mDataSetObserver;
- View mPreviousView;
- View mCurrentView;
+ /**
+ * The {@link Adapter} for this {@link AdapterViewAnimator}
+ */
+ Adapter mAdapter;
+ /**
+ * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
+ */
+ RemoteViewsAdapter mRemoteViewsAdapter;
+
+ /**
+ * Specifies whether this is the first time the animator is showing views
+ */
+ boolean mFirstTime = true;
+
+ /**
+ * Specifies if the animator should wrap from 0 to the end and vice versa
+ * or have hard boundaries at the beginning and end
+ */
+ boolean mShouldLoop = true;
+
+ /**
+ * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
+ */
Animation mInAnimation;
Animation mOutAnimation;
- Adapter mAdapter;
- RemoteViewsAdapter mRemoteViewsAdapter;
- private Handler mMainQueue;
+ private ArrayList<View> mViewsToBringToFront;
public AdapterViewAnimator(Context context) {
super(context);
- initViewAnimator(context, null);
+ initViewAnimator();
}
public AdapterViewAnimator(Context context, AttributeSet attrs) {
super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator);
- int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ViewAnimator);
+ int resource = a.getResourceId(
+ com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
if (resource > 0) {
setInAnimation(context, resource);
}
@@ -72,19 +154,72 @@
setOutAnimation(context, resource);
}
- boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+ boolean flag = a.getBoolean(
+ com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
setAnimateFirstView(flag);
a.recycle();
- initViewAnimator(context, attrs);
+ initViewAnimator();
}
/**
* Initialize this {@link AdapterViewAnimator}
*/
- private void initViewAnimator(Context context, AttributeSet attrs) {
+ private void initViewAnimator() {
mMainQueue = new Handler(Looper.myLooper());
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews = new ArrayList<View>();
+ mViewsToBringToFront = new ArrayList<View>();
+ }
+
+ /**
+ * This method is used by subclasses to configure the animator to display the
+ * desired number of views, and specify the offset
+ *
+ * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
+ * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
+ * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
+ * and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
+ * be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
+ * window would instead contain indexes 10, 11 and 12.
+ * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
+ * we loop back to the end, or do we do nothing
+ */
+ void configureViewAnimator(int numVisibleViews, int activeOffset, boolean shouldLoop) {
+ if (activeOffset > numVisibleViews - 1) {
+ // Throw an exception here.
+ }
+ mNumActiveViews = numVisibleViews;
+ mActiveOffset = activeOffset;
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews.clear();
+ removeAllViewsInLayout();
+ mCurrentWindowStart = 0;
+ mCurrentWindowEnd = -1;
+ mShouldLoop = shouldLoop;
+ }
+
+ /**
+ * This class should be overridden by subclasses to customize view transitions within
+ * the set of visible views
+ *
+ * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
+ * in the window
+ * @param toIndex The relative index within the window that the view is going to, -1 if it is
+ * being removed
+ * @param view The view that is being animated
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ PropertyAnimator pa;
+ if (fromIndex == -1) {
+ view.setAlpha(0.0f);
+ pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
+ pa.start();
+ } else if (toIndex == -1) {
+ pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
+ pa.start();
+ }
}
/**
@@ -96,9 +231,9 @@
if (mAdapter != null) {
mWhichChild = whichChild;
if (whichChild >= mAdapter.getCount()) {
- mWhichChild = 0;
+ mWhichChild = mShouldLoop ? 0 : mAdapter.getCount() - 1;
} else if (whichChild < 0) {
- mWhichChild = mAdapter.getCount() - 1;
+ mWhichChild = mShouldLoop ? mAdapter.getCount() - 1 : 0;
}
boolean hasFocus = getFocusedChild() != null;
@@ -114,18 +249,28 @@
/**
* Return default inAnimation. To be overriden by subclasses.
*/
- public Animation getDefaultInAnimation() {
+ Animation getDefaultInAnimation() {
return null;
}
/**
- * Return default outAnimation. To be overriden by subclasses.
+ * Return default outAnimation. To be overridden by subclasses.
*/
- public Animation getDefaultOutAnimation() {
+ Animation getDefaultOutAnimation() {
return null;
}
/**
+ * To be overridden by subclasses. This method applies a view / index specific
+ * transform to the child view.
+ *
+ * @param child
+ * @param relativeIndex
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ }
+
+ /**
* Returns the index of the currently displayed child view.
*/
public int getDisplayedChild() {
@@ -160,70 +305,141 @@
showOnly(childIndex, animate, false);
}
- private LayoutParams makeLayoutParams() {
- int width = mMeasuredWidth - mPaddingLeft - mPaddingRight;
- int height = mMeasuredHeight - mPaddingTop - mPaddingBottom;
- return new LayoutParams(width, height);
+ private int modulo(int pos, int size) {
+ return (size + (pos % size)) % size;
}
- protected void showOnly(int childIndex, boolean animate, boolean onLayout) {
- if (mAdapter != null) {
- // The previous view should be removed from the ViewGroup
- if (mPreviousView != null) {
- mPreviousView.clearAnimation();
+ /**
+ * Get the view at this index relative to the current window's start
+ *
+ * @param relativeIndex Position relative to the current window's start
+ * @return View at this index, null if the index is outside the bounds
+ */
+ View getViewAtRelativeIndex(int relativeIndex) {
+ if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
+ int index = mCurrentWindowStartUnbounded + relativeIndex;
+ return mActiveViews[modulo(index, mNumActiveViews)];
+ }
+ return null;
+ }
- // TODO: this is where we would store the the view for
- // recycling
- removeViewInLayout(mPreviousView);
- }
+ private LayoutParams createOrReuseLayoutParams(View v) {
+ final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
+ if (currentLp instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) currentLp;
+ lp.setHorizontalOffset(0);
+ lp.setVerticalOffset(0);
+ return lp;
+ }
+ return new LayoutParams(v);
+ }
- // If the current view is still being animated, we should
- // force the animation to end
- if (mCurrentView != null) {
- mCurrentView.clearAnimation();
- }
+ void showOnly(int childIndex, boolean animate, boolean onLayout) {
+ if (mAdapter == null) return;
- // load the new mCurrentView from our adapter
- mPreviousView = mCurrentView;
- mCurrentView = mAdapter.getView(childIndex, null, this);
- if (mPreviousView != mCurrentView) {
- addViewInLayout(mCurrentView, 0, makeLayoutParams(), true);
- mCurrentView.bringToFront();
- }
+ for (int i = 0; i < mPreviousViews.size(); i++) {
+ View viewToRemove = mPreviousViews.get(i);
+ viewToRemove.clearAnimation();
+ // applyTransformForChildAtIndex here just allows for any cleanup
+ // associated with this view that may need to be done by a subclass
+ applyTransformForChildAtIndex(viewToRemove, -1);
+ removeViewInLayout(viewToRemove);
+ }
+ mPreviousViews.clear();
+ int newWindowStartUnbounded = childIndex - mActiveOffset;
+ int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
+ int newWindowStart = Math.max(0, newWindowStartUnbounded);
+ int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded);
-
-
- // Animate as necessary
- if (mPreviousView != null && mPreviousView != mCurrentView) {
- if (animate && mOutAnimation != null) {
- mPreviousView.startAnimation(mOutAnimation);
+ // This section clears out any items that are in our mActiveViews list
+ // but are outside the effective bounds of our window (this is becomes an issue
+ // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
+ // newWindowEndUnbounded > mAdapter.getCount() - 1
+ for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
+ if (i < newWindowStart || i > newWindowEnd) {
+ int index = modulo(i, mNumActiveViews);
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+ mActiveViews[index] = null;
}
- // This line results in the view becoming invisible *after*
- // the above animation is complete, or, if there is no animation
- // then it becomes invisble immediately
- mPreviousView.setVisibility(View.GONE);
}
+ }
- if (mCurrentView != null && animate && mInAnimation != null) {
- mCurrentView.startAnimation(mInAnimation);
- }
+ // If the window has changed
+ if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
+ // Run through the indices in the new range
+ for (int i = newWindowStart; i <= newWindowEnd; i++) {
- mFirstTime = false;
- if (!onLayout) {
- requestLayout();
- invalidate();
- } else {
- // If the Adapter tries to layout the current view when we get it using getView above
- // the layout will end up being ignored since we are currently laying out, so
- // we post a delayed requestLayout and invalidate
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- mCurrentView.requestLayout();
- mCurrentView.invalidate();
+ int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
+ int newRelativeIndex = i - newWindowStartUnbounded;
+ int index = modulo(i, mNumActiveViews);
+
+ // If this item is in the current window, great, we just need to apply
+ // the transform for it's new relative position in the window, and animate
+ // between it's current and new relative positions
+ if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
+ View view = mActiveViews[index];
+ applyTransformForChildAtIndex(view, newRelativeIndex);
+ animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
+
+ // Otherwise this view is new, so first we have to displace the view that's
+ // taking the new view's place within our cache (a circular array)
+ } else {
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+
+ if (mCurrentWindowStart > newWindowStart) {
+ mViewsToBringToFront.add(previousView);
+ }
}
- });
+
+ // We've cleared a spot for the new view. Get it from the adapter, add it
+ // and apply any transform / animation
+ View newView = mAdapter.getView(i, null, this);
+ if (newView != null) {
+ mActiveViews[index] = newView;
+ addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
+ applyTransformForChildAtIndex(newView, newRelativeIndex);
+ animateViewForTransition(-1, newRelativeIndex, newView);
+ }
+ }
+ mActiveViews[index].bringToFront();
}
+
+ for (int i = 0; i < mViewsToBringToFront.size(); i++) {
+ View v = mViewsToBringToFront.get(i);
+ v.bringToFront();
+ }
+ mViewsToBringToFront.clear();
+
+ mCurrentWindowStart = newWindowStart;
+ mCurrentWindowEnd = newWindowEnd;
+ mCurrentWindowStartUnbounded = newWindowStartUnbounded;
+ }
+
+ mFirstTime = false;
+ if (!onLayout) {
+ requestLayout();
+ invalidate();
+ } else {
+ // If the Adapter tries to layout the current view when we get it using getView
+ // above the layout will end up being ignored since we are currently laying out, so
+ // we post a delayed requestLayout and invalidate
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ requestLayout();
+ invalidate();
+ }
+ });
}
}
@@ -247,11 +463,72 @@
int childRight = mPaddingLeft + child.getMeasuredWidth();
int childBottom = mPaddingTop + child.getMeasuredHeight();
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
- child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+ childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+ }
+ mDataChanged = false;
+ }
+
+ static class SavedState extends BaseSavedState {
+ int whichChild;
+
+ /**
+ * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState, int whichChild) {
+ super(superState);
+ this.whichChild = whichChild;
}
- mDataChanged = false;
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ whichChild = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(whichChild);
+ }
+
+ @Override
+ public String toString() {
+ return "AdapterViewAnimator.SavedState{ whichChild = " + whichChild + " }";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ return new SavedState(superState, mWhichChild);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ // Here we set mWhichChild in addition to setDisplayedChild
+ // We do the former in case mAdapter is null, and hence setDisplayedChild won't
+ // set mWhichChild
+ mWhichChild = ss.whichChild;
+ setDisplayedChild(mWhichChild);
}
@Override
@@ -261,8 +538,6 @@
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- Log.v(TAG, "onMeasure");
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -278,7 +553,6 @@
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
-
setMeasuredDimension(widthSpecSize, heightSpecSize);
}
@@ -302,7 +576,7 @@
* @see #getDisplayedChild()
*/
public View getCurrentView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -402,22 +676,25 @@
@Override
public void setAdapter(Adapter adapter) {
+ if (mAdapter != null && mDataSetObserver != null) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ }
+
mAdapter = adapter;
if (mAdapter != null) {
- if (mDataSetObserver != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
-
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
+ setFocusable(true);
}
/**
- * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a RemoteViewsService
- * through the specified intent.
- * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
+ * RemoteViewsService through the specified intent.
+ *
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to
+ * connect to.
*/
@android.view.RemotableViewMethod
public void setRemoteViewsAdapter(Intent intent) {
@@ -431,7 +708,7 @@
@Override
public View getSelectedView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -452,4 +729,102 @@
setAdapter(mRemoteViewsAdapter);
}
}
+
+ private final Rect dirtyRect = new Rect();
+ @Override
+ public void removeViewInLayout(View view) {
+ // TODO: need to investigate this block a bit more
+ // and perhaps fix some other invalidations issues.
+ View parent = null;
+ view.setVisibility(INVISIBLE);
+ if (view.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ parent = lp.getParentAndDirtyRegion(dirtyRect);
+ }
+
+ super.removeViewInLayout(view);
+
+ if (parent != null)
+ parent.invalidate(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+ }
+
+ static class LayoutParams extends ViewGroup.LayoutParams {
+ int horizontalOffset;
+ int verticalOffset;
+ View mView;
+
+ LayoutParams(View view) {
+ super(0, 0);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ mView = view;
+ }
+
+ LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ }
+
+ private Rect parentRect = new Rect();
+ void invalidateGlobalRegion(View v, Rect r) {
+ View p = v;
+ boolean firstPass = true;
+ parentRect.set(0, 0, 0, 0);
+ while (p.getParent() != null && p.getParent() instanceof View
+ && !parentRect.contains(r)) {
+ if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
+ firstPass = false;
+ p = (View) p.getParent();
+ parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
+ p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
+ }
+ p.invalidate(r.left, r.top, r.right, r.bottom);
+ }
+
+ public View getParentAndDirtyRegion(Rect globalRect) {
+ globalRect.set(mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom());
+ View p = mView;
+ boolean firstPass = true;
+ parentRect.set(0, 0, 0, 0);
+ while (p.getParent() != null && p.getParent() instanceof View
+ && !parentRect.contains(globalRect)) {
+ if (!firstPass) {
+ globalRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
+ }
+
+ firstPass = false;
+ p = (View) p.getParent();
+ parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
+ p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
+ }
+ return p;
+ }
+
+ private Rect invalidateRect = new Rect();
+ // This is public so that PropertyAnimator can access it
+ public void setVerticalOffset(int newVerticalOffset) {
+ int offsetDelta = newVerticalOffset - verticalOffset;
+ verticalOffset = newVerticalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+ int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+ invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
+ invalidateGlobalRegion(mView, invalidateRect);
+ }
+ }
+
+ public void setHorizontalOffset(int newHorizontalOffset) {
+ int offsetDelta = newHorizontalOffset - horizontalOffset;
+ horizontalOffset = newHorizontalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
+ int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
+ invalidateRect.set(left, mView.getTop(), right, mView.getBottom());
+ invalidateGlobalRegion(mView, invalidateRect);
+ }
+ }
+ }
}
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 901c761..895683d 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -25,8 +25,8 @@
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.RemotableViewMethod;
import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
import android.widget.RemoteViews.RemoteView;
/**
@@ -163,6 +163,40 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ @RemotableViewMethod
+ public void showNext() {
+ // if the flipper is currently flipping automatically, and showNext() is called
+ // we should we should make sure to reset the timer
+ if (mRunning) {
+ mHandler.removeMessages(FLIP_MSG);
+ Message msg = mHandler.obtainMessage(FLIP_MSG);
+ mHandler.sendMessageDelayed(msg, mFlipInterval);
+ }
+ super.showNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @RemotableViewMethod
+ public void showPrevious() {
+ // if the flipper is currently flipping automatically, and showPrevious() is called
+ // we should we should make sure to reset the timer
+ if (mRunning) {
+ mHandler.removeMessages(FLIP_MSG);
+ Message msg = mHandler.obtainMessage(FLIP_MSG);
+ mHandler.sendMessageDelayed(msg, mFlipInterval);
+ }
+ super.showPrevious();
+ }
+
+ /**
+
+ /**
* Internal method to start or stop dispatching flip {@link Message} based
* on {@link #mRunning} and {@link #mVisible} state.
*/
@@ -229,8 +263,6 @@
if (msg.what == FLIP_MSG) {
if (mRunning) {
showNext();
- msg = obtainMessage(FLIP_MSG);
- sendMessageDelayed(msg, mFlipInterval);
}
}
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e27bb4fe..e445180 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -46,27 +46,32 @@
*/
@RemoteView
public class FrameLayout extends ViewGroup {
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
boolean mMeasureAllChildren = false;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
private Drawable mForeground;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingTop = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingRight = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingBottom = 0;
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "drawing")
private int mForegroundGravity = Gravity.FILL;
/** {@hide} */
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean mForegroundInPadding = true;
boolean mForegroundBoundsChanged = false;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index a5b3ed5..ea767f6 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -104,6 +104,26 @@
a.recycle();
}
+ /**
+ * Set how the user may select items from the grid.
+ *
+ * <p>GridView only supports {@link AbsListView#CHOICE_MODE_NONE} and
+ * {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. Attempting to set an unsupported choice
+ * mode will throw an UnsupportedOperationException.
+ */
+ @Override
+ public void setChoiceMode(int choiceMode) {
+ switch (choiceMode) {
+ case CHOICE_MODE_NONE:
+ case CHOICE_MODE_MULTIPLE_MODAL:
+ super.setChoiceMode(choiceMode);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported choice mode " + choiceMode);
+ }
+ }
+
@Override
public ListAdapter getAdapter() {
return mAdapter;
@@ -136,7 +156,10 @@
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
-
+
+ // AbsListView#setAdapter will update choice mode states.
+ super.setAdapter(adapter);
+
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
@@ -1312,6 +1335,12 @@
child.setPressed(isPressed);
}
+ if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+ if (child instanceof Checkable) {
+ ((Checkable) child).setChecked(mCheckStates.get(position));
+ }
+ }
+
if (needToMeasure) {
int childHeightSpec = ViewGroup.getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 32a91464..e30d4c8 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -53,6 +53,8 @@
* within a larger container.
*
* <p>HorizontalScrollView only supports horizontal scrolling.
+ *
+ * @attr ref android.R.styleable#HorizontalScrollView_fillViewport
*/
public class HorizontalScrollView extends FrameLayout {
private static final int ANIMATED_SCROLL_GAP = ScrollView.ANIMATED_SCROLL_GAP;
@@ -248,20 +250,25 @@
}
/**
- * Indicates whether this ScrollView's content is stretched to fill the viewport.
+ * Indicates whether this HorizontalScrollView's content is stretched to
+ * fill the viewport.
*
* @return True if the content fills the viewport, false otherwise.
+ *
+ * @attr ref android.R.styleable#HorizontalScrollView_fillViewport
*/
public boolean isFillViewport() {
return mFillViewport;
}
/**
- * Indicates this ScrollView whether it should stretch its content width to fill
- * the viewport or not.
+ * Indicates this HorizontalScrollView whether it should stretch its content width
+ * to fill the viewport or not.
*
* @param fillViewport True to stretch the content's width to the viewport's
* boundaries, false otherwise.
+ *
+ * @attr ref android.R.styleable#HorizontalScrollView_fillViewport
*/
public void setFillViewport(boolean fillViewport) {
if (fillViewport != mFillViewport) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 7254c3c..53187bf 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -57,7 +57,7 @@
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mBaselineAligned = true;
/**
@@ -67,7 +67,7 @@
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private int mBaselineAlignedChildIndex = -1;
/**
@@ -75,12 +75,13 @@
* We'll calculate the baseline of this layout as we measure vertically; for
* horizontal linear layouts, the offset of 0 is appropriate.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mBaselineChildTop = 0;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mOrientation;
- @ViewDebug.ExportedProperty(mapping = {
+
+ @ViewDebug.ExportedProperty(category = "measurement", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
@@ -95,13 +96,14 @@
@ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
})
private int mGravity = Gravity.LEFT | Gravity.TOP;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mTotalLength;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private float mWeightSum;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mUseLargestChild;
private int[] mMaxAscent;
@@ -1401,7 +1403,7 @@
* 0 if the view should not be stretched. Otherwise the extra pixels
* will be pro-rated among all views whose weight is greater than 0.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public float weight;
/**
@@ -1409,7 +1411,7 @@
*
* @see android.view.Gravity
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 5c34c2c..35e0603 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -33,7 +33,7 @@
/**
* A ListPopupWindow anchors itself to a host view and displays a
- * list of choices. When one is selected, the popup is dismissed.
+ * list of choices.
*
* <p>ListPopupWindow contains a number of tricky behaviors surrounding
* positioning, scrolling parents to fit the dropdown, interacting
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 0bb41e5..de7157b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -28,17 +28,10 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
-import android.view.ActionMode;
import android.view.FocusFinder;
-import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
@@ -67,7 +60,6 @@
* @attr ref android.R.styleable#ListView_entries
* @attr ref android.R.styleable#ListView_divider
* @attr ref android.R.styleable#ListView_dividerHeight
- * @attr ref android.R.styleable#ListView_choiceMode
* @attr ref android.R.styleable#ListView_headerDividersEnabled
* @attr ref android.R.styleable#ListView_footerDividersEnabled
*/
@@ -79,26 +71,6 @@
static final int NO_POSITION = -1;
/**
- * Normal list that does not indicate choices
- */
- public static final int CHOICE_MODE_NONE = 0;
-
- /**
- * The list allows up to one choice
- */
- public static final int CHOICE_MODE_SINGLE = 1;
-
- /**
- * The list allows multiple choices
- */
- public static final int CHOICE_MODE_MULTIPLE = 2;
-
- /**
- * The list allows multiple choices in a modal selection mode
- */
- public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
-
- /**
* When arrow scrolling, ListView will never scroll more than this factor
* times the height of the list.
*/
@@ -141,11 +113,6 @@
private boolean mItemsCanFocus = false;
- private int mChoiceMode = CHOICE_MODE_NONE;
-
- private SparseBooleanArray mCheckStates;
- private LongSparseArray<Boolean> mCheckedIdStates;
-
// used for temporary calculations.
private final Rect mTempRect = new Rect();
private Paint mDividerPaint;
@@ -157,11 +124,6 @@
// Keeps focused children visible through resizes
private FocusSelector mFocusSelector;
- // Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
- private ActionMode mChoiceActionMode;
- private MultiChoiceModeWrapper mMultiChoiceModeCallback;
- private int mCheckedItemCount;
-
public ListView(Context context) {
this(context, null);
}
@@ -196,8 +158,6 @@
setDividerHeight(dividerHeight);
}
- setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE));
-
mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);
@@ -457,6 +417,10 @@
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
+
+ // AbsListView#setAdapter will update choice mode states.
+ super.setAdapter(adapter);
+
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
@@ -481,13 +445,6 @@
// Nothing selected
checkSelectionChanged();
}
-
- if (mChoiceMode != CHOICE_MODE_NONE &&
- mAdapter.hasStableIds() &&
- mCheckedIdStates == null) {
- mCheckedIdStates = new LongSparseArray<Boolean>();
- }
-
} else {
mAreAllItemsSelectable = true;
checkFocus();
@@ -495,14 +452,6 @@
checkSelectionChanged();
}
- if (mCheckStates != null) {
- mCheckStates.clear();
- }
-
- if (mCheckedIdStates != null) {
- mCheckedIdStates.clear();
- }
-
requestLayout();
}
@@ -1186,7 +1135,7 @@
* UNSPECIFIED/AT_MOST modes, false otherwise.
* @hide
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
protected boolean recycleOnMeasure() {
return true;
}
@@ -3360,270 +3309,6 @@
}
/**
- * @see #setChoiceMode(int)
- *
- * @return The current choice mode
- */
- public int getChoiceMode() {
- return mChoiceMode;
- }
-
- /**
- * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
- * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
- * List allows up to one item to be in a chosen state. By setting the choiceMode to
- * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
- *
- * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
- * {@link #CHOICE_MODE_MULTIPLE}
- */
- public void setChoiceMode(int choiceMode) {
- mChoiceMode = choiceMode;
- if (mChoiceActionMode != null) {
- mChoiceActionMode.finish();
- mChoiceActionMode = null;
- }
- if (mChoiceMode != CHOICE_MODE_NONE) {
- if (mCheckStates == null) {
- mCheckStates = new SparseBooleanArray();
- }
- if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
- mCheckedIdStates = new LongSparseArray<Boolean>();
- }
- // Modal multi-choice mode only has choices when the mode is active. Clear them.
- if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- clearChoices();
- setLongClickable(true);
- }
- }
- }
-
- /**
- * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
- * selection {@link ActionMode}. Only used when the choice mode is set to
- * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
- *
- * @param listener Listener that will manage the selection mode
- *
- * @see #setChoiceMode(int)
- */
- public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
- if (mMultiChoiceModeCallback == null) {
- mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
- }
- mMultiChoiceModeCallback.setWrapped(listener);
- }
-
- @Override
- boolean performLongPress(final View child,
- final int longPressPosition, final long longPressId) {
- boolean handled = false;
- if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- handled = true;
- if (mChoiceActionMode == null) {
- mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
- setItemChecked(longPressPosition, true);
- }
- // TODO Should we select the long pressed item if we were already in
- // selection mode? (i.e. treat it like an item click?)
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- }
- return handled | super.performLongPress(child, longPressPosition, longPressId);
- }
-
- @Override
- public boolean performItemClick(View view, int position, long id) {
- boolean handled = false;
-
- if (mChoiceMode != CHOICE_MODE_NONE) {
- handled = true;
-
- if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
- (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
- boolean newValue = !mCheckStates.get(position, false);
- mCheckStates.put(position, newValue);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- if (newValue) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- } else {
- mCheckedIdStates.delete(mAdapter.getItemId(position));
- }
- }
- if (newValue) {
- mCheckedItemCount++;
- } else {
- mCheckedItemCount--;
- }
- if (mChoiceActionMode != null) {
- mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
- position, id, newValue);
- }
- } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
- boolean newValue = !mCheckStates.get(position, false);
- if (newValue) {
- mCheckStates.clear();
- mCheckStates.put(position, true);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- mCheckedIdStates.clear();
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- }
- mCheckedItemCount = 1;
- } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
- mCheckedItemCount = 0;
- }
- }
-
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
- }
-
- handled |= super.performItemClick(view, position, id);
-
- return handled;
- }
-
- /**
- * Sets the checked state of the specified position. The is only valid if
- * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
- * {@link #CHOICE_MODE_MULTIPLE}.
- *
- * @param position The item whose checked state is to be checked
- * @param value The new checked state for the item
- */
- public void setItemChecked(int position, boolean value) {
- if (mChoiceMode == CHOICE_MODE_NONE) {
- return;
- }
-
- // Start selection mode if needed. We don't need to if we're unchecking something.
- if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
- mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
- }
-
- if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
- boolean oldValue = mCheckStates.get(position);
- mCheckStates.put(position, value);
- if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
- if (value) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- } else {
- mCheckedIdStates.delete(mAdapter.getItemId(position));
- }
- }
- if (oldValue != value) {
- if (value) {
- mCheckedItemCount++;
- } else {
- mCheckedItemCount--;
- }
- }
- if (mChoiceActionMode != null) {
- final long id = mAdapter.getItemId(position);
- mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
- position, id, value);
- }
- } else {
- boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
- // Clear all values if we're checking something, or unchecking the currently
- // selected item
- if (value || isItemChecked(position)) {
- mCheckStates.clear();
- if (updateIds) {
- mCheckedIdStates.clear();
- }
- }
- // this may end up selecting the value we just cleared but this way
- // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
- if (value) {
- mCheckStates.put(position, true);
- if (updateIds) {
- mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
- }
- mCheckedItemCount = 1;
- } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
- mCheckedItemCount = 0;
- }
- }
-
- // Do not generate a data change while we are in the layout phase
- if (!mInLayout && !mBlockLayoutRequests) {
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
- }
- }
-
- /**
- * Returns the number of items currently selected. This will only be valid
- * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
- *
- * <p>To determine the specific items that are currently selected, use one of
- * the <code>getChecked*</code> methods.
- *
- * @return The number of items currently selected
- *
- * @see #getCheckedItemPosition()
- * @see #getCheckedItemPositions()
- * @see #getCheckedItemIds()
- */
- public int getCheckedItemCount() {
- return mCheckedItemCount;
- }
-
- /**
- * Returns the checked state of the specified position. The result is only
- * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
- * or {@link #CHOICE_MODE_MULTIPLE}.
- *
- * @param position The item whose checked state to return
- * @return The item's checked state or <code>false</code> if choice mode
- * is invalid
- *
- * @see #setChoiceMode(int)
- */
- public boolean isItemChecked(int position) {
- if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
- return mCheckStates.get(position);
- }
-
- return false;
- }
-
- /**
- * Returns the currently checked item. The result is only valid if the choice
- * mode has been set to {@link #CHOICE_MODE_SINGLE}.
- *
- * @return The position of the currently checked item or
- * {@link #INVALID_POSITION} if nothing is selected
- *
- * @see #setChoiceMode(int)
- */
- public int getCheckedItemPosition() {
- if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
- return mCheckStates.keyAt(0);
- }
-
- return INVALID_POSITION;
- }
-
- /**
- * Returns the set of checked items in the list. The result is only valid if
- * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
- *
- * @return A SparseBooleanArray which will return true for each call to
- * get(int position) where position is a position in the list,
- * or <code>null</code> if the choice mode is set to
- * {@link #CHOICE_MODE_NONE}.
- */
- public SparseBooleanArray getCheckedItemPositions() {
- if (mChoiceMode != CHOICE_MODE_NONE) {
- return mCheckStates;
- }
- return null;
- }
-
- /**
* Returns the set of checked items ids. The result is only valid if the
* choice mode has not been set to {@link #CHOICE_MODE_NONE}.
*
@@ -3667,185 +3352,4 @@
}
return new long[0];
}
-
- /**
- * Returns the set of checked items ids. The result is only valid if the
- * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
- * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
- *
- * @return A new array which contains the id of each checked item in the
- * list.
- */
- public long[] getCheckedItemIds() {
- if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
- return new long[0];
- }
-
- final LongSparseArray<Boolean> idStates = mCheckedIdStates;
- final int count = idStates.size();
- final long[] ids = new long[count];
-
- for (int i = 0; i < count; i++) {
- ids[i] = idStates.keyAt(i);
- }
-
- return ids;
- }
-
- /**
- * Clear any choices previously set
- */
- public void clearChoices() {
- if (mCheckStates != null) {
- mCheckStates.clear();
- }
- if (mCheckedIdStates != null) {
- mCheckedIdStates.clear();
- }
- mCheckedItemCount = 0;
- }
-
- /**
- * A MultiChoiceModeListener receives events for {@link ListView#CHOICE_MODE_MULTIPLE_MODAL}.
- * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
- * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
- * selects and deselects list items.
- */
- public interface MultiChoiceModeListener extends ActionMode.Callback {
- /**
- * Called when an item is checked or unchecked during selection mode.
- *
- * @param mode The {@link ActionMode} providing the selection mode
- * @param position Adapter position of the item that was checked or unchecked
- * @param id Adapter ID of the item that was checked or unchecked
- * @param checked <code>true</code> if the item is now checked, <code>false</code>
- * if the item is now unchecked.
- */
- public void onItemCheckedStateChanged(ActionMode mode,
- int position, long id, boolean checked);
- }
-
- private class MultiChoiceModeWrapper implements MultiChoiceModeListener {
- private MultiChoiceModeListener mWrapped;
-
- public void setWrapped(MultiChoiceModeListener wrapped) {
- mWrapped = wrapped;
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- if (mWrapped.onCreateActionMode(mode, menu)) {
- // Initialize checked graphic state?
- setLongClickable(false);
- return true;
- }
- return false;
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return mWrapped.onPrepareActionMode(mode, menu);
- }
-
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return mWrapped.onActionItemClicked(mode, item);
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- mWrapped.onDestroyActionMode(mode);
- mChoiceActionMode = null;
-
- // Ending selection mode means deselecting everything.
- clearChoices();
-
- mDataChanged = true;
- rememberSyncState();
- requestLayout();
-
- setLongClickable(true);
- }
-
- public void onItemCheckedStateChanged(ActionMode mode,
- int position, long id, boolean checked) {
- mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
-
- // If there are no items selected we no longer need the selection mode.
- if (getCheckedItemCount() == 0) {
- mode.finish();
- }
- }
- }
-
- static class SavedState extends BaseSavedState {
- SparseBooleanArray checkState;
- LongSparseArray<Boolean> checkIdState;
-
- /**
- * Constructor called from {@link ListView#onSaveInstanceState()}
- */
- SavedState(Parcelable superState, SparseBooleanArray checkState,
- LongSparseArray<Boolean> checkIdState) {
- super(superState);
- this.checkState = checkState;
- this.checkIdState = checkIdState;
- }
-
- /**
- * Constructor called from {@link #CREATOR}
- */
- private SavedState(Parcel in) {
- super(in);
- checkState = in.readSparseBooleanArray();
- long[] idState = in.createLongArray();
-
- if (idState.length > 0) {
- checkIdState = new LongSparseArray<Boolean>();
- checkIdState.setValues(idState, Boolean.TRUE);
- }
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeSparseBooleanArray(checkState);
- out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
- }
-
- @Override
- public String toString() {
- return "ListView.SavedState{"
- + Integer.toHexString(System.identityHashCode(this))
- + " checkState=" + checkState + "}";
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- return new SavedState(superState, mCheckStates, mCheckedIdStates);
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
-
- super.onRestoreInstanceState(ss.getSuperState());
-
- if (ss.checkState != null) {
- mCheckStates = ss.checkState;
- }
-
- if (ss.checkIdState != null) {
- mCheckedIdStates = ss.checkIdState;
- }
- }
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d404ce7..4a3e2a9 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,9 +16,10 @@
package android.widget;
-import java.lang.ref.WeakReference;
+import com.android.internal.R;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -31,13 +32,13 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
+import android.view.WindowManager;
-import com.android.internal.R;
+import java.lang.ref.WeakReference;
/**
* <p>A popup window that can be used to display an arbitrary view. The popup
@@ -122,7 +123,7 @@
private OnScrollChangedListener mOnScrollChangedListener =
new OnScrollChangedListener() {
public void onScrollChanged() {
- View anchor = mAnchor.get();
+ View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && mPopupView != null) {
WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mPopupView.getLayoutParams();
@@ -1032,7 +1033,9 @@
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
- bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
+ Resources res = anchor.getContext().getResources();
+ bottomEdge = res.getDisplayMetrics().heightPixels -
+ (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom);
}
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 71f0c2f..0f52fc8 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -336,7 +336,7 @@
*
* @return true if the progress bar is in indeterminate mode
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized boolean isIndeterminate() {
return mIndeterminate;
}
@@ -609,7 +609,7 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getProgress() {
return mIndeterminate ? 0 : mProgress;
}
@@ -626,7 +626,7 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getSecondaryProgress() {
return mIndeterminate ? 0 : mSecondaryProgress;
}
@@ -640,7 +640,7 @@
* @see #getProgress()
* @see #getSecondaryProgress()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getMax() {
return mMax;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 1aa1df3..64cda49 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1011,7 +1011,7 @@
* @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- @ViewDebug.ExportedProperty(resolveId = true, indexMapping = {
+ @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
@ViewDebug.IntToString(from = ABOVE, to = "above"),
@ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
@ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
@@ -1040,7 +1040,7 @@
* When true, uses the parent as the anchor if the anchor doesn't exist or if
* the anchor's visibility is GONE.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public boolean alignWithParent;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 50745dc0..f23a723 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -266,6 +266,63 @@
public final static int TAG = 3;
}
+ private class ReflectionActionWithoutParams extends Action {
+ int viewId;
+ String methodName;
+
+ public final static int TAG = 5;
+
+ ReflectionActionWithoutParams(int viewId, String methodName) {
+ this.viewId = viewId;
+ this.methodName = methodName;
+ }
+
+ ReflectionActionWithoutParams(Parcel in) {
+ this.viewId = in.readInt();
+ this.methodName = in.readString();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(TAG);
+ out.writeInt(this.viewId);
+ out.writeString(this.methodName);
+ }
+
+ @Override
+ public void apply(View root) {
+ final View view = root.findViewById(viewId);
+ if (view == null) {
+ throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId));
+ }
+
+ Class klass = view.getClass();
+ Method method;
+ try {
+ method = klass.getMethod(this.methodName);
+ } catch (NoSuchMethodException ex) {
+ throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ + this.methodName + "()");
+ }
+
+ if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+ throw new ActionException("view: " + klass.getName()
+ + " can't use method with RemoteViews: "
+ + this.methodName + "()");
+ }
+
+ try {
+ //noinspection ConstantIfStatement
+ if (false) {
+ Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
+ + this.methodName + "()");
+ }
+ method.invoke(view);
+ } catch (Exception ex) {
+ throw new ActionException(ex);
+ }
+ }
+ }
+
/**
* Base class for the reflection actions.
*/
@@ -571,6 +628,9 @@
case ViewGroupAction.TAG:
mActions.add(new ViewGroupAction(parcel));
break;
+ case ReflectionActionWithoutParams.TAG:
+ mActions.add(new ReflectionActionWithoutParams(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -632,6 +692,24 @@
}
/**
+ * Equivalent to calling {@link AdapterViewFlipper#showNext()}
+ *
+ * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()}
+ */
+ public void showNext(int viewId) {
+ addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
+ }
+
+ /**
+ * Equivalent to calling {@link AdapterViewFlipper#showPrevious()}
+ *
+ * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()}
+ */
+ public void showPrevious(int viewId) {
+ addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
+ }
+
+ /**
* Equivalent to calling View.setVisibility
*
* @param viewId The id of the view whose visibility should change
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 52635e8..cd1e422 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -202,6 +202,7 @@
int count;
int viewTypeCount;
boolean hasStableIds;
+ boolean isDataDirty;
Map<Integer, Integer> mTypeIdIndexMap;
RemoteViewsInfo() {
@@ -209,6 +210,7 @@
// by default there is at least one dummy view type
viewTypeCount = 1;
hasStableIds = true;
+ isDataDirty = false;
mTypeIdIndexMap = new HashMap<Integer, Integer>();
}
}
@@ -282,6 +284,39 @@
}
}
+ protected void onNotifyDataSetChanged() {
+ // we mark the data as dirty so that the next call to fetch views will result in
+ // an onDataSetDirty() call from the adapter
+ synchronized (mViewCacheInfo) {
+ mViewCacheInfo.isDataDirty = true;
+ }
+ }
+
+ private void updateNotifyDataSetChanged() {
+ // actually calls through to the factory to notify it to update
+ if (mServiceConnection.isConnected()) {
+ IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+ try {
+ factory.onDataSetChanged();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ // re-request the new metadata (only after the notification to the factory)
+ requestMetaData();
+
+ // post a new runnable on the main thread to propagate the notification back
+ // to the base adapter
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ completeNotifyDataSetChanged();
+ }
+ });
+ }
+
protected void updateRemoteViewsInfo(int position) {
if (mServiceConnection.isConnected()) {
IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
@@ -433,6 +468,7 @@
int cacheIndex = getCacheIndex(position);
FrameLayout flipper = mViewCache[cacheIndex].flipper;
flipper.setVisibility(View.VISIBLE);
+ flipper.setAlpha(1.0f);
if (indexInfo == null) {
// hide the item view and show the loading view
@@ -498,6 +534,16 @@
@Override
public void run() {
while (mBackgroundLoaderEnabled) {
+ // notify the RemoteViews factory if necessary
+ boolean isDataDirty = false;
+ synchronized (mViewCacheInfo) {
+ isDataDirty = mViewCacheInfo.isDataDirty;
+ mViewCacheInfo.isDataDirty = false;
+ }
+ if (isDataDirty) {
+ updateNotifyDataSetChanged();
+ }
+
int index = -1;
synchronized (mViewCacheLoadIndices) {
if (!mViewCacheLoadIndices.isEmpty()) {
@@ -667,6 +713,12 @@
public void notifyDataSetChanged() {
// flush the cache so that we can reload new items from the service
mViewCache.flushCache();
+
+ // notify the factory that it's data may no longer be valid
+ mViewCache.onNotifyDataSetChanged();
+ }
+
+ public void completeNotifyDataSetChanged() {
super.notifyDataSetChanged();
}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 7c9d7ff..584fa25 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -43,9 +43,27 @@
* An interface for an adapter between a remote collection view (ListView, GridView, etc) and
* the underlying data for that view. The implementor is responsible for making a RemoteView
* for each item in the data set.
+ *
+ * @see android.widget.Adapter
+ * @see android.appwidget.AppWidgetManager
*/
public interface RemoteViewsFactory {
+ /**
+ * Called when your factory is first constructed. The same factory may be shared across
+ * multiple RemoteViewAdapters depending on the intent passed.
+ */
public void onCreate();
+ /**
+ * Called when notifyDataSetChanged() is triggered on the remote adapter. This allows a
+ * RemoteViewsFactory to respond to data changes by updating any internal references.
+ *
+ * @see android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int[], int)
+ */
+ public void onDataSetChanged();
+ /**
+ * Called when the last RemoteViewsAdapter that is associated with this factory is
+ * unbound.
+ */
public void onDestroy();
public int getCount();
@@ -64,7 +82,9 @@
public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
mFactory = factory;
}
-
+ public void onDataSetChanged() {
+ mFactory.onDataSetChanged();
+ }
public int getCount() {
return mFactory.getCount();
}
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index c9c217a..aee411e 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -36,9 +36,8 @@
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -51,9 +50,8 @@
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -69,6 +67,22 @@
}
/**
+ * Constructor.
+ *
+ * @param context The context where the ListView associated with this adapter is running
+ * @param layout resource identifier of a layout file that defines the views
+ * for this list item. Unless you override them later, this will
+ * define both the item views and the drop down views.
+ * @param c The cursor from which to get the data.
+ * @param flags flags used to determine the behavior of the adapter
+ */
+ public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
+ super(context, c, flags);
+ mLayout = mDropDownLayout = layout;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
* Inflates view(s) from the specified XML file.
*
* @see android.widget.CursorAdapter#newView(android.content.Context,
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 3b113ae..93a1179 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -111,8 +111,7 @@
}
Rect r = getBounds();
- if (canvas.quickReject(r.left, r.top, r.right, r.bottom,
- Canvas.EdgeType.AA)) {
+ if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
return;
}
if (drawTrack) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 959e982..eb527eb 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -49,6 +49,8 @@
* within a larger container.
*
* <p>ScrollView only supports vertical scrolling.
+ *
+ * @attr ref android.R.styleable#ScrollView_fillViewport
*/
public class ScrollView extends FrameLayout {
static final int ANIMATED_SCROLL_GAP = 250;
@@ -247,6 +249,8 @@
* Indicates whether this ScrollView's content is stretched to fill the viewport.
*
* @return True if the content fills the viewport, false otherwise.
+ *
+ * @attr ref android.R.styleable#ScrollView_fillViewport
*/
public boolean isFillViewport() {
return mFillViewport;
@@ -258,6 +262,8 @@
*
* @param fillViewport True to stretch the content's height to the viewport's
* boundaries, false otherwise.
+ *
+ * @attr ref android.R.styleable#ScrollView_fillViewport
*/
public void setFillViewport(boolean fillViewport) {
if (fillViewport != mFillViewport) {
@@ -566,16 +572,6 @@
}
}
- private int getScrollRange() {
- int scrollRange = 0;
- if (getChildCount() > 0) {
- View child = getChildAt(0);
- scrollRange = Math.max(0,
- child.getHeight() - getHeight() - mPaddingBottom - mPaddingTop);
- }
- return scrollRange;
- }
-
/**
* <p>
* Finds the next focusable component that fits in this View's bounds
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
new file mode 100644
index 0000000..5797cbb
--- /dev/null
+++ b/core/java/android/widget/StackView.java
@@ -0,0 +1,631 @@
+/*
+ * 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.widget;
+
+import java.util.WeakHashMap;
+
+import android.animation.PropertyAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+/**
+ * A view that displays its children in a stack and allows users to discretely swipe
+ * through the children.
+ */
+public class StackView extends AdapterViewAnimator {
+ private final String TAG = "StackView";
+
+ /**
+ * Default animation parameters
+ */
+ private final int DEFAULT_ANIMATION_DURATION = 500;
+ private final int MINIMUM_ANIMATION_DURATION = 50;
+
+ /**
+ * These specify the different gesture states
+ */
+ private static final int GESTURE_NONE = 0;
+ private static final int GESTURE_SLIDE_UP = 1;
+ private static final int GESTURE_SLIDE_DOWN = 2;
+
+ /**
+ * Specifies how far you need to swipe (up or down) before it
+ * will be consider a completed gesture when you lift your finger
+ */
+ private static final float SWIPE_THRESHOLD_RATIO = 0.35f;
+ private static final float SLIDE_UP_RATIO = 0.7f;
+
+ private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
+ private final WeakHashMap<View, Integer>
+ mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
+
+ /**
+ * Sentinel value for no current active pointer.
+ * Used by {@link #mActivePointerId}.
+ */
+ private static final int INVALID_POINTER = -1;
+
+ /**
+ * These variables are all related to the current state of touch interaction
+ * with the stack
+ */
+ private float mInitialY;
+ private float mInitialX;
+ private int mActivePointerId;
+ private int mYVelocity = 0;
+ private int mSwipeGestureType = GESTURE_NONE;
+ private int mViewHeight;
+ private int mSwipeThreshold;
+ private int mTouchSlop;
+ private int mMaximumVelocity;
+ private VelocityTracker mVelocityTracker;
+
+ private ImageView mHighlight;
+ private StackSlider mStackSlider;
+ private boolean mFirstLayoutHappened = false;
+
+ public StackView(Context context) {
+ super(context);
+ initStackView();
+ }
+
+ public StackView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initStackView();
+ }
+
+ private void initStackView() {
+ configureViewAnimator(4, 2, false);
+ setStaticTransformationsEnabled(true);
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mActivePointerId = INVALID_POINTER;
+
+ mHighlight = new ImageView(getContext());
+ mHighlight.setLayoutParams(new LayoutParams(mHighlight));
+ addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight));
+ mStackSlider = new StackSlider();
+
+ if (!sPaintsInitialized) {
+ initializePaints();
+ }
+ }
+
+ /**
+ * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ if (fromIndex == -1 && toIndex == 0) {
+ // Fade item in
+ if (view.getAlpha() == 1) {
+ view.setAlpha(0);
+ }
+ view.setVisibility(VISIBLE);
+
+ PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 1.0f);
+ fadeIn.start();
+ } else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
+ // Slide item in
+ view.setVisibility(VISIBLE);
+
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = Math.round(
+ (lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator slideInY = new PropertyAnimator(duration, animationSlider,
+ "YProgress", mStackSlider.getYProgress(), 0);
+ slideInY.setInterpolator(new LinearInterpolator());
+ slideInY.start();
+ PropertyAnimator slideInX = new PropertyAnimator(duration, animationSlider,
+ "XProgress", mStackSlider.getXProgress(), 0);
+ slideInX.setInterpolator(new LinearInterpolator());
+ slideInX.start();
+ } else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
+ // Slide item out
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator slideOutY = new PropertyAnimator(duration, animationSlider,
+ "YProgress", mStackSlider.getYProgress(), 1);
+ slideOutY.setInterpolator(new LinearInterpolator());
+ slideOutY.start();
+ PropertyAnimator slideOutX = new PropertyAnimator(duration, animationSlider,
+ "XProgress", mStackSlider.getXProgress(), 0);
+ slideOutX.setInterpolator(new LinearInterpolator());
+ slideOutX.start();
+ } else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
+ // Make sure this view that is "waiting in the wings" is invisible
+ view.setAlpha(0.0f);
+ view.setVisibility(INVISIBLE);
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.setVerticalOffset(-mViewHeight);
+ } else if (toIndex == -1) {
+ // Fade item out
+ PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 0);
+ fadeOut.start();
+ }
+ }
+
+ /**
+ * Apply any necessary tranforms for the child that is being added.
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ if (!mRotations.containsKey(child)) {
+ float rotation = (float) (Math.random()*26 - 13);
+ mRotations.put(child, rotation);
+ }
+
+ // Child has been removed
+ if (relativeIndex == -1) {
+ if (mRotations.containsKey(child)) {
+ mRotations.remove(child);
+ }
+ if (mChildrenToApplyTransformsTo.containsKey(child)) {
+ mChildrenToApplyTransformsTo.remove(child);
+ }
+ }
+
+ // if this view is already in the layout, we need to
+ // wait until layout has finished in order to set the
+ // pivot point of the rotation (requiring getMeasuredWidth/Height())
+ mChildrenToApplyTransformsTo.put(child, relativeIndex);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (!mChildrenToApplyTransformsTo.isEmpty()) {
+ for (View child: mChildrenToApplyTransformsTo.keySet()) {
+ if (mRotations.containsKey(child)) {
+ child.setPivotX(child.getMeasuredWidth()/2);
+ child.setPivotY(child.getMeasuredHeight()/2);
+ child.setRotation(mRotations.get(child));
+ }
+ }
+ mChildrenToApplyTransformsTo.clear();
+ }
+
+ if (!mFirstLayoutHappened) {
+ mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
+ mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+
+ // TODO: Right now this walks all the way up the view hierarchy and disables
+ // ClipChildren and ClipToPadding. We're probably going to want to reset
+ // these flags as well.
+ setClipChildren(false);
+ ViewGroup view = this;
+ while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
+ view = (ViewGroup) view.getParent();
+ view.setClipChildren(false);
+ view.setClipToPadding(false);
+ }
+ mFirstLayoutHappened = true;
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ switch(action & MotionEvent.ACTION_MASK) {
+
+ case MotionEvent.ACTION_DOWN: {
+ if (mActivePointerId == INVALID_POINTER) {
+ mInitialX = ev.getX();
+ mInitialY = ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+ float newY = ev.getY(pointerIndex);
+ float deltaY = newY - mInitialY;
+
+ beginGestureIfNeeded(deltaY);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mSwipeGestureType = GESTURE_NONE;
+ }
+ }
+
+ return mSwipeGestureType != GESTURE_NONE;
+ }
+
+ private void beginGestureIfNeeded(float deltaY) {
+ if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+ mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+
+ int activeIndex = mSwipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
+ : mNumActiveViews - 2;
+
+ View v = getViewAtRelativeIndex(activeIndex);
+ if (v != null) {
+ mHighlight.setImageBitmap(createOutline(v));
+ mHighlight.bringToFront();
+ v.bringToFront();
+ mStackSlider.setView(v);
+ if (mSwipeGestureType == GESTURE_SLIDE_DOWN)
+ v.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+
+ float newY = ev.getY(pointerIndex);
+ float newX = ev.getX(pointerIndex);
+ float deltaY = newY - mInitialY;
+ float deltaX = newX - mInitialX;
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ beginGestureIfNeeded(deltaY);
+
+ float rx = 0.3f*deltaX/(mViewHeight*1.0f);
+ if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+ float r = (deltaY-mTouchSlop*1.0f)/mViewHeight*1.0f;
+ mStackSlider.setYProgress(1 - r);
+ mStackSlider.setXProgress(rx);
+ return true;
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+ float r = -(deltaY + mTouchSlop*1.0f)/mViewHeight*1.0f;
+ mStackSlider.setYProgress(r);
+ mStackSlider.setXProgress(rx);
+ return true;
+ }
+
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ handlePointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mSwipeGestureType = GESTURE_NONE;
+ break;
+ }
+ }
+ return true;
+ }
+
+ private final Rect touchRect = new Rect();
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int activePointerIndex = ev.getActionIndex();
+ final int pointerId = ev.getPointerId(activePointerIndex);
+ if (pointerId == mActivePointerId) {
+
+ int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? mNumActiveViews - 1
+ : mNumActiveViews - 2;
+
+ View v = getViewAtRelativeIndex(activeViewIndex);
+ if (v == null) return;
+
+ // Our primary pointer has gone up -- let's see if we can find
+ // another pointer on the view. If so, then we should replace
+ // our primary pointer with this new pointer and adjust things
+ // so that the view doesn't jump
+ for (int index = 0; index < ev.getPointerCount(); index++) {
+ if (index != activePointerIndex) {
+
+ float x = ev.getX(index);
+ float y = ev.getY(index);
+
+ touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+ if (touchRect.contains(Math.round(x), Math.round(y))) {
+ float oldX = ev.getX(activePointerIndex);
+ float oldY = ev.getY(activePointerIndex);
+
+ // adjust our frame of reference to avoid a jump
+ mInitialY += (y - oldY);
+ mInitialX += (x - oldX);
+
+ mActivePointerId = ev.getPointerId(index);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ // ok, we're good, we found a new pointer which is touching the active view
+ return;
+ }
+ }
+ }
+ // if we made it this far, it means we didn't find a satisfactory new pointer :(,
+ // so end the
+ handlePointerUp(ev);
+ }
+ }
+
+ private void handlePointerUp(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ float newY = ev.getY(pointerIndex);
+ int deltaY = (int) (newY - mInitialY);
+
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+ // Swipe threshold exceeded, swipe down
+ showNext();
+ mHighlight.bringToFront();
+ } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP) {
+ // Swipe threshold exceeded, swipe up
+ showPrevious();
+ mHighlight.bringToFront();
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+ // Didn't swipe up far enough, snap back down
+ int duration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
+
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
+ "YProgress", mStackSlider.getYProgress(), 0);
+ snapBackY.setInterpolator(new LinearInterpolator());
+ snapBackY.start();
+ PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
+ "XProgress", mStackSlider.getXProgress(), 0);
+ snapBackX.setInterpolator(new LinearInterpolator());
+ snapBackX.start();
+ } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+ // Didn't swipe down far enough, snap back up
+ int duration = Math.round((1 -
+ mStackSlider.getYProgress())*DEFAULT_ANIMATION_DURATION);
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
+ "YProgress", mStackSlider.getYProgress(), 1);
+ snapBackY.setInterpolator(new LinearInterpolator());
+ snapBackY.start();
+ PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
+ "XProgress", mStackSlider.getXProgress(), 0);
+ snapBackX.setInterpolator(new LinearInterpolator());
+ snapBackX.start();
+ }
+
+ mActivePointerId = INVALID_POINTER;
+ mSwipeGestureType = GESTURE_NONE;
+ }
+
+ private class StackSlider {
+ View mView;
+ float mYProgress;
+ float mXProgress;
+
+ public StackSlider() {
+ }
+
+ public StackSlider(StackSlider copy) {
+ mView = copy.mView;
+ mYProgress = copy.mYProgress;
+ mXProgress = copy.mXProgress;
+ }
+
+ private float cubic(float r) {
+ return (float) (Math.pow(2*r-1, 3) + 1)/2.0f;
+ }
+
+ private float highlightAlphaInterpolator(float r) {
+ float pivot = 0.4f;
+ if (r < pivot) {
+ return 0.85f*cubic(r/pivot);
+ } else {
+ return 0.85f*cubic(1 - (r-pivot)/(1-pivot));
+ }
+ }
+
+ private float viewAlphaInterpolator(float r) {
+ float pivot = 0.3f;
+ if (r > pivot) {
+ return (r - pivot)/(1 - pivot);
+ } else {
+ return 0;
+ }
+ }
+
+ private float rotationInterpolator(float r) {
+ float pivot = 0.2f;
+ if (r < pivot) {
+ return 0;
+ } else {
+ return (r-pivot)/(1-pivot);
+ }
+ }
+
+ void setView(View v) {
+ mView = v;
+ }
+
+ public void setYProgress(float r) {
+ // enforce r between 0 and 1
+ r = Math.min(1.0f, r);
+ r = Math.max(0, r);
+
+ mYProgress = r;
+
+ final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
+ final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
+
+ viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
+ highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
+ mHighlight.setAlpha(highlightAlphaInterpolator(r));
+
+ float alpha = viewAlphaInterpolator(1-r);
+
+ // We make sure that views which can't be seen (have 0 alpha) are also invisible
+ // so that they don't interfere with click events.
+ if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
+ mView.setVisibility(VISIBLE);
+ } else if (alpha == 0 && mView.getAlpha() != 0 && mView.getVisibility() == VISIBLE) {
+ mView.setVisibility(INVISIBLE);
+ }
+
+ mView.setAlpha(viewAlphaInterpolator(1-r));
+ mView.setRotationX(90.0f*rotationInterpolator(r));
+ mHighlight.setRotationX(90.0f*rotationInterpolator(r));
+ }
+
+ public void setXProgress(float r) {
+ // enforce r between 0 and 1
+ r = Math.min(1.0f, r);
+ r = Math.max(-1.0f, r);
+
+ mXProgress = r;
+
+ final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
+ final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
+
+ viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
+ highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
+ }
+
+ float getYProgress() {
+ return mYProgress;
+ }
+
+ float getXProgress() {
+ return mXProgress;
+ }
+ }
+
+ @Override
+ public void onRemoteAdapterConnected() {
+ super.onRemoteAdapterConnected();
+ setDisplayedChild(mWhichChild);
+ }
+
+ private static final Paint sHolographicPaint = new Paint();
+ private static final Paint sErasePaint = new Paint();
+ private static boolean sPaintsInitialized = false;
+ private static final float STROKE_WIDTH = 3.0f;
+
+ static void initializePaints() {
+ sHolographicPaint.setColor(0xff6699ff);
+ sHolographicPaint.setFilterBitmap(true);
+ sErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ sErasePaint.setFilterBitmap(true);
+ sPaintsInitialized = true;
+ }
+
+ static Bitmap createOutline(View v) {
+ if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
+ return null;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ float rotationX = v.getRotationX();
+ v.setRotationX(0);
+ canvas.concat(v.getMatrix());
+ v.draw(canvas);
+ v.setRotationX(rotationX);
+
+ Bitmap outlineBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas outlineCanvas = new Canvas(outlineBitmap);
+ drawOutline(outlineCanvas, bitmap);
+ bitmap.recycle();
+ return outlineBitmap;
+ }
+
+ static void drawOutline(Canvas dest, Bitmap src) {
+ dest.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ Bitmap mask = src.extractAlpha();
+ Matrix id = new Matrix();
+
+ Matrix m = new Matrix();
+ float xScale = STROKE_WIDTH*2/(src.getWidth());
+ float yScale = STROKE_WIDTH*2/(src.getHeight());
+ m.preScale(1+xScale, 1+yScale, src.getWidth()/2, src.getHeight()/2);
+ dest.drawBitmap(mask, m, sHolographicPaint);
+
+ dest.drawBitmap(src, id, sErasePaint);
+ mask.recycle();
+ }
+}
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 48d12df..b612004 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -387,13 +387,13 @@
/**
* <p>The column index of the cell represented by the widget.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int column;
/**
* <p>The number of columns the widgets spans over.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int span;
private static final int LOCATION = 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f3f68f9..825de25 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,9 +21,9 @@
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ClipboardManager;
import android.content.ClippedData;
import android.content.Context;
-import android.content.ClipboardManager;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -4410,6 +4410,8 @@
return super.onKeyUp(keyCode, event);
}
+ hideControllers();
+
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
/*
@@ -5818,7 +5820,7 @@
/**
* Convenience for {@link Selection#getSelectionStart}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionStart() {
return Selection.getSelectionStart(getText());
}
@@ -5826,7 +5828,7 @@
/**
* Convenience for {@link Selection#getSelectionEnd}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionEnd() {
return Selection.getSelectionEnd(getText());
}
@@ -6263,6 +6265,7 @@
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
+ hideControllers();
}
/**
@@ -6747,11 +6750,8 @@
}
private void prepareCursorControllers() {
- boolean atLeastOneController = false;
-
// TODO Add an extra android:cursorController flag to disable the controller?
if (mCursorVisible && mLayout != null) {
- atLeastOneController = true;
if (mInsertionPointCursorController == null) {
mInsertionPointCursorController = new InsertionPointCursorController();
}
@@ -6760,7 +6760,6 @@
}
if (canSelectText() && mLayout != null) {
- atLeastOneController = true;
if (mSelectionModifierCursorController == null) {
mSelectionModifierCursorController = new SelectionModifierCursorController();
}
@@ -6769,12 +6768,6 @@
// Stop selection mode if the controller becomes unavailable.
finishSelectionActionMode();
}
-
- if (atLeastOneController) {
- Resources res = mContext.getResources();
- mCursorControllerVerticalOffset = res.getDimensionPixelOffset(
- com.android.internal.R.dimen.cursor_controller_vertical_offset);
- }
}
/**
@@ -7620,6 +7613,75 @@
public void draw(Canvas canvas);
}
+ private class Handle {
+ Drawable mDrawable;
+ // Vertical extension of the touch region
+ int mTopExtension, mBottomExtension;
+ // Position of the virtual finger position on screen
+ int mHopSpotVertcalPosition;
+
+ Handle(Drawable drawable) {
+ mDrawable = drawable;
+ }
+
+ void positionAtCursor(final int offset, boolean bottom) {
+ final int drawableWidth = mDrawable.getIntrinsicWidth();
+ final int drawableHeight = mDrawable.getIntrinsicHeight();
+ final int line = mLayout.getLineForOffset(offset);
+ final int lineTop = mLayout.getLineTop(line);
+ final int lineBottom = mLayout.getLineBottom(line);
+
+ mHopSpotVertcalPosition = lineTop + (bottom ? (3 * (lineBottom - lineTop)) / 4 :
+ (lineBottom - lineTop) / 4);
+
+ final Rect bounds = sCursorControllerTempRect;
+ bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
+ bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2;
+
+ mTopExtension = bottom ? 0 : drawableHeight / 2;
+ mBottomExtension = drawableHeight;
+
+ // Extend touch region up when editing the last line of text (or a single line) so that
+ // it is easier to grab.
+ if (line == mLayout.getLineCount() - 1) {
+ mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
+ }
+
+ bounds.right = bounds.left + drawableWidth;
+ bounds.bottom = bounds.top + drawableHeight;
+
+ int boundTopBefore = bounds.top;
+ convertFromViewportToContentCoordinates(bounds);
+ mHopSpotVertcalPosition += bounds.top - boundTopBefore;
+ mDrawable.setBounds(bounds);
+ postInvalidate();
+ }
+
+ boolean hasFingerOn(float x, float y) {
+ // Simulate a 'fat finger' to ease grabbing of the controller.
+ // Expands according to controller image size instead of using dip distance.
+ // Assumes controller imager has a sensible size, proportionnal to screen density.
+ final int drawableWidth = mDrawable.getIntrinsicWidth();
+ final Rect fingerRect = sCursorControllerTempRect;
+ fingerRect.set((int) (x - drawableWidth / 2.0),
+ (int) (y - mBottomExtension),
+ (int) (x + drawableWidth / 2.0),
+ (int) (y + mTopExtension));
+ return Rect.intersects(mDrawable.getBounds(), fingerRect);
+ }
+
+ void postInvalidate() {
+ final Rect bounds = mDrawable.getBounds();
+ TextView.this.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+ void postInvalidateDelayed(long delay) {
+ final Rect bounds = mDrawable.getBounds();
+ TextView.this.postInvalidateDelayed(delay, bounds.left, bounds.top,
+ bounds.right, bounds.bottom);
+ }
+ }
+
class InsertionPointCursorController implements CursorController {
private static final int DELAY_BEFORE_FADE_OUT = 2100;
@@ -7628,7 +7690,7 @@
// Starting time of the fade timer
private long mFadeOutTimerStart;
// The cursor controller image
- private final Drawable mDrawable;
+ private final Handle mHandle;
// Used to detect a tap (vs drag) on the controller
private long mOnDownTimerStart;
// Offset between finger hot point on cursor controller and actual cursor
@@ -7636,7 +7698,7 @@
InsertionPointCursorController() {
Resources res = mContext.getResources();
- mDrawable = res.getDrawable(com.android.internal.R.drawable.cursor_controller);
+ mHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
}
public void show() {
@@ -7652,7 +7714,7 @@
// Start fading out, only if not already in progress
if (time - mFadeOutTimerStart < DELAY_BEFORE_FADE_OUT) {
mFadeOutTimerStart = time - DELAY_BEFORE_FADE_OUT;
- postInvalidate(mDrawable);
+ mHandle.postInvalidate();
}
}
}
@@ -7661,19 +7723,20 @@
if (mIsVisible) {
int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
if (time <= DELAY_BEFORE_FADE_OUT) {
- postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time, mDrawable);
+ mHandle.postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time);
} else {
time -= DELAY_BEFORE_FADE_OUT;
if (time <= FADE_OUT_DURATION) {
- final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
- mDrawable.setAlpha(alpha);
- postInvalidateDelayed(30, mDrawable);
+ final int alpha = (int)
+ ((255.0 * (FADE_OUT_DURATION - time)) / FADE_OUT_DURATION);
+ mHandle.mDrawable.setAlpha(alpha);
+ mHandle.postInvalidateDelayed(30);
} else {
- mDrawable.setAlpha(0);
+ mHandle.mDrawable.setAlpha(0);
mIsVisible = false;
}
}
- mDrawable.draw(canvas);
+ mHandle.mDrawable.draw(canvas);
}
}
@@ -7688,7 +7751,7 @@
private void updateDrawablePosition() {
if (mIsVisible) {
// Clear previous cursor controller before bounds are updated
- postInvalidate(mDrawable);
+ mHandle.postInvalidate();
}
final int offset = getSelectionStart();
@@ -7700,10 +7763,10 @@
return;
}
- positionDrawableUnderCursor(offset, mDrawable);
+ mHandle.positionAtCursor(offset, true);
mFadeOutTimerStart = System.currentTimeMillis();
- mDrawable.setAlpha(255);
+ mHandle.mDrawable.setAlpha(255);
}
public void onTouchEvent(MotionEvent event) {
@@ -7713,7 +7776,7 @@
final float x = event.getX();
final float y = event.getY();
- if (fingerIsOnDrawable(x, y, mDrawable)) {
+ if (mHandle.hasFingerOn(x, y)) {
show();
if (mMovement instanceof ArrowKeyMovementMethod) {
@@ -7726,9 +7789,9 @@
mParent.requestDisallowInterceptTouchEvent(true);
}
- final Rect bounds = mDrawable.getBounds();
+ final Rect bounds = mHandle.mDrawable.getBounds();
mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ mOffsetY = mHandle.mHopSpotVertcalPosition - y;
mOnDownTimerStart = event.getEventTime();
}
@@ -7769,7 +7832,7 @@
// Starting time of the fade timer
private long mFadeOutTimerStart;
// The cursor controller images
- private final Drawable mStartDrawable, mEndDrawable;
+ private final Handle mStartHandle, mEndHandle;
// Offset between finger hot point on active cursor controller and actual cursor
private float mOffsetX, mOffsetY;
// The offsets of that last touch down event. Remembered to start selection there.
@@ -7777,8 +7840,8 @@
SelectionModifierCursorController() {
Resources res = mContext.getResources();
- mStartDrawable = res.getDrawable(com.android.internal.R.drawable.selection_start_handle);
- mEndDrawable = res.getDrawable(com.android.internal.R.drawable.selection_end_handle);
+ mStartHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+ mEndHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
}
public void show() {
@@ -7793,15 +7856,15 @@
public void hide() {
if (mIsVisible && (mFadeOutTimerStart < 0)) {
mFadeOutTimerStart = System.currentTimeMillis();
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
}
public void cancelFadeOutAnimation() {
mIsVisible = false;
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
public void draw(Canvas canvas) {
@@ -7810,18 +7873,18 @@
int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
if (time <= FADE_OUT_DURATION) {
final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
- mStartDrawable.setAlpha(alpha);
- mEndDrawable.setAlpha(alpha);
- postInvalidateDelayed(30, mStartDrawable);
- postInvalidateDelayed(30, mEndDrawable);
+ mStartHandle.mDrawable.setAlpha(alpha);
+ mEndHandle.mDrawable.setAlpha(alpha);
+ mStartHandle.postInvalidateDelayed(30);
+ mEndHandle.postInvalidateDelayed(30);
} else {
- mStartDrawable.setAlpha(0);
- mEndDrawable.setAlpha(0);
+ mStartHandle.mDrawable.setAlpha(0);
+ mEndHandle.mDrawable.setAlpha(0);
mIsVisible = false;
}
}
- mStartDrawable.draw(canvas);
- mEndDrawable.draw(canvas);
+ mStartHandle.mDrawable.draw(canvas);
+ mEndHandle.mDrawable.draw(canvas);
}
}
@@ -7861,8 +7924,8 @@
private void updateDrawablesPositions() {
if (mIsVisible) {
// Clear previous cursor controller before bounds are updated
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
final int selectionStart = getSelectionStart();
@@ -7875,11 +7938,12 @@
return;
}
- positionDrawableUnderCursor(selectionStart, mStartDrawable);
- positionDrawableUnderCursor(selectionEnd, mEndDrawable);
+ boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd);
+ mStartHandle.positionAtCursor(selectionStart, oneLineSelection);
+ mEndHandle.positionAtCursor(selectionEnd, true);
- mStartDrawable.setAlpha(255);
- mEndDrawable.setAlpha(255);
+ mStartHandle.mDrawable.setAlpha(255);
+ mEndHandle.mDrawable.setAlpha(255);
}
public void onTouchEvent(MotionEvent event) {
@@ -7889,26 +7953,27 @@
final int x = (int) event.getX();
final int y = (int) event.getY();
- // Remember finger down position, to be able to start selection on that point
+ // Remember finger down position, to be able to start selection from there
mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
if (mIsVisible) {
if (mMovement instanceof ArrowKeyMovementMethod) {
- boolean isOnStart = fingerIsOnDrawable(x, y, mStartDrawable);
- boolean isOnEnd = fingerIsOnDrawable(x, y, mEndDrawable);
+ boolean isOnStart = mStartHandle.hasFingerOn(x, y);
+ boolean isOnEnd = mEndHandle.hasFingerOn(x, y);
if (isOnStart || isOnEnd) {
if (mParent != null) {
- // Prevent possible scrollView parent from scrolling, so that
- // we can use auto-scrolling.
+ // Prevent possible scrollView parent from scrolling, so
+ // that we can use auto-scrolling.
mParent.requestDisallowInterceptTouchEvent(true);
}
- // Start handle will be dragged in case BOTH controller are under finger
- mStartIsDragged = isOnStart;
- final Rect bounds =
- (mStartIsDragged ? mStartDrawable : mEndDrawable).getBounds();
+ // In case both controllers are under finger (very small
+ // selection region), arbitrarily pick end controller.
+ mStartIsDragged = !isOnEnd;
+ final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
+ final Rect bounds = draggedHandle.mDrawable.getBounds();
mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ mOffsetY = draggedHandle.mHopSpotVertcalPosition - y;
((ArrowKeyMovementMethod)mMovement).setCursorController(this);
}
@@ -7967,61 +8032,17 @@
}
}
- // Helper methods used by CursorController implementations
-
- private void positionDrawableUnderCursor(final int offset, Drawable drawable) {
- final int drawableWidth = drawable.getIntrinsicWidth();
- final int drawableHeight = drawable.getIntrinsicHeight();
- final int line = mLayout.getLineForOffset(offset);
-
- final Rect bounds = sCursorControllerTempRect;
- bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5 - drawableWidth / 2.0);
- bounds.top = mLayout.getLineTop(line + 1);
-
- // Move cursor controller a little bit up when editing the last line of text
- // (or a single line) so that it is visible and easier to grab.
- if (line == mLayout.getLineCount() - 1) {
- bounds.top -= Math.max(0, drawableHeight / 2 - getExtendedPaddingBottom());
- }
-
- bounds.right = bounds.left + drawableWidth;
- bounds.bottom = bounds.top + drawableHeight;
-
- convertFromViewportToContentCoordinates(bounds);
- drawable.setBounds(bounds);
- postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
- private boolean fingerIsOnDrawable(float x, float y, Drawable drawable) {
- // Simulate a 'fat finger' to ease grabbing of the controller.
- // Expands according to controller image size instead of using density.
- // Assumes controller imager has a sensible size, proportionnal to density.
- final int drawableWidth = drawable.getIntrinsicWidth();
- final int drawableHeight = drawable.getIntrinsicHeight();
- final Rect fingerRect = sCursorControllerTempRect;
- fingerRect.set((int) (x - drawableWidth / 2.0),
- (int) (y - drawableHeight),
- (int) (x + drawableWidth / 2.0),
- (int) y);
- return Rect.intersects(drawable.getBounds(), fingerRect);
- }
-
- private void postInvalidate(Drawable drawable) {
- final Rect bounds = drawable.getBounds();
- postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
- private void postInvalidateDelayed(long delay, Drawable drawable) {
- final Rect bounds = drawable.getBounds();
- postInvalidateDelayed(delay, bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
private void hideInsertionPointCursorController() {
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.hide();
}
}
+ private void hideControllers() {
+ hideInsertionPointCursorController();
+ finishSelectionActionMode();
+ }
+
/**
* Get the offset character closest to the specified absolute position.
*
@@ -8062,7 +8083,7 @@
}
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
@@ -8083,7 +8104,7 @@
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
private final Paint mHighlightPaint;
- private int mHighlightColor = 0xD077A14B;
+ private int mHighlightColor = 0xCC475925;
private Layout mLayout;
private long mShowCursor;
@@ -8093,8 +8114,6 @@
// Cursor Controllers. Null when disabled.
private CursorController mInsertionPointCursorController;
private CursorController mSelectionModifierCursorController;
- // Stored once and for all.
- private int mCursorControllerVerticalOffset;
private boolean mShouldStartSelectionActionMode = false;
private ActionMode mSelectionActionMode;
// Created once and shared by different CursorController helper methods.
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 281f32c..ef0ab41 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -24,8 +24,10 @@
import android.app.ActionBar;
import android.app.Activity;
+import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
+import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.ActionMode;
@@ -54,7 +56,9 @@
private static final int TAB_SWITCH_SHOW_HIDE = 0;
private static final int TAB_SWITCH_ADD_REMOVE = 1;
+ private Context mContext;
private Activity mActivity;
+ private Dialog mDialog;
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
@@ -88,8 +92,17 @@
};
public ActionBarImpl(Activity activity) {
- final View decor = activity.getWindow().getDecorView();
mActivity = activity;
+ init(activity.getWindow().getDecorView());
+ }
+
+ public ActionBarImpl(Dialog dialog) {
+ mDialog = dialog;
+ init(dialog.getWindow().getDecorView());
+ }
+
+ private void init(View decor) {
+ mContext = decor.getContext();
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -97,6 +110,7 @@
com.android.internal.R.id.lower_action_context_bar);
mAnimatorView = (ViewAnimator) decor.findViewById(
com.android.internal.R.id.action_bar_animator);
+ mActionView.setContextView(mUpperContextView);
if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
@@ -109,23 +123,23 @@
@Override
public void setStandardNavigationMode(int titleResId, int subtitleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId),
- mActivity.getString(subtitleResId));
+ setStandardNavigationMode(mContext.getString(titleResId),
+ mContext.getString(subtitleResId));
}
@Override
public void setStandardNavigationMode(int titleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId));
+ setStandardNavigationMode(mContext.getString(titleResId));
}
@Override
public void setTitle(int resId) {
- setTitle(mActivity.getString(resId));
+ setTitle(mContext.getString(resId));
}
@Override
public void setSubtitle(int resId) {
- setSubtitle(mActivity.getString(resId));
+ setSubtitle(mContext.getString(resId));
}
public void setCustomNavigationMode(View view) {
@@ -345,6 +359,10 @@
@Override
public void setTabNavigationMode() {
+ if (mActivity == null) {
+ throw new IllegalStateException(
+ "Tab navigation mode cannot be used outside of an Activity");
+ }
mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
}
@@ -380,6 +398,27 @@
trans.commit();
}
+ @Override
+ public int getHeight() {
+ return mActionView.getHeight();
+ }
+
+ @Override
+ public void show() {
+ // TODO animate!
+ mAnimatorView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void hide() {
+ // TODO animate!
+ mAnimatorView.setVisibility(View.GONE);
+ }
+
+ public boolean isShowing() {
+ return mAnimatorView.getVisibility() == View.VISIBLE;
+ }
+
/**
* @hide
*/
@@ -390,13 +429,14 @@
public ActionModeImpl(ActionMode.Callback callback) {
mCallback = callback;
- mMenu = new MenuBuilder(mActionView.getContext());
+ mMenu = new MenuBuilder(mActionView.getContext())
+ .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
}
@Override
public MenuInflater getMenuInflater() {
- return new MenuInflater(mActivity);
+ return new MenuInflater(mContext);
}
@Override
@@ -485,7 +525,7 @@
return true;
}
- new MenuPopupHelper(mActivity, subMenu).show();
+ new MenuPopupHelper(mContext, subMenu).show();
return true;
}
@@ -493,6 +533,8 @@
}
public void onMenuModeChange(MenuBuilder menu) {
+ invalidate();
+ mUpperContextView.showOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d040d3f..1620778 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,8 +22,8 @@
interface IBatteryStats {
byte[] getStatistics();
- void noteStartWakelock(int uid, String name, int type);
- void noteStopWakelock(int uid, String name, int type);
+ void noteStartWakelock(int uid, int pid, String name, int type);
+ void noteStopWakelock(int uid, int pid, String name, int type);
/* DO NOT CHANGE the position of noteStartSensor without updating
SensorService.cpp */
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 89649a9..5d1f632 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.content.pm.PackageInfoLite;
+import android.content.res.ObbInfo;
interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI,
@@ -28,4 +29,5 @@
in ParcelFileDescriptor outStream);
PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
boolean checkFreeStorage(boolean external, in Uri fileUri);
+ ObbInfo getObbInfo(String filename);
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
new file mode 100644
index 0000000..e1c5564
--- /dev/null
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+public class PlatLogoActivity extends Activity {
+ Toast mToast;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mToast = Toast.makeText(this, "Zombie art by Jack Larson", Toast.LENGTH_SHORT);
+
+ ImageView content = new ImageView(this);
+ content.setImageResource(com.android.internal.R.drawable.platlogo);
+ content.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+ setContentView(content);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mToast.show();
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index f0920d1..216d985 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -24,6 +24,6 @@
oneway interface IAppWidgetHost {
void updateAppWidget(int appWidgetId, in RemoteViews views);
void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
- void viewDataChanged(int appWidgetId, in RemoteViews views, int viewId);
+ void viewDataChanged(int appWidgetId, int viewId);
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index af75d5b..4d56745 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -40,8 +40,9 @@
// for AppWidgetManager
//
void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
+ void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
- void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, in RemoteViews views, int viewId);
+ void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
List<AppWidgetProviderInfo> getInstalledProviders();
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 13b3033..a70dbf6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -63,7 +63,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 49;
+ private static final int VERSION = 50;
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 1000;
@@ -107,6 +107,7 @@
int mNumHistoryItems;
HistoryItem mHistory;
HistoryItem mHistoryEnd;
+ HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -451,7 +452,7 @@
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
mTotalTime = mLoadedTime = mLastTime = 0;
mCount = mLoadedCount = mLastCount = 0;
if (detachIfReset) {
@@ -713,8 +714,8 @@
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
- boolean reset(boolean detachIfReset) {
- super.reset(detachIfReset);
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ super.reset(stats, detachIfReset);
setStale();
return true;
}
@@ -749,7 +750,7 @@
long mUpdateTime;
/**
- * The total time at which the timer was acquired, to determine if
+ * The total time at which the timer was acquired, to determine if it
* was actually held for an interesting duration.
*/
long mAcquireTime;
@@ -890,9 +891,14 @@
return mCount;
}
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
boolean canDetach = mNesting <= 0;
- super.reset(canDetach && detachIfReset);
+ super.reset(stats, canDetach && detachIfReset);
+ if (mNesting > 0) {
+ mUpdateTime = stats.getBatteryRealtimeLocked(
+ SystemClock.elapsedRealtime() * 1000);
+ }
+ mAcquireTime = mTotalTime;
return canDetach;
}
@@ -1115,6 +1121,26 @@
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
+
+ // If the current time is basically the same as the last time,
+ // just collapse into one record.
+ if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+ // If the current is the same as the one before, then we no
+ // longer need the entry.
+ if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+ && mHistoryLastEnd.same(mHistoryCur)) {
+ mHistoryLastEnd.next = null;
+ mHistoryEnd.next = mHistoryCache;
+ mHistoryCache = mHistoryEnd;
+ mHistoryEnd = mHistoryLastEnd;
+ mHistoryLastEnd = null;
+ } else {
+ mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+ }
+ return;
+ }
+
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
// record changes to the battery level.
@@ -1123,6 +1149,7 @@
return;
}
}
+
addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
}
@@ -1141,6 +1168,7 @@
void addHistoryRecordLocked(HistoryItem rec) {
mNumHistoryItems++;
rec.next = null;
+ mHistoryLastEnd = mHistoryEnd;
if (mHistoryEnd != null) {
mHistoryEnd.next = rec;
mHistoryEnd = rec;
@@ -1153,7 +1181,7 @@
if (mHistory != null) {
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistory;
- mHistory = mHistoryEnd = null;
+ mHistory = mHistoryLastEnd = mHistoryEnd = null;
}
mNumHistoryItems = 0;
mHistoryBaseTime = 0;
@@ -1211,6 +1239,89 @@
mBluetoothPingStart = -1;
}
+ int mWakeLockNesting;
+
+ public void noteStartWakeLocked(int uid, int pid, String name, int type) {
+ if (type == WAKE_TYPE_PARTIAL) {
+ // Only care about partial wake locks, since full wake locks
+ // will be canceled when the user puts the screen to sleep.
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mWakeLockNesting++;
+ }
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+ if (type == WAKE_TYPE_PARTIAL) {
+ mWakeLockNesting--;
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ }
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteProcessDiedLocked(int uid, int pid) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.mPids.remove(pid);
+ }
+ }
+
+ public long getProcessWakeTime(int uid, int pid, long realtime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ Uid.Pid p = u.mPids.get(pid);
+ if (p != null) {
+ return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+ }
+ }
+ return 0;
+ }
+
+ public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.reportExcessiveWakeLocked(proc, overTime, usedTime);
+ }
+ }
+
+ int mSensorNesting;
+
+ public void noteStartSensorLocked(int uid, int sensor) {
+ if (mSensorNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mSensorNesting++;
+ getUidStatsLocked(uid).noteStartSensor(sensor);
+ }
+
+ public void noteStopSensorLocked(int uid, int sensor) {
+ mSensorNesting--;
+ if (mSensorNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ getUidStatsLocked(uid).noteStopSensor(sensor);
+ }
+
int mGpsNesting;
public void noteStartGpsLocked(int uid) {
@@ -1246,6 +1357,10 @@
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this);
}
+
+ // Fake a wake lock, so we consider the device waked as long
+ // as the screen is on.
+ noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -1260,6 +1375,8 @@
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
}
+
+ noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -1800,6 +1917,11 @@
*/
final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
+ /**
+ * The transient wake stats we have collected for this uid's pids.
+ */
+ final SparseArray<Pid> mPids = new SparseArray<Pid>();
+
public Uid(int uid) {
mUid = uid;
mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
@@ -2083,27 +2205,27 @@
boolean active = false;
if (mWifiTurnedOnTimer != null) {
- active |= !mWifiTurnedOnTimer.reset(false);
+ active |= !mWifiTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiTurnedOn;
}
if (mFullWifiLockTimer != null) {
- active |= !mFullWifiLockTimer.reset(false);
+ active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mFullWifiLockOut;
}
if (mScanWifiLockTimer != null) {
- active |= !mScanWifiLockTimer.reset(false);
+ active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mScanWifiLockOut;
}
if (mWifiMulticastTimer != null) {
- active |= !mWifiMulticastTimer.reset(false);
+ active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiMulticastEnabled;
}
if (mAudioTurnedOnTimer != null) {
- active |= !mAudioTurnedOnTimer.reset(false);
+ active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
- active |= !mVideoTurnedOnTimer.reset(false);
+ active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mVideoTurnedOn;
}
@@ -2148,6 +2270,14 @@
}
mProcessStats.clear();
}
+ if (mPids.size() > 0) {
+ for (int i=0; !active && i<mPids.size(); i++) {
+ Pid pid = mPids.valueAt(i);
+ if (pid.mWakeStart != 0) {
+ active = true;
+ }
+ }
+ }
if (mPackageStats.size() > 0) {
Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
while (it.hasNext()) {
@@ -2166,6 +2296,8 @@
mPackageStats.clear();
}
+ mPids.clear();
+
if (!active) {
if (mWifiTurnedOnTimer != null) {
mWifiTurnedOnTimer.detach();
@@ -2414,13 +2546,13 @@
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
- wlactive |= !mTimerFull.reset(false);
+ wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false);
}
if (mTimerPartial != null) {
- wlactive |= !mTimerPartial.reset(false);
+ wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false);
}
if (mTimerWindow != null) {
- wlactive |= !mTimerWindow.reset(false);
+ wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false);
}
if (!wlactive) {
if (mTimerFull != null) {
@@ -2488,7 +2620,7 @@
}
boolean reset() {
- if (mTimer.reset(true)) {
+ if (mTimer.reset(BatteryStatsImpl.this, true)) {
mTimer = null;
return true;
}
@@ -2600,6 +2732,8 @@
SamplingCounter[] mSpeedBins;
+ ArrayList<ExcessiveWake> mExcessiveWake;
+
Proc() {
mUnpluggables.add(this);
mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
@@ -2626,6 +2760,58 @@
}
}
+ public int countExcessiveWakes() {
+ return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+ }
+
+ public ExcessiveWake getExcessiveWake(int i) {
+ if (mExcessiveWake != null) {
+ return mExcessiveWake.get(i);
+ }
+ return null;
+ }
+
+ public void addExcessiveWake(long overTime, long usedTime) {
+ if (mExcessiveWake == null) {
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ }
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = overTime;
+ ew.usedTime = usedTime;
+ mExcessiveWake.add(ew);
+ }
+
+ void writeExcessiveWakeToParcelLocked(Parcel out) {
+ if (mExcessiveWake == null) {
+ out.writeInt(0);
+ return;
+ }
+
+ final int N = mExcessiveWake.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = mExcessiveWake.get(i);
+ out.writeLong(ew.overTime);
+ out.writeLong(ew.usedTime);
+ }
+ }
+
+ void readExcessiveWakeFromParcelLocked(Parcel in) {
+ final int N = in.readInt();
+ if (N == 0) {
+ mExcessiveWake = null;
+ return;
+ }
+
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = in.readLong();
+ ew.usedTime = in.readLong();
+ mExcessiveWake.add(ew);
+ }
+ }
+
void writeToParcelLocked(Parcel out) {
out.writeLong(mUserTime);
out.writeLong(mSystemTime);
@@ -2650,6 +2836,8 @@
out.writeInt(0);
}
}
+
+ writeExcessiveWakeToParcelLocked(out);
}
void readFromParcelLocked(Parcel in) {
@@ -2678,6 +2866,8 @@
mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
}
}
+
+ readExcessiveWakeFromParcelLocked(in);
}
public BatteryStatsImpl getBatteryStats() {
@@ -3155,6 +3345,11 @@
}
}
+ public class Pid {
+ long mWakeSum;
+ long mWakeStart;
+ }
+
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
@@ -3169,6 +3364,15 @@
return ps;
}
+ public Pid getPidStatsLocked(int pid) {
+ Pid p = mPids.get(pid);
+ if (p == null) {
+ p = new Pid();
+ mPids.put(pid, p);
+ }
+ return p;
+ }
+
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
@@ -3261,18 +3465,36 @@
return t;
}
- public void noteStartWakeLocked(String name, int type) {
+ public void noteStartWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
+ Pid p = getPidStatsLocked(pid);
+ p.mWakeStart = SystemClock.elapsedRealtime();
+ }
}
- public void noteStopWakeLocked(String name, int type) {
+ public void noteStopWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
+ Pid p = mPids.get(pid);
+ if (p != null) {
+ p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
+ p.mWakeStart = 0;
+ }
+ }
+ }
+
+ public void reportExcessiveWakeLocked(String proc, long overTime, long usedTime) {
+ Proc p = getProcessStatsLocked(proc);
+ if (p != null) {
+ p.addExcessiveWake(overTime, usedTime);
+ }
}
public void noteStartSensor(int sensor) {
@@ -3374,6 +3596,10 @@
return mOnBattery;
}
+ public boolean isScreenOn() {
+ return mScreenOn;
+ }
+
void initTimes() {
mBatteryRealtime = mTrackBatteryPastUptime = 0;
mBatteryUptime = mTrackBatteryPastRealtime = 0;
@@ -3386,24 +3612,24 @@
public void resetAllStatsLocked() {
mStartCount = 0;
initTimes();
- mScreenOnTimer.reset(false);
+ mScreenOnTimer.reset(this, false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].reset(false);
+ mScreenBrightnessTimer[i].reset(this, false);
}
mInputEventCounter.reset(false);
- mPhoneOnTimer.reset(false);
- mAudioOnTimer.reset(false);
- mVideoOnTimer.reset(false);
+ mPhoneOnTimer.reset(this, false);
+ mAudioOnTimer.reset(this, false);
+ mVideoOnTimer.reset(this, false);
for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].reset(false);
+ mPhoneSignalStrengthsTimer[i].reset(this, false);
}
- mPhoneSignalScanningTimer.reset(false);
+ mPhoneSignalScanningTimer.reset(this, false);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].reset(false);
+ mPhoneDataConnectionsTimer[i].reset(this, false);
}
- mWifiOnTimer.reset(false);
- mWifiRunningTimer.reset(false);
- mBluetoothOnTimer.reset(false);
+ mWifiOnTimer.reset(this, false);
+ mWifiRunningTimer.reset(this, false);
+ mBluetoothOnTimer.reset(this, false);
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset()) {
@@ -4085,6 +4311,7 @@
p.mUserTime = p.mLoadedUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
+ p.readExcessiveWakeFromParcelLocked(in);
}
NP = in.readInt();
@@ -4273,6 +4500,7 @@
out.writeLong(ps.mUserTime);
out.writeLong(ps.mSystemTime);
out.writeInt(ps.mStarts);
+ ps.writeExcessiveWakeToParcelLocked(out);
}
}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index e6d6ba0..d381901 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -44,7 +44,7 @@
mContextView = view;
mCallback = callback;
- mMenu = new MenuBuilder(context);
+ mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
}
@@ -135,5 +135,7 @@
}
public void onMenuModeChange(MenuBuilder menu) {
+ invalidate();
+ mContextView.showOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index e281536..cb5f179 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -20,10 +20,12 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -39,6 +41,7 @@
private int mMaxItems;
private boolean mReserveOverflow;
private OverflowMenuButton mOverflowButton;
+ private WeakReference<MenuPopupHelper> mOverflowPopup;
public ActionMenuView(Context context) {
this(context, null);
@@ -80,6 +83,7 @@
if (p instanceof LayoutParams) {
LayoutParams lp = (LayoutParams) p;
return lp.leftMargin == mItemMargin && lp.rightMargin == mItemMargin &&
+ lp.gravity == Gravity.CENTER_VERTICAL &&
lp.width == LayoutParams.WRAP_CONTENT && lp.height == LayoutParams.WRAP_CONTENT;
}
return false;
@@ -91,6 +95,7 @@
LayoutParams.WRAP_CONTENT);
params.leftMargin = mItemMargin;
params.rightMargin = mItemMargin;
+ params.gravity = Gravity.CENTER_VERTICAL;
return params;
}
@@ -143,8 +148,32 @@
public boolean showOverflowMenu() {
if (mOverflowButton != null) {
- MenuPopupHelper popup = new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
- popup.show();
+ final MenuPopupHelper popup =
+ new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ post(new Runnable() {
+ public void run() {
+ popup.show();
+ }
+ });
+ mOverflowPopup = new WeakReference<MenuPopupHelper>(popup);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ if (popup != null) {
+ return popup.isShowing();
+ }
+ return false;
+ }
+
+ public boolean hideOverflowMenu() {
+ MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ if (popup != null) {
+ popup.dismiss();
return true;
}
return false;
@@ -162,7 +191,7 @@
final Resources res = context.getResources();
setClickable(true);
setFocusable(true);
- // TODO setTitle() to a localized string for accessibility
+ setContentDescription(res.getString(com.android.internal.R.string.more_item_label));
setImageDrawable(res.getDrawable(com.android.internal.R.drawable.ic_menu_more));
setVisibility(VISIBLE);
setEnabled(true);
@@ -174,7 +203,8 @@
return true;
}
- showOverflowMenu();
+ // Change to overflow mode
+ mMenu.getCallback().onMenuModeChange(mMenu);
return true;
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 215d809..d160fec 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -171,6 +171,11 @@
private boolean mReserveActionOverflow;
/**
+ * Default value for how added items should show in the action list.
+ */
+ private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+
+ /**
* Current use case is Context Menus: As Views populate the context menu, each one has
* extra information that should be passed along. This is the current menu info that
* should be set on all items added to this menu.
@@ -328,6 +333,11 @@
(mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS);
}
+ public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
+ mDefaultShowAsAction = defaultShowAsAction;
+ return this;
+ }
+
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -411,7 +421,8 @@
private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
final int ordering = getOrdering(categoryOrder);
- final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder, ordering, title);
+ final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
+ ordering, title, mDefaultShowAsAction);
if (mCurrentMenuInfo != null) {
// Pass along the current menu info
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index fecbd77..07a2a94 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -108,7 +108,7 @@
* @param title The text to display for the item.
*/
MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
- CharSequence title) {
+ CharSequence title, int showAsAction) {
if (sPrependShortcutLabel == null) {
// This is instantiated from the UI thread, so no chance of sync issues
@@ -129,6 +129,7 @@
mCategoryOrder = categoryOrder;
mOrdering = ordering;
mTitle = title;
+ mShowAsAction = showAsAction;
}
/**
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index ce0ec04..f52c93c 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -92,7 +92,9 @@
}
public void dismiss() {
- mPopup.dismiss();
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
mPopup = null;
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 5518b3e..acf75e3 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -27,6 +27,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.View.MeasureSpec;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -35,9 +36,6 @@
* @hide
*/
public class ActionBarContextView extends ViewGroup {
- // TODO: This must be defined in the default theme
- private static final int CONTENT_HEIGHT_DIP = 50;
-
private int mItemPadding;
private int mItemMargin;
private int mActionSpacing;
@@ -75,11 +73,21 @@
com.android.internal.R.styleable.Theme_actionModeCloseDrawable);
mItemMargin = mItemPadding / 2;
- mContentHeight =
- (int) (CONTENT_HEIGHT_DIP * getResources().getDisplayMetrics().density + 0.5f);
+ mContentHeight = a.getLayoutDimension(
+ com.android.internal.R.styleable.Theme_windowActionBarSize, 0);
a.recycle();
}
+ @Override
+ public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+ // No starting an action mode for an existing action mode UI child! (Where would it go?)
+ return null;
+ }
+
+ public void setHeight(int height) {
+ mContentHeight = height;
+ }
+
public void setCustomView(View view) {
if (mCustomView != null) {
removeView(mCustomView);
@@ -163,6 +171,27 @@
mMenuView = null;
}
+ public boolean showOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.showOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean hideOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom views if they don't supply layout params. Everything else
@@ -187,8 +216,12 @@
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
final int itemMargin = mItemPadding;
+ int maxHeight = mContentHeight > 0 ?
+ mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
- final int height = mContentHeight - getPaddingTop() - getPaddingBottom();
+ final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
if (mCloseButton != null) {
@@ -225,7 +258,20 @@
MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
}
- setMeasuredDimension(contentWidth, mContentHeight);
+ if (mContentHeight <= 0) {
+ int measuredHeight = 0;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View v = getChildAt(i);
+ int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+ if (paddedViewHeight > measuredHeight) {
+ measuredHeight = paddedViewHeight;
+ }
+ }
+ setMeasuredDimension(contentWidth, measuredHeight);
+ } else {
+ setMeasuredDimension(contentWidth, maxHeight);
+ }
}
@Override
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d703a2f..067d022 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -22,8 +22,8 @@
import com.android.internal.view.menu.MenuBuilder;
import android.app.ActionBar;
-import android.app.Activity;
import android.app.ActionBar.NavigationCallback;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -34,6 +34,7 @@
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.view.ActionMode;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -53,7 +54,6 @@
private static final String TAG = "ActionBarView";
// TODO: This must be defined in the default theme
- private static final int CONTENT_HEIGHT_DIP = 50;
private static final int CONTENT_PADDING_DIP = 3;
private static final int CONTENT_SPACING_DIP = 6;
private static final int CONTENT_ACTION_SPACING_DIP = 12;
@@ -90,12 +90,17 @@
private LinearLayout mTabLayout;
private View mCustomNavView;
+ private int mTitleStyleRes;
+ private int mSubtitleStyleRes;
+
private boolean mShowMenu;
private boolean mUserTitle;
private MenuBuilder mOptionsMenu;
private ActionMenuView mMenuView;
+ private ActionBarContextView mContextView;
+
private ActionMenuItem mLogoNavItem;
private NavigationCallback mCallback;
@@ -118,7 +123,6 @@
super(context, attrs);
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- mContentHeight = (int) (CONTENT_HEIGHT_DIP * metrics.density + 0.5f);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
@@ -152,6 +156,9 @@
setBackgroundDrawable(background);
}
+ mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
+ mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
+
final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
if (customNavId != 0) {
LayoutInflater inflater = LayoutInflater.from(context);
@@ -159,12 +166,11 @@
mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
}
+ mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+
a.recycle();
// TODO: Set this in the theme
- int padding = (int) (CONTENT_PADDING_DIP * metrics.density + 0.5f);
- setPadding(padding, padding, padding, padding);
-
mSpacing = (int) (CONTENT_SPACING_DIP * metrics.density + 0.5f);
mActionSpacing = (int) (CONTENT_ACTION_SPACING_DIP * metrics.density + 0.5f);
@@ -182,6 +188,12 @@
}
}
+ @Override
+ public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+ // No starting an action mode for an action bar child! (Where would it go?)
+ return null;
+ }
+
public void setCallback(NavigationCallback callback) {
mCallback = callback;
}
@@ -209,6 +221,20 @@
return false;
}
+ public boolean hideOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
public boolean isOverflowReserved() {
return mMenuView != null && mMenuView.isOverflowReserved();
}
@@ -456,13 +482,22 @@
mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+
+ if (mTitleStyleRes != 0) {
+ mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+ }
if (mTitle != null) {
mTitleView.setText(mTitle);
}
+
+ if (mSubtitleStyleRes != 0) {
+ mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+ }
if (mSubtitle != null) {
mSubtitleView.setText(mSubtitle);
mSubtitleView.setVisibility(VISIBLE);
}
+
addView(mTitleLayout);
}
@@ -474,6 +509,10 @@
}
}
+ public void setContextView(ActionBarContextView view) {
+ mContextView = view;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
@@ -489,9 +528,13 @@
}
int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+ int maxHeight = mContentHeight > 0 ?
+ mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
- final int height = mContentHeight - getPaddingTop() - getPaddingBottom();
+ final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
if (mLogoView != null && mLogoView.getVisibility() != GONE) {
@@ -516,7 +559,7 @@
if (mSpinner != null) {
mSpinner.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
@@ -526,8 +569,16 @@
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
final int customNavWidth = lp.width >= 0 ?
Math.min(lp.width, availableWidth) : availableWidth;
- final int customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
- MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+
+ // If the action bar is wrapping to its content height, don't allow a custom
+ // view to MATCH_PARENT.
+ int customNavHeightMode;
+ if (mContentHeight <= 0) {
+ customNavHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ }
final int customNavHeight = lp.height >= 0 ?
Math.min(lp.height, height) : height;
mCustomNavView.measure(
@@ -539,12 +590,29 @@
if (mTabLayout != null) {
mTabLayout.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
break;
}
- setMeasuredDimension(contentWidth, mContentHeight);
+ if (mContentHeight <= 0) {
+ int measuredHeight = 0;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View v = getChildAt(i);
+ int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+ if (paddedViewHeight > measuredHeight) {
+ measuredHeight = paddedViewHeight;
+ }
+ }
+ setMeasuredDimension(contentWidth, measuredHeight);
+ } else {
+ setMeasuredDimension(contentWidth, maxHeight);
+ }
+
+ if (mContextView != null) {
+ mContextView.setHeight(getMeasuredHeight());
+ }
}
private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 7851347..ae9900c 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -20,6 +20,7 @@
/** {@hide} */
interface IRemoteViewsFactory {
+ void onDataSetChanged();
int getCount();
RemoteViews getViewAt(int position);
RemoteViews getLoadingView();
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
index e1a6737..facda36 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
@@ -67,8 +67,22 @@
this(context, xmlLayoutResId, 0);
}
+ public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int width, int height) {
+ this(context, xmlLayoutResId, 0, width, height);
+ }
+
public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) {
super(context, xmlLayoutResId, mode);
+ init(context);
+ }
+
+ public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode,
+ int width, int height) {
+ super(context, xmlLayoutResId, mode, width, height);
+ init(context);
+ }
+
+ private void init(Context context) {
final Resources res = context.getResources();
mRes = res;
mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift);
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 53720e4..384f7bc 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -54,10 +54,20 @@
private Vibrator mVibrator;
public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
+ this(context, keyboardView, targetView, true);
+ }
+
+ public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView,
+ boolean useFullScreenWidth) {
mContext = context;
mTargetView = targetView;
mKeyboardView = keyboardView;
- createKeyboards();
+ if (useFullScreenWidth || mKeyboardView.getLayoutParams().width == -1) {
+ createKeyboards();
+ } else {
+ createKeyboardsWithSpecificSize(mKeyboardView.getLayoutParams().width,
+ mKeyboardView.getLayoutParams().height);
+ }
mKeyboardView.setOnKeyboardActionListener(this);
mVibrator = new Vibrator();
}
@@ -66,6 +76,29 @@
return mKeyboardMode == KEYBOARD_MODE_ALPHA;
}
+ private void createKeyboardsWithSpecificSize(int viewWidth, int viewHeight) {
+ mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric,
+ viewWidth, viewHeight);
+ mQwertyKeyboard = new PasswordEntryKeyboard(mContext,
+ R.xml.password_kbd_qwerty, R.id.mode_normal, viewWidth, viewHeight);
+ mQwertyKeyboard.enableShiftLock();
+
+ mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext,
+ R.xml.password_kbd_qwerty_shifted,
+ R.id.mode_normal, viewWidth, viewHeight);
+ mQwertyKeyboardShifted.enableShiftLock();
+ mQwertyKeyboardShifted.setShifted(true); // always shifted.
+
+ mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols,
+ viewWidth, viewHeight);
+ mSymbolsKeyboard.enableShiftLock();
+
+ mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext,
+ R.xml.password_kbd_symbols_shift, viewWidth, viewHeight);
+ mSymbolsKeyboardShifted.enableShiftLock();
+ mSymbolsKeyboardShifted.setShifted(true); // always shifted
+ }
+
private void createKeyboards() {
mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric);
mQwertyKeyboard = new PasswordEntryKeyboard(mContext,
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 3218ba8..4b56cb4 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -474,10 +474,13 @@
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
- Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
- +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
- new RuntimeException(LOG_TAG + "stack:"));
+ if (DBG) {
+ if (widthSpecMode == MeasureSpec.UNSPECIFIED
+ || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
+ +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
+ new RuntimeException(LOG_TAG + "stack:"));
+ }
}
mLeftSlider.measure();
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d1a5ae14..cb0bdd3 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -82,6 +82,7 @@
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
@@ -105,6 +106,7 @@
android_graphics_PixelFormat.cpp \
android/graphics/Picture.cpp \
android/graphics/PorterDuff.cpp \
+ android/graphics/LargeBitmap.cpp \
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
@@ -139,7 +141,8 @@
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelperBase.cpp \
android_backup_BackupHelperDispatcher.cpp \
- android_content_res_ObbScanner.cpp
+ android_content_res_ObbScanner.cpp \
+ android_content_res_Configuration.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8b09e5f..dba1cea 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@
extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_LargeBitmap(JNIEnv*);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -167,6 +168,7 @@
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
+extern int register_android_content_res_Configuration(JNIEnv* env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1255,6 +1257,7 @@
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_LargeBitmap),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_ColorFilter),
@@ -1331,6 +1334,7 @@
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_content_res_ObbScanner),
+ REG_JNI(register_android_content_res_Configuration),
};
/*
diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp
new file mode 100644
index 0000000..f0739ea
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.cpp
@@ -0,0 +1,100 @@
+#include "AutoDecodeCancel.h"
+
+static SkMutex gAutoDecoderCancelMutex;
+static AutoDecoderCancel* gAutoDecoderCancel;
+#ifdef SK_DEBUG
+static int gAutoDecoderCancelCount;
+#endif
+
+AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
+ SkImageDecoder* decoder) {
+ fJOptions = joptions;
+ fDecoder = decoder;
+
+ if (NULL != joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // Add us as the head of the list
+ fPrev = NULL;
+ fNext = gAutoDecoderCancel;
+ if (gAutoDecoderCancel) {
+ gAutoDecoderCancel->fPrev = this;
+ }
+ gAutoDecoderCancel = this;
+
+ SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
+ Validate();
+ }
+}
+
+AutoDecoderCancel::~AutoDecoderCancel() {
+ if (NULL != fJOptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // take us out of the dllist
+ AutoDecoderCancel* prev = fPrev;
+ AutoDecoderCancel* next = fNext;
+
+ if (prev) {
+ SkASSERT(prev->fNext == this);
+ prev->fNext = next;
+ } else {
+ SkASSERT(gAutoDecoderCancel == this);
+ gAutoDecoderCancel = next;
+ }
+ if (next) {
+ SkASSERT(next->fPrev == this);
+ next->fPrev = prev;
+ }
+
+ SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
+ Validate();
+ }
+}
+
+bool AutoDecoderCancel::RequestCancel(jobject joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ Validate();
+
+ AutoDecoderCancel* pair = gAutoDecoderCancel;
+ while (pair != NULL) {
+ if (pair->fJOptions == joptions) {
+ pair->fDecoder->cancelDecode();
+ return true;
+ }
+ pair = pair->fNext;
+ }
+ return false;
+}
+
+#ifdef SK_DEBUG
+// can only call this inside a lock on gAutoDecoderCancelMutex
+void AutoDecoderCancel::Validate() {
+ const int gCount = gAutoDecoderCancelCount;
+
+ if (gCount == 0) {
+ SkASSERT(gAutoDecoderCancel == NULL);
+ } else {
+ SkASSERT(gCount > 0);
+
+ AutoDecoderCancel* curr = gAutoDecoderCancel;
+ SkASSERT(curr);
+ SkASSERT(curr->fPrev == NULL);
+
+ int count = 0;
+ while (curr) {
+ count += 1;
+ SkASSERT(count <= gCount);
+ if (curr->fPrev) {
+ SkASSERT(curr->fPrev->fNext == curr);
+ }
+ if (curr->fNext) {
+ SkASSERT(curr->fNext->fPrev == curr);
+ }
+ curr = curr->fNext;
+ }
+ SkASSERT(count == gCount);
+ }
+}
+#endif
diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h
new file mode 100644
index 0000000..37b86f9
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.h
@@ -0,0 +1,27 @@
+#ifndef AutoDecodeCancel_DEFINED
+#define AutoDecodeCancel_DEFINED
+
+#include <jni.h>
+#include "SkImageDecoder.h"
+
+class AutoDecoderCancel {
+public:
+ AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
+ ~AutoDecoderCancel();
+
+ static bool RequestCancel(jobject options);
+
+private:
+ AutoDecoderCancel* fNext;
+ AutoDecoderCancel* fPrev;
+ jobject fJOptions; // java options object
+ SkImageDecoder* fDecoder;
+
+#ifdef SK_DEBUG
+ static void Validate();
+#else
+ static void Validate() {}
+#endif
+};
+
+#endif
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b41bad0..21b2e3b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,14 +1,15 @@
#define LOG_TAG "BitmapFactory"
+#include "BitmapFactory.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPixelRef.h"
#include "SkStream.h"
-#include "GraphicsJNI.h"
#include "SkTemplates.h"
#include "SkUtils.h"
#include "CreateJavaOutputStreamAdaptor.h"
+#include "AutoDecodeCancel.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Asset.h>
@@ -16,18 +17,18 @@
#include <netinet/in.h>
#include <sys/mman.h>
-static jclass gOptions_class;
-static jfieldID gOptions_justBoundsFieldID;
-static jfieldID gOptions_sampleSizeFieldID;
-static jfieldID gOptions_configFieldID;
-static jfieldID gOptions_ditherFieldID;
-static jfieldID gOptions_purgeableFieldID;
-static jfieldID gOptions_shareableFieldID;
-static jfieldID gOptions_nativeAllocFieldID;
-static jfieldID gOptions_widthFieldID;
-static jfieldID gOptions_heightFieldID;
-static jfieldID gOptions_mimeFieldID;
-static jfieldID gOptions_mCancelID;
+jclass gOptions_class;
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_purgeableFieldID;
+jfieldID gOptions_shareableFieldID;
+jfieldID gOptions_nativeAllocFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_mCancelID;
static jclass gFileDescriptor_class;
static jfieldID gFileDescriptor_descriptor;
@@ -38,129 +39,6 @@
#define TRACE_BITMAP(code)
#endif
-///////////////////////////////////////////////////////////////////////////////
-
-class AutoDecoderCancel {
-public:
- AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
- ~AutoDecoderCancel();
-
- static bool RequestCancel(jobject options);
-
-private:
- AutoDecoderCancel* fNext;
- AutoDecoderCancel* fPrev;
- jobject fJOptions; // java options object
- SkImageDecoder* fDecoder;
-
-#ifdef SK_DEBUG
- static void Validate();
-#else
- static void Validate() {}
-#endif
-};
-
-static SkMutex gAutoDecoderCancelMutex;
-static AutoDecoderCancel* gAutoDecoderCancel;
-#ifdef SK_DEBUG
- static int gAutoDecoderCancelCount;
-#endif
-
-AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
- SkImageDecoder* decoder) {
- fJOptions = joptions;
- fDecoder = decoder;
-
- if (NULL != joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // Add us as the head of the list
- fPrev = NULL;
- fNext = gAutoDecoderCancel;
- if (gAutoDecoderCancel) {
- gAutoDecoderCancel->fPrev = this;
- }
- gAutoDecoderCancel = this;
-
- SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
- Validate();
- }
-}
-
-AutoDecoderCancel::~AutoDecoderCancel() {
- if (NULL != fJOptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // take us out of the dllist
- AutoDecoderCancel* prev = fPrev;
- AutoDecoderCancel* next = fNext;
-
- if (prev) {
- SkASSERT(prev->fNext == this);
- prev->fNext = next;
- } else {
- SkASSERT(gAutoDecoderCancel == this);
- gAutoDecoderCancel = next;
- }
- if (next) {
- SkASSERT(next->fPrev == this);
- next->fPrev = prev;
- }
-
- SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
- Validate();
- }
-}
-
-bool AutoDecoderCancel::RequestCancel(jobject joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- Validate();
-
- AutoDecoderCancel* pair = gAutoDecoderCancel;
- while (pair != NULL) {
- if (pair->fJOptions == joptions) {
- pair->fDecoder->cancelDecode();
- return true;
- }
- pair = pair->fNext;
- }
- return false;
-}
-
-#ifdef SK_DEBUG
-// can only call this inside a lock on gAutoDecoderCancelMutex
-void AutoDecoderCancel::Validate() {
- const int gCount = gAutoDecoderCancelCount;
-
- if (gCount == 0) {
- SkASSERT(gAutoDecoderCancel == NULL);
- } else {
- SkASSERT(gCount > 0);
-
- AutoDecoderCancel* curr = gAutoDecoderCancel;
- SkASSERT(curr);
- SkASSERT(curr->fPrev == NULL);
-
- int count = 0;
- while (curr) {
- count += 1;
- SkASSERT(count <= gCount);
- if (curr->fPrev) {
- SkASSERT(curr->fPrev->fNext == curr);
- }
- if (curr->fNext) {
- SkASSERT(curr->fNext->fPrev == curr);
- }
- curr = curr->fNext;
- }
- SkASSERT(count == gCount);
- }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
using namespace android;
class NinePatchPeeker : public SkImageDecoder::Peeker {
@@ -279,7 +157,7 @@
return ((int32_t)isValid - 1) | value;
}
-static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
static const struct {
SkImageDecoder::Format fFormat;
const char* fMimeType;
@@ -477,7 +355,7 @@
jobject padding,
jobject options) { // BitmapFactory$Options
jobject bitmap = NULL;
- SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
@@ -682,6 +560,107 @@
}
}
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
+ SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+ int width, height;
+ if (NULL == decoder) {
+ doThrowIOE(env, "Image format not supported");
+ return nullObjectReturn("SkImageDecoder::Factory returned null");
+ }
+
+ JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+ decoder->setAllocator(javaAllocator);
+ JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+ decoder->setReporter(javaMemoryReporter);
+ javaAllocator->unref();
+ javaMemoryReporter->unref();
+
+ if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
+ char msg[1024];
+ snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
+ doThrowIOE(env, msg);
+ return nullObjectReturn("decoder->buildTileIndex returned false");
+ }
+
+ SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
+
+ return GraphicsJNI::createLargeBitmap(env, bm);
+}
+
+static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+ int offset, int length, jboolean isShareable) {
+ AutoJavaByteArray ar(env, byteArray);
+ SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jboolean isShareable) {
+ NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+ jint descriptor = env->GetIntField(fileDescriptor,
+ gFileDescriptor_descriptor);
+ bool weOwnTheFD = false;
+
+ if (isShareable) {
+ int newFD = ::dup(descriptor);
+ if (-1 != newFD) {
+ weOwnTheFD = true;
+ descriptor = newFD;
+ }
+ }
+
+ SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
+ SkAutoUnref aur(stream);
+ if (!stream->isValid()) {
+ return NULL;
+ }
+
+ if (isShareable) {
+ aur.detach();
+ }
+
+ /* Restore our offset when we leave, so we can be called more than once
+ with the same descriptor. This is only required if we didn't dup the
+ file descriptor, but it is OK to do it all the time.
+ */
+ AutoFDSeek as(descriptor);
+
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
+ jobject is, // InputStream
+ jbyteArray storage, // byte[]
+ jboolean isShareable) {
+ jobject largeBitmap = NULL;
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+ if (stream) {
+ // for now we don't allow shareable with java inputstreams
+ largeBitmap = doBuildTileIndex(env, stream, false);
+ stream->unref();
+ }
+ return largeBitmap;
+}
+
+static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
+ jint native_asset, // Asset
+ jboolean isShareable) {
+ SkStream* stream;
+ Asset* asset = reinterpret_cast<Asset*>(native_asset);
+ stream = new AssetStreamAdaptor(asset);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gMethods[] = {
@@ -711,6 +690,26 @@
},
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
+
+ { "nativeCreateLargeBitmap",
+ "([BIIZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromByteArray
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromStream
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromFileDescriptor
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(IZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromAsset
+ },
};
static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
new file mode 100644
index 0000000..f868434
--- /dev/null
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -0,0 +1,21 @@
+#ifndef BitmapFactory_DEFINE
+#define BitmapFactory_DEFINE
+
+#include "GraphicsJNI.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_mCancelID;
+
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
+
+#endif
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 007757f..137acc6 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -5,6 +5,7 @@
static jclass gInputStream_Clazz;
static jmethodID gInputStream_resetMethodID;
+static jmethodID gInputStream_markMethodID;
static jmethodID gInputStream_availableMethodID;
static jmethodID gInputStream_readMethodID;
static jmethodID gInputStream_skipMethodID;
@@ -143,7 +144,7 @@
};
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage) {
+ jbyteArray storage, int markSize) {
static bool gInited;
if (!gInited) {
@@ -153,6 +154,8 @@
gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
"reset", "()V");
+ gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
+ "mark", "(I)V");
gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
"available", "()I");
gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
@@ -161,6 +164,7 @@
"skip", "(J)J");
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
+ RETURN_NULL_IF_NULL(gInputStream_markMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
@@ -168,6 +172,10 @@
gInited = true;
}
+ if (markSize) {
+ env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
+ }
+
return new JavaInputStreamAdaptor(env, stream, storage);
}
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index cf21dde..c34c96a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -6,7 +6,7 @@
#include "SkStream.h"
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage);
+ jbyteArray storage, int markSize = 0);
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
jbyteArray storage);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5659ba2..204bb74 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,6 +46,10 @@
doThrow(env, "java/lang/OutOfMemoryError", msg);
}
+void doThrowIOE(JNIEnv* env, const char* msg) {
+ doThrow(env, "java/lang/IOException", msg);
+}
+
bool GraphicsJNI::hasException(JNIEnv *env) {
if (env->ExceptionCheck() != 0) {
LOGE("*** Uncaught exception returned from Java call!\n");
@@ -165,6 +169,9 @@
static jclass gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
+static jclass gLargeBitmap_class;
+static jmethodID gLargeBitmap_constructorMethodID;
+
static jclass gCanvas_class;
static jfieldID gCanvas_nativeInstanceID;
@@ -370,6 +377,23 @@
}
return obj;
}
+jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+{
+ SkASSERT(bitmap != NULL);
+
+ jobject obj = env->AllocObject(gLargeBitmap_class);
+ if (hasException(env)) {
+ obj = NULL;
+ return obj;
+ }
+ if (obj) {
+ env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+ if (hasException(env)) {
+ obj = NULL;
+ }
+ }
+ return obj;
+}
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
{
@@ -502,6 +526,35 @@
////////////////////////////////////////////////////////////////////////////////
+JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
+ : fEnv(env), fTotalSize(0) {}
+
+JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+ jlong jtotalSize = fTotalSize;
+ fEnv->CallVoidMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalFreeMethodID,
+ jtotalSize);
+}
+
+bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
+ jlong jsize = memorySize; // the VM wants longs for the size
+ bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalAllocationMethodID,
+ jsize);
+ if (GraphicsJNI::hasException(fEnv)) {
+ return false;
+ }
+ if (!r) {
+ LOGE("VM won't let us allocate %zd bytes\n", memorySize);
+ doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+ return false;
+ }
+ fTotalSize += memorySize;
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
static jclass make_globalref(JNIEnv* env, const char classname[])
{
jclass c = env->FindClass(classname);
@@ -547,6 +600,9 @@
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
"(IZ[BI)V");
+ gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
+ gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
"nativeInt", "I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index fe24b05..8d6528b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,8 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkBitmap.h"
+#include "../images/SkLargeBitmap.h"
+#include "../images/SkImageDecoder.h"
#include <jni.h>
class SkCanvas;
@@ -54,6 +56,8 @@
static jobject createRegion(JNIEnv* env, SkRegion* region);
+ static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+
/** Set a pixelref for the bitmap (needs setConfig to already be called)
Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised.
@@ -80,6 +84,18 @@
bool fReportSizeToVM;
};
+class JavaMemoryUsageReporter : public SkVMMemoryReporter {
+public:
+ JavaMemoryUsageReporter(JNIEnv* env);
+ virtual ~JavaMemoryUsageReporter();
+ // overrides
+ virtual bool reportMemory(size_t memorySize);
+
+private:
+ JNIEnv* fEnv;
+ size_t fTotalSize;
+};
+
enum JNIAccess {
kRO_JNIAccess,
kRW_JNIAccess
@@ -156,6 +172,7 @@
void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime
void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State
void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
#define NPE_CHECK_RETURN_ZERO(env, object) \
do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
new file mode 100644
index 0000000..4cf5dfa
--- /dev/null
+++ b/core/jni/android/graphics/LargeBitmap.cpp
@@ -0,0 +1,138 @@
+#define LOG_TAG "LargeBitmap"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkLargeBitmap.h"
+
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+static jobject nullObjectReturn(const char msg[]) {
+ if (msg) {
+ SkDebugf("--- %s\n", msg);
+ }
+ return NULL;
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
+ int start_x, int start_y, int width, int height, jobject options) {
+ SkImageDecoder *decoder = bm->getDecoder();
+ int sampleSize = 1;
+ SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ bool doDither = true;
+
+ if (NULL != options) {
+ sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+ // initialize these, in case we fail later on
+ env->SetIntField(options, gOptions_widthFieldID, -1);
+ env->SetIntField(options, gOptions_heightFieldID, -1);
+ env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+ jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+ prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+ }
+
+ decoder->setDitherImage(doDither);
+ SkBitmap* bitmap = new SkBitmap;
+ SkAutoTDelete<SkBitmap> adb(bitmap);
+ AutoDecoderCancel adc(options, decoder);
+
+ // To fix the race condition in case "requestCancelDecode"
+ // happens earlier than AutoDecoderCancel object is added
+ // to the gAutoDecoderCancelMutex linked list.
+ if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+ return nullObjectReturn("gOptions_mCancelID");;
+ }
+
+ SkIRect region;
+ region.fLeft = start_x;
+ region.fTop = start_y;
+ region.fRight = start_x + width;
+ region.fBottom = start_y + height;
+
+ if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+ return nullObjectReturn("decoder->decodeRegion returned false");
+ }
+
+ // update options (if any)
+ if (NULL != options) {
+ env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+ env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+ // TODO: set the mimeType field with the data from the codec.
+ // but how to reuse a set of strings, rather than allocating new one
+ // each time?
+ env->SetObjectField(options, gOptions_mimeFieldID,
+ getMimeTypeString(env, decoder->getFormat()));
+ }
+
+ // detach bitmap from its autotdeleter, since we want to own it now
+ adb.detach();
+
+ SkPixelRef* pr;
+ pr = bitmap->pixelRef();
+ // promise we will never change our pixels (great for sharing and pictures)
+ pr->setImmutable();
+ // now create the java bitmap
+ return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ delete bm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gLargeBitmapMethods[] = {
+ { "nativeDecodeRegion",
+ "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeRegion},
+ { "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+ { "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+ { "nativeClean", "(I)V", (void*)nativeClean},
+};
+
+#define kClassPathName "android/graphics/LargeBitmap"
+
+int register_android_graphics_LargeBitmap(JNIEnv* env);
+int register_android_graphics_LargeBitmap(JNIEnv* env)
+{
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
+}
+
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e4d4850..ca9f371 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -738,7 +738,7 @@
(void*) SkPaintGlue::getStringBounds },
{"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getCharArrayBounds },
- {"setShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
+ {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
};
static jfieldID req_fieldID(jfieldID id) {
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 2b98e89..cb1c333 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -149,7 +149,9 @@
storedBounds[2] = x1; storedBounds[3] = y1;
jfloat* storedPositions = new jfloat[count];
uint32_t* storedColors = new uint32_t[count];
- memcpy(storedColors, colorValues, count);
+ for (size_t i = 0; i < count; i++) {
+ storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+ }
if (posArray) {
AutoJavaFloatArray autoPos(env, posArray, count);
@@ -185,8 +187,8 @@
storedPositions[1] = 1.0f;
uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = color0;
- storedColors[1] = color1;
+ storedColors[0] = static_cast<uint32_t>(color0);
+ storedColors[1] = static_cast<uint32_t>(color1);
SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
@@ -329,13 +331,8 @@
static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) {
#ifdef USE_OPENGL_RENDERER
- SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
- SkXfermode* mode = (SkXfermode*) au.get();
- SkXfermode::Mode skiaMode;
- if (!SkXfermode::IsMode(mode, &skiaMode)) {
- skiaMode = SkXfermode::kSrcOver_Mode;
- }
- return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+ SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
+ return new SkiaComposeShader(shaderA, shaderB, mode, shader);
#else
return NULL;
#endif
@@ -346,6 +343,7 @@
#ifdef USE_OPENGL_RENDERER
SkXfermode::Mode skiaMode;
if (!SkXfermode::IsMode(mode, &skiaMode)) {
+ // TODO: Support other modes
skiaMode = SkXfermode::kSrcOver_Mode;
}
return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 716d960..147e1fa 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -221,6 +221,18 @@
}
}
+bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped) {
+ UErrorCode status = U_ZERO_ERROR;
+ count = shapeRtlText(context, start, count, contextCount, shaped, status);
+ if (U_SUCCESS(status)) {
+ return true;
+ } else {
+ LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
+ }
+ return false;
+}
+
void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
jint start, jint count, jint contextCount,
int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
@@ -231,12 +243,8 @@
uint8_t rtl = dirFlags & 0x1;
if (rtl) {
SkAutoSTMalloc<80, jchar> buffer(contextCount);
- UErrorCode status = U_ZERO_ERROR;
- count = shapeRtlText(chars, start, count, contextCount, buffer.get(), status);
- if (U_SUCCESS(status)) {
+ if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) {
canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
- } else {
- LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
}
} else {
canvas->drawText(chars + start, count << 1, x_, y_, *paint);
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 3d05e18..8f666c0 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -66,6 +66,9 @@
static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
const jchar** outText, int32_t* outBytes, jchar** outBuffer);
+ static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped);
+
private:
static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 1feb3b3..0932473a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -600,7 +600,7 @@
static jint
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
jstring internalDataDir, jstring externalDataDir, int sdkVersion,
- jobject jAssetMgr)
+ jobject jAssetMgr, jbyteArray savedState)
{
LOG_TRACE("loadNativeCode_native");
@@ -666,7 +666,18 @@
code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
- code->createActivityFunc(code, NULL, 0);
+ jbyte* rawSavedState = NULL;
+ jsize rawSavedSize = 0;
+ if (savedState != NULL) {
+ rawSavedState = env->GetByteArrayElements(savedState, NULL);
+ rawSavedSize = env->GetArrayLength(savedState);
+ }
+
+ code->createActivityFunc(code, rawSavedState, rawSavedSize);
+
+ if (rawSavedState != NULL) {
+ env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+ }
}
return (jint)code;
@@ -706,17 +717,31 @@
}
}
-static void
+static jbyteArray
onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onSaveInstanceState_native");
+
+ jbyteArray array = NULL;
+
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onSaveInstanceState != NULL) {
size_t len = 0;
- code->callbacks.onSaveInstanceState(code, &len);
+ jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
+ if (len > 0) {
+ array = env->NewByteArray(len);
+ if (array != NULL) {
+ env->SetByteArrayRegion(array, 0, len, state);
+ }
+ }
+ if (state != NULL) {
+ free(state);
+ }
}
}
+
+ return array;
}
static void
@@ -744,6 +769,18 @@
}
static void
+onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
+{
+ LOG_TRACE("onConfigurationChanged_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->callbacks.onConfigurationChanged != NULL) {
+ code->callbacks.onConfigurationChanged(code);
+ }
+ }
+}
+
+static void
onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onLowMemory_native");
@@ -934,14 +971,15 @@
}
static const JNINativeMethod g_methods[] = {
- { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
+ { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
(void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
- { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native },
+ { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
{ "onPauseNative", "(I)V", (void*)onPause_native },
{ "onStopNative", "(I)V", (void*)onStop_native },
+ { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
{ "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
{ "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
{ "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
new file mode 100644
index 0000000..28a43ab
--- /dev/null
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Configuration"
+
+#include <utils/Log.h>
+#include "utils/misc.h"
+
+#include "jni.h"
+#include <android_runtime/android_content_res_Configuration.h>
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jfieldID mcc;
+ jfieldID mnc;
+ jfieldID locale;
+ jfieldID screenLayout;
+ jfieldID touchscreen;
+ jfieldID keyboard;
+ jfieldID keyboardHidden;
+ jfieldID hardKeyboardHidden;
+ jfieldID navigation;
+ jfieldID navigationHidden;
+ jfieldID orientation;
+ jfieldID uiMode;
+} gConfigurationClassInfo;
+
+void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out) {
+ out->mcc = env->GetIntField(clazz, gConfigurationClassInfo.mcc);
+ out->mnc = env->GetIntField(clazz, gConfigurationClassInfo.mnc);
+ out->screenLayout = env->GetIntField(clazz, gConfigurationClassInfo.screenLayout);
+ out->touchscreen = env->GetIntField(clazz, gConfigurationClassInfo.touchscreen);
+ out->keyboard = env->GetIntField(clazz, gConfigurationClassInfo.keyboard);
+ out->navigation = env->GetIntField(clazz, gConfigurationClassInfo.navigation);
+
+ out->inputFlags = env->GetIntField(clazz, gConfigurationClassInfo.keyboardHidden);
+ int hardKeyboardHidden = env->GetIntField(clazz, gConfigurationClassInfo.hardKeyboardHidden);
+ if (out->inputFlags == ACONFIGURATION_KEYSHIDDEN_NO
+ && hardKeyboardHidden == 2) {
+ out->inputFlags = ACONFIGURATION_KEYSHIDDEN_SOFT;
+ }
+ out->inputFlags |= env->GetIntField(clazz, gConfigurationClassInfo.navigationHidden)
+ << ResTable_config::SHIFT_NAVHIDDEN;
+
+ out->orientation = env->GetIntField(clazz, gConfigurationClassInfo.orientation);
+ out->uiMode = env->GetIntField(clazz, gConfigurationClassInfo.uiMode);
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+ // (void*) android_content_res_ObbScanner_getObbInfo },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_content_res_Configuration(JNIEnv* env)
+{
+ FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+
+ GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz,
+ "mcc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz,
+ "mnc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz,
+ "locale", "Ljava/util/Locale;");
+ GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz,
+ "screenLayout", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+ "touchscreen", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+ "keyboard", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz,
+ "keyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz,
+ "hardKeyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+ "navigation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz,
+ "navigationHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz,
+ "orientation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz,
+ "uiMode", "I");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
+ NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
index 2212d9a..0f3114b 100644
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ b/core/jni/android_database_SQLiteStatement.cpp
@@ -18,17 +18,24 @@
#undef LOG_TAG
#define LOG_TAG "SQLiteStatementCpp"
+#include "android_util_Binder.h"
+
#include <jni.h>
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <sqlite3.h>
+#include <cutils/ashmem.h>
#include <utils/Log.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "sqlite3_exception.h"
@@ -133,6 +140,105 @@
return value;
}
+static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
+{
+ // Create FileDescriptor object
+ jobject fileDesc = newFileDescriptor(env, fd);
+ if (fileDesc == NULL) {
+ // FileDescriptor constructor has thrown an exception
+ close(fd);
+ return NULL;
+ }
+
+ // Wrap it in a ParcelFileDescriptor
+ jobject parcelFileDesc = newParcelFileDescriptor(env, fileDesc);
+ if (parcelFileDesc == NULL) {
+ // ParcelFileDescriptor constructor has thrown an exception
+ close(fd);
+ return NULL;
+ }
+
+ return parcelFileDesc;
+}
+
+// Creates an ashmem area, copies some data into it, and returns
+// a ParcelFileDescriptor for the ashmem area.
+static jobject create_ashmem_region_with_data(JNIEnv * env,
+ const void * data, int length)
+{
+ // Create ashmem area
+ int fd = ashmem_create_region(NULL, length);
+ if (fd < 0) {
+ LOGE("ashmem_create_region failed: %s", strerror(errno));
+ jniThrowIOException(env, errno);
+ return NULL;
+ }
+
+ if (length > 0) {
+ // mmap the ashmem area
+ void * ashmem_ptr =
+ mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ashmem_ptr == MAP_FAILED) {
+ LOGE("mmap failed: %s", strerror(errno));
+ jniThrowIOException(env, errno);
+ close(fd);
+ return NULL;
+ }
+
+ // Copy data to ashmem area
+ memcpy(ashmem_ptr, data, length);
+
+ // munmap ashmem area
+ if (munmap(ashmem_ptr, length) < 0) {
+ LOGE("munmap failed: %s", strerror(errno));
+ jniThrowIOException(env, errno);
+ close(fd);
+ return NULL;
+ }
+ }
+
+ // Make ashmem area read-only
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ LOGE("ashmem_set_prot_region failed: %s", strerror(errno));
+ jniThrowIOException(env, errno);
+ close(fd);
+ return NULL;
+ }
+
+ // Wrap it in a ParcelFileDescriptor
+ return createParcelFileDescriptor(env, fd);
+}
+
+static jobject native_1x1_blob_ashmem(JNIEnv* env, jobject object)
+{
+ int err;
+ sqlite3 * handle = GET_HANDLE(env, object);
+ sqlite3_stmt * statement = GET_STATEMENT(env, object);
+ jobject value = NULL;
+
+ // Execute the statement
+ err = sqlite3_step(statement);
+
+ // Handle the result
+ if (err == SQLITE_ROW) {
+ // No errors, read the data and return it
+ const void * blob = sqlite3_column_blob(statement, 0);
+ if (blob != NULL) {
+ int len = sqlite3_column_bytes(statement, 0);
+ if (len >= 0) {
+ value = create_ashmem_region_with_data(env, blob, len);
+ }
+ }
+ } else {
+ throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
+ }
+
+ // Reset the statment so it's ready to use again
+ sqlite3_reset(statement);
+
+ return value;
+}
+
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
@@ -140,6 +246,7 @@
{"native_executeInsert", "()J", (void *)native_executeInsert},
{"native_1x1_long", "()J", (void *)native_1x1_long},
{"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
+ {"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem},
};
int register_android_database_SQLiteStatement(JNIEnv * env)
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index bb1a9e3..4c6eced 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -27,6 +27,7 @@
#include <SkPaint.h>
#include <SkRegion.h>
#include <SkScalerContext.h>
+#include <SkTemplates.h>
#include <SkXfermode.h>
#include <OpenGLRenderer.h>
@@ -235,6 +236,16 @@
renderer->drawRect(left, top, right, bottom, paint);
}
+static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) {
+ SkRegion::Iterator it(*region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+ it.next();
+ }
+}
+
static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
renderer->drawPath(path, paint);
@@ -248,6 +259,7 @@
OpenGLRenderer* renderer) {
renderer->resetShader();
renderer->resetColorFilter();
+ renderer->resetShadow();
}
static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject canvas,
@@ -260,6 +272,11 @@
renderer->setupColorFilter(filter);
}
+static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jfloat radius, jfloat dx, jfloat dy, jint color) {
+ renderer->setupShadow(radius, dx, dy, color);
+}
+
// ----------------------------------------------------------------------------
// Text
// ----------------------------------------------------------------------------
@@ -275,6 +292,23 @@
}
}
+static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
+ jint start, jint count, jint contextCount, jfloat x, jfloat y,
+ int flags, SkPaint* paint) {
+ uint8_t rtl = flags & 0x1;
+ if (rtl) {
+ SkAutoSTMalloc<80, jchar> buffer(contextCount);
+ jchar* shaped = buffer.get();
+ if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) {
+ renderer->drawText((const char*) shaped, count << 1, count, x, y, paint);
+ } else {
+ LOGW("drawTextRun error");
+ }
+ } else {
+ renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
+ }
+}
+
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
@@ -291,6 +325,28 @@
env->ReleaseStringChars(text, textArray);
}
+static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jcharArray text, int index, int count,
+ int contextIndex, int contextCount, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jstring text, int start, int end,
+ int contextStart, int contextEnd, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ jint count = end - start;
+ jint contextCount = contextEnd - contextStart;
+ renderTextRun(renderer, textArray + contextStart, start - contextStart,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseStringChars(text, textArray);
+}
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -312,50 +368,56 @@
const char* const kClassPathName = "android/view/GLES20Canvas";
static JNINativeMethod gMethods[] = {
- { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
+ { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
+
#ifdef USE_OPENGL_RENDERER
+ { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
+ { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
+ { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
+ { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
- { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
- { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
- { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
- { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
+ { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
+ { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
+ { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
+ { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
- { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
- { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
- { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
- { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
+ { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
+ { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
- { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
- { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
+ { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
+ { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
+ { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
- { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
- { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
- { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
+ { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
+ { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
+ { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
- { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
- { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
- { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
+ { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
+ { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
+ { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
- { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
- { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
- { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
+ { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
+ { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+ { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+ { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+ { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
+ { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
+ { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects },
+ { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
- { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
- { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
- { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
- { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
- { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
- { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
- { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
+ { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
+ { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
+ { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
+ { "nSetupShadow", "(IFFFI)V", (void*) android_view_GLES20Canvas_setupShadow },
- { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
- { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
- { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
-
- { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
- { "nDrawText", "(ILjava/lang/String;IIFFII)V",
+ { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
+ { "nDrawText", "(ILjava/lang/String;IIFFII)V",
(void*) android_view_GLES20Canvas_drawText },
+ { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
+ { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V",
+ (void*) android_view_GLES20Canvas_drawTextRun },
+
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 01ded68..1f66d05 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1284,6 +1284,9 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.PlatLogoActivity"
+ android:theme="@style/Theme.NoTitleBar.Fullscreen">
+ </activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
android:excludeFromRecents="true">
diff --git a/core/res/res/anim/animator_fade_in.xml b/core/res/res/anim/animator_fade_in.xml
new file mode 100644
index 0000000..cd5042f
--- /dev/null
+++ b/core/res/res/anim/animator_fade_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<property xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/animator_fade_out.xml b/core/res/res/anim/animator_fade_out.xml
new file mode 100644
index 0000000..dfb5d9c
--- /dev/null
+++ b/core/res/res/anim/animator_fade_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<property xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
new file mode 100644
index 0000000..d4091e8
--- /dev/null
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="-400"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
new file mode 100644
index 0000000..3e2cd22
--- /dev/null
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo=".5"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo=".5"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="400"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
new file mode 100644
index 0000000..c89001c
--- /dev/null
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="400"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
new file mode 100644
index 0000000..427fe4f
--- /dev/null
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="2"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="2"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="0"
+ android:valueTo="-400"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer>
\ No newline at end of file
diff --git a/core/res/res/anim/push_down_in_no_alpha.xml b/core/res/res/anim/push_down_in_no_alpha.xml
new file mode 100644
index 0000000..045d691
--- /dev/null
+++ b/core/res/res/anim/push_down_in_no_alpha.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromYDelta="-100%p" android:toYDelta="0"
+ android:duration="@android:integer/config_longAnimTime"/>
+</set>
diff --git a/core/res/res/anim/push_down_out_no_alpha.xml b/core/res/res/anim/push_down_out_no_alpha.xml
new file mode 100644
index 0000000..3c2474a
--- /dev/null
+++ b/core/res/res/anim/push_down_out_no_alpha.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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromYDelta="0" android:toYDelta="100%p"
+ android:duration="@android:integer/config_longAnimTime"/>
+</set>
diff --git a/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
new file mode 100644
index 0000000..97e6806
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png
index aad9ef7..911e1aa 100644
--- a/core/res/res/drawable-hdpi/btn_check_off.png
+++ b/core/res/res/drawable-hdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png
index eaee9e0..d72e2b9 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
index 6d2c293..d72e2b9 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png
new file mode 100644
index 0000000..240a044
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png
new file mode 100644
index 0000000..240a044
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_light.png b/core/res/res/drawable-hdpi/btn_check_off_light.png
new file mode 100644
index 0000000..4ca3c56
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
index 1c442e9..08f41812 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png
new file mode 100644
index 0000000..d3754dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png
index b852b2c..264f102 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png
new file mode 100644
index 0000000..48506bf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png
index cd5c181..5541c67 100644
--- a/core/res/res/drawable-hdpi/btn_check_on.png
+++ b/core/res/res/drawable-hdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png
index b4fc51a..7805458 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
index bf34647..7805458 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png
new file mode 100644
index 0000000..4e268d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png
new file mode 100644
index 0000000..4e268d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_light.png b/core/res/res/drawable-hdpi/btn_check_on_light.png
new file mode 100644
index 0000000..768c4af
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
index fa5c7a2..37e3953 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png
new file mode 100644
index 0000000..fc29e46
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png
index a6a21ad..a0beac4 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png
new file mode 100644
index 0000000..5df45c7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
new file mode 100644
index 0000000..45c5c6a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png
index c0b14aa..301c97d 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_light.png b/core/res/res/drawable-hdpi/btn_radio_off_light.png
new file mode 100644
index 0000000..657c8e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
index 3189581..5e6ef2b 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png
new file mode 100644
index 0000000..342bf11
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
index f337703..d11ae85 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png
new file mode 100644
index 0000000..68bd1df
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png
index c90d2eb..5b0dbe8 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_light.png b/core/res/res/drawable-hdpi/btn_radio_on_light.png
new file mode 100644
index 0000000..45ae36b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
index d79450b8..c3a0d48 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png
new file mode 100644
index 0000000..ca22358
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
index db50c43..6c05f47 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png
new file mode 100644
index 0000000..a17fa1e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cursor_controller.png b/core/res/res/drawable-hdpi/cursor_controller.png
deleted file mode 100644
index 720aded..0000000
--- a/core/res/res/drawable-hdpi/cursor_controller.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/selection_end_handle.png b/core/res/res/drawable-hdpi/selection_end_handle.png
deleted file mode 100644
index 624ab58..0000000
--- a/core/res/res/drawable-hdpi/selection_end_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/selection_start_handle.png b/core/res/res/drawable-hdpi/selection_start_handle.png
deleted file mode 100644
index 7d6f24c..0000000
--- a/core/res/res/drawable-hdpi/selection_start_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle.png b/core/res/res/drawable-hdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
new file mode 100644
index 0000000..79367b8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png
index 56d3861..5e44c29 100644
--- a/core/res/res/drawable-mdpi/btn_check_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png
index e012afd..a603fb1 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
index 0837bbd..a603fb1 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png
new file mode 100644
index 0000000..69e9ff9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png
new file mode 100644
index 0000000..69e9ff9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_light.png b/core/res/res/drawable-mdpi/btn_check_off_light.png
new file mode 100644
index 0000000..5b2ec92
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
index 984dfd7..611bb1d 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png
new file mode 100644
index 0000000..5a0ea441
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png
index 20842d4..aa28df2 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png
new file mode 100644
index 0000000..ade1136
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png
index 791ac1d..130d562 100644
--- a/core/res/res/drawable-mdpi/btn_check_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png
index 6cb02f3..f19972a 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
index 8a73b33..f19972a 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png
new file mode 100644
index 0000000..13ef46e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png
new file mode 100644
index 0000000..13ef46e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_light.png b/core/res/res/drawable-mdpi/btn_check_on_light.png
new file mode 100644
index 0000000..6b7808b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
index 300d64a..df753f5 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png
new file mode 100644
index 0000000..6a4dd2c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png
index 0b36adb..7586881 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png
new file mode 100644
index 0000000..24701ce
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
new file mode 100644
index 0000000..16e8939
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png
index 407632b..16c1c6b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_light.png b/core/res/res/drawable-mdpi/btn_radio_off_light.png
new file mode 100644
index 0000000..e8287f3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
index d6d8a9d..b25217b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png
new file mode 100644
index 0000000..b63b9b0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
index 53f3e87..bef7572 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png
new file mode 100644
index 0000000..af754e1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png
index 25a3ccc..4ed7471 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_light.png b/core/res/res/drawable-mdpi/btn_radio_on_light.png
new file mode 100644
index 0000000..62aaa41
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
index c904a35..7cf91c6 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png
new file mode 100644
index 0000000..0d93507
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
index 78e1fc0..56f6f5b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png
new file mode 100644
index 0000000..48dd8e9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cursor_controller.png b/core/res/res/drawable-mdpi/cursor_controller.png
deleted file mode 100644
index 1a8a459..0000000
--- a/core/res/res/drawable-mdpi/cursor_controller.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/selection_end_handle.png b/core/res/res/drawable-mdpi/selection_end_handle.png
deleted file mode 100644
index 7e075eb..0000000
--- a/core/res/res/drawable-mdpi/selection_end_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/selection_start_handle.png b/core/res/res/drawable-mdpi/selection_start_handle.png
deleted file mode 100644
index d8022f7..0000000
--- a/core/res/res/drawable-mdpi/selection_start_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle.png b/core/res/res/drawable-mdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
new file mode 100644
index 0000000..0e7780c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.jpg
Binary files differ
diff --git a/core/res/res/drawable-xlarge/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge/ic_lock_idle_alarm.png
new file mode 100644
index 0000000..336a820
--- /dev/null
+++ b/core/res/res/drawable-xlarge/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge/ic_lock_idle_charging.png
new file mode 100644
index 0000000..ebef531
--- /dev/null
+++ b/core/res/res/drawable-xlarge/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge/ic_lock_idle_lock.png
new file mode 100644
index 0000000..405e218
--- /dev/null
+++ b/core/res/res/drawable-xlarge/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge/ic_lock_idle_low_battery.png
new file mode 100644
index 0000000..f349b63
--- /dev/null
+++ b/core/res/res/drawable-xlarge/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_light.xml b/core/res/res/drawable/btn_check_light.xml
new file mode 100644
index 0000000..85f119a
--- /dev/null
+++ b/core/res/res/drawable/btn_check_light.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- Enabled states -->
+
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_light" />
+
+ <item android:state_checked="true" android:state_pressed="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_pressed_light" />
+ <item android:state_checked="false" android:state_pressed="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_pressed_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_selected_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_selected_light" />
+
+ <item android:state_checked="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_light" />
+ <item android:state_checked="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_light" />
+
+
+ <!-- Disabled states -->
+
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:drawable="@drawable/btn_check_on_disable_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:drawable="@drawable/btn_check_off_disable_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:drawable="@drawable/btn_check_on_disable_focused_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:drawable="@drawable/btn_check_off_disable_focused_light" />
+
+ <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_light" />
+ <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_light" />
+
+</selector>
diff --git a/core/res/res/drawable/btn_radio_light.xml b/core/res/res/drawable/btn_radio_light.xml
new file mode 100644
index 0000000..51c930b
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_light.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:drawable="@drawable/btn_radio_on_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:drawable="@drawable/btn_radio_off_light" />
+
+ <item android:state_checked="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_radio_on_pressed_light" />
+ <item android:state_checked="false" android:state_pressed="true"
+ android:drawable="@drawable/btn_radio_off_pressed_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:drawable="@drawable/btn_radio_on_selected_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:drawable="@drawable/btn_radio_off_selected_light" />
+
+ <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_light" />
+ <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_light" />
+</selector>
diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-xlarge/keyguard.xml
new file mode 100644
index 0000000..ca629f8
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/keyguard.xml
+**
+** Copyright 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingLeft="20dip"
+ android:paddingTop="20dip"
+ android:paddingRight="20dip"
+ android:paddingBottom="20dip"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="#ff000000">
+
+ <TextView
+ android:id="@+id/label"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:textColor="#FFFFFFFF"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/keyguard_label_text" />
+</LinearLayout>
+
diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
new file mode 100644
index 0000000..8a46546
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
@@ -0,0 +1,126 @@
+<?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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/background_dark"
+ >
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:layout_above="@+id/emergencyCall">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <TextView
+ android:id="@+id/topHeader"
+ android:layout_width="match_parent"
+ android:layout_height="64dip"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="4dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:drawableLeft="@drawable/ic_lock_idle_lock"
+ android:drawablePadding="5dip"
+ />
+
+ <!-- spacer below header -->
+ <View
+ android:id="@+id/spacerTop"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_below="@id/topHeader"
+ android:background="@drawable/divider_horizontal_dark"/>
+
+ <TextView
+ android:id="@+id/instructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/spacerTop"
+ android:layout_marginTop="8dip"
+ android:layout_marginLeft="9dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@android:string/lockscreen_glogin_instructions"
+ />
+
+ <EditText
+ android:id="@+id/login"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/instructions"
+ android:layout_marginTop="8dip"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:hint="@android:string/lockscreen_glogin_username_hint"
+ android:inputType="textEmailAddress"
+ />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/login"
+ android:layout_marginTop="15dip"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:inputType="textPassword"
+ android:hint="@android:string/lockscreen_glogin_password_hint"
+ android:nextFocusRight="@+id/ok"
+ android:nextFocusDown="@+id/ok"
+ />
+
+ <!-- ok below password, aligned to right of screen -->
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="85dip"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/password"
+ android:layout_marginTop="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_alignParentRight="true"
+ android:text="@android:string/lockscreen_glogin_submit_button"
+ />
+
+ </RelativeLayout>
+ </ScrollView>
+
+ <!-- spacer above emergency call -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginBottom="4dip"
+
+ android:background="@drawable/divider_horizontal_dark"/>
+
+ <!-- emergency call button at bottom center -->
+ <Button
+ android:id="@+id/emergencyCall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@android:string/lockscreen_emergency_call"
+ />
+
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-xlarge/keyguard_screen_lock.xml
new file mode 100644
index 0000000..733a350
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_lock.xml
@@ -0,0 +1,220 @@
+<?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.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+ state of the device, as well as instructions on how to get past it
+ depending on the state of the device. It is the same for landscape
+ and portrait.-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ff800000"
+ >
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="15dip"
+ android:layout_marginLeft="15dip"
+ android:layout_marginRight="15dip"
+ android:paddingTop="20dip"
+ android:paddingBottom="20dip"
+ android:background="@android:drawable/popup_full_dark"
+ >
+
+ <!-- when sim is present -->
+ <TextView android:id="@+id/headerSimOk1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="34sp"/>
+ <TextView android:id="@+id/headerSimOk2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="34sp"/>
+
+ <!-- when sim is missing / locked -->
+ <TextView android:id="@+id/headerSimBad1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@android:string/lockscreen_missing_sim_message"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+ <TextView android:id="@+id/headerSimBad2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="7dip"
+ android:layout_marginBottom="7dip"
+ android:gravity="center"
+ android:text="@android:string/lockscreen_missing_sim_instructions"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ <!-- spacer after carrier info / sim messages -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginTop="8dip"
+ android:background="@android:drawable/divider_horizontal_dark"/>
+
+ <!-- time and date -->
+ <TextView android:id="@+id/time"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="34sp"/>
+
+ <TextView android:id="@+id/date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="18sp"/>
+
+ <!-- spacer after time and date -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginBottom="8dip"
+ android:background="@android:drawable/divider_horizontal_dark"
+ />
+
+ <!-- battery info -->
+ <LinearLayout android:id="@+id/batteryInfo"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+
+ <ImageView android:id="@+id/batteryInfoIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+ android:baselineAligned="true"
+ android:gravity="center"
+ />
+
+ <TextView android:id="@+id/batteryInfoText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:gravity="center"
+ />
+
+ </LinearLayout>
+
+ <!-- spacer after battery info -->
+ <View android:id="@+id/batteryInfoSpacer"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
+ android:background="@android:drawable/divider_horizontal_dark"
+ />
+
+ <!-- next alarm info -->
+
+ <LinearLayout android:id="@+id/nextAlarmInfo"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+ android:baselineAligned="true"
+ android:src="@android:drawable/ic_lock_idle_alarm"
+ android:gravity="center"
+ />
+
+ <TextView android:id="@+id/nextAlarmText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:gravity="center"
+ />
+ </LinearLayout>
+
+ <!-- spacer after alarm info -->
+ <View android:id="@+id/nextAlarmSpacer"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip"
+ android:background="@android:drawable/divider_horizontal_dark"/>
+
+ <!-- lock icon with 'screen locked' message
+ (shown when SIM card is present) -->
+ <LinearLayout android:id="@+id/screenLockedInfo"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+ android:baselineAligned="true"
+ android:src="@android:drawable/ic_lock_idle_lock"
+ android:gravity="center"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:text="@android:string/lockscreen_screen_locked"
+ android:gravity="center"
+ />
+ </LinearLayout>
+
+ <!-- message about how to unlock
+ (shown when SIM card is present) -->
+ <TextView android:id="@+id/lockInstructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="5dip"
+ android:gravity="center"
+ android:textSize="14sp"/>
+
+
+ <!-- emergency call button shown when sim is missing or PUKd -->
+ <Button
+ android:id="@+id/emergencyCallButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="5dip"
+ android:layout_marginTop="5dip"
+ android:layout_gravity="center_horizontal"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@android:string/lockscreen_emergency_call"
+ />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
new file mode 100644
index 0000000..c2d87a2
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
@@ -0,0 +1,90 @@
+<?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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+ <include layout="@layout/keyguard_screen_status_land" />
+ </LinearLayout>
+
+ <!-- right side: password -->
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <!-- Password entry field -->
+ <EditText android:id="@+id/passwordEntry"
+ android:layout_height="wrap_content"
+ android:layout_width="330dip"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:textSize="24sp"
+ android:layout_marginTop="120dip"
+ android:layout_marginBottom="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@drawable/password_field_default"
+ android:textColor="#ffffffff"
+ />
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="330dip"
+ android:layout_height="260dip"
+ android:background="#00000000"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ />
+ <!-- Alphanumeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+ android:layout_width="450dip"
+ android:layout_height="230dip"
+ android:background="#00000000"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ />
+
+ <!-- emergency call button -->
+ <Button
+ android:id="@+id/emergencyCall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@string/lockscreen_emergency_call"
+ android:visibility="gone"
+ style="@style/Widget.Button.Transparent"
+ />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
new file mode 100644
index 0000000..0927e59
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
@@ -0,0 +1,89 @@
+<?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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+ <include layout="@layout/keyguard_screen_status_land" />
+ </LinearLayout>
+
+ <!-- right side: password -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <!-- Password entry field -->
+ <EditText android:id="@+id/passwordEntry"
+ android:layout_height="wrap_content"
+ android:layout_width="330dip"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:textSize="24sp"
+ android:layout_marginTop="120dip"
+ android:layout_marginBottom="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@drawable/password_field_default"
+ android:textColor="#ffffffff"
+ />
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="330dip"
+ android:layout_height="260dip"
+ android:background="#00000000"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ />
+ <!-- Alphanumeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+ android:layout_width="450dip"
+ android:layout_height="230dip"
+ android:background="#00000000"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ />
+
+ <!-- emergency call button -->
+ <Button
+ android:id="@+id/emergencyCall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@string/lockscreen_emergency_call"
+ android:visibility="gone"
+ style="@style/Widget.Button.Transparent"
+ />
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
new file mode 100644
index 0000000..b8cbe51
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
@@ -0,0 +1,122 @@
+<?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.
+*/
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_dark"
+ >
+
+
+ <!-- right side -->
+ <!-- header text ('Enter Pin Code') -->
+ <TextView android:id="@+id/headerText"
+ android:layout_above="@+id/carrier"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="30dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="24sp"
+ />
+
+ <!-- Carrier info -->
+ <TextView android:id="@+id/carrier"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/pinDisplayGroup"
+ android:layout_marginTop="9dip"
+ android:gravity="left|bottom"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <!-- displays dots as user enters pin -->
+ <LinearLayout android:id="@+id/pinDisplayGroup"
+ android:orientation="horizontal"
+ android:layout_centerInParent="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:addStatesFromChildren="true"
+ android:gravity="center_vertical"
+ android:baselineAligned="false"
+ android:paddingRight="0dip"
+ android:layout_marginRight="30dip"
+ android:layout_marginLeft="30dip"
+ android:background="@android:drawable/edit_text"
+ >
+
+ <EditText android:id="@+id/pinDisplay"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:maxLines="1"
+ android:background="@null"
+ android:textSize="32sp"
+ android:inputType="textPassword"
+ />
+
+ <ImageButton android:id="@+id/backspace"
+ android:src="@android:drawable/ic_input_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginTop="2dip"
+ android:layout_marginRight="2dip"
+ android:layout_marginBottom="2dip"
+ android:gravity="center"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dip"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip">
+
+ <Button android:id="@+id/ok"
+ android:text="@android:string/ok"
+ android:layout_alignParentBottom="true"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginBottom="8dip"
+ android:layout_marginRight="8dip"
+ android:textSize="18sp"
+ />
+
+ <Button android:id="@+id/emergencyCall"
+ android:text="@android:string/lockscreen_emergency_call"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginBottom="8dip"
+ android:layout_marginLeft="8dip"
+ android:textSize="18sp"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ />
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
new file mode 100644
index 0000000..009148f
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
@@ -0,0 +1,119 @@
+<?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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/background_dark"
+ android:gravity="center_horizontal">
+
+ <LinearLayout android:id="@+id/topDisplayGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- header text ('Enter Pin Code') -->
+ <TextView android:id="@+id/headerText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <!-- Carrier info -->
+ <TextView android:id="@+id/carrier"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="9dip"
+ android:gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <!-- password entry -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginRight="6dip"
+ android:layout_marginLeft="6dip"
+ android:gravity="center_vertical"
+ android:background="@android:drawable/edit_text">
+
+ <!-- displays dots as user enters pin -->
+ <TextView android:id="@+id/pinDisplay"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:textStyle="bold"
+ android:inputType="textPassword"
+ />
+
+ <ImageButton android:id="@+id/backspace"
+ android:src="@android:drawable/ic_input_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="-3dip"
+ android:layout_marginBottom="-3dip"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <include
+ android:id="@+id/keyPad"
+ layout="@android:layout/twelve_key_entry"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/topDisplayGroup"
+ android:layout_marginTop="10dip"
+ />
+
+ <!-- spacer below keypad -->
+ <View
+ android:id="@+id/spacerBottom"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginTop="6dip"
+ android:layout_above="@id/emergencyCall"
+ android:background="@android:drawable/divider_horizontal_dark"
+ />
+
+ <!-- The emergency button should take the rest of the space and be centered vertically -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <!-- emergency call button -->
+ <Button
+ android:id="@+id/emergencyCall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@android:drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@android:string/lockscreen_emergency_call"
+ />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-xlarge/keyguard_screen_status_land.xml
new file mode 100644
index 0000000..8589862
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_status_land.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="140dip"
+ android:layout_marginTop="20dip"
+ android:gravity="left"
+ >
+
+ <TextView
+ android:id="@+id/carrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:drawablePadding="4dip"
+ android:layout_marginTop="32dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <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:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="120sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:shadowColor="#C0000000"
+ android:shadowDx="0"
+ android:shadowDy="0"
+ android:shadowRadius="3.0"
+ android:layout_marginBottom="6dip"
+ />
+
+
+ <TextView android:id="@+id/am_pm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="30sp"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:shadowColor="#C0000000"
+ android:shadowDx="0"
+ android:shadowDy="0"
+ android:shadowRadius="3.0"
+ />
+
+ </com.android.internal.widget.DigitalClock>
+
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/time"
+ android:layout_marginTop="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="32sp"
+ />
+
+ <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging
+ status. -->
+ <TextView
+ android:id="@+id/status2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="32sp"
+ android:layout_marginTop="50dip"
+ android:drawablePadding="4dip"
+ />
+ <TextView
+ android:id="@+id/status1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="15dip"
+ android:textSize="32sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <TextView
+ android:id="@+id/propertyOf"
+ android:lineSpacingExtra="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="22sp"
+ android:layout_marginTop="50dip"
+ android:singleLine="false"
+ android:visibility="invisible"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-xlarge/keyguard_screen_status_port.xml
new file mode 100644
index 0000000..8589862
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_status_port.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="140dip"
+ android:layout_marginTop="20dip"
+ android:gravity="left"
+ >
+
+ <TextView
+ android:id="@+id/carrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:drawablePadding="4dip"
+ android:layout_marginTop="32dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <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:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="120sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:shadowColor="#C0000000"
+ android:shadowDx="0"
+ android:shadowDy="0"
+ android:shadowRadius="3.0"
+ android:layout_marginBottom="6dip"
+ />
+
+
+ <TextView android:id="@+id/am_pm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplay"
+ android:layout_alignBaseline="@id/timeDisplay"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="30sp"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:shadowColor="#C0000000"
+ android:shadowDx="0"
+ android:shadowDy="0"
+ android:shadowRadius="3.0"
+ />
+
+ </com.android.internal.widget.DigitalClock>
+
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/time"
+ android:layout_marginTop="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="32sp"
+ />
+
+ <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging
+ status. -->
+ <TextView
+ android:id="@+id/status2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="32sp"
+ android:layout_marginTop="50dip"
+ android:drawablePadding="4dip"
+ />
+ <TextView
+ android:id="@+id/status1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="15dip"
+ android:textSize="32sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <TextView
+ android:id="@+id/propertyOf"
+ android:lineSpacingExtra="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="22sp"
+ android:layout_marginTop="50dip"
+ android:singleLine="false"
+ android:visibility="invisible"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
new file mode 100644
index 0000000..4761800
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+ state of the device, as well as instructions on how to get past it
+ depending on the state of the device. It is the same for landscape
+ and portrait.-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#70000000"
+ android:gravity="center_horizontal"
+ android:id="@+id/root">
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+ <include layout="@layout/keyguard_screen_status_land" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ >
+ <TextView
+ android:id="@+id/screenLocked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/status2"
+ android:layout_marginLeft="24dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginTop="12dip"
+ android:drawablePadding="4dip"
+ />
+
+ <com.android.internal.widget.SlidingTab
+ android:id="@+id/tab_selector"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="80dip"
+ />
+
+ <!-- "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_below="@id/carrier"
+ android:layout_alignParentRight="true"
+ android:layout_marginTop="0dip"
+ android:layout_marginRight="8dip"
+ android:text="@string/emergency_calls_only"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/white"
+ />
+
+ <!-- emergency call button shown when sim is PUKd and tab_selector is
+ hidden -->
+ <Button
+ android:id="@+id/emergencyCallButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:layout_centerInParent="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="80dip"
+ style="@style/Widget.Button.Transparent"
+ android:drawablePadding="8dip"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
+</RelativeLayout>
+
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
new file mode 100644
index 0000000..bb398f6
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+ state of the device, as well as instructions on how to get past it
+ depending on the state of the device.-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:background="#70000000"
+ android:id="@+id/root">
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+ <include layout="@layout/keyguard_screen_status_land" />
+ </LinearLayout>
+
+ <!-- right side -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal"
+ >
+ <TextView
+ android:id="@+id/screenLocked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/status2"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center"
+ android:layout_marginTop="12dip"
+ android:drawablePadding="4dip"
+ />
+
+ <com.android.internal.widget.SlidingTab
+ android:id="@+id/tab_selector"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="0dip"
+ android:layout_weight="1.0"
+ />
+
+ <!-- "emergency calls only" shown when sim is missing or PUKd -->
+ <TextView
+ android:id="@+id/emergencyCallText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="20dip"
+ android:text="@string/emergency_calls_only"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/white"
+ />
+
+ <!-- emergency call button shown when sim is PUKd and tab_selector is
+ hidden -->
+ <Button
+ android:id="@+id/emergencyCallButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableLeft="@drawable/ic_emergency"
+ style="@style/Widget.Button.Transparent"
+ android:drawablePadding="8dip"
+ android:layout_marginRight="80dip"
+ android:visibility="gone"
+ />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
new file mode 100644
index 0000000..42636ad
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
@@ -0,0 +1,106 @@
+<?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.
+*/
+-->
+
+<!-- This is the screen that shows the 9 circle unlock widget and instructs
+ the user how to unlock their device, or make an emergency call. This
+ is the portrait layout. -->
+
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+
+ <include layout="@layout/keyguard_screen_status_land" />
+
+ <!-- footer -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="140dip"
+ >
+
+ <!-- option 1: a single emergency call button -->
+ <RelativeLayout android:id="@+id/footerNormal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ >
+ <Button android:id="@+id/emergencyCallAlone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/lockscreen_emergency_call"
+ style="@style/Widget.Button.Transparent"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:visibility="gone"
+ />
+ </RelativeLayout>
+
+ <!-- option 2: an emergency call button, and a 'forgot pattern?' button -->
+ <LinearLayout android:id="@+id/footerForgotPattern"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ >
+ <Button android:id="@+id/forgotPattern"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.Button.Transparent"
+ />
+ <Button android:id="@+id/emergencyCallTogether"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lockscreen_emergency_call"
+ style="@style/Widget.Button.Transparent"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ </FrameLayout>
+ </LinearLayout>
+
+ <!-- right side: lock pattern -->
+ <LinearLayout
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ >
+ <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+ android:layout_width="350dip"
+ android:layout_height="350dip"
+ android:layout_marginTop="90dip"
+ android:layout_marginRight="90dip"
+ />
+ </LinearLayout>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
new file mode 100644
index 0000000..aeed79b
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
@@ -0,0 +1,102 @@
+<?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.
+*/
+-->
+
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <!-- left side: status and emergency call button -->
+ <LinearLayout
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+
+ <include layout="@layout/keyguard_screen_status_port" />
+
+ <!-- footer -->
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="140dip"
+ >
+
+ <!-- option 1: a single emergency call button -->
+ <RelativeLayout android:id="@+id/footerNormal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ >
+ <Button android:id="@+id/emergencyCallAlone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/lockscreen_emergency_call"
+ style="@style/Widget.Button.Transparent"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:visibility="gone"
+ />
+ </RelativeLayout>
+
+ <!-- option 2: an emergency call button, and a 'forgot pattern?' button -->
+ <LinearLayout android:id="@+id/footerForgotPattern"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ >
+ <Button android:id="@+id/forgotPattern"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.Button.Transparent"
+ />
+ <Button android:id="@+id/emergencyCallTogether"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lockscreen_emergency_call"
+ style="@style/Widget.Button.Transparent"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ </FrameLayout>
+ </LinearLayout>
+
+ <!-- right side: lock pattern -->
+ <LinearLayout
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:gravity="center"
+ >
+ <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+ android:layout_width="350dip"
+ android:layout_height="350dip"
+ android:layout_marginTop="50dip"
+ />
+ </LinearLayout>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+
diff --git a/core/res/res/layout-xlarge/screen_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml
index 41ce8e4..30a73184 100644
--- a/core/res/res/layout-xlarge/screen_action_bar.xml
+++ b/core/res/res/layout-xlarge/screen_action_bar.xml
@@ -24,8 +24,8 @@
<ViewAnimator android:id="@+id/action_bar_animator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:inAnimation="@anim/push_down_in"
- android:outAnimation="@anim/push_down_out">
+ android:inAnimation="@anim/push_down_in_no_alpha"
+ android:outAnimation="@anim/push_down_out_no_alpha">
<com.android.internal.widget.ActionBarView
android:id="@+id/action_bar"
android:layout_width="match_parent"
diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
new file mode 100644
index 0000000..cb35ac1
--- /dev/null
+++ b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <ViewAnimator android:id="@+id/action_bar_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:inAnimation="@anim/push_down_in_no_alpha"
+ android:outAnimation="@anim/push_down_out_no_alpha">
+ <com.android.internal.widget.ActionBarView
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle" />
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </ViewAnimator>
+ <ImageView android:src="?android:attr/windowContentOverlay"
+ android:scaleType="fitXY"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/action_bar_animator" />
+</RelativeLayout>
diff --git a/core/res/res/layout/action_mode_bar.xml b/core/res/res/layout/action_mode_bar.xml
index 8e2e69d..acf327e 100644
--- a/core/res/res/layout/action_mode_bar.xml
+++ b/core/res/res/layout/action_mode_bar.xml
@@ -19,4 +19,5 @@
<com.android.internal.widget.ActionBarContextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index ab675c7..3d52f71 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- "Enter PIN(Password) to unlock" -->
- <TextView android:id="@+id/enter_password_label"
+ <TextView android:id="@+id/status1"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 9ee8781..af8a3ef 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -24,7 +24,7 @@
android:gravity="center_horizontal">
<!-- "Enter PIN(Password) to unlock" -->
- <TextView android:id="@+id/enter_password_label"
+ <TextView android:id="@+id/status1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index d530e96..eeba18e 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -59,15 +59,28 @@
android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
-
- <Button android:id="@+id/next_button"
- android:layout_width="150dip"
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="5dip"
- android:layout_alignParentRight="true"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
- android:text="@string/next_button_label"
- />
+ android:layout_alignParentRight="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
</RelativeLayout>
</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index f39852b..3c44b8c 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -24,8 +24,8 @@
<ViewAnimator android:id="@+id/action_bar_animator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:inAnimation="@anim/push_down_in"
- android:outAnimation="@anim/push_down_out">
+ android:inAnimation="@anim/push_down_in_no_alpha"
+ android:outAnimation="@anim/push_down_out_no_alpha">
<com.android.internal.widget.ActionBarView
android:id="@+id/action_bar"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
new file mode 100644
index 0000000..c854c6f
--- /dev/null
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <ViewAnimator android:id="@+id/action_bar_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:inAnimation="@anim/push_down_in_no_alpha"
+ android:outAnimation="@anim/push_down_out_no_alpha">
+ <com.android.internal.widget.ActionBarView
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle" />
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </ViewAnimator>
+ <ImageView android:src="?android:attr/windowContentOverlay"
+ android:scaleType="fitXY"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/action_bar_animator" />
+ <LinearLayout android:id="@+id/lower_action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ style="?android:attr/windowActionBarStyle"
+ android:visibility="gone" />
+</RelativeLayout>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f7f4d69..9c51aeb 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Ukončit aplikaci"</string>
<string name="report" msgid="4060218260984795706">"Nahlásit"</string>
<string name="wait" msgid="7147118217226317732">"Počkat"</string>
- <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces<xliff:g id="PROCESS">%2$s</xliff:g>) porušila své samovynucované zásady StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své samovynucované zásady StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své vlastní vynucené zásady StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Tuto možnost vyberte, chcete-li přepnout na aplikaci."</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Přepnout mezi aplikacemi?"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Dotykem zahájíte konfiguraci"</string>
<string name="back_button_label" msgid="2300470004503343439">"Zpět"</string>
<string name="next_button_label" msgid="1080555104677992408">"Další"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Vysoké využití mobilních dat"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotykem zobrazíte další informace o využití mobilních dat"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Byl překročen limit mobilních dat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 2b67a2a..cf76818 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Tryk for at konfigurere"</string>
<string name="back_button_label" msgid="2300470004503343439">"Tilbage"</string>
<string name="next_button_label" msgid="1080555104677992408">"Næste"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Højt mobildataforbrug"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryk for oplysninger om brug af mobildata"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Grænsen for mobildata er overskredet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac42fd1..712e10e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Die Anwendung <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen ihre selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="smv_process" msgid="5120397012047462446">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> hat gegen seine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswahl zum Wechseln in die Anwendung"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswählen zum Wechseln in die Anwendung"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Anwendung wechseln?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Eine andere Anwendung wird bereits ausgeführt und muss vor dem Start einer neuen Anwendung beendet werden."</string>
<string name="old_app_action" msgid="493129172238566282">"Zu <xliff:g id="OLD_APP">%1$s</xliff:g> zurückkehren"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Zum Konfigurieren berühren"</string>
<string name="back_button_label" msgid="2300470004503343439">"Zurück"</string>
<string name="next_button_label" msgid="1080555104677992408">"Weiter"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hohe Mobildatennutzung"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Mobildatenlimit überschritten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c08e1da..9a6d9ee 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Αγγίξτε για να γίνει διαμόρφωση"</string>
<string name="back_button_label" msgid="2300470004503343439">"Πίσω"</string>
<string name="next_button_label" msgid="1080555104677992408">"Επόμενο"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Υψηλή χρήση δεδομένων κινητής τηλεφωνίας"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Αγγίξτε για να μάθετε περισσότερα σχετικά με τη χρήση δεδομένων κινητής τηλεφωνίας"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Ξεπεράστηκε το όριο δεδομένων κινητής τηλεφωνίας"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 26d329e..b720339 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
<string name="back_button_label" msgid="2300470004503343439">"Atrás"</string>
<string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Amplia utilización de datos móviles"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido "</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8568211..0aeaf2e 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Toca para iniciar la configuración."</string>
<string name="back_button_label" msgid="2300470004503343439">"Atrás"</string>
<string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Uso elevado datos móviles"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Más información sobre uso de datos"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Límite datos superado"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c2dd90b..8884ddc 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Forcer la fermeture"</string>
<string name="report" msgid="4060218260984795706">"Rapport"</string>
<string name="wait" msgid="7147118217226317732">"Attendre"</string>
- <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles du mode strict."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Sélectionner pour changer d\'application"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Passer d\'une application à l\'autre ?"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Toucher pour configurer"</string>
<string name="back_button_label" msgid="2300470004503343439">"Retour"</string>
<string name="next_button_label" msgid="1080555104677992408">"Suivant"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilisation élevée des données mobiles"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Touchez pour en savoir plus sur l\'utilisation des données mobiles"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Quota d\'utilisation des données mobiles dépassé"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7870ccf..0cf7f38 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Tocca per configurare"</string>
<string name="back_button_label" msgid="2300470004503343439">"Indietro"</string>
<string name="next_button_label" msgid="1080555104677992408">"Avanti"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilizzo dati cell. elevato"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tocca per informazioni sull\'utilizzo dati cell."</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Limite dati cell. superato"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index a0b7e0e..468b692 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"タップして設定する"</string>
<string name="back_button_label" msgid="2300470004503343439">"戻る"</string>
<string name="next_button_label" msgid="1080555104677992408">"次へ"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"モバイルデータの使用量が増えています"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"タップしてモバイルデータ利用の詳細を表示します"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"モバイルデータの制限を超えました"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index bffb034..cb8f31b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"구성하려면 터치하세요."</string>
<string name="back_button_label" msgid="2300470004503343439">"뒤로"</string>
<string name="next_button_label" msgid="1080555104677992408">"다음"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"높은 모바일 데이터 사용량"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"모바일 데이터 사용에 대해 자세히 알아보려면 터치하세요."</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"모바일 데이터 제한을 초과했습니다."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 052eb08..4485d27 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Trykk for å konfigurere"</string>
<string name="back_button_label" msgid="2300470004503343439">"Tilbake"</string>
<string name="next_button_label" msgid="1080555104677992408">"Neste"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Høy mobildatabruk"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Berør for å lese mer om bruk av mobildata"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Grensen for mobildatabruk er overskredet"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 10ebc4e..abd791a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Aanraken om te configureren"</string>
<string name="back_button_label" msgid="2300470004503343439">"Vorige"</string>
<string name="next_button_label" msgid="1080555104677992408">"Volgende"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hoog mobiel gegevensgebruik"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Raak aan voor meer informatie over mobiel gegevensgebruik"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Mobiele gegevenslimiet overschreden"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 07946c7..28ba755 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Dotknij, aby skonfigurować"</string>
<string name="back_button_label" msgid="2300470004503343439">"Wróć"</string>
<string name="next_button_label" msgid="1080555104677992408">"Dalej"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Wysoki poziom użycia danych"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotknij, aby zobaczyć statystyki przesyłu danych"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Przekroczono limit danych"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d44a80d..0cbc7c1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
<string name="back_button_label" msgid="2300470004503343439">"Anterior"</string>
<string name="next_button_label" msgid="1080555104677992408">"Seguinte"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilização elevada de dados móveis"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre a utilização de dados móveis"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados móveis excedido"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a6c49d8..4f229d7 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Toque para configurar"</string>
<string name="back_button_label" msgid="2300470004503343439">"Voltar"</string>
<string name="next_button_label" msgid="1080555104677992408">"Avançar"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Alto uso de dados do celular"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre uso de dados do celular"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados do celular excedido"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fcb7eb7..ff03e35 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Позволяет приложению отключать строку состояния или добавлять/удалять системные значки."</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению быть строкой состояния."</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению заменять строку состояния."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/сворачивать строку состояния"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"Позволяет приложению разворачивать или сворачивать строку состояния."</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"перехватывать исходящие вызовы"</string>
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"записывать аудио"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"Позволяет приложению получать доступ к пути аудиозаписи."</string>
<string name="permlab_camera" msgid="3616391919559751192">"снимать фото и видео"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры. Это дает приложению возможность в любое время получать изображения с объектива камеры."</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры в любое время."</string>
<string name="permlab_brick" msgid="8337817093326370537">"отключать телефон"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"Позволяет данному приложению отключить телефон навсегда. Это очень опасно."</string>
<string name="permlab_reboot" msgid="2898560872462638242">"принудительно перезагружать телефон"</string>
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (процесс <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string>
<string name="smv_process" msgid="5120397012047462446">"Процесс <xliff:g id="PROCESS">%1$s</xliff:g> нарушил собственную политику StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы переключиться в приложение"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы перейти к приложению"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Переключить приложения?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Выполняется другое приложение, которое должно быть остановлено прежде, чем запускать новое."</string>
<string name="old_app_action" msgid="493129172238566282">"Вернуться к приложению <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Нажмите для настройки"</string>
<string name="back_button_label" msgid="2300470004503343439">"Назад"</string>
<string name="next_button_label" msgid="1080555104677992408">"Далее"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Активная передача данных"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Нажмите, чтобы узнать больше о мобильной передаче данных"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Превышен лимит на мобильные данные"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8e623e8..4d76987 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -563,7 +563,7 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet är låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser upp SIM-kort…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett ditt lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har angett din PIN-kod fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till kommer du att uppmanas att låsa upp telefonen med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Tryck om du vill konfigurera"</string>
<string name="back_button_label" msgid="2300470004503343439">"Tillbaka"</string>
<string name="next_button_label" msgid="1080555104677992408">"Nästa"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hög mobildataanvändning"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryck om du vill veta mer om mobildataanvändning"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Gränsen för mobildata har överskridits"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b207db9..b462c61 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
<string name="report" msgid="4060218260984795706">"Rapor"</string>
<string name="wait" msgid="7147118217226317732">"Bekle"</string>
- <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
- <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
+ <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
+ <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Uygulama değiştirmeyi seçin"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Uygulamaların arasında geçiş yapılsın mı?"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"Yapılandırmak için dokunun"</string>
<string name="back_button_label" msgid="2300470004503343439">"Geri"</string>
<string name="next_button_label" msgid="1080555104677992408">"İleri"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"Yüksek düzeyde mobil veri kullanımı"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"Mobil veri kullanımı hakkında daha fazla bilgi edinmek için dokunun"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"Mobil veri limiti aşıldı"</string>
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index c5a53b2..e92ed11 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -24,5 +24,10 @@
interface. This name is in the ComponentName flattened format (package/class) -->
<string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.tablet.TabletStatusBarService</string>
<bool name="config_statusBarCanHide">false</bool>
+
+ <!-- Show sliding tab before lockscreen -->
+ <bool name="config_enableSlidingTabFirst">false</bool>
+ <!-- Enable lockscreen rotation -->
+ <bool name="config_enableLockScreenRotation">true</bool>
</resources>
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index b3fdf46..1a16da7 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -22,5 +22,12 @@
<!-- Height of the status bar -->
<dimen name="status_bar_icon_size">50dip</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+ <!-- Margin for permanent screen decorations at the bottom. -->
+ <dimen name="screen_margin_bottom">50dip</dimen>
+
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">0.35in</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">0.47in</dimen>
</resources>
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
index 40e423e..ff7df7c 100644
--- a/core/res/res/values-xlarge/styles.xml
+++ b/core/res/res/values-xlarge/styles.xml
@@ -19,7 +19,6 @@
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
</style>
<style name="TextAppearance.StatusBar.Ticker">
</style>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4d78b86..ff5d0e9 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允许应用程序访问录音路径。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中看到的图片。"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中的图片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用手机"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允许应用程序永久停用整个手机,这非常危险。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"强行重新启动手机"</string>
@@ -747,7 +747,7 @@
<string name="report" msgid="4060218260984795706">"报告"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
<string name="smv_application" msgid="295583804361236288">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策"</string>
+ <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"选择以切换到该应用程序"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"要切换应用程序吗?"</string>
@@ -871,6 +871,8 @@
<string name="tethered_notification_message" msgid="3067108323903048927">"触摸可进行配置"</string>
<string name="back_button_label" msgid="2300470004503343439">"上一步"</string>
<string name="next_button_label" msgid="1080555104677992408">"下一步"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"手机流量过多"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"轻触以了解有关手机流量详情"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"已超出手机数据上限"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 4ab9907..4d42a4e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"語音留言"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"連線發生問題或錯誤的 MMI 碼。"</string>
- <string name="mmiFdnError" msgid="5224398216385316471">"僅允許在固定撥號時使用此操作。"</string>
+ <string name="mmiFdnError" msgid="5224398216385316471">"僅限對固定撥號號碼執行此作業。"</string>
<string name="serviceEnabled" msgid="8147278346414714315">"服務已啟用。"</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"已啟用服務:"</string>
<string name="serviceDisabled" msgid="1937553226592516411">"服務已停用。"</string>
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"允許應用程式停用狀態列或新增、移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式成為狀態列。"</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式以狀態列顯示。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允許應用程式展開或收攏狀態列。"</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"攔截撥出電話"</string>
@@ -314,8 +314,8 @@
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"允許應用程式編輯全域音訊設定,例如音量與路由。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允許應用程式存取音訊錄製路徑。"</string>
- <string name="permlab_camera" msgid="3616391919559751192">"拍照和錄影"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影。此功能可讓應用程式隨時透過相機收集圖片。"</string>
+ <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影,此功能可讓應用程式隨時透過相機收集圖片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用電話"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允許應用程式永久停用手機。此項操作非常危險。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"強制重開機"</string>
@@ -535,7 +535,7 @@
<string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string>
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
<string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 即可解鎖"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 進行解鎖"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
@@ -563,8 +563,8 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡已鎖定。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"解鎖 SIM 卡中..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n" 請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再嘗試。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒內再試一次。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。再錯誤 <xliff:g id="NUMBER_1">%d</xliff:g> 次後,系統會要求使用 Google 登入來解鎖。"\n\n" 請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘記解鎖圖形?"</string>
@@ -746,12 +746,12 @@
<string name="force_close" msgid="3653416315450806396">"強制關閉"</string>
<string name="report" msgid="4060218260984795706">"回報"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
- <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行實施的 StrictMode 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行實施的 StrictMode 政策。"</string>
+ <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
+ <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取切換應用程式"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取以切換到該應用程式"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"切換應用程式?"</string>
- <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行,才能啟動新的應用程式。"</string>
+ <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行該應用程式,才能啟動新的應用程式。"</string>
<string name="old_app_action" msgid="493129172238566282">"返回 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="old_app_description" msgid="942967900237208466">"請勿啟動新的應用程式。"</string>
<string name="new_app_action" msgid="5472756926945440706">"啟動 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -862,15 +862,17 @@
<string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"採用預先共用金鑰的 L2TP/IPSec VPN"</string>
<string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"採用憑證的 L2TP/IPSec VPN"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
- <string name="no_file_chosen" msgid="6363648562170759465">"沒有選擇檔案"</string>
+ <string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
<string name="submit" msgid="1602335572089911941">"提交"</string>
<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="3146694234398202601">"數據連線或無線基地台已啟用"</string>
<string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
- <string name="back_button_label" msgid="2300470004503343439">"上一個"</string>
- <string name="next_button_label" msgid="1080555104677992408">"下一個"</string>
+ <string name="back_button_label" msgid="2300470004503343439">"返回"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"繼續"</string>
+ <!-- no translation found for skip_button_label (1275362299471631819) -->
+ <skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"高行動資料用量"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"已達行動資料上限"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 97c5822..4fb3039 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -46,6 +46,7 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
+
<!-- =========== -->
<!-- Text styles -->
@@ -95,6 +96,12 @@
<!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide -->
<attr name="textColorSearchUrl" format="reference|color" />
+
+ <!-- Color of highlighted text, when used in a light theme. @hide -->
+ <attr name="textColorHighlightInverse" format="reference|color" />
+ <!-- Color of link text (URLs), when used in a light theme. @hide -->
+ <attr name="textColorLinkInverse" format="reference|color" />
+
<!-- Search widget more corpus result item background. -->
<attr name="searchWidgetCorpusItemBackground" format="reference|color" />
@@ -248,9 +255,20 @@
in place of the usual title bar. -->
<attr name="windowActionBar" format="boolean" />
+ <!-- Flag indicating whether this window's Action Bar should overlay
+ application content. Does nothing if the window would not
+ have an Action Bar. -->
+ <attr name="windowActionBarOverlay" format="boolean" />
+
<!-- Reference to a style for the Action Bar -->
<attr name="windowActionBarStyle" format="reference" />
+ <!-- Size of the Action Bar, including the contextual
+ bar used to present Action Modes. -->
+ <attr name="windowActionBarSize" format="dimension" >
+ <enum name="wrap_content" value="0" />
+ </attr>
+
<!-- Flag indicating whether action modes should overlay window content
when there is not reserved space for their UI (such as an Action Bar). -->
<attr name="windowActionModeOverlay" format="boolean" />
@@ -999,6 +1017,8 @@
<attr name="windowActionBar" />
<attr name="windowActionBarStyle" />
<attr name="windowActionModeOverlay" />
+ <attr name="windowActionBarOverlay" />
+ <attr name="windowActionBarSize" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -1015,6 +1035,14 @@
<attr name="centerMedium" format="reference|color" />
</declare-styleable>
+ <!-- Fragment animation class attributes. -->
+ <declare-styleable name="FragmentAnimation">
+ <attr name="fragmentOpenEnterAnimation" format="reference" />
+ <attr name="fragmentOpenExitAnimation" format="reference" />
+ <attr name="fragmentCloseEnterAnimation" format="reference" />
+ <attr name="fragmentCloseExitAnimation" format="reference" />
+ </declare-styleable>
+
<!-- Window animation class attributes. -->
<declare-styleable name="WindowAnimation">
<!-- The animation used when a window is being added. -->
@@ -1522,6 +1550,28 @@
<attr name="isDefault" format="boolean" />
</declare-styleable>
+ <!-- This is the subtype of InputMethod. Subtype can describe locales (e.g. en_US, fr_FR...)
+ and modes (e.g. voice, keyboard...), and is used for IME switch. This subtype allows
+ the system to call the specified subtype of the IME directly. -->
+ <declare-styleable name="InputMethod_Subtype">
+ <!-- The name of the subtype. -->
+ <attr name="label" />
+ <!-- The icon of the subtype. -->
+ <attr name="icon" />
+ <!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...)
+ and will be passed to the IME when the framework calls the IME
+ with the subtype. This is also used by the framework to know the supported locales
+ of the IME. -->
+ <attr name="imeSubtypeLocale" format="string" />
+ <!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this
+ string will be passed to the IME when the framework calls the IME with the
+ subtype. -->
+ <attr name="imeSubtypeMode" format="string" />
+ <!-- The extra value of the subtype. This string can be any string and will be passed to
+ the IME when the framework calls the IME with the subtype. -->
+ <attr name="imeSubtypeExtraValue" format="string" />
+ </declare-styleable>
+
<!-- =============================== -->
<!-- Widget package class attributes -->
<!-- =============================== -->
@@ -1573,6 +1623,26 @@
<attr name="smoothScrollbar" format="boolean" />
<!-- A reference to an XML description of the adapter to attach to the list. -->
<attr name="adapter" format="reference" />
+ <!-- Defines the choice behavior for the view. By default, lists do not have
+ any choice behavior. By setting the choiceMode to singleChoice, the list
+ allows up to one item to be in a chosen state. By setting the choiceMode to
+ multipleChoice, the list allows any number of items to be chosen.
+ Finally, by setting the choiceMode to multipleChoiceModal the list allows
+ any number of items to be chosen in a special selection mode.
+ The application will supply a
+ {@link android.widget.AbsListView.MultiChoiceModeListener} using
+ {@link android.widget.AbsListView#setMultiChoiceModeListener} to control the
+ selection mode. This uses the {@link android.view.ActionMode} API. -->
+ <attr name="choiceMode">
+ <!-- Normal list that does not indicate choices. -->
+ <enum name="none" value="0" />
+ <!-- The list allows up to one choice. -->
+ <enum name="singleChoice" value="1" />
+ <!-- The list allows multiple choices. -->
+ <enum name="multipleChoice" value="2" />
+ <!-- The list allows multiple choices in a custom selection mode. -->
+ <enum name="multipleChoiceModal" value="3" />
+ </attr>
</declare-styleable>
<declare-styleable name="AbsSpinner">
<!-- Reference to an array resource that will populate the Spinner. For static content,
@@ -1779,20 +1849,6 @@
<!-- Height of the divider. Will use the intrinsic height of the divider if this
is not specified. -->
<attr name="dividerHeight" format="dimension" />
- <!-- Defines the choice behavior for the ListView. By default, lists do not have
- any choice behavior. By setting the choiceMode to singleChoice, the List
- allows up to one item to be in a chosen state. By setting the choiceMode to
- multipleChoice, the list allows any number of items to be chosen. -->
- <attr name="choiceMode">
- <!-- Normal list that does not indicate choices. -->
- <enum name="none" value="0" />
- <!-- The list allows up to one choice. -->
- <enum name="singleChoice" value="1" />
- <!-- The list allows multiple choices. -->
- <enum name="multipleChoice" value="2" />
- <!-- The list allows multiple choices in a custom selection mode. -->
- <enum name="multipleChoiceModal" value="3" />
- </attr>
<!-- When set to false, the ListView will not draw the divider after each header view.
The default value is true. -->
<attr name="headerDividersEnabled" format="boolean" />
@@ -3987,6 +4043,8 @@
<attr name="background" />
<!-- Specifies a layout for custom navigation. Overrides navigationMode. -->
<attr name="customNavigationLayout" format="reference" />
+ <!-- Specifies a fixed height. -->
+ <attr name="height" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b6af6b2..3a0c651 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,8 +18,8 @@
*/
-->
<resources>
- <drawable name="screen_background_light">#ffffffff</drawable>
- <drawable name="screen_background_dark">#ff000000</drawable>
+ <drawable name="screen_background_light">#ffefefef</drawable>
+ <drawable name="screen_background_dark">#ff101010</drawable>
<drawable name="status_bar_closed_default_background">#ff000000</drawable>
<drawable name="status_bar_opened_default_background">#ff000000</drawable>
<drawable name="search_bar_default_color">#ff000000</drawable>
@@ -36,24 +36,30 @@
<color name="white">#ffffffff</color>
<color name="black">#ff000000</color>
<color name="transparent">#00000000</color>
- <color name="background_dark">#ff000000</color>
- <color name="bright_foreground_dark">#ffffffff</color>
+ <color name="background_dark">#ff101010</color>
+ <color name="bright_foreground_dark">#ffefefef</color>
<color name="bright_foreground_dark_disabled">#80ffffff</color>
- <color name="bright_foreground_dark_inverse">#ff000000</color>
+ <color name="bright_foreground_dark_inverse">@android:color/background_dark</color>
<color name="dim_foreground_dark">#bebebe</color>
<color name="dim_foreground_dark_disabled">#80bebebe</color>
<color name="dim_foreground_dark_inverse">#323232</color>
<color name="dim_foreground_dark_inverse_disabled">#80323232</color>
<color name="hint_foreground_dark">#808080</color>
- <color name="background_light">#ffffffff</color>
- <color name="bright_foreground_light">#ff000000</color>
- <color name="bright_foreground_light_inverse">#ffffffff</color>
+ <color name="background_light">@android:color/bright_foreground_dark</color>
+ <color name="bright_foreground_light">@android:color/background_dark</color>
+ <color name="bright_foreground_light_inverse">@android:color/bright_foreground_dark</color>
<color name="bright_foreground_light_disabled">#80000000</color>
<color name="dim_foreground_light">#323232</color>
<color name="dim_foreground_light_disabled">#80323232</color>
<color name="dim_foreground_light_inverse">#bebebe</color>
<color name="dim_foreground_light_inverse_disabled">#80bebebe</color>
<color name="hint_foreground_light">#808080</color>
+ <color name="highlight_background">#cc475925</color>
+ <color name="highlight_background_inverse">#ccd2e461</color>
+ <color name="highlighted_text_dark">#cc475925</color>
+ <color name="highlighted_text_light">#ccd2e461</color>
+ <color name="link_text_dark">#5c5cff</color>
+ <color name="link_text_light">#0000ee</color>
<drawable name="stat_notify_sync_noanim">@drawable/stat_notify_sync_anim0</drawable>
<drawable name="stat_sys_download_done">@drawable/stat_sys_download_anim0</drawable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cf2b423..05cae9f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -269,9 +269,30 @@
<!-- Default LED off time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOff">2000</integer>
+ <!-- Default value for led color when battery is low on charge -->
+ <integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer>
+
+ <!-- Default value for led color when battery is medium charged -->
+ <integer name="config_notificationsBatteryMediumARGB">0xFFFFFF00</integer>
+
+ <!-- Default value for led color when battery is fully charged -->
+ <integer name="config_notificationsBatteryFullARGB">0xFF00FF00</integer>
+
+ <!-- Default value for LED on time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOn">125</integer>
+
+ <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOff">2875</integer>
+
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <!-- Show sliding tab before lockscreen -->
+ <bool name="config_enableSlidingTabFirst">true</bool>
+
+ <!-- Diable lockscreen rotation by default -->
+ <bool name="config_enableLockScreenRotation">false</bool>
+
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
The N entries of this array define N + 1 zones as follows:
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 679e642..d3aa2d4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -35,16 +35,18 @@
<dimen name="status_bar_icon_size">25dip</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
+ <!-- Margin for permanent screen decorations at the bottom. -->
+ <dimen name="screen_margin_bottom">0dip</dimen>
<!-- Size of the fastscroll hint letter -->
<dimen name="fastscroll_overlay_size">104dp</dimen>
<!-- Width of the fastscroll thumb -->
<dimen name="fastscroll_thumb_width">64dp</dimen>
<!-- Height of the fastscroll thumb -->
<dimen name="fastscroll_thumb_height">52dp</dimen>
- <!-- Default height of a key in the password keyboard -->
- <dimen name="password_keyboard_key_height">56dip</dimen>
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">56dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">56dip</dimen>
<!-- Default correction for the space key in the password keyboard -->
<dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
- <!-- Distance between the text base line and virtual finger position used to position cursor -->
- <dimen name="cursor_controller_vertical_offset">12dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 99263d7..6aca361 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1315,12 +1315,24 @@
<public type="attr" name="propertyName" />
<public type="attr" name="ordering" />
<public type="attr" name="fragment" />
+ <public type="attr" name="windowActionBarOverlay" />
+ <public type="attr" name="fragmentOpenEnterAnimation" />
+ <public type="attr" name="fragmentOpenExitAnimation" />
+ <public type="attr" name="fragmentCloseEnterAnimation" />
+ <public type="attr" name="fragmentCloseExitAnimation" />
+ <public type="attr" name="windowActionBarSize" />
+ <public type="attr" name="imeSubtypeLocale" />
+ <public type="attr" name="imeSubtypeMode" />
+ <public type="attr" name="imeSubtypeExtraValue" />
+
+ <public type="anim" name="animator_fade_in" />
+ <public type="anim" name="animator_fade_out" />
<public type="id" name="home" />
<!-- Context menu ID for the "Select text..." menu item to switch to text
selection context mode in text views. -->
<public type="id" name="selectTextMode" />
-
+
<public type="style" name="Theme.WithActionBar" />
<public type="style" name="Widget.Spinner.DropDown" />
<public type="style" name="Widget.ActionButton" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 00ebe2b..a107048 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -807,8 +807,8 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_writeCalendar">add or modify calendar events and send email to guests</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_writeCalendar">Allows an application to add or change the
- events on your calendar, which may send email to guests. Malicious applications can use this
+ <string name="permdesc_writeCalendar">Allows an application to add or change the
+ events on your calendar, which may send email to guests. Malicious applications can use this
to erase or modify your calendar events or to send email to guests.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1610,7 +1610,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.5 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.5</string>
+ AppleWebKit/534.6 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.6</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
@@ -1918,7 +1918,7 @@
<!-- Text shown by list fragment when waiting for data to display. -->
<string name="loading">Loading...</string>
-
+
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_on">ON</string>
<!-- Default text for a button that can be toggled on and off. -->
@@ -1969,23 +1969,23 @@
<!-- Notification text to tell the user that a heavy-weight application is running. -->
<string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
-
+
<!-- Notification details to tell the user that a heavy-weight application is running. -->
<string name="heavy_weight_notification_detail">Select to switch to application</string>
-
+
<!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. -->
<string name="heavy_weight_switcher_title">Switch applications?</string>
-
+
<!-- Descriptive text for switching to a new heavy-weight application. -->
<string name="heavy_weight_switcher_text">Another application is already running
that must be stopped before you can start a new one.</string>
-
+
<string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string>
<string name="old_app_description">Don\'t start the new application.</string>
-
+
<string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string>
<string name="new_app_description">Stop the old application without saving.</string>
-
+
<!-- Displayed in the title of the chooser for things to do with text that
is to be sent to another application. For example, I can send
text through SMS or IM. A dialog with those choices would be shown,
@@ -2278,6 +2278,9 @@
<string name="back_button_label">Back</string>
<string name="next_button_label">Next</string>
+ <!-- Optional button to Skip a PreferenceActivity [CHAR LIMIT=20] -->
+ <string name="skip_button_label">Skip</string>
+
<!-- Strings for throttling notification -->
<!-- Shown when the user is in danger of being throttled -->
<string name="throttle_warning_notification_title">High mobile data use</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 993048d..4294c22 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -74,6 +74,10 @@
<item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
<item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
<item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
+ <item name="fragmentOpenEnterAnimation">@anim/fragment_open_enter</item>
+ <item name="fragmentOpenExitAnimation">@anim/fragment_open_exit</item>
+ <item name="fragmentCloseEnterAnimation">@anim/fragment_close_enter</item>
+ <item name="fragmentCloseExitAnimation">@anim/fragment_close_exit</item>
</style>
<!-- Standard animations for a non-full-screen window or activity. -->
@@ -279,11 +283,21 @@
<item name="android:button">@android:drawable/btn_check</item>
</style>
+ <style name="Widget.CompoundButton.CheckBox.Inverse">
+ <item name="android:background">@android:drawable/btn_check_label_background_light</item>
+ <item name="android:button">@android:drawable/btn_check_light</item>
+ </style>
+
<style name="Widget.CompoundButton.RadioButton">
<item name="android:background">@android:drawable/btn_radio_label_background</item>
<item name="android:button">@android:drawable/btn_radio</item>
</style>
+ <style name="Widget.CompoundButton.RadioButton.Inverse">
+ <item name="android:background">@android:drawable/btn_radio_label_background_light</item>
+ <item name="android:button">@android:drawable/btn_radio_light</item>
+ </style>
+
<style name="Widget.CompoundButton.Star">
<item name="android:background">@android:drawable/btn_star_label_background</item>
<item name="android:button">@android:drawable/btn_star</item>
@@ -626,9 +640,9 @@
<style name="TextAppearance">
<item name="android:textColor">?textColorPrimary</item>
- <item name="android:textColorHighlight">#D077A14B</item>
+ <item name="android:textColorHighlight">?textColorHighlight</item>
<item name="android:textColorHint">?textColorHint</item>
- <item name="android:textColorLink">#5C5CFF</item>
+ <item name="android:textColorLink">?textColorLink</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">normal</item>
</style>
@@ -636,7 +650,8 @@
<style name="TextAppearance.Inverse">
<item name="textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
- <item name="android:textColorLink">#0000EE</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Theme">
@@ -657,6 +672,8 @@
<style name="TextAppearance.Large.Inverse">
<item name="android:textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Medium">
@@ -668,6 +685,8 @@
<style name="TextAppearance.Medium.Inverse">
<item name="android:textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Small">
@@ -679,6 +698,8 @@
<style name="TextAppearance.Small.Inverse">
<item name="android:textColor">?textColorSecondaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme">
@@ -879,6 +900,11 @@
<item name="android:background">@android:drawable/action_bar_background</item>
<item name="android:displayOptions">useLogo</item>
<item name="android:divider">@android:drawable/action_bar_divider</item>
+ <item name="android:height">?android:attr/windowActionBarSize</item>
+ <item name="android:paddingLeft">3dip</item>
+ <item name="android:paddingTop">3dip</item>
+ <item name="android:paddingRight">3dip</item>
+ <item name="android:paddingBottom">3dip</item>
</style>
<style name="Widget.ActionButton">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 739912a1a..348a7de51 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -53,6 +53,10 @@
<item name="textColorHint">@android:color/hint_foreground_dark</item>
<item name="textColorHintInverse">@android:color/hint_foreground_light</item>
<item name="textColorSearchUrl">@android:color/search_url_text</item>
+ <item name="textColorHighlight">@android:color/highlighted_text_dark</item>
+ <item name="textColorHighlightInverse">@android:color/highlighted_text_light</item>
+ <item name="textColorLink">@android:color/link_text_dark</item>
+ <item name="textColorLinkInverse">@android:color/link_text_light</item>
<item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
<item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
@@ -84,9 +88,9 @@
<item name="searchResultListItemHeight">58dip</item>
<item name="listDivider">@drawable/divider_horizontal_dark</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item>
-
- <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
- <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
+
+ <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
+ <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
<item name="expandableListPreferredItemPaddingLeft">40dip</item>
<item name="expandableListPreferredChildPaddingLeft">
@@ -118,6 +122,7 @@
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">false</item>
<item name="windowActionBarStyle">@android:style/ActionBar</item>
+ <item name="windowActionBarSize">50dip</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog</item>
@@ -160,8 +165,8 @@
<item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
<item name="seekBarStyle">@android:style/Widget.SeekBar</item>
<item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
<item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -232,7 +237,7 @@
<item name="colorBackground">@android:color/background_light</item>
<item name="colorForeground">@color/bright_foreground_light</item>
<item name="colorForegroundInverse">@android:color/bright_foreground_light_inverse</item>
-
+
<item name="textColorPrimary">@android:color/primary_text_light</item>
<item name="textColorSecondary">@android:color/secondary_text_light</item>
<item name="textColorTertiary">@android:color/tertiary_text_light</item>
@@ -246,25 +251,35 @@
<item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_dark_nodisable</item>
<item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_dark_nodisable</item>
<item name="textColorHint">@android:color/hint_foreground_light</item>
- <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>
+ <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>
+ <item name="textColorHighlight">@android:color/highlighted_text_light</item>
+ <item name="textColorHighlightInverse">@android:color/highlighted_text_dark</item>
+ <item name="textColorLink">@android:color/link_text_light</item>
+ <item name="textColorLinkInverse">@android:color/link_text_dark</item>
<item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
<item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
<item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
+ <!-- List attributes -->
+ <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_light</item>
+ <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_light</item>
+
+ <!-- Widget styles -->
+ <item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox.Inverse</item>
<item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item>
<item name="expandableListViewStyle">@android:style/Widget.ExpandableListView.White</item>
<item name="listViewStyle">@android:style/Widget.ListView.White</item>
<item name="listDivider">@drawable/divider_horizontal_bright</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
-
<item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
- <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
- <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
- <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
+ <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+ <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
+ <item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton.Inverse</item>
</style>
<!-- Variant of the light theme with no title bar -->
diff --git a/core/res/res/xml-land/password_kbd_qwerty.xml b/core/res/res/xml-land/password_kbd_qwerty.xml
index 700c527..fd8bd49 100755
--- a/core/res/res/xml-land/password_kbd_qwerty.xml
+++ b/core/res/res/xml-land/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row>
diff --git a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
index 1e37b6c..9ff6fd7 100755
--- a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row>
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty.xml b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
index bae1b42..82a7c75 100755
--- a/core/res/res/xml-mdpi/password_kbd_qwerty.xml
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row>
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
index 612df9c..9fff3cc 100755
--- a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row>
diff --git a/core/res/res/xml-xlarge/password_kbd_qwerty.xml b/core/res/res/xml-xlarge/password_kbd_qwerty.xml
new file mode 100755
index 0000000..0a35040
--- /dev/null
+++ b/core/res/res/xml-xlarge/password_kbd_qwerty.xml
@@ -0,0 +1,102 @@
+<?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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
+ >
+
+ <Row android:rowEdgeFlags="top">
+ <Key android:keyLabel="1" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="2"/>
+ <Key android:keyLabel="3"/>
+ <Key android:keyLabel="4"/>
+ <Key android:keyLabel="5"/>
+ <Key android:keyLabel="6"/>
+ <Key android:keyLabel="7"/>
+ <Key android:keyLabel="8"/>
+ <Key android:keyLabel="9"/>
+ <Key android:keyLabel="0" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="w"/>
+ <Key android:keyLabel="e"/>
+ <Key android:keyLabel="r"/>
+ <Key android:keyLabel="t"/>
+ <Key android:keyLabel="y"/>
+ <Key android:keyLabel="u"/>
+ <Key android:keyLabel="i"/>
+ <Key android:keyLabel="o"/>
+ <Key android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="a" android:horizontalGap="5%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="s"/>
+ <Key android:keyLabel="d"/>
+ <Key android:keyLabel="f"/>
+ <Key android:keyLabel="g"/>
+ <Key android:keyLabel="h"/>
+ <Key android:keyLabel="j"/>
+ <Key android:keyLabel="k"/>
+ <Key android:keyLabel="l" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="z"/>
+ <Key android:keyLabel="x"/>
+ <Key android:keyLabel="c"/>
+ <Key android:keyLabel="v"/>
+ <Key android:keyLabel="b"/>
+ <Key android:keyLabel="n"/>
+ <Key android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," />
+ <Key android:keyLabel="-" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="=" />
+ <Key android:keyLabel="."
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+ android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml b/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml
new file mode 100755
index 0000000..9e9db81
--- /dev/null
+++ b/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml
@@ -0,0 +1,101 @@
+<?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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
+ >
+
+ <Row android:rowEdgeFlags="top">
+ <Key android:keyLabel="\@" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\#"/>
+ <Key android:keyLabel="$"/>
+ <Key android:keyLabel="%"/>
+ <Key android:keyLabel="&"/>
+ <Key android:keyLabel="*"/>
+ <Key android:keyLabel="-"/>
+ <Key android:keyLabel="+"/>
+ <Key android:keyLabel="("/>
+ <Key android:keyLabel=")" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="w"/>
+ <Key android:keyLabel="e"/>
+ <Key android:keyLabel="r"/>
+ <Key android:keyLabel="t"/>
+ <Key android:keyLabel="y"/>
+ <Key android:keyLabel="u"/>
+ <Key android:keyLabel="i"/>
+ <Key android:keyLabel="o"/>
+ <Key android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="a" android:horizontalGap="5%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="s"/>
+ <Key android:keyLabel="d"/>
+ <Key android:keyLabel="f"/>
+ <Key android:keyLabel="g"/>
+ <Key android:keyLabel="h"/>
+ <Key android:keyLabel="j"/>
+ <Key android:keyLabel="k"/>
+ <Key android:keyLabel="l" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="z"/>
+ <Key android:keyLabel="x"/>
+ <Key android:keyLabel="c"/>
+ <Key android:keyLabel="v"/>
+ <Key android:keyLabel="b"/>
+ <Key android:keyLabel="n"/>
+ <Key android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="," />
+ <Key android:keyLabel="_" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="+" />
+ <Key android:keyLabel="."/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+ android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml/password_kbd_extension.xml b/core/res/res/xml/password_kbd_extension.xml
index 28b7efe..f3fa57b 100755
--- a/core/res/res/xml/password_kbd_extension.xml
+++ b/core/res/res/xml/password_kbd_extension.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_numeric.xml b/core/res/res/xml/password_kbd_numeric.xml
index bdd8afb..2270b8a 100755
--- a/core/res/res/xml/password_kbd_numeric.xml
+++ b/core/res/res/xml/password_kbd_numeric.xml
@@ -19,9 +19,8 @@
-->
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:keyWidth="33.33%p"
- android:horizontalGap="2px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_numeric"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_popup_template.xml b/core/res/res/xml/password_kbd_popup_template.xml
index 5ddfd3e..9b853e2 100644
--- a/core/res/res/xml/password_kbd_popup_template.xml
+++ b/core/res/res/xml/password_kbd_popup_template.xml
@@ -22,6 +22,6 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
</Keyboard>
diff --git a/core/res/res/xml/password_kbd_qwerty.xml b/core/res/res/xml/password_kbd_qwerty.xml
index 5fa9b8a..0a35040 100755
--- a/core/res/res/xml/password_kbd_qwerty.xml
+++ b/core/res/res/xml/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_qwerty_shifted.xml b/core/res/res/xml/password_kbd_qwerty_shifted.xml
index e491aff..9e9db81 100755
--- a/core/res/res/xml/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_symbols.xml b/core/res/res/xml/password_kbd_symbols.xml
index 9901526..9a94930 100755
--- a/core/res/res/xml/password_kbd_symbols.xml
+++ b/core/res/res/xml/password_kbd_symbols.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_symbols_shift.xml b/core/res/res/xml/password_kbd_symbols_shift.xml
index 5b73914..a972eb2 100755
--- a/core/res/res/xml/password_kbd_symbols_shift.xml
+++ b/core/res/res/xml/password_kbd_symbols_shift.xml
@@ -22,7 +22,7 @@
android:keyWidth="10%p"
android:horizontalGap="0px"
android:verticalGap="0px"
- android:keyHeight="@dimen/password_keyboard_key_height"
+ android:keyHeight="@dimen/password_keyboard_key_height_alpha"
>
<Row android:rowEdgeFlags="top">
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 0fe83e1..cbd8714 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -17,13 +17,11 @@
package android.bluetooth;
import android.app.Instrumentation;
-import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
public class BluetoothStressTest extends InstrumentationTestCase {
@@ -161,7 +159,6 @@
mContext.unregisterReceiver(mReceiver);
}
- @LargeTest
public void testEnableDisable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -172,7 +169,6 @@
}
}
- @LargeTest
public void testDiscoverable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -186,7 +182,6 @@
disable(adapter);
}
- @LargeTest
public void testScan() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -336,7 +331,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("undiscoverable(): bluetooth not enabled");
+ fail("undiscoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
@@ -374,7 +369,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("startScan(): bluetooth not enabled");
+ fail("startScan() bluetooth not enabled");
}
if (adapter.isDiscovering()) {
@@ -404,7 +399,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("stopScan(): bluetooth not enabled");
+ fail("stopScan() bluetooth not enabled");
}
if (!adapter.isDiscovering()) {
diff --git a/core/tests/coretests/src/android/content/MemoryFileProvider.java b/core/tests/coretests/src/android/content/MemoryFileProvider.java
index c4bc767..73530d7 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProvider.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProvider.java
@@ -16,16 +16,11 @@
package android.content;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.UriMatcher;
-import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
-import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -134,64 +129,34 @@
}
@Override
- public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException {
+ public ParcelFileDescriptor openFile(Uri url, String mode) throws FileNotFoundException {
int match = sURLMatcher.match(url);
switch (match) {
case DATA_ID_BLOB:
String sql = "SELECT _blob FROM data WHERE _id=" + url.getPathSegments().get(1);
- return getBlobColumnAsAssetFile(url, mode, sql);
+ return getBlobColumnAsFile(url, mode, sql);
case HUGE:
try {
- MemoryFile memoryFile = new MemoryFile(null, 5000000);
- memoryFile.writeBytes(TEST_BLOB, 0, 1000000, TEST_BLOB.length);
- memoryFile.deactivate();
- return AssetFileDescriptor.fromMemoryFile(memoryFile);
+ return ParcelFileDescriptor.fromData(TEST_BLOB, null);
} catch (IOException ex) {
throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
}
case FILE:
File file = getContext().getFileStreamPath(DATA_FILE);
- ParcelFileDescriptor fd =
- ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- return new AssetFileDescriptor(fd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
default:
throw new FileNotFoundException("No files supported by provider at " + url);
}
}
- private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql)
+ private ParcelFileDescriptor getBlobColumnAsFile(Uri url, String mode, String sql)
throws FileNotFoundException {
if (!"r".equals(mode)) {
throw new FileNotFoundException("Mode " + mode + " not supported for " + url);
}
- try {
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- MemoryFile file = simpleQueryForBlobMemoryFile(db, sql);
- if (file == null) throw new FileNotFoundException("No such entry: " + url);
- AssetFileDescriptor afd = AssetFileDescriptor.fromMemoryFile(file);
- file.deactivate();
- // need to dup and then close? openFileHelper() doesn't do that though
- return afd;
- } catch (IOException ex) {
- throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
- }
- }
- private MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql) throws IOException {
- Cursor cursor = db.rawQuery(sql, null);
- try {
- if (!cursor.moveToFirst()) {
- return null;
- }
- byte[] bytes = cursor.getBlob(0);
- MemoryFile file = new MemoryFile(null, bytes.length);
- file.writeBytes(bytes, 0, 0, bytes.length);
- return file;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ return DatabaseUtils.blobFileDescriptorForQuery(db, sql, null);
}
@Override
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
index 62b4e7e..bbe7c10 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
@@ -56,7 +56,6 @@
Uri uri = Uri.parse("content://android.content.MemoryFileProvider/huge");
InputStream in = resolver.openInputStream(uri);
assertNotNull("Failed to open stream number " + i, in);
- assertEquals(1000000, in.skip(1000000));
byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
int count = in.read(buf);
assertEquals(buf.length, count);
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 009a0e2..e627bb4 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -237,51 +237,6 @@
}
}
- @SmallTest
- public void testIsMemoryFile() throws Exception {
- MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
- FileDescriptor fd = file.getFileDescriptor();
- assertNotNull(fd);
- assertTrue(fd.valid());
- assertTrue(MemoryFile.isMemoryFile(fd));
- file.close();
-
- assertFalse(MemoryFile.isMemoryFile(FileDescriptor.in));
- assertFalse(MemoryFile.isMemoryFile(FileDescriptor.out));
- assertFalse(MemoryFile.isMemoryFile(FileDescriptor.err));
-
- File tempFile = File.createTempFile("MemoryFileTest",".tmp", getContext().getFilesDir());
- assertNotNull(file);
- FileOutputStream out = null;
- try {
- out = new FileOutputStream(tempFile);
- FileDescriptor fileFd = out.getFD();
- assertNotNull(fileFd);
- assertFalse(MemoryFile.isMemoryFile(fileFd));
- } finally {
- if (out != null) {
- out.close();
- }
- tempFile.delete();
- }
- }
-
- @SmallTest
- public void testFileDescriptor() throws Exception {
- MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
- MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r");
- byte[] buffer;
-
- // write to original, read from reference
- file.writeBytes(testString, 0, 2000, testString.length);
- buffer = new byte[testString.length];
- ref.readBytes(buffer, 2000, 0, testString.length);
- compareBuffers(testString, buffer, testString.length);
-
- file.close();
- ref.close(); // Doesn't actually do anything, since the file descriptor is not dup(2):ed
- }
-
private static final byte[] testString = new byte[] {
3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4,
0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2,
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
index 6d59648..599c4ae 100644
--- a/docs/html/resources/community-groups.jd
+++ b/docs/html/resources/community-groups.jd
@@ -87,27 +87,27 @@
<h3 id="ApplicationDeveloperLists">Application developer mailing lists</h3>
<ul>
<li><strong><a href="http://groups.google.com/group/android-developers">android-developers</a></strong>
-(<a href="mailto:android-developers-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-developers/subscribe">subscribe</a>)<br>
You're now an experienced Android application developer. You've grasped the basics of Android app development, you're comfortable using the SDK, now you want to move to advanced topics. Get help here with troubleshooting applications, advice on implementation, and strategies for improving your application's performance and user experience. This is the not the right place to discuss user issues (use android-discuss for that) or beginner questions with the Android SDK (use android-beginners for that).
</li>
<li><strong><a href="http://groups.google.com/group/android-discuss">android-discuss</a></strong>
-(<a href="mailto:android-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-discuss/subscribe">subscribe</a>)<br>
The "water cooler" of Android discussion. You can discuss just about anything Android-related here, ideas for the Android platform, announcements about your applications, discussions about Android devices, community resources... As long as your discussion is related to Android, it's on-topic here. However, if you have a discussion here that could belong on another list, you are probably not reaching all of your target audience here and may want to consider shifting to a more targeted list.
</li>
<li><strong><a href="http://groups.google.com/group/android-ndk">android-ndk</a></strong>
-(<a href="mailto:android-ndk-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-ndk/subscribe">subscribe</a>)<br>
A place for discussing the Android NDK and topics related to using native code in Android applications.
</li>
<li><strong><a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a></strong>
-(<a href="mailto:android-security-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-security-discuss/subscribe">subscribe</a>)<br>
A place for open discussion on secure development, emerging security concerns, and best practices for and by android developers. Please don't disclose vulnerabilities directly on this list, you'd be putting all Android users at risk.
</li>
<li><strong><a href="http://groups.google.com/group/android-security-announce">android-security-announce</a></strong>
-(<a href="mailto:android-security-announce-subscribe@googlegroups.com">subscribe via email</a>)<br>
+(<a href="http://groups.google.com/group/android-security-announce/subscribe">subscribe</a>)<br>
A low-volume group for security-related announcements by the Android Security Team.
</li>
</ul>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 320fc4d..d2845cf 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,7 +18,6 @@
import android.content.res.AssetManager;
import android.content.res.Resources;
-import android.os.MemoryFile;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -27,6 +26,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.FileNotFoundException;
/**
* Creates Bitmap objects from various sources, including files, streams,
@@ -531,18 +531,6 @@
* @return the decoded bitmap, or null
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
- try {
- if (MemoryFile.isMemoryFile(fd)) {
- int mappedlength = MemoryFile.getSize(fd);
- MemoryFile file = new MemoryFile(fd, mappedlength, "r");
- InputStream is = file.getInputStream();
- Bitmap bm = decodeStream(is, outPadding, opts);
- return finishDecode(bm, outPadding, opts);
- }
- } catch (IOException ex) {
- // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
- return null;
- }
Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
return finishDecode(bm, outPadding, opts);
}
@@ -582,6 +570,133 @@
nativeSetDefaultConfig(config.nativeInt);
}
+ /**
+ * Create a LargeBitmap from the specified byte array.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param data byte array of compressed image data.
+ * @param offset offset into data for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(byte[] data,
+ int offset, int length, boolean isShareable) throws IOException {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return nativeCreateLargeBitmap(data, offset, length, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from the file descriptor.
+ * The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param fd The file descriptor containing the data to decode
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(
+ FileDescriptor fd, boolean isShareable) throws IOException {
+ return nativeCreateLargeBitmap(fd, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from an input stream.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * LargeBitmap.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(InputStream is,
+ boolean isShareable) throws IOException {
+ // we need mark/reset to work properly in JNI
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ return nativeCreateLargeBitmap(
+ ((AssetManager.AssetInputStream) is).getAssetInt(),
+ isShareable);
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...).
+ byte [] tempStorage = null;
+ tempStorage = new byte[16 * 1024];
+ return nativeCreateLargeBitmap(is, tempStorage, isShareable);
+ }
+ }
+
+ /**
+ * Create a LargeBitmap from a file path.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(String pathName,
+ boolean isShareable) throws FileNotFoundException, IOException {
+ LargeBitmap bm = null;
+ InputStream stream = null;
+
+ try {
+ stream = new FileInputStream(pathName);
+ bm = createLargeBitmap(stream, isShareable);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
private static native void nativeSetDefaultConfig(int nativeConfig);
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
@@ -591,5 +706,14 @@
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
+
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ byte[] data, int offset, int length, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ FileDescriptor fd, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ InputStream is, byte[] storage, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ int asset, boolean isShareable);
}
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 4c92942..4ba679b 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -22,6 +22,12 @@
*/
public class BitmapShader extends Shader {
/**
+ * Prevent garbage collection.
+ */
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ private final Bitmap mBitmap;
+
+ /**
* Call this to create a new shader that will draw with a bitmap.
*
* @param bitmap The bitmap to use inside the shader
@@ -29,6 +35,7 @@
* @param tileY The tiling mode for y to draw the bitmap in.
*/
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
+ mBitmap = bitmap;
final int b = bitmap.ni();
native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
native_shader = nativePostCreate(native_instance, b, tileX.nativeInt, tileY.nativeInt);
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index ac1f277..8d5c913 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -20,7 +20,12 @@
an {@link android.graphics.Xfermode} subclass.
*/
public class ComposeShader extends Shader {
+ /**
+ * Hold onto the shaders to avoid GC.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
private final Shader mShaderA;
+ @SuppressWarnings({"UnusedDeclaration"})
private final Shader mShaderB;
/** Create a new compose shader, given shaders A, B, and a combining mode.
@@ -36,8 +41,14 @@
mShaderB = shaderB;
native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
(mode != null) ? mode.native_instance : 0);
- native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
- shaderB.native_shader, (mode != null) ? mode.native_instance : 0);
+ if (mode instanceof PorterDuffXfermode) {
+ PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
+ native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
+ shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
+ } else {
+ native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
+ shaderB.native_shader, mode != null ? mode.native_instance : 0);
+ }
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
diff --git a/graphics/java/android/graphics/LargeBitmap.java b/graphics/java/android/graphics/LargeBitmap.java
new file mode 100644
index 0000000..6656b17
--- /dev/null
+++ b/graphics/java/android/graphics/LargeBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * 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.graphics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LargeBitmap can be used to decode a rectangle region from an image.
+ * LargeBimap is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
+ * Given a LargeBitmap, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ * @hide
+ */
+public final class LargeBitmap {
+ private int mNativeLargeBitmap;
+ private boolean mRecycled;
+
+ /* Private constructor that must received an already allocated native
+ large bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ private LargeBitmap(int lbm) {
+ mNativeLargeBitmap = lbm;
+ mRecycled = false;
+ }
+
+ /**
+ * Decodes a rectangle region in the image specified by rect.
+ *
+ * @param rect The rectangle that specified the region to be decode.
+ * @param opts null-ok; Options that control downsampling.
+ * inPurgeable is not supported.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded.
+ */
+ public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ checkRecycled("decodeRegion called on recycled large bitmap");
+ if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
+ throw new IllegalArgumentException("rectangle is not inside the image");
+ return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, options);
+ }
+
+ /** Returns the original image's width */
+ public int getWidth() {
+ checkRecycled("getWidth called on recycled large bitmap");
+ return nativeGetWidth(mNativeLargeBitmap);
+ }
+
+ /** Returns the original image's height */
+ public int getHeight() {
+ checkRecycled("getHeight called on recycled large bitmap");
+ return nativeGetHeight(mNativeLargeBitmap);
+ }
+
+ /**
+ * Frees up the memory associated with this large bitmap, and mark the
+ * large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
+ * getWidth() or getHeight() is called.
+ * This operation cannot be reversed, so it should only be called if you are
+ * sure there are no further uses for the large bitmap. This is an advanced call,
+ * and normally need not be called, since the normal GC process will free up this
+ * memory when there are no more references to this bitmap.
+ */
+ public void recycle() {
+ if (!mRecycled) {
+ nativeClean(mNativeLargeBitmap);
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Returns true if this large bitmap has been recycled.
+ * If so, then it is an error to try use its method.
+ *
+ * @return true if the large bitmap has been recycled
+ */
+ public final boolean isRecycled() {
+ return mRecycled;
+ }
+
+ /**
+ * Called by methods that want to throw an exception if the bitmap
+ * has already been recycled.
+ */
+ private void checkRecycled(String errorMessage) {
+ if (mRecycled) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ protected void finalize() {
+ recycle();
+ }
+
+ private static native Bitmap nativeDecodeRegion(int lbm,
+ int start_x, int start_y, int width, int height,
+ BitmapFactory.Options options);
+ private static native int nativeGetWidth(int lbm);
+ private static native int nativeGetHeight(int lbm);
+ private static native void nativeClean(int lbm);
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6349cb3..62fbfb4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -43,6 +43,28 @@
private boolean mHasCompatScaling;
private float mCompatScaling;
private float mInvCompatScaling;
+
+ /**
+ * @hide
+ */
+ public boolean hasShadow;
+ /**
+ * @hide
+ */
+ public float shadowDx;
+ /**
+ * @hide
+ */
+ public float shadowDy;
+ /**
+ * @hide
+ */
+ public float shadowRadius;
+ /**
+ * @hide
+ */
+ public int shadowColor;
+
/**
* @hide
*/
@@ -935,13 +957,23 @@
* offset and color, and blur radius. If radius is 0, then the shadow
* layer is removed.
*/
- public native void setShadowLayer(float radius, float dx, float dy, int color);
+ public void setShadowLayer(float radius, float dx, float dy, int color) {
+ hasShadow = radius > 0.0f;
+ shadowRadius = radius;
+ shadowDx = dx;
+ shadowDy = dy;
+ shadowColor = color;
+ nSetShadowLayer(radius, dx, dy, color);
+ }
+
+ private native void nSetShadowLayer(float radius, float dx, float dy, int color);
/**
* Clear the shadow layer.
*/
public void clearShadowLayer() {
- setShadowLayer(0, 0, 0, 0);
+ hasShadow = false;
+ nSetShadowLayer(0, 0, 0, 0);
}
/**
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index cb2c6a2..c3416a0 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.view.HardwareRenderer;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -24,12 +26,27 @@
* text on a path.
*/
public class Path {
+ /**
+ * @hide
+ */
+ public final int mNativePath;
+
+ /**
+ * @hide
+ */
+ public boolean isSimplePath = true;
+ /**
+ * @hide
+ */
+ public Region rects;
+ private boolean mDetectSimplePaths;
/**
* Create an empty path
*/
public Path() {
mNativePath = init1();
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -43,6 +60,7 @@
valNative = src.mNativePath;
}
mNativePath = init2(valNative);
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -50,6 +68,10 @@
* This does NOT change the fill-type setting.
*/
public void reset() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_reset(mNativePath);
}
@@ -58,6 +80,10 @@
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_rewind(mNativePath);
}
@@ -65,6 +91,7 @@
*/
public void set(Path src) {
if (this != src) {
+ isSimplePath = src.isSimplePath;
native_set(mNativePath, src.mNativePath);
}
}
@@ -160,6 +187,7 @@
* @param bounds Returns the computed bounds of the path's control points.
* @param exact This parameter is no longer used.
*/
+ @SuppressWarnings({"UnusedDeclaration"})
public void computeBounds(RectF bounds, boolean exact) {
native_computeBounds(mNativePath, bounds);
}
@@ -236,6 +264,7 @@
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
+ isSimplePath = false;
native_quadTo(mNativePath, x1, y1, x2, y2);
}
@@ -254,6 +283,7 @@
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+ isSimplePath = false;
native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
@@ -271,6 +301,7 @@
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -281,6 +312,7 @@
*/
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -299,6 +331,7 @@
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
}
@@ -314,6 +347,7 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
}
@@ -322,6 +356,7 @@
* first point of the contour, a line segment is automatically added.
*/
public void close() {
+ isSimplePath = false;
native_close(mNativePath);
}
@@ -351,6 +386,11 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom,
+ Region.Op.UNION);
+ }
native_addRect(mNativePath, rect, dir.nativeInt);
}
@@ -363,8 +403,11 @@
* @param bottom The bottom of a rectangle to add to the path
* @param dir The direction to wind the rectangle's contour
*/
- public void addRect(float left, float top, float right, float bottom,
- Direction dir) {
+ public void addRect(float left, float top, float right, float bottom, Direction dir) {
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
+ }
native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -378,6 +421,7 @@
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addOval(mNativePath, oval, dir.nativeInt);
}
@@ -390,6 +434,7 @@
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, Direction dir) {
+ isSimplePath = false;
native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
}
@@ -404,6 +449,7 @@
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addArc(mNativePath, oval, startAngle, sweepAngle);
}
@@ -419,6 +465,7 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
}
@@ -438,6 +485,7 @@
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
}
@@ -448,6 +496,7 @@
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(Path src, float dx, float dy) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, dx, dy);
}
@@ -457,6 +506,7 @@
* @param src The path that is appended to the current path
*/
public void addPath(Path src) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath);
}
@@ -466,6 +516,7 @@
* @param src The path to add as a new contour
*/
public void addPath(Path src, Matrix matrix) {
+ if (!src.isSimplePath) isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
}
@@ -502,6 +553,7 @@
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
+ isSimplePath = false;
native_setLastPoint(mNativePath, dx, dy);
}
@@ -537,8 +589,8 @@
super.finalize();
}
}
-
- /*package*/ final int ni() {
+
+ final int ni() {
return mNativePath;
}
@@ -592,9 +644,4 @@
int dst_path);
private static native void native_transform(int nPath, int matrix);
private static native void finalizer(int nPath);
-
- /**
- * @hide
- */
- public final int mNativePath;
}
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index cb127fd..6ba064c 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,11 +18,17 @@
public class PorterDuffXfermode extends Xfermode {
/**
+ * @hide
+ */
+ public final PorterDuff.Mode mode;
+
+ /**
* Create an xfermode that uses the specified porter-duff mode.
*
* @param mode The porter-duff mode that is applied
*/
public PorterDuffXfermode(PorterDuff.Mode mode) {
+ this.mode = mode;
native_instance = nativeCreateXfermode(mode.nativeInt);
}
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 489ef83..e540806 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -20,6 +20,10 @@
import android.os.Parcelable;
public class Region implements Parcelable {
+ /**
+ * @hide
+ */
+ public final int mNativeRegion;
// the native values for these must match up with the enum in SkRegion.h
public enum Op {
@@ -329,10 +333,14 @@
}
protected void finalize() throws Throwable {
- nativeDestructor(mNativeRegion);
+ try {
+ nativeDestructor(mNativeRegion);
+ } finally {
+ super.finalize();
+ }
}
- /*package*/ Region(int ni) {
+ Region(int ni) {
if (ni == 0) {
throw new RuntimeException();
}
@@ -345,7 +353,7 @@
this(ni);
}
- /*package*/ final int ni() {
+ final int ni() {
return mNativeRegion;
}
@@ -374,6 +382,4 @@
Parcel p);
private static native boolean nativeEquals(int native_r1, int native_r2);
-
- private final int mNativeRegion;
}
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 42c410e..2467bdc 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -31,7 +31,11 @@
public class Xfermode {
protected void finalize() throws Throwable {
- finalizer(native_instance);
+ try {
+ finalizer(native_instance);
+ } finally {
+ super.finalize();
+ }
}
private static native void finalizer(int native_instance);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea..88f6d43 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -313,18 +313,16 @@
case RECTANGLE:
if (st.mRadiusArray != null) {
mPath.reset();
- mPath.addRoundRect(mRect, st.mRadiusArray,
- Path.Direction.CW);
+ mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
}
- }
- else {
+ } else if (st.mRadius > 0.0f) {
// since the caller is only giving us 1 value, we will force
// it to be square if the rect is too small in one dimension
// to show it. If we did nothing, Skia would clamp the rad
- // independently along each axis, giving us a thin ellips
+ // independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall
float rad = st.mRadius;
float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
@@ -335,6 +333,11 @@
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
}
+ } else {
+ canvas.drawRect(mRect, mFillPaint);
+ if (haveStroke) {
+ canvas.drawRect(mRect, mStrokePaint);
+ }
}
break;
case OVAL:
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 46f3eae..f1f673b 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -35,14 +35,12 @@
Bitmap mBitmap;
Allocation(int id, RenderScript rs, Type t) {
- super(rs);
- mID = id;
+ super(id, rs);
mType = t;
}
Allocation(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
@Override
@@ -182,8 +180,7 @@
public class Adapter1D extends BaseObj {
Adapter1D(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
public void setConstraint(Dimension dim, int value) {
@@ -225,8 +222,7 @@
public class Adapter2D extends BaseObj {
Adapter2D(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
public void setConstraint(Dimension dim, int value) {
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 28675dc..5dce18f 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -24,10 +24,10 @@
**/
class BaseObj {
- BaseObj(RenderScript rs) {
+ BaseObj(int id, RenderScript rs) {
rs.validate();
mRS = rs;
- mID = 0;
+ mID = id;
mDestroyed = false;
}
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 5d2a059..b811479 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -286,32 +286,27 @@
}
- Element(RenderScript rs, Element[] e, String[] n) {
- super(rs);
+ Element(int id, RenderScript rs, Element[] e, String[] n) {
+ super(id, rs);
mSize = 0;
mElements = e;
mElementNames = n;
- int[] ids = new int[mElements.length];
for (int ct = 0; ct < mElements.length; ct++ ) {
mSize += mElements[ct].mSize;
- ids[ct] = mElements[ct].mID;
}
- mID = rs.nElementCreate2(ids, mElementNames);
}
- Element(RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
- super(rs);
+ Element(int id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
+ super(id, rs);
mSize = dt.mSize * size;
mType = dt;
mKind = dk;
mNormalized = norm;
mVectorSize = size;
- mID = rs.nElementCreate(dt.mID, dk.mID, norm, size);
}
- Element(RenderScript rs, int id) {
- super(rs);
- mID = id;
+ Element(int id, RenderScript rs) {
+ super(id, rs);
}
@Override
@@ -320,9 +315,14 @@
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
int[] dataBuffer = new int[5];
mRS.nElementGetNativeData(mID, dataBuffer);
+
+ mNormalized = dataBuffer[2] == 1 ? true : false;
+ mVectorSize = dataBuffer[3];
+ mSize = 0;
for (DataType dt: DataType.values()) {
if(dt.mID == dataBuffer[0]){
mType = dt;
+ mSize = mType.mSize * mVectorSize;
}
}
for (DataKind dk: DataKind.values()) {
@@ -331,8 +331,6 @@
}
}
- mNormalized = dataBuffer[2] == 1 ? true : false;
- mVectorSize = dataBuffer[3];
int numSubElements = dataBuffer[4];
if(numSubElements > 0) {
mElements = new Element[numSubElements];
@@ -341,8 +339,9 @@
int[] subElementIds = new int[numSubElements];
mRS.nElementGetSubElements(mID, subElementIds, mElementNames);
for(int i = 0; i < numSubElements; i ++) {
- mElements[i] = new Element(mRS, subElementIds[i]);
+ mElements[i] = new Element(subElementIds[i], mRS);
mElements[i].updateFromNative();
+ mSize += mElements[i].mSize;
}
}
@@ -354,14 +353,21 @@
/////////////////////////////////////////
public static Element createUser(RenderScript rs, DataType dt) {
- return new Element(rs, dt, DataKind.USER, false, 1);
+ DataKind dk = DataKind.USER;
+ boolean norm = false;
+ int vecSize = 1;
+ int id = rs.nElementCreate(dt.mID, dk.mID, norm, vecSize);
+ return new Element(id, rs, dt, dk, norm, vecSize);
}
public static Element createVector(RenderScript rs, DataType dt, int size) {
if (size < 2 || size > 4) {
throw new IllegalArgumentException("Bad size");
}
- return new Element(rs, dt, DataKind.USER, false, size);
+ DataKind dk = DataKind.USER;
+ boolean norm = false;
+ int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+ return new Element(id, rs, dt, dk, norm, size);
}
public static Element createPixel(RenderScript rs, DataType dt, DataKind dk) {
@@ -399,7 +405,9 @@
size = 4;
}
- return new Element(rs, dt, dk, true, size);
+ boolean norm = true;
+ int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+ return new Element(id, rs, dt, dk, norm, size);
}
public static class Builder {
@@ -435,7 +443,13 @@
String[] sin = new String[mCount];
java.lang.System.arraycopy(mElements, 0, ein, 0, mCount);
java.lang.System.arraycopy(mElementNames, 0, sin, 0, mCount);
- return new Element(mRS, ein, sin);
+
+ int[] ids = new int[ein.length];
+ for (int ct = 0; ct < ein.length; ct++ ) {
+ ids[ct] = ein[ct].mID;
+ }
+ int id = mRS.nElementCreate2(ids, sin);
+ return new Element(id, mRS, ein, sin);
}
}
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index f03b51c..24f0409 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -76,6 +76,7 @@
public void addU8(short v) {
if ((v < 0) || (v > 0xff)) {
+ android.util.Log.e("rs", "FieldPacker.addU8( " + v + " )");
throw new IllegalArgumentException("Saving value out of range for type");
}
mData[mPos++] = (byte)v;
@@ -83,6 +84,7 @@
public void addU16(int v) {
if ((v < 0) || (v > 0xffff)) {
+ android.util.Log.e("rs", "FieldPacker.addU16( " + v + " )");
throw new IllegalArgumentException("Saving value out of range for type");
}
align(2);
@@ -91,7 +93,8 @@
}
public void addU32(long v) {
- if ((v < 0) || (v > 0xffffffff)) {
+ if ((v < 0) || (v > 0xffffffffL)) {
+ android.util.Log.e("rs", "FieldPacker.addU32( " + v + " )");
throw new IllegalArgumentException("Saving value out of range for type");
}
align(4);
@@ -103,6 +106,7 @@
public void addU64(long v) {
if (v < 0) {
+ android.util.Log.e("rs", "FieldPacker.addU64( " + v + " )");
throw new IllegalArgumentException("Saving value out of range for type");
}
align(8);
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index 302a5f4..2414062 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -146,8 +146,7 @@
IndexEntry[] mFileEntries;
FileA3D(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
private void initEntries() {
diff --git a/graphics/java/android/renderscript/Float2.java b/graphics/java/android/renderscript/Float2.java
index 8fea91f..889bf7b 100644
--- a/graphics/java/android/renderscript/Float2.java
+++ b/graphics/java/android/renderscript/Float2.java
@@ -28,6 +28,11 @@
public Float2() {
}
+ public Float2(float initX, float initY) {
+ x = initX;
+ y = initY;
+ }
+
public float x;
public float y;
}
diff --git a/graphics/java/android/renderscript/Float3.java b/graphics/java/android/renderscript/Float3.java
index 9d9e406..ebe140d 100644
--- a/graphics/java/android/renderscript/Float3.java
+++ b/graphics/java/android/renderscript/Float3.java
@@ -27,6 +27,11 @@
public class Float3 {
public Float3() {
}
+ public Float3(float initX, float initY, float initZ) {
+ x = initX;
+ y = initY;
+ z = initZ;
+ }
public float x;
public float y;
diff --git a/graphics/java/android/renderscript/Float4.java b/graphics/java/android/renderscript/Float4.java
index a703e80..847732f 100644
--- a/graphics/java/android/renderscript/Float4.java
+++ b/graphics/java/android/renderscript/Float4.java
@@ -28,6 +28,13 @@
public Float4() {
}
+ public Float4(float initX, float initY, float initZ, float initW) {
+ x = initX;
+ y = initY;
+ z = initZ;
+ w = initW;
+ }
+
public float x;
public float y;
public float z;
diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java
index 41f8827..d79909e 100644
--- a/graphics/java/android/renderscript/Font.java
+++ b/graphics/java/android/renderscript/Font.java
@@ -31,8 +31,7 @@
public class Font extends BaseObj {
Font(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
static public Font create(RenderScript rs, Resources res, String fileName, int size)
diff --git a/graphics/java/android/renderscript/Light.java b/graphics/java/android/renderscript/Light.java
deleted file mode 100644
index aab656f..0000000
--- a/graphics/java/android/renderscript/Light.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.renderscript;
-
-import android.util.Config;
-import android.util.Log;
-
-/**
- * @hide
- *
- **/
-public class Light extends BaseObj {
- Light(int id, RenderScript rs) {
- super(rs);
- mID = id;
- }
-
- public void setColor(float r, float g, float b) {
- mRS.validate();
- mRS.nLightSetColor(mID, r, g, b);
- }
-
- public void setPosition(float x, float y, float z) {
- mRS.validate();
- mRS.nLightSetPosition(mID, x, y, z);
- }
-
- public static class Builder {
- RenderScript mRS;
- boolean mIsMono;
- boolean mIsLocal;
-
- public Builder(RenderScript rs) {
- mRS = rs;
- mIsMono = false;
- mIsLocal = false;
- }
-
- public void lightSetIsMono(boolean isMono) {
- mIsMono = isMono;
- }
-
- public void lightSetIsLocal(boolean isLocal) {
- mIsLocal = isLocal;
- }
-
- static synchronized Light internalCreate(RenderScript rs, Builder b) {
- rs.nSamplerBegin();
- rs.nLightSetIsMono(b.mIsMono);
- rs.nLightSetIsLocal(b.mIsLocal);
- int id = rs.nLightCreate();
- return new Light(id, rs);
- }
-
- public Light create() {
- mRS.validate();
- return internalCreate(mRS, this);
- }
- }
-
-}
-
diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java
index e854cd9..5ffc21a 100644
--- a/graphics/java/android/renderscript/Matrix4f.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -179,6 +179,76 @@
tmp.loadTranslate(x, y, z);
multiply(tmp);
}
+ private float computeCofactor(int i, int j) {
+ int c0 = (i+1) % 4;
+ int c1 = (i+2) % 4;
+ int c2 = (i+3) % 4;
+ int r0 = (j+1) % 4;
+ int r1 = (j+2) % 4;
+ int r2 = (j+3) % 4;
+
+ float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] -
+ mMat[c1 + 4*r2] * mMat[c2 + 4*r1]))
+ - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] -
+ mMat[c1 + 4*r2] * mMat[c2 + 4*r0]))
+ + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] -
+ mMat[c1 + 4*r1] * mMat[c2 + 4*r0]));
+
+ float cofactor = ((i+j) & 1) != 0 ? -minor : minor;
+ return cofactor;
+ }
+
+ public boolean inverse() {
+
+ Matrix4f result = new Matrix4f();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ result.mMat[4*i + j] = computeCofactor(i, j);
+ }
+ }
+
+ // Dot product of 0th column of source and 0th row of result
+ float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] +
+ mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3];
+
+ if (Math.abs(det) < 1e-6) {
+ return false;
+ }
+
+ det = 1.0f / det;
+ for (int i = 0; i < 16; ++i) {
+ mMat[i] = result.mMat[i] * det;
+ }
+
+ return true;
+ }
+
+ public boolean inverseTranspose() {
+
+ Matrix4f result = new Matrix4f();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ result.mMat[4*j + i] = computeCofactor(i, j);
+ }
+ }
+
+ float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] +
+ mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12];
+
+ if (Math.abs(det) < 1e-6) {
+ return false;
+ }
+
+ det = 1.0f / det;
+ for (int i = 0; i < 16; ++i) {
+ mMat[i] = result.mMat[i] * det;
+ }
+
+ return true;
+ }
+
public void transpose() {
for(int i = 0; i < 3; ++i) {
for(int j = i + 1; j < 4; ++j) {
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index bf02319..b74c1f8 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -32,8 +32,7 @@
Primitive[] mPrimitives;
Mesh(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
public int getVertexAllocationCount() {
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index b16dac1..1628a97 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -38,8 +38,7 @@
String mShader;
Program(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
public void bindConstants(Allocation a, int slot) {
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index 6fc9fff..08065cf 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -45,8 +45,7 @@
CullMode mCullMode;
ProgramRaster(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
mLineWidth = 1.0f;
mPointSmooth = false;
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index a92cbb6..cce4064 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -76,8 +76,7 @@
ProgramStore(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 4eeb4d3..e42f9f6 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -57,151 +57,457 @@
}
}
+ // Non-threadsafe functions.
native void nInitElements(int a8, int rgba4444, int rgba8888, int rgb565);
-
native int nDeviceCreate();
native void nDeviceDestroy(int dev);
native void nDeviceSetConfig(int dev, int param, int value);
- native int nContextCreateGL(int dev, int ver, boolean useDepth);
- native int nContextCreate(int dev, int ver);
- native void nContextDestroy(int con);
- native void nContextSetSurface(int w, int h, Surface sur);
- native void nContextSetPriority(int p);
- native void nContextDump(int bits);
- native void nContextFinish();
-
- native void nContextBindRootScript(int script);
- native void nContextBindSampler(int sampler, int slot);
- native void nContextBindProgramStore(int pfs);
- native void nContextBindProgramFragment(int pf);
- native void nContextBindProgramVertex(int pf);
- native void nContextBindProgramRaster(int pr);
- native void nContextPause();
- native void nContextResume();
- native int nContextGetMessage(int[] data, boolean wait);
- native void nContextInitToClient();
- native void nContextDeinitToClient();
-
- native void nAssignName(int obj, byte[] name);
- native String nGetName(int obj);
- native void nObjDestroy(int id);
- native void nObjDestroyOOB(int id);
- native int nFileOpen(byte[] name);
+ native int nContextGetMessage(int con, int[] data, boolean wait);
+ native void nContextInitToClient(int con);
+ native void nContextDeinitToClient(int con);
- native int nElementCreate(int type, int kind, boolean norm, int vecSize);
- native int nElementCreate2(int[] elements, String[] names);
- native void nElementGetNativeData(int id, int[] elementData);
- native void nElementGetSubElements(int id, int[] IDs, String[] names);
+ // Methods below are wrapped to protect the non-threadsafe
+ // lockless fifo.
+ native int rsnContextCreateGL(int dev, int ver, boolean useDepth);
+ synchronized int nContextCreateGL(int dev, int ver, boolean useDepth) {
+ return rsnContextCreateGL(dev, ver, useDepth);
+ }
+ native int rsnContextCreate(int dev, int ver);
+ synchronized int nContextCreate(int dev, int ver) {
+ return rsnContextCreate(dev, ver);
+ }
+ native void rsnContextDestroy(int con);
+ synchronized void nContextDestroy() {
+ rsnContextDestroy(mContext);
+ }
+ native void rsnContextSetSurface(int con, int w, int h, Surface sur);
+ synchronized void nContextSetSurface(int w, int h, Surface sur) {
+ rsnContextSetSurface(mContext, w, h, sur);
+ }
+ native void rsnContextSetPriority(int con, int p);
+ synchronized void nContextSetPriority(int p) {
+ rsnContextSetPriority(mContext, p);
+ }
+ native void rsnContextDump(int con, int bits);
+ synchronized void nContextDump(int bits) {
+ rsnContextDump(mContext, bits);
+ }
+ native void rsnContextFinish(int con);
+ synchronized void nContextFinish() {
+ rsnContextFinish(mContext);
+ }
- native void nTypeBegin(int elementID);
- native void nTypeAdd(int dim, int val);
- native int nTypeCreate();
- native void nTypeFinalDestroy(Type t);
- native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
- native void nTypeGetNativeData(int id, int[] typeData);
+ native void rsnContextBindRootScript(int con, int script);
+ synchronized void nContextBindRootScript(int script) {
+ rsnContextBindRootScript(mContext, script);
+ }
+ native void rsnContextBindSampler(int con, int sampler, int slot);
+ synchronized void nContextBindSampler(int sampler, int slot) {
+ rsnContextBindSampler(mContext, sampler, slot);
+ }
+ native void rsnContextBindProgramStore(int con, int pfs);
+ synchronized void nContextBindProgramStore(int pfs) {
+ rsnContextBindProgramStore(mContext, pfs);
+ }
+ native void rsnContextBindProgramFragment(int con, int pf);
+ synchronized void nContextBindProgramFragment(int pf) {
+ rsnContextBindProgramFragment(mContext, pf);
+ }
+ native void rsnContextBindProgramVertex(int con, int pv);
+ synchronized void nContextBindProgramVertex(int pv) {
+ rsnContextBindProgramVertex(mContext, pv);
+ }
+ native void rsnContextBindProgramRaster(int con, int pr);
+ synchronized void nContextBindProgramRaster(int pr) {
+ rsnContextBindProgramRaster(mContext, pr);
+ }
+ native void rsnContextPause(int con);
+ synchronized void nContextPause() {
+ rsnContextPause(mContext);
+ }
+ native void rsnContextResume(int con);
+ synchronized void nContextResume() {
+ rsnContextResume(mContext);
+ }
- native int nAllocationCreateTyped(int type);
- native int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
- native int nAllocationCreateBitmapRef(int type, Bitmap bmp);
- native int nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp);
- native int nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream);
+ native void rsnAssignName(int con, int obj, byte[] name);
+ synchronized void nAssignName(int obj, byte[] name) {
+ rsnAssignName(mContext, obj, name);
+ }
+ native String rsnGetName(int con, int obj);
+ synchronized String nGetName(int obj) {
+ return rsnGetName(mContext, obj);
+ }
+ native void rsnObjDestroy(int con, int id);
+ synchronized void nObjDestroy(int id) {
+ rsnObjDestroy(mContext, id);
+ }
+ native void rsnObjDestroyOOB(int con, int id);
+ synchronized void nObjDestroyOOB(int id) {
+ rsnObjDestroyOOB(mContext, id);
+ }
+ native int rsnFileOpen(int con, byte[] name);
+ synchronized int nFileOpen(byte[] name) {
+ return rsnFileOpen(mContext, name);
+ }
- native void nAllocationUploadToTexture(int alloc, boolean genMips, int baseMioLevel);
- native void nAllocationUploadToBufferObject(int alloc);
+ native int rsnElementCreate(int con, int type, int kind, boolean norm, int vecSize);
+ synchronized int nElementCreate(int type, int kind, boolean norm, int vecSize) {
+ return rsnElementCreate(mContext, type, kind, norm, vecSize);
+ }
+ native int rsnElementCreate2(int con, int[] elements, String[] names);
+ synchronized int nElementCreate2(int[] elements, String[] names) {
+ return rsnElementCreate2(mContext, elements, names);
+ }
+ native void rsnElementGetNativeData(int con, int id, int[] elementData);
+ synchronized void nElementGetNativeData(int id, int[] elementData) {
+ rsnElementGetNativeData(mContext, id, elementData);
+ }
+ native void rsnElementGetSubElements(int con, int id, int[] IDs, String[] names);
+ synchronized void nElementGetSubElements(int id, int[] IDs, String[] names) {
+ rsnElementGetSubElements(mContext, id, IDs, names);
+ }
- native void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes);
- native void nAllocationSubData1D(int id, int off, int count, short[] d, int sizeBytes);
- native void nAllocationSubData1D(int id, int off, int count, byte[] d, int sizeBytes);
- native void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes);
+ native void rsnTypeBegin(int con, int elementID);
+ synchronized void nTypeBegin(int elementID) {
+ rsnTypeBegin(mContext, elementID);
+ }
+ native void rsnTypeAdd(int con, int dim, int val);
+ synchronized void nTypeAdd(int dim, int val) {
+ rsnTypeAdd(mContext, dim, val);
+ }
+ native int rsnTypeCreate(int con);
+ synchronized int nTypeCreate() {
+ return rsnTypeCreate(mContext);
+ }
+ native void rsnTypeFinalDestroy(int con, Type t);
+ synchronized void nTypeFinalDestroy(Type t) {
+ rsnTypeFinalDestroy(mContext, t);
+ }
+ native void rsnTypeSetupFields(int con, Type t, int[] types, int[] bits, Field[] IDs);
+ synchronized void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs) {
+ rsnTypeSetupFields(mContext, t, types, bits, IDs);
+ }
+ native void rsnTypeGetNativeData(int con, int id, int[] typeData);
+ synchronized void nTypeGetNativeData(int id, int[] typeData) {
+ rsnTypeGetNativeData(mContext, id, typeData);
+ }
- native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes);
- native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes);
- native void nAllocationRead(int id, int[] d);
- native void nAllocationRead(int id, float[] d);
- native void nAllocationSubDataFromObject(int id, Type t, int offset, Object o);
- native void nAllocationSubReadFromObject(int id, Type t, int offset, Object o);
- native int nAllocationGetType(int id);
+ native int rsnAllocationCreateTyped(int con, int type);
+ synchronized int nAllocationCreateTyped(int type) {
+ return rsnAllocationCreateTyped(mContext, type);
+ }
+ native int rsnAllocationCreateFromBitmap(int con, int dstFmt, boolean genMips, Bitmap bmp);
+ synchronized int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp) {
+ return rsnAllocationCreateFromBitmap(mContext, dstFmt, genMips, bmp);
+ }
+ native int rsnAllocationCreateBitmapRef(int con, int type, Bitmap bmp);
+ synchronized int nAllocationCreateBitmapRef(int type, Bitmap bmp) {
+ return rsnAllocationCreateBitmapRef(mContext, type, bmp);
+ }
+ native int rsnAllocationCreateFromBitmapBoxed(int con, int dstFmt, boolean genMips, Bitmap bmp);
+ synchronized int nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp) {
+ return rsnAllocationCreateFromBitmapBoxed(mContext, dstFmt, genMips, bmp);
+ }
+ native int rsnAllocationCreateFromAssetStream(int con, int dstFmt, boolean genMips, int assetStream);
+ synchronized int nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream) {
+ return rsnAllocationCreateFromAssetStream(mContext, dstFmt, genMips, assetStream);
+ }
- native int nFileA3DCreateFromAssetStream(int assetStream);
- native int nFileA3DGetNumIndexEntries(int fileA3D);
- native void nFileA3DGetIndexEntries(int fileA3D, int numEntries, int[] IDs, String[] names);
- native int nFileA3DGetEntryByIndex(int fileA3D, int index);
+ native void rsnAllocationUploadToTexture(int con, int alloc, boolean genMips, int baseMioLevel);
+ synchronized void nAllocationUploadToTexture(int alloc, boolean genMips, int baseMioLevel) {
+ rsnAllocationUploadToTexture(mContext, alloc, genMips, baseMioLevel);
+ }
+ native void rsnAllocationUploadToBufferObject(int con, int alloc);
+ synchronized void nAllocationUploadToBufferObject(int alloc) {
+ rsnAllocationUploadToBufferObject(mContext, alloc);
+ }
- native int nFontCreateFromFile(String fileName, int size, int dpi);
+ native void rsnAllocationSubData1D(int con, int id, int off, int count, int[] d, int sizeBytes);
+ synchronized void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes) {
+ rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+ }
+ native void rsnAllocationSubData1D(int con, int id, int off, int count, short[] d, int sizeBytes);
+ synchronized void nAllocationSubData1D(int id, int off, int count, short[] d, int sizeBytes) {
+ rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+ }
+ native void rsnAllocationSubData1D(int con, int id, int off, int count, byte[] d, int sizeBytes);
+ synchronized void nAllocationSubData1D(int id, int off, int count, byte[] d, int sizeBytes) {
+ rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+ }
+ native void rsnAllocationSubData1D(int con, int id, int off, int count, float[] d, int sizeBytes);
+ synchronized void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes) {
+ rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+ }
- native void nAdapter1DBindAllocation(int ad, int alloc);
- native void nAdapter1DSetConstraint(int ad, int dim, int value);
- native void nAdapter1DData(int ad, int[] d);
- native void nAdapter1DData(int ad, float[] d);
- native void nAdapter1DSubData(int ad, int off, int count, int[] d);
- native void nAdapter1DSubData(int ad, int off, int count, float[] d);
- native int nAdapter1DCreate();
+ native void rsnAllocationSubData2D(int con, int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes);
+ synchronized void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes) {
+ rsnAllocationSubData2D(mContext, id, xoff, yoff, w, h, d, sizeBytes);
+ }
+ native void rsnAllocationSubData2D(int con, int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes);
+ synchronized void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes) {
+ rsnAllocationSubData2D(mContext, id, xoff, yoff, w, h, d, sizeBytes);
+ }
+ native void rsnAllocationRead(int con, int id, int[] d);
+ synchronized void nAllocationRead(int id, int[] d) {
+ rsnAllocationRead(mContext, id, d);
+ }
+ native void rsnAllocationRead(int con, int id, float[] d);
+ synchronized void nAllocationRead(int id, float[] d) {
+ rsnAllocationRead(mContext, id, d);
+ }
+ native void rsnAllocationSubDataFromObject(int con, int id, Type t, int offset, Object o);
+ synchronized void nAllocationSubDataFromObject(int id, Type t, int offset, Object o) {
+ rsnAllocationSubDataFromObject(mContext, id, t, offset, o);
+ }
+ native void rsnAllocationSubReadFromObject(int con, int id, Type t, int offset, Object o);
+ synchronized void nAllocationSubReadFromObject(int id, Type t, int offset, Object o) {
+ rsnAllocationSubReadFromObject(mContext, id, t, offset, o);
+ }
+ native int rsnAllocationGetType(int con, int id);
+ synchronized int nAllocationGetType(int id) {
+ return rsnAllocationGetType(mContext, id);
+ }
- native void nAdapter2DBindAllocation(int ad, int alloc);
- native void nAdapter2DSetConstraint(int ad, int dim, int value);
- native void nAdapter2DData(int ad, int[] d);
- native void nAdapter2DData(int ad, float[] d);
- native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, int[] d);
- native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, float[] d);
- native int nAdapter2DCreate();
+ native int rsnFileA3DCreateFromAssetStream(int con, int assetStream);
+ synchronized int nFileA3DCreateFromAssetStream(int assetStream) {
+ return rsnFileA3DCreateFromAssetStream(mContext, assetStream);
+ }
+ native int rsnFileA3DGetNumIndexEntries(int con, int fileA3D);
+ synchronized int nFileA3DGetNumIndexEntries(int fileA3D) {
+ return rsnFileA3DGetNumIndexEntries(mContext, fileA3D);
+ }
+ native void rsnFileA3DGetIndexEntries(int con, int fileA3D, int numEntries, int[] IDs, String[] names);
+ synchronized void nFileA3DGetIndexEntries(int fileA3D, int numEntries, int[] IDs, String[] names) {
+ rsnFileA3DGetIndexEntries(mContext, fileA3D, numEntries, IDs, names);
+ }
+ native int rsnFileA3DGetEntryByIndex(int con, int fileA3D, int index);
+ synchronized int nFileA3DGetEntryByIndex(int fileA3D, int index) {
+ return rsnFileA3DGetEntryByIndex(mContext, fileA3D, index);
+ }
- native void nScriptBindAllocation(int script, int alloc, int slot);
- native void nScriptSetTimeZone(int script, byte[] timeZone);
- native void nScriptInvoke(int id, int slot);
- native void nScriptInvokeV(int id, int slot, byte[] params);
- native void nScriptSetVarI(int id, int slot, int val);
- native void nScriptSetVarF(int id, int slot, float val);
- native void nScriptSetVarV(int id, int slot, byte[] val);
+ native int rsnFontCreateFromFile(int con, String fileName, int size, int dpi);
+ synchronized int nFontCreateFromFile(String fileName, int size, int dpi) {
+ return rsnFontCreateFromFile(mContext, fileName, size, dpi);
+ }
- native void nScriptCBegin();
- native void nScriptCSetScript(byte[] script, int offset, int length);
- native int nScriptCCreate();
+ native void rsnAdapter1DBindAllocation(int con, int ad, int alloc);
+ synchronized void nAdapter1DBindAllocation(int ad, int alloc) {
+ rsnAdapter1DBindAllocation(mContext, ad, alloc);
+ }
+ native void rsnAdapter1DSetConstraint(int con, int ad, int dim, int value);
+ synchronized void nAdapter1DSetConstraint(int ad, int dim, int value) {
+ rsnAdapter1DSetConstraint(mContext, ad, dim, value);
+ }
+ native void rsnAdapter1DData(int con, int ad, int[] d);
+ synchronized void nAdapter1DData(int ad, int[] d) {
+ rsnAdapter1DData(mContext, ad, d);
+ }
+ native void rsnAdapter1DData(int con, int ad, float[] d);
+ synchronized void nAdapter1DData(int ad, float[] d) {
+ rsnAdapter1DData(mContext, ad, d);
+ }
+ native void rsnAdapter1DSubData(int con, int ad, int off, int count, int[] d);
+ synchronized void nAdapter1DSubData(int ad, int off, int count, int[] d) {
+ rsnAdapter1DSubData(mContext, ad, off, count, d);
+ }
+ native void rsnAdapter1DSubData(int con, int ad, int off, int count, float[] d);
+ synchronized void nAdapter1DSubData(int ad, int off, int count, float[] d) {
+ rsnAdapter1DSubData(mContext, ad, off, count, d);
+ }
+ native int rsnAdapter1DCreate(int con);
+ synchronized int nAdapter1DCreate() {
+ return rsnAdapter1DCreate(mContext);
+ }
- native void nSamplerBegin();
- native void nSamplerSet(int param, int value);
- native int nSamplerCreate();
+ native void rsnAdapter2DBindAllocation(int con, int ad, int alloc);
+ synchronized void nAdapter2DBindAllocation(int ad, int alloc) {
+ rsnAdapter2DBindAllocation(mContext, ad, alloc);
+ }
+ native void rsnAdapter2DSetConstraint(int con, int ad, int dim, int value);
+ synchronized void nAdapter2DSetConstraint(int ad, int dim, int value) {
+ rsnAdapter2DSetConstraint(mContext, ad, dim, value);
+ }
+ native void rsnAdapter2DData(int con, int ad, int[] d);
+ synchronized void nAdapter2DData(int ad, int[] d) {
+ rsnAdapter2DData(mContext, ad, d);
+ }
+ native void rsnAdapter2DData(int con, int ad, float[] d);
+ synchronized void nAdapter2DData(int ad, float[] d) {
+ rsnAdapter2DData(mContext, ad, d);
+ }
+ native void rsnAdapter2DSubData(int con, int ad, int xoff, int yoff, int w, int h, int[] d);
+ synchronized void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, int[] d) {
+ rsnAdapter2DSubData(mContext, ad, xoff, yoff, w, h, d);
+ }
+ native void rsnAdapter2DSubData(int con, int ad, int xoff, int yoff, int w, int h, float[] d);
+ synchronized void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, float[] d) {
+ rsnAdapter2DSubData(mContext, ad, xoff, yoff, w, h, d);
+ }
+ native int rsnAdapter2DCreate(int con);
+ synchronized int nAdapter2DCreate() {
+ return rsnAdapter2DCreate(mContext);
+ }
- native void nProgramStoreBegin(int in, int out);
- native void nProgramStoreDepthFunc(int func);
- native void nProgramStoreDepthMask(boolean enable);
- native void nProgramStoreColorMask(boolean r, boolean g, boolean b, boolean a);
- native void nProgramStoreBlendFunc(int src, int dst);
- native void nProgramStoreDither(boolean enable);
- native int nProgramStoreCreate();
+ native void rsnScriptBindAllocation(int con, int script, int alloc, int slot);
+ synchronized void nScriptBindAllocation(int script, int alloc, int slot) {
+ rsnScriptBindAllocation(mContext, script, alloc, slot);
+ }
+ native void rsnScriptSetTimeZone(int con, int script, byte[] timeZone);
+ synchronized void nScriptSetTimeZone(int script, byte[] timeZone) {
+ rsnScriptSetTimeZone(mContext, script, timeZone);
+ }
+ native void rsnScriptInvoke(int con, int id, int slot);
+ synchronized void nScriptInvoke(int id, int slot) {
+ rsnScriptInvoke(mContext, id, slot);
+ }
+ native void rsnScriptInvokeV(int con, int id, int slot, byte[] params);
+ synchronized void nScriptInvokeV(int id, int slot, byte[] params) {
+ rsnScriptInvokeV(mContext, id, slot, params);
+ }
+ native void rsnScriptSetVarI(int con, int id, int slot, int val);
+ synchronized void nScriptSetVarI(int id, int slot, int val) {
+ rsnScriptSetVarI(mContext, id, slot, val);
+ }
+ native void rsnScriptSetVarF(int con, int id, int slot, float val);
+ synchronized void nScriptSetVarF(int id, int slot, float val) {
+ rsnScriptSetVarF(mContext, id, slot, val);
+ }
+ native void rsnScriptSetVarV(int con, int id, int slot, byte[] val);
+ synchronized void nScriptSetVarV(int id, int slot, byte[] val) {
+ rsnScriptSetVarV(mContext, id, slot, val);
+ }
- native int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
- native void nProgramRasterSetLineWidth(int pr, float v);
- native void nProgramRasterSetCullMode(int pr, int mode);
+ native void rsnScriptCBegin(int con);
+ synchronized void nScriptCBegin() {
+ rsnScriptCBegin(mContext);
+ }
+ native void rsnScriptCSetScript(int con, byte[] script, int offset, int length);
+ synchronized void nScriptCSetScript(byte[] script, int offset, int length) {
+ rsnScriptCSetScript(mContext, script, offset, length);
+ }
+ native int rsnScriptCCreate(int con);
+ synchronized int nScriptCCreate() {
+ return rsnScriptCCreate(mContext);
+ }
- native void nProgramBindConstants(int pv, int slot, int mID);
- native void nProgramBindTexture(int vpf, int slot, int a);
- native void nProgramBindSampler(int vpf, int slot, int s);
+ native void rsnSamplerBegin(int con);
+ synchronized void nSamplerBegin() {
+ rsnSamplerBegin(mContext);
+ }
+ native void rsnSamplerSet(int con, int param, int value);
+ synchronized void nSamplerSet(int param, int value) {
+ rsnSamplerSet(mContext, param, value);
+ }
+ native int rsnSamplerCreate(int con);
+ synchronized int nSamplerCreate() {
+ return rsnSamplerCreate(mContext);
+ }
- native int nProgramFragmentCreate(int[] params);
- native int nProgramFragmentCreate2(String shader, int[] params);
+ native void rsnProgramStoreBegin(int con, int in, int out);
+ synchronized void nProgramStoreBegin(int in, int out) {
+ rsnProgramStoreBegin(mContext, in, out);
+ }
+ native void rsnProgramStoreDepthFunc(int con, int func);
+ synchronized void nProgramStoreDepthFunc(int func) {
+ rsnProgramStoreDepthFunc(mContext, func);
+ }
+ native void rsnProgramStoreDepthMask(int con, boolean enable);
+ synchronized void nProgramStoreDepthMask(boolean enable) {
+ rsnProgramStoreDepthMask(mContext, enable);
+ }
+ native void rsnProgramStoreColorMask(int con, boolean r, boolean g, boolean b, boolean a);
+ synchronized void nProgramStoreColorMask(boolean r, boolean g, boolean b, boolean a) {
+ rsnProgramStoreColorMask(mContext, r, g, b, a);
+ }
+ native void rsnProgramStoreBlendFunc(int con, int src, int dst);
+ synchronized void nProgramStoreBlendFunc(int src, int dst) {
+ rsnProgramStoreBlendFunc(mContext, src, dst);
+ }
+ native void rsnProgramStoreDither(int con, boolean enable);
+ synchronized void nProgramStoreDither(boolean enable) {
+ rsnProgramStoreDither(mContext, enable);
+ }
+ native int rsnProgramStoreCreate(int con);
+ synchronized int nProgramStoreCreate() {
+ return rsnProgramStoreCreate(mContext);
+ }
- native int nProgramVertexCreate(boolean texMat);
- native int nProgramVertexCreate2(String shader, int[] params);
+ native int rsnProgramRasterCreate(int con, boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
+ synchronized int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth, boolean pointSprite) {
+ return rsnProgramRasterCreate(mContext, pointSmooth, lineSmooth, pointSprite);
+ }
+ native void rsnProgramRasterSetLineWidth(int con, int pr, float v);
+ synchronized void nProgramRasterSetLineWidth(int pr, float v) {
+ rsnProgramRasterSetLineWidth(mContext, pr, v);
+ }
+ native void rsnProgramRasterSetCullMode(int con, int pr, int mode);
+ synchronized void nProgramRasterSetCullMode(int pr, int mode) {
+ rsnProgramRasterSetCullMode(mContext, pr, mode);
+ }
- native void nLightBegin();
- native void nLightSetIsMono(boolean isMono);
- native void nLightSetIsLocal(boolean isLocal);
- native int nLightCreate();
- native void nLightSetColor(int l, float r, float g, float b);
- native void nLightSetPosition(int l, float x, float y, float z);
+ native void rsnProgramBindConstants(int con, int pv, int slot, int mID);
+ synchronized void nProgramBindConstants(int pv, int slot, int mID) {
+ rsnProgramBindConstants(mContext, pv, slot, mID);
+ }
+ native void rsnProgramBindTexture(int con, int vpf, int slot, int a);
+ synchronized void nProgramBindTexture(int vpf, int slot, int a) {
+ rsnProgramBindTexture(mContext, vpf, slot, a);
+ }
+ native void rsnProgramBindSampler(int con, int vpf, int slot, int s);
+ synchronized void nProgramBindSampler(int vpf, int slot, int s) {
+ rsnProgramBindSampler(mContext, vpf, slot, s);
+ }
- native int nMeshCreate(int vtxCount, int indexCount);
- native void nMeshBindVertex(int id, int alloc, int slot);
- native void nMeshBindIndex(int id, int alloc, int prim, int slot);
- native int nMeshGetVertexBufferCount(int id);
- native int nMeshGetIndexCount(int id);
- native void nMeshGetVertices(int id, int[] vtxIds, int vtxIdCount);
- native void nMeshGetIndices(int id, int[] idxIds, int[] primitives, int vtxIdCount);
+ native int rsnProgramFragmentCreate(int con, int[] params);
+ synchronized int nProgramFragmentCreate(int[] params) {
+ return rsnProgramFragmentCreate(mContext, params);
+ }
+ native int rsnProgramFragmentCreate2(int con, String shader, int[] params);
+ synchronized int nProgramFragmentCreate2(String shader, int[] params) {
+ return rsnProgramFragmentCreate2(mContext, shader, params);
+ }
- native void nAnimationBegin(int attribCount, int keyframeCount);
- native void nAnimationAdd(float time, float[] attribs);
- native int nAnimationCreate();
+ native int rsnProgramVertexCreate(int con, boolean texMat);
+ synchronized int nProgramVertexCreate(boolean texMat) {
+ return rsnProgramVertexCreate(mContext, texMat);
+ }
+ native int rsnProgramVertexCreate2(int con, String shader, int[] params);
+ synchronized int nProgramVertexCreate2(String shader, int[] params) {
+ return rsnProgramVertexCreate2(mContext, shader, params);
+ }
+
+
+ native int rsnMeshCreate(int con, int vtxCount, int indexCount);
+ synchronized int nMeshCreate(int vtxCount, int indexCount) {
+ return rsnMeshCreate(mContext, vtxCount, indexCount);
+ }
+ native void rsnMeshBindVertex(int con, int id, int alloc, int slot);
+ synchronized void nMeshBindVertex(int id, int alloc, int slot) {
+ rsnMeshBindVertex(mContext, id, alloc, slot);
+ }
+ native void rsnMeshBindIndex(int con, int id, int alloc, int prim, int slot);
+ synchronized void nMeshBindIndex(int id, int alloc, int prim, int slot) {
+ rsnMeshBindIndex(mContext, id, alloc, prim, slot);
+ }
+ native int rsnMeshGetVertexBufferCount(int con, int id);
+ synchronized int nMeshGetVertexBufferCount(int id) {
+ return rsnMeshGetVertexBufferCount(mContext, id);
+ }
+ native int rsnMeshGetIndexCount(int con, int id);
+ synchronized int nMeshGetIndexCount(int id) {
+ return rsnMeshGetIndexCount(mContext, id);
+ }
+ native void rsnMeshGetVertices(int con, int id, int[] vtxIds, int vtxIdCount);
+ synchronized void nMeshGetVertices(int id, int[] vtxIds, int vtxIdCount) {
+ rsnMeshGetVertices(mContext, id, vtxIds, vtxIdCount);
+ }
+ native void rsnMeshGetIndices(int con, int id, int[] idxIds, int[] primitives, int vtxIdCount);
+ synchronized void nMeshGetIndices(int id, int[] idxIds, int[] primitives, int vtxIdCount) {
+ rsnMeshGetIndices(mContext, id, idxIds, primitives, vtxIdCount);
+ }
+
protected int mDev;
protected int mContext;
@@ -293,9 +599,9 @@
// This function is a temporary solution. The final solution will
// used typed allocations where the message id is the type indicator.
int[] rbuf = new int[16];
- mRS.nContextInitToClient();
+ mRS.nContextInitToClient(mRS.mContext);
while(mRun) {
- int msg = mRS.nContextGetMessage(rbuf, true);
+ int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
if (msg == 0) {
// Should only happen during teardown.
// But we want to avoid starving other threads during
@@ -341,10 +647,10 @@
public void destroy() {
validate();
- nContextDeinitToClient();
+ nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
- nContextDestroy(mContext);
+ nContextDestroy();
mContext = 0;
nDeviceDestroy(mDev);
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index e90b4fc..61ecc8d 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -102,8 +102,7 @@
public class File extends BaseObj {
File(int id) {
- super(RenderScriptGL.this);
- mID = id;
+ super(id, RenderScriptGL.this);
}
}
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index da83d04..60c6cf4 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -47,8 +47,7 @@
}
Sampler(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
Sampler mSampler_CLAMP_NEAREST;
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index d9aec59..19c444c 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -56,8 +56,7 @@
Script(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
}
public void bindAllocation(Allocation va, int slot) {
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 8e45f2b..21053c9 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -97,8 +97,7 @@
Type(int id, RenderScript rs) {
- super(rs);
- mID = id;
+ super(id, rs);
mNativeCache = 0;
}
@@ -125,7 +124,7 @@
int elementID = dataBuffer[5];
if(elementID != 0) {
- mElement = new Element(mRS, elementID);
+ mElement = new Element(elementID, mRS);
mElement.updateFromNative();
}
calcElementCount();
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 8968dfb..3b85fdd 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -85,19 +85,16 @@
// ---------------------------------------------------------------------------
static void
-nContextFinish(JNIEnv *_env, jobject _this)
+nContextFinish(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextFinish, con(%p)", con);
rsContextFinish(con);
}
static void
-nAssignName(JNIEnv *_env, jobject _this, jint obj, jbyteArray str)
+nAssignName(JNIEnv *_env, jobject _this, RsContext con, jint obj, jbyteArray str)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAssignName, con(%p), obj(%p)", con, (void *)obj);
-
jint len = _env->GetArrayLength(str);
jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
rsAssignName(con, (void *)obj, (const char *)cptr, len);
@@ -105,40 +102,34 @@
}
static jstring
-nGetName(JNIEnv *_env, jobject _this, jint obj)
+nGetName(JNIEnv *_env, jobject _this, RsContext con, jint obj)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nGetName, con(%p), obj(%p)", con, (void *)obj);
-
const char *name = NULL;
rsGetName(con, (void *)obj, &name);
return _env->NewStringUTF(name);
}
static void
-nObjDestroy(JNIEnv *_env, jobject _this, jint obj)
+nObjDestroy(JNIEnv *_env, jobject _this, RsContext con, jint obj)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nObjDestroy, con(%p) obj(%p)", con, (void *)obj);
rsObjDestroy(con, (void *)obj);
}
static void
-nObjDestroyOOB(JNIEnv *_env, jobject _this, jint obj)
+nObjDestroyOOB(JNIEnv *_env, jobject _this, RsContext con, jint obj)
{
// This function only differs from nObjDestroy in that it calls the
// special Out Of Band version of ObjDestroy which is thread safe.
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nObjDestroyOOB, con(%p) obj(%p)", con, (void *)obj);
rsObjDestroyOOB(con, (void *)obj);
}
static jint
-nFileOpen(JNIEnv *_env, jobject _this, jbyteArray str)
+nFileOpen(JNIEnv *_env, jobject _this, RsContext con, jbyteArray str)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nFileOpen, con(%p)", con);
-
jint len = _env->GetArrayLength(str);
jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
jint ret = (jint)rsFileOpen(con, (const char *)cptr, len);
@@ -184,9 +175,8 @@
}
static void
-nContextSetPriority(JNIEnv *_env, jobject _this, jint p)
+nContextSetPriority(JNIEnv *_env, jobject _this, RsContext con, jint p)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("ContextSetPriority, con(%p), priority(%i)", con, p);
rsContextSetPriority(con, p);
}
@@ -194,9 +184,8 @@
static void
-nContextSetSurface(JNIEnv *_env, jobject _this, jint width, jint height, jobject wnd)
+nContextSetSurface(JNIEnv *_env, jobject _this, RsContext con, jint width, jint height, jobject wnd)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextSetSurface, con(%p), width(%i), height(%i), surface(%p)", con, width, height, (Surface *)wnd);
Surface * window = NULL;
@@ -212,40 +201,36 @@
}
static void
-nContextDestroy(JNIEnv *_env, jobject _this, jint con)
+nContextDestroy(JNIEnv *_env, jobject _this, RsContext con)
{
- LOG_API("nContextDestroy, con(%p)", (RsContext)con);
- rsContextDestroy((RsContext)con);
+ LOG_API("nContextDestroy, con(%p)", con);
+ rsContextDestroy(con);
}
static void
-nContextDump(JNIEnv *_env, jobject _this, jint bits)
+nContextDump(JNIEnv *_env, jobject _this, RsContext con, jint bits)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextDump, con(%p) bits(%i)", (RsContext)con, bits);
rsContextDump((RsContext)con, bits);
}
static void
-nContextPause(JNIEnv *_env, jobject _this)
+nContextPause(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextPause, con(%p)", con);
rsContextPause(con);
}
static void
-nContextResume(JNIEnv *_env, jobject _this)
+nContextResume(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextResume, con(%p)", con);
rsContextResume(con);
}
static jint
-nContextGetMessage(JNIEnv *_env, jobject _this, jintArray data, jboolean wait)
+nContextGetMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray data, jboolean wait)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nContextGetMessage, con(%p), len(%i)", con, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -258,34 +243,30 @@
return id;
}
-static void nContextInitToClient(JNIEnv *_env, jobject _this)
+static void nContextInitToClient(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextInitToClient, con(%p)", con);
rsContextInitToClient(con);
}
-static void nContextDeinitToClient(JNIEnv *_env, jobject _this)
+static void nContextDeinitToClient(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextDeinitToClient, con(%p)", con);
rsContextDeinitToClient(con);
}
static jint
-nElementCreate(JNIEnv *_env, jobject _this, jint type, jint kind, jboolean norm, jint size)
+nElementCreate(JNIEnv *_env, jobject _this, RsContext con, jint type, jint kind, jboolean norm, jint size)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", con, type, kind, norm, size);
return (jint)rsElementCreate(con, (RsDataType)type, (RsDataKind)kind, norm, size);
}
static jint
-nElementCreate2(JNIEnv *_env, jobject _this, jintArray _ids, jobjectArray _names)
+nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names)
{
int fieldCount = _env->GetArrayLength(_ids);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nElementCreate2, con(%p)", con);
jint *ids = _env->GetIntArrayElements(_ids, NULL);
@@ -309,10 +290,9 @@
}
static void
-nElementGetNativeData(JNIEnv *_env, jobject _this, jint id, jintArray _elementData)
+nElementGetNativeData(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _elementData)
{
int dataSize = _env->GetArrayLength(_elementData);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nElementGetNativeData, con(%p)", con);
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
@@ -328,10 +308,9 @@
static void
-nElementGetSubElements(JNIEnv *_env, jobject _this, jint id, jintArray _IDs, jobjectArray _names)
+nElementGetSubElements(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _IDs, jobjectArray _names)
{
int dataSize = _env->GetArrayLength(_IDs);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nElementGetSubElements, con(%p)", con);
uint32_t *ids = (uint32_t *)malloc((uint32_t)dataSize * sizeof(uint32_t));
@@ -351,39 +330,34 @@
// -----------------------------------
static void
-nTypeBegin(JNIEnv *_env, jobject _this, jint eID)
+nTypeBegin(JNIEnv *_env, jobject _this, RsContext con, jint eID)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nTypeBegin, con(%p) e(%p)", con, (RsElement)eID);
rsTypeBegin(con, (RsElement)eID);
}
static void
-nTypeAdd(JNIEnv *_env, jobject _this, jint dim, jint val)
+nTypeAdd(JNIEnv *_env, jobject _this, RsContext con, jint dim, jint val)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nTypeAdd, con(%p) dim(%i), val(%i)", con, dim, val);
rsTypeAdd(con, (RsDimension)dim, val);
}
static jint
-nTypeCreate(JNIEnv *_env, jobject _this)
+nTypeCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nTypeCreate, con(%p)", con);
return (jint)rsTypeCreate(con);
}
static void
-nTypeGetNativeData(JNIEnv *_env, jobject _this, jint id, jintArray _typeData)
+nTypeGetNativeData(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _typeData)
{
// We are packing 6 items: mDimX; mDimY; mDimZ;
// mDimLOD; mDimFaces; mElement; into typeData
int elementCount = _env->GetArrayLength(_typeData);
assert(elementCount == 6);
-
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nTypeCreate, con(%p)", con);
uint32_t typeData[6];
@@ -457,7 +431,7 @@
//{"nTypeFinalDestroy", "(Landroid/renderscript/Type;)V", (void*)nTypeFinalDestroy },
static void
-nTypeFinalDestroy(JNIEnv *_env, jobject _this, jobject _type)
+nTypeFinalDestroy(JNIEnv *_env, jobject _this, RsContext con, jobject _type)
{
TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
free(tc);
@@ -465,7 +439,7 @@
// native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
static void
-nTypeSetupFields(JNIEnv *_env, jobject _this, jobject _type, jintArray _types, jintArray _bits, jobjectArray _IDs)
+nTypeSetupFields(JNIEnv *_env, jobject _this, RsContext con, jobject _type, jintArray _types, jintArray _bits, jobjectArray _IDs)
{
int fieldCount = _env->GetArrayLength(_types);
size_t structSize = sizeof(TypeCache) + (sizeof(TypeFieldCache) * (fieldCount-1));
@@ -515,25 +489,22 @@
// -----------------------------------
static jint
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, jint e)
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint e)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationCreateTyped, con(%p), e(%p)", con, (RsElement)e);
return (jint) rsAllocationCreateTyped(con, (RsElement)e);
}
static void
-nAllocationUploadToTexture(JNIEnv *_env, jobject _this, jint a, jboolean genMip, jint mip)
+nAllocationUploadToTexture(JNIEnv *_env, jobject _this, RsContext con, jint a, jboolean genMip, jint mip)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationUploadToTexture, con(%p), a(%p), genMip(%i), mip(%i)", con, (RsAllocation)a, genMip, mip);
rsAllocationUploadToTexture(con, (RsAllocation)a, genMip, mip);
}
static void
-nAllocationUploadToBufferObject(JNIEnv *_env, jobject _this, jint a)
+nAllocationUploadToBufferObject(JNIEnv *_env, jobject _this, RsContext con, jint a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationUploadToBufferObject, con(%p), a(%p)", con, (RsAllocation)a);
rsAllocationUploadToBufferObject(con, (RsAllocation)a);
}
@@ -559,9 +530,8 @@
}
static int
-nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint dstFmt, jboolean genMips, jobject jbitmap)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
SkBitmap const * nativeBitmap =
(SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
const SkBitmap& bitmap(*nativeBitmap);
@@ -587,9 +557,8 @@
}
static int
-nAllocationCreateBitmapRef(JNIEnv *_env, jobject _this, jint type, jobject jbitmap)
+nAllocationCreateBitmapRef(JNIEnv *_env, jobject _this, RsContext con, jint type, jobject jbitmap)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
SkBitmap * nativeBitmap =
(SkBitmap *)_env->GetIntField(jbitmap, gNativeBitmapID);
@@ -601,10 +570,8 @@
}
static int
-nAllocationCreateFromAssetStream(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jint native_asset)
+nAllocationCreateFromAssetStream(JNIEnv *_env, jobject _this, RsContext con, jint dstFmt, jboolean genMips, jint native_asset)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-
Asset* asset = reinterpret_cast<Asset*>(native_asset);
SkBitmap bitmap;
SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
@@ -627,9 +594,8 @@
}
static int
-nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, RsContext con, jint dstFmt, jboolean genMips, jobject jbitmap)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
SkBitmap const * nativeBitmap =
(SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
const SkBitmap& bitmap(*nativeBitmap);
@@ -651,9 +617,8 @@
static void
-nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data, int sizeBytes)
+nAllocationSubData1D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jintArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -662,9 +627,8 @@
}
static void
-nAllocationSubData1D_s(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jshortArray data, int sizeBytes)
+nAllocationSubData1D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jshortArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation1DSubData_s, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
jshort *ptr = _env->GetShortArrayElements(data, NULL);
@@ -673,9 +637,8 @@
}
static void
-nAllocationSubData1D_b(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jbyteArray data, int sizeBytes)
+nAllocationSubData1D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jbyteArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation1DSubData_b, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
@@ -684,9 +647,8 @@
}
static void
-nAllocationSubData1D_f(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jfloatArray data, int sizeBytes)
+nAllocationSubData1D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jfloatArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -695,9 +657,8 @@
}
static void
-nAllocationSubData2D_i(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data, int sizeBytes)
+nAllocationSubData2D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -706,9 +667,8 @@
}
static void
-nAllocationSubData2D_f(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data, int sizeBytes)
+nAllocationSubData2D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data, int sizeBytes)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -717,9 +677,8 @@
}
static void
-nAllocationRead_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data)
+nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -728,9 +687,8 @@
}
static void
-nAllocationRead_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data)
+nAllocationRead_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jfloatArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -741,9 +699,8 @@
//{"nAllocationDataFromObject", "(ILandroid/renderscript/Type;Ljava/lang/Object;)V", (void*)nAllocationDataFromObject },
static void
-nAllocationSubDataFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _type, jint offset, jobject _o)
+nAllocationSubDataFromObject(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jobject _type, jint offset, jobject _o)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationDataFromObject con(%p), alloc(%p)", con, (RsAllocation)alloc);
const TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
@@ -759,9 +716,8 @@
}
static void
-nAllocationSubReadFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _type, jint offset, jobject _o)
+nAllocationSubReadFromObject(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jobject _type, jint offset, jobject _o)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationReadFromObject con(%p), alloc(%p)", con, (RsAllocation)alloc);
assert(offset == 0);
@@ -780,9 +736,8 @@
}
static jint
-nAllocationGetType(JNIEnv *_env, jobject _this, jint a)
+nAllocationGetType(JNIEnv *_env, jobject _this, RsContext con, jint a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAllocationGetType, con(%p), a(%p)", con, (RsAllocation)a);
return (jint) rsAllocationGetType(con, (RsAllocation)a);
}
@@ -790,10 +745,9 @@
// -----------------------------------
static int
-nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jint native_asset)
+nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, RsContext con, jint native_asset)
{
LOGV("______nFileA3D %u", (uint32_t) native_asset);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
Asset* asset = reinterpret_cast<Asset*>(native_asset);
@@ -802,21 +756,17 @@
}
static int
-nFileA3DGetNumIndexEntries(JNIEnv *_env, jobject _this, jint fileA3D)
+nFileA3DGetNumIndexEntries(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-
int32_t numEntries = 0;
rsFileA3DGetNumIndexEntries(con, &numEntries, (RsFile)fileA3D);
return numEntries;
}
static void
-nFileA3DGetIndexEntries(JNIEnv *_env, jobject _this, jint fileA3D, jint numEntries, jintArray _ids, jobjectArray _entries)
+nFileA3DGetIndexEntries(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D, jint numEntries, jintArray _ids, jobjectArray _entries)
{
LOGV("______nFileA3D %u", (uint32_t) fileA3D);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-
RsFileIndexEntry *fileEntries = (RsFileIndexEntry*)malloc((uint32_t)numEntries * sizeof(RsFileIndexEntry));
rsFileA3DGetIndexEntries(con, fileEntries, (uint32_t)numEntries, (RsFile)fileA3D);
@@ -830,11 +780,9 @@
}
static int
-nFileA3DGetEntryByIndex(JNIEnv *_env, jobject _this, jint fileA3D, jint index)
+nFileA3DGetEntryByIndex(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D, jint index)
{
LOGV("______nFileA3D %u", (uint32_t) fileA3D);
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-
jint id = (jint)rsFileA3DGetEntryByIndex(con, (uint32_t)index, (RsFile)fileA3D);
return id;
}
@@ -842,9 +790,8 @@
// -----------------------------------
static int
-nFontCreateFromFile(JNIEnv *_env, jobject _this, jstring fileName, jint fontSize, jint dpi)
+nFontCreateFromFile(JNIEnv *_env, jobject _this, RsContext con, jstring fileName, jint fontSize, jint dpi)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
const char* fileNameUTF = _env->GetStringUTFChars(fileName, NULL);
jint id = (jint)rsFontCreateFromFile(con, fileNameUTF, fontSize, dpi);
@@ -855,25 +802,22 @@
// -----------------------------------
static void
-nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint alloc)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter1DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter1D)adapter, (RsAllocation)alloc);
rsAdapter1DBindAllocation(con, (RsAdapter1D)adapter, (RsAllocation)alloc);
}
static void
-nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint dim, jint value)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter1DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter1D)adapter, dim, value);
rsAdapter1DSetConstraint(con, (RsAdapter1D)adapter, (RsDimension)dim, value);
}
static void
-nAdapter1DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+nAdapter1DData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jintArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter1DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -882,9 +826,8 @@
}
static void
-nAdapter1DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jintArray data)
+nAdapter1DSubData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint offset, jint count, jintArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -893,9 +836,8 @@
}
static void
-nAdapter1DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+nAdapter1DData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jfloatArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter1DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -904,9 +846,8 @@
}
static void
-nAdapter1DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jfloatArray data)
+nAdapter1DSubData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint offset, jint count, jfloatArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -915,9 +856,8 @@
}
static jint
-nAdapter1DCreate(JNIEnv *_env, jobject _this)
+nAdapter1DCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter1DCreate, con(%p)", con);
return (jint)rsAdapter1DCreate(con);
}
@@ -925,25 +865,22 @@
// -----------------------------------
static void
-nAdapter2DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+nAdapter2DBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint alloc)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter2DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter2D)adapter, (RsAllocation)alloc);
rsAdapter2DBindAllocation(con, (RsAdapter2D)adapter, (RsAllocation)alloc);
}
static void
-nAdapter2DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+nAdapter2DSetConstraint(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint dim, jint value)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter2DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter2D)adapter, dim, value);
rsAdapter2DSetConstraint(con, (RsAdapter2D)adapter, (RsDimension)dim, value);
}
static void
-nAdapter2DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+nAdapter2DData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jintArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter2DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -952,9 +889,8 @@
}
static void
-nAdapter2DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+nAdapter2DData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jfloatArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter2DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -963,9 +899,8 @@
}
static void
-nAdapter2DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jintArray data)
+nAdapter2DSubData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint xoff, jint yoff, jint w, jint h, jintArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
@@ -975,9 +910,8 @@
}
static void
-nAdapter2DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
+nAdapter2DSubData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint len = _env->GetArrayLength(data);
LOG_API("nAdapter2DSubData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
@@ -987,9 +921,8 @@
}
static jint
-nAdapter2DCreate(JNIEnv *_env, jobject _this)
+nAdapter2DCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nAdapter2DCreate, con(%p)", con);
return (jint)rsAdapter2DCreate(con);
}
@@ -997,33 +930,29 @@
// -----------------------------------
static void
-nScriptBindAllocation(JNIEnv *_env, jobject _this, jint script, jint alloc, jint slot)
+nScriptBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint script, jint alloc, jint slot)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)", con, (RsScript)script, (RsAllocation)alloc, slot);
rsScriptBindAllocation(con, (RsScript)script, (RsAllocation)alloc, slot);
}
static void
-nScriptSetVarI(JNIEnv *_env, jobject _this, jint script, jint slot, jint val)
+nScriptSetVarI(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i), b(%f), a(%f)", con, (void *)script, slot, val);
rsScriptSetVarI(con, (RsScript)script, slot, val);
}
static void
-nScriptSetVarF(JNIEnv *_env, jobject _this, jint script, jint slot, float val)
+nScriptSetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, float val)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i), b(%f), a(%f)", con, (void *)script, slot, val);
rsScriptSetVarF(con, (RsScript)script, slot, val);
}
static void
-nScriptSetVarV(JNIEnv *_env, jobject _this, jint script, jint slot, jbyteArray data)
+nScriptSetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
@@ -1033,9 +962,8 @@
static void
-nScriptSetTimeZone(JNIEnv *_env, jobject _this, jint script, jbyteArray timeZone)
+nScriptSetTimeZone(JNIEnv *_env, jobject _this, RsContext con, jint script, jbyteArray timeZone)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", con, (void *)script, (const char *)timeZone);
jint length = _env->GetArrayLength(timeZone);
@@ -1050,17 +978,15 @@
}
static void
-nScriptInvoke(JNIEnv *_env, jobject _this, jint obj, jint slot)
+nScriptInvoke(JNIEnv *_env, jobject _this, RsContext con, jint obj, jint slot)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptInvoke, con(%p), script(%p)", con, (void *)obj);
rsScriptInvoke(con, (RsScript)obj, slot);
}
static void
-nScriptInvokeV(JNIEnv *_env, jobject _this, jint script, jint slot, jbyteArray data)
+nScriptInvokeV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptInvokeV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
@@ -1072,18 +998,16 @@
// -----------------------------------
static void
-nScriptCBegin(JNIEnv *_env, jobject _this)
+nScriptCBegin(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptCBegin, con(%p)", con);
rsScriptCBegin(con);
}
static void
-nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef,
+nScriptCSetScript(JNIEnv *_env, jobject _this, RsContext con, jbyteArray scriptRef,
jint offset, jint length)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("!!! nScriptCSetScript, con(%p)", con);
jint _exception = 0;
jint remaining;
@@ -1124,9 +1048,8 @@
}
static jint
-nScriptCCreate(JNIEnv *_env, jobject _this)
+nScriptCCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nScriptCCreate, con(%p)", con);
return (jint)rsScriptCCreate(con);
}
@@ -1134,84 +1057,73 @@
// ---------------------------------------------------------------------------
static void
-nProgramStoreBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+nProgramStoreBegin(JNIEnv *_env, jobject _this, RsContext con, jint in, jint out)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
rsProgramStoreBegin(con, (RsElement)in, (RsElement)out);
}
static void
-nProgramStoreDepthFunc(JNIEnv *_env, jobject _this, jint func)
+nProgramStoreDepthFunc(JNIEnv *_env, jobject _this, RsContext con, jint func)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreDepthFunc, con(%p), func(%i)", con, func);
rsProgramStoreDepthFunc(con, (RsDepthFunc)func);
}
static void
-nProgramStoreDepthMask(JNIEnv *_env, jobject _this, jboolean enable)
+nProgramStoreDepthMask(JNIEnv *_env, jobject _this, RsContext con, jboolean enable)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreDepthMask, con(%p), enable(%i)", con, enable);
rsProgramStoreDepthMask(con, enable);
}
static void
-nProgramStoreColorMask(JNIEnv *_env, jobject _this, jboolean r, jboolean g, jboolean b, jboolean a)
+nProgramStoreColorMask(JNIEnv *_env, jobject _this, RsContext con, jboolean r, jboolean g, jboolean b, jboolean a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreColorMask, con(%p), r(%i), g(%i), b(%i), a(%i)", con, r, g, b, a);
rsProgramStoreColorMask(con, r, g, b, a);
}
static void
-nProgramStoreBlendFunc(JNIEnv *_env, jobject _this, int src, int dst)
+nProgramStoreBlendFunc(JNIEnv *_env, jobject _this, RsContext con, int src, int dst)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreBlendFunc, con(%p), src(%i), dst(%i)", con, src, dst);
rsProgramStoreBlendFunc(con, (RsBlendSrcFunc)src, (RsBlendDstFunc)dst);
}
static void
-nProgramStoreDither(JNIEnv *_env, jobject _this, jboolean enable)
+nProgramStoreDither(JNIEnv *_env, jobject _this, RsContext con, jboolean enable)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreDither, con(%p), enable(%i)", con, enable);
rsProgramStoreDither(con, enable);
}
static jint
-nProgramStoreCreate(JNIEnv *_env, jobject _this)
+nProgramStoreCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramStoreCreate, con(%p)", con);
-
return (jint)rsProgramStoreCreate(con);
}
// ---------------------------------------------------------------------------
static void
-nProgramBindConstants(JNIEnv *_env, jobject _this, jint vpv, jint slot, jint a)
+nProgramBindConstants(JNIEnv *_env, jobject _this, RsContext con, jint vpv, jint slot, jint a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramBindConstants, con(%p), vpf(%p), sloat(%i), a(%p)", con, (RsProgramVertex)vpv, slot, (RsAllocation)a);
rsProgramBindConstants(con, (RsProgram)vpv, slot, (RsAllocation)a);
}
static void
-nProgramBindTexture(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+nProgramBindTexture(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint slot, jint a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramBindTexture, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
rsProgramBindTexture(con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
}
static void
-nProgramBindSampler(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+nProgramBindSampler(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint slot, jint a)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramBindSampler, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsSampler)a);
rsProgramBindSampler(con, (RsProgramFragment)vpf, slot, (RsSampler)a);
}
@@ -1219,9 +1131,8 @@
// ---------------------------------------------------------------------------
static jint
-nProgramFragmentCreate(JNIEnv *_env, jobject _this, jintArray params)
+nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jintArray params)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
jint *paramPtr = _env->GetIntArrayElements(params, NULL);
jint paramLen = _env->GetArrayLength(params);
@@ -1233,9 +1144,8 @@
}
static jint
-nProgramFragmentCreate2(JNIEnv *_env, jobject _this, jstring shader, jintArray params)
+nProgramFragmentCreate2(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
const char* shaderUTF = _env->GetStringUTFChars(shader, NULL);
jint shaderLen = _env->GetStringUTFLength(shader);
jint *paramPtr = _env->GetIntArrayElements(params, NULL);
@@ -1253,17 +1163,15 @@
// ---------------------------------------------------------------------------
static jint
-nProgramVertexCreate(JNIEnv *_env, jobject _this, jboolean texMat)
+nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean texMat)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramVertexCreate, con(%p), texMat(%i)", con, texMat);
return (jint)rsProgramVertexCreate(con, texMat);
}
static jint
-nProgramVertexCreate2(JNIEnv *_env, jobject _this, jstring shader, jintArray params)
+nProgramVertexCreate2(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
const char* shaderUTF = _env->GetStringUTFChars(shader, NULL);
jint shaderLen = _env->GetStringUTFLength(shader);
jint *paramPtr = _env->GetIntArrayElements(params, NULL);
@@ -1280,26 +1188,23 @@
// ---------------------------------------------------------------------------
static jint
-nProgramRasterCreate(JNIEnv *_env, jobject _this, jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
+nProgramRasterCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramRasterCreate, con(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
con, pointSmooth, lineSmooth, pointSprite);
return (jint)rsProgramRasterCreate(con, pointSmooth, lineSmooth, pointSprite);
}
static void
-nProgramRasterSetLineWidth(JNIEnv *_env, jobject _this, jint vpr, jfloat v)
+nProgramRasterSetLineWidth(JNIEnv *_env, jobject _this, RsContext con, jint vpr, jfloat v)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramRasterSetLineWidth, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v);
rsProgramRasterSetLineWidth(con, (RsProgramRaster)vpr, v);
}
static void
-nProgramRasterSetCullMode(JNIEnv *_env, jobject _this, jint vpr, jint v)
+nProgramRasterSetCullMode(JNIEnv *_env, jobject _this, RsContext con, jint vpr, jint v)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramRasterSetCullMode, con(%p), vpf(%p), value(%i)", con, (RsProgramRaster)vpr, v);
rsProgramRasterSetCullMode(con, (RsProgramRaster)vpr, (RsCullMode)v);
}
@@ -1308,41 +1213,36 @@
// ---------------------------------------------------------------------------
static void
-nContextBindRootScript(JNIEnv *_env, jobject _this, jint script)
+nContextBindRootScript(JNIEnv *_env, jobject _this, RsContext con, jint script)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextBindRootScript, con(%p), script(%p)", con, (RsScript)script);
rsContextBindRootScript(con, (RsScript)script);
}
static void
-nContextBindProgramStore(JNIEnv *_env, jobject _this, jint pfs)
+nContextBindProgramStore(JNIEnv *_env, jobject _this, RsContext con, jint pfs)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextBindProgramStore, con(%p), pfs(%p)", con, (RsProgramStore)pfs);
rsContextBindProgramStore(con, (RsProgramStore)pfs);
}
static void
-nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, RsContext con, jint pf)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", con, (RsProgramFragment)pf);
rsContextBindProgramFragment(con, (RsProgramFragment)pf);
}
static void
-nContextBindProgramVertex(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, RsContext con, jint pf)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", con, (RsProgramVertex)pf);
rsContextBindProgramVertex(con, (RsProgramVertex)pf);
}
static void
-nContextBindProgramRaster(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramRaster(JNIEnv *_env, jobject _this, RsContext con, jint pf)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nContextBindProgramRaster, con(%p), pf(%p)", con, (RsProgramRaster)pf);
rsContextBindProgramRaster(con, (RsProgramRaster)pf);
}
@@ -1351,110 +1251,53 @@
// ---------------------------------------------------------------------------
static void
-nSamplerBegin(JNIEnv *_env, jobject _this)
+nSamplerBegin(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nSamplerBegin, con(%p)", con);
rsSamplerBegin(con);
}
static void
-nSamplerSet(JNIEnv *_env, jobject _this, jint p, jint v)
+nSamplerSet(JNIEnv *_env, jobject _this, RsContext con, jint p, jint v)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nSamplerSet, con(%p), param(%i), value(%i)", con, p, v);
rsSamplerSet(con, (RsSamplerParam)p, (RsSamplerValue)v);
}
static jint
-nSamplerCreate(JNIEnv *_env, jobject _this)
+nSamplerCreate(JNIEnv *_env, jobject _this, RsContext con)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nSamplerCreate, con(%p)", con);
return (jint)rsSamplerCreate(con);
}
// ---------------------------------------------------------------------------
-static void
-nLightBegin(JNIEnv *_env, jobject _this)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightBegin, con(%p)", con);
- rsLightBegin(con);
-}
-
-static void
-nLightSetIsMono(JNIEnv *_env, jobject _this, jboolean isMono)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightSetIsMono, con(%p), isMono(%i)", con, isMono);
- rsLightSetMonochromatic(con, isMono);
-}
-
-static void
-nLightSetIsLocal(JNIEnv *_env, jobject _this, jboolean isLocal)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightSetIsLocal, con(%p), isLocal(%i)", con, isLocal);
- rsLightSetLocal(con, isLocal);
-}
-
static jint
-nLightCreate(JNIEnv *_env, jobject _this)
+nMeshCreate(JNIEnv *_env, jobject _this, RsContext con, jint vtxCount, jint idxCount)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightCreate, con(%p)", con);
- return (jint)rsLightCreate(con);
-}
-
-static void
-nLightSetColor(JNIEnv *_env, jobject _this, jint light, float r, float g, float b)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightSetColor, con(%p), light(%p), r(%f), g(%f), b(%f)", con, (RsLight)light, r, g, b);
- rsLightSetColor(con, (RsLight)light, r, g, b);
-}
-
-static void
-nLightSetPosition(JNIEnv *_env, jobject _this, jint light, float x, float y, float z)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nLightSetPosition, con(%p), light(%p), x(%f), y(%f), z(%f)", con, (RsLight)light, x, y, z);
- rsLightSetPosition(con, (RsLight)light, x, y, z);
-}
-
-// ---------------------------------------------------------------------------
-
-static jint
-nMeshCreate(JNIEnv *_env, jobject _this, jint vtxCount, jint idxCount)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshCreate, con(%p), vtxCount(%i), idxCount(%i)", con, vtxCount, idxCount);
int id = (int)rsMeshCreate(con, vtxCount, idxCount);
return id;
}
static void
-nMeshBindVertex(JNIEnv *_env, jobject _this, jint mesh, jint alloc, jint slot)
+nMeshBindVertex(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jint alloc, jint slot)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)mesh, (RsAllocation)alloc, slot);
rsMeshBindVertex(con, (RsMesh)mesh, (RsAllocation)alloc, slot);
}
static void
-nMeshBindIndex(JNIEnv *_env, jobject _this, jint mesh, jint alloc, jint primID, jint slot)
+nMeshBindIndex(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jint alloc, jint primID, jint slot)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)mesh, (RsAllocation)alloc);
rsMeshBindIndex(con, (RsMesh)mesh, (RsAllocation)alloc, primID, slot);
}
static jint
-nMeshGetVertexBufferCount(JNIEnv *_env, jobject _this, jint mesh)
+nMeshGetVertexBufferCount(JNIEnv *_env, jobject _this, RsContext con, jint mesh)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshGetVertexBufferCount, con(%p), Mesh(%p)", con, (RsMesh)mesh);
jint vtxCount = 0;
rsMeshGetVertexBufferCount(con, (RsMesh)mesh, &vtxCount);
@@ -1462,9 +1305,8 @@
}
static jint
-nMeshGetIndexCount(JNIEnv *_env, jobject _this, jint mesh)
+nMeshGetIndexCount(JNIEnv *_env, jobject _this, RsContext con, jint mesh)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshGetIndexCount, con(%p), Mesh(%p)", con, (RsMesh)mesh);
jint idxCount = 0;
rsMeshGetIndexCount(con, (RsMesh)mesh, &idxCount);
@@ -1472,9 +1314,8 @@
}
static void
-nMeshGetVertices(JNIEnv *_env, jobject _this, jint mesh, jintArray _ids, int numVtxIDs)
+nMeshGetVertices(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jintArray _ids, int numVtxIDs)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh);
RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numVtxIDs * sizeof(RsAllocation));
@@ -1488,9 +1329,8 @@
}
static void
-nMeshGetIndices(JNIEnv *_env, jobject _this, jint mesh, jintArray _idxIds, jintArray _primitives, int numIndices)
+nMeshGetIndices(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jintArray _idxIds, jintArray _primitives, int numIndices)
{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh);
RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numIndices * sizeof(RsAllocation));
@@ -1519,137 +1359,133 @@
{"nDeviceCreate", "()I", (void*)nDeviceCreate },
{"nDeviceDestroy", "(I)V", (void*)nDeviceDestroy },
{"nDeviceSetConfig", "(III)V", (void*)nDeviceSetConfig },
-{"nContextCreate", "(II)I", (void*)nContextCreate },
-{"nContextCreateGL", "(IIZ)I", (void*)nContextCreateGL },
-{"nContextFinish", "()V", (void*)nContextFinish },
-{"nContextSetPriority", "(I)V", (void*)nContextSetPriority },
-{"nContextSetSurface", "(IILandroid/view/Surface;)V", (void*)nContextSetSurface },
-{"nContextDestroy", "(I)V", (void*)nContextDestroy },
-{"nContextDump", "(I)V", (void*)nContextDump },
-{"nContextPause", "()V", (void*)nContextPause },
-{"nContextResume", "()V", (void*)nContextResume },
-{"nAssignName", "(I[B)V", (void*)nAssignName },
-{"nGetName", "(I)Ljava/lang/String;", (void*)nGetName },
-{"nObjDestroy", "(I)V", (void*)nObjDestroy },
-{"nObjDestroyOOB", "(I)V", (void*)nObjDestroyOOB },
-{"nContextGetMessage", "([IZ)I", (void*)nContextGetMessage },
-{"nContextInitToClient", "()V", (void*)nContextInitToClient },
-{"nContextDeinitToClient", "()V", (void*)nContextDeinitToClient },
+{"nContextGetMessage", "(I[IZ)I", (void*)nContextGetMessage },
+{"nContextInitToClient", "(I)V", (void*)nContextInitToClient },
+{"nContextDeinitToClient", "(I)V", (void*)nContextDeinitToClient },
-{"nFileOpen", "([B)I", (void*)nFileOpen },
-{"nFileA3DCreateFromAssetStream", "(I)I", (void*)nFileA3DCreateFromAssetStream },
-{"nFileA3DGetNumIndexEntries", "(I)I", (void*)nFileA3DGetNumIndexEntries },
-{"nFileA3DGetIndexEntries", "(II[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries },
-{"nFileA3DGetEntryByIndex", "(II)I", (void*)nFileA3DGetEntryByIndex },
-{"nFontCreateFromFile", "(Ljava/lang/String;II)I", (void*)nFontCreateFromFile },
+// All methods below are thread protected in java.
+{"rsnContextCreate", "(II)I", (void*)nContextCreate },
+{"rsnContextCreateGL", "(IIZ)I", (void*)nContextCreateGL },
+{"rsnContextFinish", "(I)V", (void*)nContextFinish },
+{"rsnContextSetPriority", "(II)V", (void*)nContextSetPriority },
+{"rsnContextSetSurface", "(IIILandroid/view/Surface;)V", (void*)nContextSetSurface },
+{"rsnContextDestroy", "(I)V", (void*)nContextDestroy },
+{"rsnContextDump", "(II)V", (void*)nContextDump },
+{"rsnContextPause", "(I)V", (void*)nContextPause },
+{"rsnContextResume", "(I)V", (void*)nContextResume },
+{"rsnAssignName", "(II[B)V", (void*)nAssignName },
+{"rsnGetName", "(II)Ljava/lang/String;", (void*)nGetName },
+{"rsnObjDestroy", "(II)V", (void*)nObjDestroy },
+{"rsnObjDestroyOOB", "(II)V", (void*)nObjDestroyOOB },
-{"nElementCreate", "(IIZI)I", (void*)nElementCreate },
-{"nElementCreate2", "([I[Ljava/lang/String;)I", (void*)nElementCreate2 },
-{"nElementGetNativeData", "(I[I)V", (void*)nElementGetNativeData },
-{"nElementGetSubElements", "(I[I[Ljava/lang/String;)V", (void*)nElementGetSubElements },
+{"rsnFileOpen", "(I[B)I", (void*)nFileOpen },
+{"rsnFileA3DCreateFromAssetStream", "(II)I", (void*)nFileA3DCreateFromAssetStream },
+{"rsnFileA3DGetNumIndexEntries", "(II)I", (void*)nFileA3DGetNumIndexEntries },
+{"rsnFileA3DGetIndexEntries", "(III[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries },
+{"rsnFileA3DGetEntryByIndex", "(III)I", (void*)nFileA3DGetEntryByIndex },
-{"nTypeBegin", "(I)V", (void*)nTypeBegin },
-{"nTypeAdd", "(II)V", (void*)nTypeAdd },
-{"nTypeCreate", "()I", (void*)nTypeCreate },
-{"nTypeFinalDestroy", "(Landroid/renderscript/Type;)V", (void*)nTypeFinalDestroy },
-{"nTypeSetupFields", "(Landroid/renderscript/Type;[I[I[Ljava/lang/reflect/Field;)V", (void*)nTypeSetupFields },
-{"nTypeGetNativeData", "(I[I)V", (void*)nTypeGetNativeData },
+{"rsnFontCreateFromFile", "(ILjava/lang/String;II)I", (void*)nFontCreateFromFile },
-{"nAllocationCreateTyped", "(I)I", (void*)nAllocationCreateTyped },
-{"nAllocationCreateFromBitmap", "(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap },
-{"nAllocationCreateBitmapRef", "(ILandroid/graphics/Bitmap;)I", (void*)nAllocationCreateBitmapRef },
-{"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmapBoxed },
-{"nAllocationCreateFromAssetStream","(IZI)I", (void*)nAllocationCreateFromAssetStream },
-{"nAllocationUploadToTexture", "(IZI)V", (void*)nAllocationUploadToTexture },
-{"nAllocationUploadToBufferObject","(I)V", (void*)nAllocationUploadToBufferObject },
-{"nAllocationSubData1D", "(III[II)V", (void*)nAllocationSubData1D_i },
-{"nAllocationSubData1D", "(III[SI)V", (void*)nAllocationSubData1D_s },
-{"nAllocationSubData1D", "(III[BI)V", (void*)nAllocationSubData1D_b },
-{"nAllocationSubData1D", "(III[FI)V", (void*)nAllocationSubData1D_f },
-{"nAllocationSubData2D", "(IIIII[II)V", (void*)nAllocationSubData2D_i },
-{"nAllocationSubData2D", "(IIIII[FI)V", (void*)nAllocationSubData2D_f },
-{"nAllocationRead", "(I[I)V", (void*)nAllocationRead_i },
-{"nAllocationRead", "(I[F)V", (void*)nAllocationRead_f },
-{"nAllocationSubDataFromObject", "(ILandroid/renderscript/Type;ILjava/lang/Object;)V", (void*)nAllocationSubDataFromObject },
-{"nAllocationSubReadFromObject", "(ILandroid/renderscript/Type;ILjava/lang/Object;)V", (void*)nAllocationSubReadFromObject },
-{"nAllocationGetType", "(I)I", (void*)nAllocationGetType},
+{"rsnElementCreate", "(IIIZI)I", (void*)nElementCreate },
+{"rsnElementCreate2", "(I[I[Ljava/lang/String;)I", (void*)nElementCreate2 },
+{"rsnElementGetNativeData", "(II[I)V", (void*)nElementGetNativeData },
+{"rsnElementGetSubElements", "(II[I[Ljava/lang/String;)V", (void*)nElementGetSubElements },
-{"nAdapter1DBindAllocation", "(II)V", (void*)nAdapter1DBindAllocation },
-{"nAdapter1DSetConstraint", "(III)V", (void*)nAdapter1DSetConstraint },
-{"nAdapter1DData", "(I[I)V", (void*)nAdapter1DData_i },
-{"nAdapter1DData", "(I[F)V", (void*)nAdapter1DData_f },
-{"nAdapter1DSubData", "(III[I)V", (void*)nAdapter1DSubData_i },
-{"nAdapter1DSubData", "(III[F)V", (void*)nAdapter1DSubData_f },
-{"nAdapter1DCreate", "()I", (void*)nAdapter1DCreate },
+{"rsnTypeBegin", "(II)V", (void*)nTypeBegin },
+{"rsnTypeAdd", "(III)V", (void*)nTypeAdd },
+{"rsnTypeCreate", "(I)I", (void*)nTypeCreate },
+{"rsnTypeFinalDestroy", "(ILandroid/renderscript/Type;)V", (void*)nTypeFinalDestroy },
+{"rsnTypeSetupFields", "(ILandroid/renderscript/Type;[I[I[Ljava/lang/reflect/Field;)V", (void*)nTypeSetupFields },
+{"rsnTypeGetNativeData", "(II[I)V", (void*)nTypeGetNativeData },
-{"nAdapter2DBindAllocation", "(II)V", (void*)nAdapter2DBindAllocation },
-{"nAdapter2DSetConstraint", "(III)V", (void*)nAdapter2DSetConstraint },
-{"nAdapter2DData", "(I[I)V", (void*)nAdapter2DData_i },
-{"nAdapter2DData", "(I[F)V", (void*)nAdapter2DData_f },
-{"nAdapter2DSubData", "(IIIII[I)V", (void*)nAdapter2DSubData_i },
-{"nAdapter2DSubData", "(IIIII[F)V", (void*)nAdapter2DSubData_f },
-{"nAdapter2DCreate", "()I", (void*)nAdapter2DCreate },
+{"rsnAllocationCreateTyped", "(II)I", (void*)nAllocationCreateTyped },
+{"rsnAllocationCreateFromBitmap", "(IIZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap },
+{"rsnAllocationCreateBitmapRef", "(IILandroid/graphics/Bitmap;)I", (void*)nAllocationCreateBitmapRef },
+{"rsnAllocationCreateFromBitmapBoxed","(IIZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmapBoxed },
+{"rsnAllocationCreateFromAssetStream","(IIZI)I", (void*)nAllocationCreateFromAssetStream },
+{"rsnAllocationUploadToTexture", "(IIZI)V", (void*)nAllocationUploadToTexture },
+{"rsnAllocationUploadToBufferObject","(II)V", (void*)nAllocationUploadToBufferObject },
+{"rsnAllocationSubData1D", "(IIII[II)V", (void*)nAllocationSubData1D_i },
+{"rsnAllocationSubData1D", "(IIII[SI)V", (void*)nAllocationSubData1D_s },
+{"rsnAllocationSubData1D", "(IIII[BI)V", (void*)nAllocationSubData1D_b },
+{"rsnAllocationSubData1D", "(IIII[FI)V", (void*)nAllocationSubData1D_f },
+{"rsnAllocationSubData2D", "(IIIIII[II)V", (void*)nAllocationSubData2D_i },
+{"rsnAllocationSubData2D", "(IIIIII[FI)V", (void*)nAllocationSubData2D_f },
+{"rsnAllocationRead", "(II[I)V", (void*)nAllocationRead_i },
+{"rsnAllocationRead", "(II[F)V", (void*)nAllocationRead_f },
+{"rsnAllocationSubDataFromObject", "(IILandroid/renderscript/Type;ILjava/lang/Object;)V", (void*)nAllocationSubDataFromObject },
+{"rsnAllocationSubReadFromObject", "(IILandroid/renderscript/Type;ILjava/lang/Object;)V", (void*)nAllocationSubReadFromObject },
+{"rsnAllocationGetType", "(II)I", (void*)nAllocationGetType},
-{"nScriptBindAllocation", "(III)V", (void*)nScriptBindAllocation },
-{"nScriptSetTimeZone", "(I[B)V", (void*)nScriptSetTimeZone },
-{"nScriptInvoke", "(II)V", (void*)nScriptInvoke },
-{"nScriptInvokeV", "(II[B)V", (void*)nScriptInvokeV },
-{"nScriptSetVarI", "(III)V", (void*)nScriptSetVarI },
-{"nScriptSetVarF", "(IIF)V", (void*)nScriptSetVarF },
-{"nScriptSetVarV", "(II[B)V", (void*)nScriptSetVarV },
+{"rsnAdapter1DBindAllocation", "(III)V", (void*)nAdapter1DBindAllocation },
+{"rsnAdapter1DSetConstraint", "(IIII)V", (void*)nAdapter1DSetConstraint },
+{"rsnAdapter1DData", "(II[I)V", (void*)nAdapter1DData_i },
+{"rsnAdapter1DData", "(II[F)V", (void*)nAdapter1DData_f },
+{"rsnAdapter1DSubData", "(IIII[I)V", (void*)nAdapter1DSubData_i },
+{"rsnAdapter1DSubData", "(IIII[F)V", (void*)nAdapter1DSubData_f },
+{"rsnAdapter1DCreate", "(I)I", (void*)nAdapter1DCreate },
-{"nScriptCBegin", "()V", (void*)nScriptCBegin },
-{"nScriptCSetScript", "([BII)V", (void*)nScriptCSetScript },
-{"nScriptCCreate", "()I", (void*)nScriptCCreate },
+{"rsnAdapter2DBindAllocation", "(III)V", (void*)nAdapter2DBindAllocation },
+{"rsnAdapter2DSetConstraint", "(IIII)V", (void*)nAdapter2DSetConstraint },
+{"rsnAdapter2DData", "(II[I)V", (void*)nAdapter2DData_i },
+{"rsnAdapter2DData", "(II[F)V", (void*)nAdapter2DData_f },
+{"rsnAdapter2DSubData", "(IIIIII[I)V", (void*)nAdapter2DSubData_i },
+{"rsnAdapter2DSubData", "(IIIIII[F)V", (void*)nAdapter2DSubData_f },
+{"rsnAdapter2DCreate", "(I)I", (void*)nAdapter2DCreate },
-{"nProgramStoreBegin", "(II)V", (void*)nProgramStoreBegin },
-{"nProgramStoreDepthFunc", "(I)V", (void*)nProgramStoreDepthFunc },
-{"nProgramStoreDepthMask", "(Z)V", (void*)nProgramStoreDepthMask },
-{"nProgramStoreColorMask", "(ZZZZ)V", (void*)nProgramStoreColorMask },
-{"nProgramStoreBlendFunc", "(II)V", (void*)nProgramStoreBlendFunc },
-{"nProgramStoreDither", "(Z)V", (void*)nProgramStoreDither },
-{"nProgramStoreCreate", "()I", (void*)nProgramStoreCreate },
+{"rsnScriptBindAllocation", "(IIII)V", (void*)nScriptBindAllocation },
+{"rsnScriptSetTimeZone", "(II[B)V", (void*)nScriptSetTimeZone },
+{"rsnScriptInvoke", "(III)V", (void*)nScriptInvoke },
+{"rsnScriptInvokeV", "(III[B)V", (void*)nScriptInvokeV },
+{"rsnScriptSetVarI", "(IIII)V", (void*)nScriptSetVarI },
+{"rsnScriptSetVarF", "(IIIF)V", (void*)nScriptSetVarF },
+{"rsnScriptSetVarV", "(III[B)V", (void*)nScriptSetVarV },
-{"nProgramBindConstants", "(III)V", (void*)nProgramBindConstants },
-{"nProgramBindTexture", "(III)V", (void*)nProgramBindTexture },
-{"nProgramBindSampler", "(III)V", (void*)nProgramBindSampler },
+{"rsnScriptCBegin", "(I)V", (void*)nScriptCBegin },
+{"rsnScriptCSetScript", "(I[BII)V", (void*)nScriptCSetScript },
+{"rsnScriptCCreate", "(I)I", (void*)nScriptCCreate },
-{"nProgramFragmentCreate", "([I)I", (void*)nProgramFragmentCreate },
-{"nProgramFragmentCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramFragmentCreate2 },
+{"rsnProgramStoreBegin", "(III)V", (void*)nProgramStoreBegin },
+{"rsnProgramStoreDepthFunc", "(II)V", (void*)nProgramStoreDepthFunc },
+{"rsnProgramStoreDepthMask", "(IZ)V", (void*)nProgramStoreDepthMask },
+{"rsnProgramStoreColorMask", "(IZZZZ)V", (void*)nProgramStoreColorMask },
+{"rsnProgramStoreBlendFunc", "(III)V", (void*)nProgramStoreBlendFunc },
+{"rsnProgramStoreDither", "(IZ)V", (void*)nProgramStoreDither },
+{"rsnProgramStoreCreate", "(I)I", (void*)nProgramStoreCreate },
-{"nProgramRasterCreate", "(ZZZ)I", (void*)nProgramRasterCreate },
-{"nProgramRasterSetLineWidth", "(IF)V", (void*)nProgramRasterSetLineWidth },
-{"nProgramRasterSetCullMode", "(II)V", (void*)nProgramRasterSetCullMode },
+{"rsnProgramBindConstants", "(IIII)V", (void*)nProgramBindConstants },
+{"rsnProgramBindTexture", "(IIII)V", (void*)nProgramBindTexture },
+{"rsnProgramBindSampler", "(IIII)V", (void*)nProgramBindSampler },
-{"nProgramVertexCreate", "(Z)I", (void*)nProgramVertexCreate },
-{"nProgramVertexCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramVertexCreate2 },
+{"rsnProgramFragmentCreate", "(I[I)I", (void*)nProgramFragmentCreate },
+{"rsnProgramFragmentCreate2", "(ILjava/lang/String;[I)I", (void*)nProgramFragmentCreate2 },
-{"nLightBegin", "()V", (void*)nLightBegin },
-{"nLightSetIsMono", "(Z)V", (void*)nLightSetIsMono },
-{"nLightSetIsLocal", "(Z)V", (void*)nLightSetIsLocal },
-{"nLightCreate", "()I", (void*)nLightCreate },
-{"nLightSetColor", "(IFFF)V", (void*)nLightSetColor },
-{"nLightSetPosition", "(IFFF)V", (void*)nLightSetPosition },
+{"rsnProgramRasterCreate", "(IZZZ)I", (void*)nProgramRasterCreate },
+{"rsnProgramRasterSetLineWidth", "(IIF)V", (void*)nProgramRasterSetLineWidth },
+{"rsnProgramRasterSetCullMode", "(III)V", (void*)nProgramRasterSetCullMode },
-{"nContextBindRootScript", "(I)V", (void*)nContextBindRootScript },
-{"nContextBindProgramStore", "(I)V", (void*)nContextBindProgramStore },
-{"nContextBindProgramFragment", "(I)V", (void*)nContextBindProgramFragment },
-{"nContextBindProgramVertex", "(I)V", (void*)nContextBindProgramVertex },
-{"nContextBindProgramRaster", "(I)V", (void*)nContextBindProgramRaster },
+{"rsnProgramVertexCreate", "(IZ)I", (void*)nProgramVertexCreate },
+{"rsnProgramVertexCreate2", "(ILjava/lang/String;[I)I", (void*)nProgramVertexCreate2 },
-{"nSamplerBegin", "()V", (void*)nSamplerBegin },
-{"nSamplerSet", "(II)V", (void*)nSamplerSet },
-{"nSamplerCreate", "()I", (void*)nSamplerCreate },
+{"rsnContextBindRootScript", "(II)V", (void*)nContextBindRootScript },
+{"rsnContextBindProgramStore", "(II)V", (void*)nContextBindProgramStore },
+{"rsnContextBindProgramFragment", "(II)V", (void*)nContextBindProgramFragment },
+{"rsnContextBindProgramVertex", "(II)V", (void*)nContextBindProgramVertex },
+{"rsnContextBindProgramRaster", "(II)V", (void*)nContextBindProgramRaster },
-{"nMeshCreate", "(II)I", (void*)nMeshCreate },
-{"nMeshBindVertex", "(III)V", (void*)nMeshBindVertex },
-{"nMeshBindIndex", "(IIII)V", (void*)nMeshBindIndex },
+{"rsnSamplerBegin", "(I)V", (void*)nSamplerBegin },
+{"rsnSamplerSet", "(III)V", (void*)nSamplerSet },
+{"rsnSamplerCreate", "(I)I", (void*)nSamplerCreate },
-{"nMeshGetVertexBufferCount", "(I)I", (void*)nMeshGetVertexBufferCount },
-{"nMeshGetIndexCount", "(I)I", (void*)nMeshGetIndexCount },
-{"nMeshGetVertices", "(I[II)V", (void*)nMeshGetVertices },
-{"nMeshGetIndices", "(I[I[II)V", (void*)nMeshGetIndices },
+{"rsnMeshCreate", "(III)I", (void*)nMeshCreate },
+{"rsnMeshBindVertex", "(IIII)V", (void*)nMeshBindVertex },
+{"rsnMeshBindIndex", "(IIIII)V", (void*)nMeshBindIndex },
+
+{"rsnMeshGetVertexBufferCount", "(II)I", (void*)nMeshGetVertexBufferCount },
+{"rsnMeshGetIndexCount", "(II)I", (void*)nMeshGetIndexCount },
+{"rsnMeshGetVertices", "(II[II)V", (void*)nMeshGetVertices },
+{"rsnMeshGetIndices", "(II[I[II)V", (void*)nMeshGetIndices },
};
diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h
new file mode 100644
index 0000000..2f5a982
--- /dev/null
+++ b/include/android_runtime/android_content_res_Configuration.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H
+#define _ANDROID_CONTENT_RES_CONFIGURATION_H
+
+#include <utils/ResourceTypes.h>
+#include <android/configuration.h>
+
+#include "jni.h"
+
+struct AConfiguration : android::ResTable_config {
+};
+
+namespace android {
+
+extern void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out);
+
+} // namespace android
+
+
+#endif // _ANDROID_CONTENT_RES_CONFIGURATION_H
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index b2808f5..bed174b 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -188,10 +188,9 @@
// Supported flash modes.
// Example value: "auto,on,off". Read only.
static const char KEY_SUPPORTED_FLASH_MODES[];
- // Current focus mode. If the camera does not support auto-focus, the value
- // should be FOCUS_MODE_FIXED. If the focus mode is not FOCUS_MODE_FIXED or
- // or FOCUS_MODE_INFINITY, applications should call
- // CameraHardwareInterface.autoFocus to start the focus.
+ // Current focus mode. This will not be empty. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus if focus mode is
+ // FOCUS_MODE_AUTO or FOCUS_MODE_MACRO.
// Example value: "auto" or FOCUS_MODE_XXX constants. Read/write.
static const char KEY_FOCUS_MODE[];
// Supported focus modes.
@@ -248,11 +247,16 @@
// be in focus. The object is sharpest at the optimal focus distance. The
// depth of field is the far focus distance minus near focus distance.
//
- // Applications can read this parameter anytime to get the latest focus
- // distances. If the focus mode is FOCUS_MODE_EDOF, the values may be all
- // 0, which means focus distance is not applicable. If the focus mode is
- // FOCUS_MODE_CONTINUOUS and autofocus has started, focus distances may
- // change from time to time.
+ // Focus distances may change after starting auto focus, canceling auto
+ // focus, or starting the preview. Applications can read this anytime to get
+ // the latest focus distances. If the focus mode is FOCUS_MODE_CONTINUOUS,
+ // focus distances may change from time to time.
+ //
+ // This is intended to estimate the distance between the camera and the
+ // subject. After autofocus, the subject distance may be within near and far
+ // focus distance. However, the precision depends on the camera hardware,
+ // autofocus algorithm, the focus area, and the scene. The error can be
+ // large and it should be only used as a reference.
//
// Far focus distance > optimal focus distance > near focus distance. If
// the far focus distance is infinity, the value should be "Infinity" (case
@@ -365,10 +369,10 @@
// continuously. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_EDOF[];
- // Continuous focus mode. The camera continuously tries to focus. This is
- // ideal for shooting video or shooting photo of moving object. Continuous
- // focus starts when CameraHardwareInterface.autoFocus is called. Focus
- // callback will be only called once as soon as the picture is in focus.
+ // Continuous auto focus mode. The camera continuously tries to focus. This
+ // is ideal for shooting video or shooting photo of moving object. Auto
+ // focus starts when the parameter is set. Applications should not call
+ // CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_CONTINUOUS[];
// The camera determines the exposure by giving more weight to the
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index f1d45d2..2597e9e 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -60,7 +60,8 @@
int64_t mStartTimeUs;
int16_t mMaxAmplitude;
int64_t mPrevSampleTimeUs;
- int64_t mNumLostFrames;
+ int64_t mTotalLostFrames;
+ int64_t mPrevLostBytes;
MediaBufferGroup *mGroup;
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 2e1e8d8..be96935 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -128,6 +128,12 @@
// Write the first chunk from the given ChunkInfo.
void writeFirstChunk(ChunkInfo* info);
+ // Adjust other track media clock (presumably wall clock)
+ // based on audio track media clock with the drift time.
+ int64_t mDriftTimeUs;
+ void addDriftTimeUs(int64_t driftTimeUs);
+ int64_t getDriftTimeUs();
+
void lock();
void unlock();
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 9cddab2..92ce068 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -34,6 +34,8 @@
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index ab1fa4f..43354c2 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -86,10 +86,10 @@
// Track authoring progress status
// kKeyTrackTimeStatus is used to track progress in elapsed time
- // kKeyTrackFrameStatus is used to track progress in authored frames
- kKeyTrackFrameStatus = 'tkfm', // int32_t
kKeyTrackTimeStatus = 'tktm', // int64_t
+ kKeyNotRealTime = 'ntrt', // bool (int32_t)
+
};
enum {
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index dab35b3..05cd96d 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -224,6 +224,7 @@
uint8_t* keyBitmask;
KeyLayoutMap* layoutMap;
String8 keylayoutFilename;
+ int fd;
device_t* next;
device_t(int32_t _id, const char* _path, const char* name);
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f162231..71c6c51 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -454,6 +454,8 @@
virtual void reset();
virtual void process(const RawEvent* rawEvent);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index 97694ff..9e2bf37 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -129,6 +129,8 @@
*/
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
+ void getConfiguration(ResTable_config* outConfig) const;
+
typedef Asset::AccessMode AccessMode; // typing shortcut
/*
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index 075927c..d2ca82e 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -35,6 +35,8 @@
bool readFrom(int fd);
bool writeTo(const char* filename);
bool writeTo(int fd);
+ bool removeFrom(const char* filename);
+ bool removeFrom(int fd);
const char* getFileName() const {
return mFileName;
@@ -78,6 +80,8 @@
size_t mFileSize;
+ size_t mFooterStart;
+
unsigned char* mReadBuf;
bool parseObbFile(int fd);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index c7d9ff1..da86da4 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -31,6 +31,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/configuration.h>
+
namespace android {
/** ********************************************************************
@@ -822,25 +824,25 @@
};
enum {
- ORIENTATION_ANY = 0x0000,
- ORIENTATION_PORT = 0x0001,
- ORIENTATION_LAND = 0x0002,
- ORIENTATION_SQUARE = 0x0003,
+ ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY,
+ ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT,
+ ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND,
+ ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE,
};
enum {
- TOUCHSCREEN_ANY = 0x0000,
- TOUCHSCREEN_NOTOUCH = 0x0001,
- TOUCHSCREEN_STYLUS = 0x0002,
- TOUCHSCREEN_FINGER = 0x0003,
+ TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY,
+ TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
+ TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS,
+ TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER,
};
enum {
- DENSITY_DEFAULT = 0,
- DENSITY_LOW = 120,
- DENSITY_MEDIUM = 160,
- DENSITY_HIGH = 240,
- DENSITY_NONE = 0xffff
+ DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
+ DENSITY_LOW = ACONFIGURATION_DENSITY_LOW,
+ DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM,
+ DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH,
+ DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
};
union {
@@ -853,33 +855,34 @@
};
enum {
- KEYBOARD_ANY = 0x0000,
- KEYBOARD_NOKEYS = 0x0001,
- KEYBOARD_QWERTY = 0x0002,
- KEYBOARD_12KEY = 0x0003,
+ KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY,
+ KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS,
+ KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY,
+ KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY,
};
enum {
- NAVIGATION_ANY = 0x0000,
- NAVIGATION_NONAV = 0x0001,
- NAVIGATION_DPAD = 0x0002,
- NAVIGATION_TRACKBALL = 0x0003,
- NAVIGATION_WHEEL = 0x0004,
+ NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY,
+ NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV,
+ NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD,
+ NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL,
+ NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL,
};
enum {
MASK_KEYSHIDDEN = 0x0003,
- KEYSHIDDEN_ANY = 0x0000,
- KEYSHIDDEN_NO = 0x0001,
- KEYSHIDDEN_YES = 0x0002,
- KEYSHIDDEN_SOFT = 0x0003,
+ KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY,
+ KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO,
+ KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES,
+ KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT,
};
enum {
MASK_NAVHIDDEN = 0x000c,
- NAVHIDDEN_ANY = 0x0000,
- NAVHIDDEN_NO = 0x0004,
- NAVHIDDEN_YES = 0x0008,
+ SHIFT_NAVHIDDEN = 2,
+ NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN,
};
union {
@@ -929,32 +932,34 @@
enum {
// screenLayout bits for screen size class.
MASK_SCREENSIZE = 0x0f,
- SCREENSIZE_ANY = 0x00,
- SCREENSIZE_SMALL = 0x01,
- SCREENSIZE_NORMAL = 0x02,
- SCREENSIZE_LARGE = 0x03,
- SCREENSIZE_XLARGE = 0x04,
+ SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY,
+ SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL,
+ SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL,
+ SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE,
+ SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE,
// screenLayout bits for wide/long screen variation.
MASK_SCREENLONG = 0x30,
- SCREENLONG_ANY = 0x00,
- SCREENLONG_NO = 0x10,
- SCREENLONG_YES = 0x20,
+ SHIFT_SCREENLONG = 4,
+ SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
+ SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
+ SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
};
enum {
// uiMode bits for the mode type.
MASK_UI_MODE_TYPE = 0x0f,
- UI_MODE_TYPE_ANY = 0x00,
- UI_MODE_TYPE_NORMAL = 0x01,
- UI_MODE_TYPE_DESK = 0x02,
- UI_MODE_TYPE_CAR = 0x03,
+ UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY,
+ UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL,
+ UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK,
+ UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
- UI_MODE_NIGHT_ANY = 0x00,
- UI_MODE_NIGHT_NO = 0x10,
- UI_MODE_NIGHT_YES = 0x20,
+ SHIFT_UI_MODE_NIGHT = 4,
+ UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT,
};
union {
@@ -1023,19 +1028,19 @@
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
enum {
- CONFIG_MCC = 0x0001,
- CONFIG_MNC = 0x0002,
- CONFIG_LOCALE = 0x0004,
- CONFIG_TOUCHSCREEN = 0x0008,
- CONFIG_KEYBOARD = 0x0010,
- CONFIG_KEYBOARD_HIDDEN = 0x0020,
- CONFIG_NAVIGATION = 0x0040,
- CONFIG_ORIENTATION = 0x0080,
- CONFIG_DENSITY = 0x0100,
- CONFIG_SCREEN_SIZE = 0x0200,
- CONFIG_VERSION = 0x0400,
- CONFIG_SCREEN_LAYOUT = 0x0800,
- CONFIG_UI_MODE = 0x1000
+ CONFIG_MCC = ACONFIGURATION_MCC,
+ CONFIG_MNC = ACONFIGURATION_MCC,
+ CONFIG_LOCALE = ACONFIGURATION_LOCALE,
+ CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
+ CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
+ CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
+ CONFIG_DENSITY = ACONFIGURATION_DENSITY,
+ CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
+ CONFIG_VERSION = ACONFIGURATION_VERSION,
+ CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
};
// Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 4e41410..ef0b51a 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,7 +171,8 @@
status_t append(const char* other);
status_t append(const char* other, size_t numChars);
- status_t appendFormat(const char* fmt, ...);
+ status_t appendFormat(const char* fmt, ...)
+ __attribute__((format (printf, 2, 3)));
// Note that this function takes O(N) time to calculate the value.
// No cache value is stored.
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1efe6b5..8ed3d7b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -17,7 +17,8 @@
ProgramCache.cpp \
SkiaColorFilter.cpp \
SkiaShader.cpp \
- TextureCache.cpp
+ TextureCache.cpp \
+ TextDropShadowCache.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8d00e85..ccc92eb 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -69,16 +69,16 @@
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
- if(bounds->bottom > nPenY) {
+ if (bounds->bottom > nPenY) {
bounds->bottom = nPenY;
}
- if(bounds->left > nPenX) {
+ if (bounds->left > nPenX) {
bounds->left = nPenX;
}
- if(bounds->right < nPenX + width) {
+ if (bounds->right < nPenX + width) {
bounds->right = nPenX + width;
}
- if(bounds->top < nPenY + height) {
+ if (bounds->top < nPenY + height) {
bounds->top = nPenY + height;
}
}
@@ -102,7 +102,7 @@
}
void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
@@ -116,7 +116,7 @@
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
- if(bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
+ if (bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
LOGE("Skipping invalid index");
continue;
}
@@ -128,8 +128,11 @@
}
Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
- if (cachedGlyph == NULL) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
cachedGlyph = cacheGlyph(paint, utfChar);
}
@@ -143,22 +146,19 @@
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP,
- bitmap, bitmapW, bitmapH, NULL);
- }
- else {
- renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER,
- NULL, 0, 0, NULL);
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL);
+ } else {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
}
}
void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds) {
- if(bounds == NULL) {
+ int numGlyphs, Rect *bounds) {
+ if (bounds == NULL) {
LOGE("No return rectangle provided to measure text");
return;
}
@@ -167,9 +167,8 @@
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect *bounds) {
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
if (numGlyphs == 0 || text == NULL || len == 0) {
return;
}
@@ -185,7 +184,7 @@
while (glyphsLeft > 0) {
int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
- // Reached the end of the string or encountered
+ // Reached the end of the string
if (utfChar < 0) {
break;
}
@@ -422,7 +421,7 @@
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimentions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -514,10 +513,10 @@
uint32_t yOffset = cl->mCurrentRow;
uint32_t width = mCacheWidth;
uint32_t height = cl->mMaxHeight;
- void* textureData = mTextTexture + yOffset*width;
+ void* textureData = mTextTexture + yOffset*width;
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
- GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
+ GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
cl->mDirty = false;
}
@@ -527,7 +526,6 @@
}
void FontRenderer::issueDrawCommand() {
-
checkTextureUpdate();
float* vtx = mTextMeshPtr;
@@ -622,21 +620,33 @@
}
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
+ uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
+ checkInit();
+
+ if (!mCurrentFont) {
+ DropShadow image;
+ image.width = 0;
+ image.height = 0;
+ image.image = NULL;
+ image.penX = 0;
+ image.penY = 0;
+ return image;
+ }
Rect bounds;
mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
- uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2*radius;
- uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2*radius;
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
- for(uint32_t i = 0; i < paddedWidth * paddedHeight; i ++) {
+ for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
dataBuffer[i] = 0;
}
+
int penX = radius - bounds.left;
int penY = radius - bounds.bottom;
mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
- dataBuffer, paddedWidth, paddedHeight);
+ dataBuffer, paddedWidth, paddedHeight);
blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
DropShadow image;
@@ -704,8 +714,7 @@
}
void FontRenderer::horizontalBlur(float* weights, int32_t radius,
- const uint8_t* source, uint8_t* dest,
- int32_t width, int32_t height) {
+ const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
float blurredPixel = 0.0f;
float currentPixel = 0.0f;
@@ -749,8 +758,7 @@
}
void FontRenderer::verticalBlur(float* weights, int32_t radius,
- const uint8_t* source, uint8_t* dest,
- int32_t width, int32_t height) {
+ const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
float blurredPixel = 0.0f;
float currentPixel = 0.0f;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 6346ded..96c92d5 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,6 +129,14 @@
uint32_t len, int numGlyphs, int x, int y);
struct DropShadow {
+ DropShadow() { };
+
+ DropShadow(const DropShadow& dropShadow):
+ width(dropShadow.width), height(dropShadow.height),
+ image(dropShadow.image), penX(dropShadow.penX),
+ penY(dropShadow.penY) {
+ }
+
uint32_t width;
uint32_t height;
uint8_t* image;
@@ -139,7 +147,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius);
+ uint32_t len, int numGlyphs, uint32_t radius);
GLuint getTexture() {
checkInit();
@@ -154,7 +162,7 @@
uint16_t mMaxWidth;
uint32_t mCurrentRow;
uint32_t mCurrentCol;
- bool mDirty;
+ bool mDirty;
CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
uint32_t currentCol):
@@ -237,9 +245,9 @@
void computeGaussianWeights(float* weights, int32_t radius);
void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
- int32_t width, int32_t height);
+ int32_t width, int32_t height);
void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
- int32_t width, int32_t height);
+ int32_t width, int32_t height);
void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
};
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 0c31ba9..264ad3d 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -30,86 +30,106 @@
namespace uirenderer {
void Matrix4::loadIdentity() {
- data[0] = 1.0f;
- data[1] = 0.0f;
- data[2] = 0.0f;
- data[3] = 0.0f;
+ data[kScaleX] = 1.0f;
+ data[kSkewY] = 0.0f;
+ data[2] = 0.0f;
+ data[kPerspective0] = 0.0f;
- data[4] = 0.0f;
- data[5] = 1.0f;
- data[6] = 0.0f;
- data[7] = 0.0f;
+ data[kSkewX] = 0.0f;
+ data[kScaleY] = 1.0f;
+ data[6] = 0.0f;
+ data[kPerspective1] = 0.0f;
- data[8] = 0.0f;
- data[9] = 0.0f;
- data[10] = 1.0f;
- data[11] = 0.0f;
+ data[8] = 0.0f;
+ data[9] = 0.0f;
+ data[kScaleZ] = 1.0f;
+ data[11] = 0.0f;
- data[12] = 0.0f;
- data[13] = 0.0f;
- data[14] = 0.0f;
- data[15] = 1.0f;
+ data[kTranslateX] = 0.0f;
+ data[kTranslateY] = 0.0f;
+ data[kTranslateZ] = 0.0f;
+ data[kPerspective2] = 1.0f;
+
+ mSimpleMatrix = true;
}
void Matrix4::load(const float* v) {
memcpy(data, v, sizeof(data));
+ mSimpleMatrix = false;
}
void Matrix4::load(const Matrix4& v) {
memcpy(data, v.data, sizeof(data));
+ mSimpleMatrix = v.mSimpleMatrix;
}
void Matrix4::load(const SkMatrix& v) {
memset(data, 0, sizeof(data));
- data[0] = v[SkMatrix::kMScaleX];
- data[4] = v[SkMatrix::kMSkewX];
- data[12] = v[SkMatrix::kMTransX];
+ data[kScaleX] = v[SkMatrix::kMScaleX];
+ data[kSkewX] = v[SkMatrix::kMSkewX];
+ data[kTranslateX] = v[SkMatrix::kMTransX];
- data[1] = v[SkMatrix::kMSkewY];
- data[5] = v[SkMatrix::kMScaleY];
- data[13] = v[SkMatrix::kMTransY];
+ data[kSkewY] = v[SkMatrix::kMSkewY];
+ data[kScaleY] = v[SkMatrix::kMScaleY];
+ data[kTranslateY] = v[SkMatrix::kMTransY];
- data[3] = v[SkMatrix::kMPersp0];
- data[7] = v[SkMatrix::kMPersp1];
- data[15] = v[SkMatrix::kMPersp2];
+ data[kPerspective0] = v[SkMatrix::kMPersp0];
+ data[kPerspective1] = v[SkMatrix::kMPersp1];
+ data[kPerspective2] = v[SkMatrix::kMPersp2];
- data[10] = 1.0f;
+ data[kScaleZ] = 1.0f;
+
+ mSimpleMatrix = (v.getType() <= SkMatrix::kScale_Mask);
}
void Matrix4::copyTo(SkMatrix& v) const {
v.reset();
- v.set(SkMatrix::kMScaleX, data[0]);
- v.set(SkMatrix::kMSkewX, data[4]);
- v.set(SkMatrix::kMTransX, data[12]);
+ v.set(SkMatrix::kMScaleX, data[kScaleX]);
+ v.set(SkMatrix::kMSkewX, data[kSkewX]);
+ v.set(SkMatrix::kMTransX, data[kTranslateX]);
- v.set(SkMatrix::kMSkewY, data[1]);
- v.set(SkMatrix::kMScaleY, data[5]);
- v.set(SkMatrix::kMTransY, data[13]);
+ v.set(SkMatrix::kMSkewY, data[kSkewY]);
+ v.set(SkMatrix::kMScaleY, data[kScaleY]);
+ v.set(SkMatrix::kMTransY, data[kTranslateY]);
- v.set(SkMatrix::kMPersp0, data[3]);
- v.set(SkMatrix::kMPersp1, data[7]);
- v.set(SkMatrix::kMPersp2, data[15]);
+ v.set(SkMatrix::kMPersp0, data[kPerspective0]);
+ v.set(SkMatrix::kMPersp1, data[kPerspective1]);
+ v.set(SkMatrix::kMPersp2, data[kPerspective2]);
}
void Matrix4::loadInverse(const Matrix4& v) {
double scale = 1.0 /
- (v.data[0] * ((double) v.data[5] * v.data[15] - (double) v.data[13] * v.data[7]) +
- v.data[4] * ((double) v.data[13] * v.data[3] - (double) v.data[1] * v.data[15]) +
- v.data[12] * ((double) v.data[1] * v.data[7] - (double) v.data[5] * v.data[3]));
+ (v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] -
+ (double) v.data[kTranslateY] * v.data[kPerspective1]) +
+ v.data[kSkewX] * ((double) v.data[kTranslateY] * v.data[kPerspective0] -
+ (double) v.data[kSkewY] * v.data[kPerspective2]) +
+ v.data[kTranslateX] * ((double) v.data[kSkewY] * v.data[kPerspective1] -
+ (double) v.data[kScaleY] * v.data[kPerspective0]));
- data[0] = (v.data[5] * v.data[15] - v.data[13] * v.data[7]) * scale;
- data[4] = (v.data[12] * v.data[7] - v.data[4] * v.data[15]) * scale;
- data[12] = (v.data[4] * v.data[13] - v.data[12] * v.data[5]) * scale;
+ data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] -
+ v.data[kTranslateY] * v.data[kPerspective1]) * scale;
+ data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] -
+ v.data[kSkewX] * v.data[kPerspective2]) * scale;
+ data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] -
+ v.data[kTranslateX] * v.data[kScaleY]) * scale;
- data[1] = (v.data[13] * v.data[3] - v.data[1] * v.data[15]) * scale;
- data[5] = (v.data[0] * v.data[15] - v.data[12] * v.data[3]) * scale;
- data[13] = (v.data[12] * v.data[1] - v.data[0] * v.data[13]) * scale;
+ data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] -
+ v.data[kSkewY] * v.data[kPerspective2]) * scale;
+ data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] -
+ v.data[kTranslateX] * v.data[kPerspective0]) * scale;
+ data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] -
+ v.data[kScaleX] * v.data[kTranslateY]) * scale;
- data[3] = (v.data[1] * v.data[7] - v.data[5] * v.data[3]) * scale;
- data[7] = (v.data[4] * v.data[3] - v.data[0] * v.data[7]) * scale;
- data[15] = (v.data[0] * v.data[5] - v.data[4] * v.data[1]) * scale;
+ data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] -
+ v.data[kScaleY] * v.data[kPerspective0]) * scale;
+ data[kPerspective1] = (v.data[kSkewX] * v.data[kPerspective0] -
+ v.data[kScaleX] * v.data[kPerspective1]) * scale;
+ data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] -
+ v.data[kSkewX] * v.data[kSkewY]) * scale;
+
+ mSimpleMatrix = v.mSimpleMatrix;
}
void Matrix4::copyTo(float* v) const {
@@ -117,35 +137,35 @@
}
float Matrix4::getTranslateX() {
- return data[12];
+ return data[kTranslateX];
}
float Matrix4::getTranslateY() {
- return data[13];
+ return data[kTranslateY];
}
void Matrix4::loadTranslate(float x, float y, float z) {
loadIdentity();
- data[12] = x;
- data[13] = y;
- data[14] = z;
+ data[kTranslateX] = x;
+ data[kTranslateY] = y;
+ data[kTranslateZ] = z;
}
void Matrix4::loadScale(float sx, float sy, float sz) {
loadIdentity();
- data[0] = sx;
- data[5] = sy;
- data[10] = sz;
+ data[kScaleX] = sx;
+ data[kScaleY] = sy;
+ data[kScaleZ] = sz;
}
void Matrix4::loadRotate(float angle, float x, float y, float z) {
- data[3] = 0.0f;
- data[7] = 0.0f;
- data[11] = 0.0f;
- data[12] = 0.0f;
- data[13] = 0.0f;
- data[14] = 0.0f;
- data[15] = 1.0f;
+ data[kPerspective0] = 0.0f;
+ data[kPerspective1] = 0.0f;
+ data[11] = 0.0f;
+ data[kTranslateX] = 0.0f;
+ data[kTranslateY] = 0.0f;
+ data[kTranslateZ] = 0.0f;
+ data[kPerspective2] = 1.0f;
angle *= float(M_PI / 180.0f);
float c = cosf(angle);
@@ -165,15 +185,17 @@
const float ys = y * s;
const float zs = z * s;
- data[0] = x * x * nc + c;
- data[4] = xy * nc - zs;
- data[8] = zx * nc + ys;
- data[1] = xy * nc + zs;
- data[5] = y * y * nc + c;
- data[9] = yz * nc - xs;
- data[2] = zx * nc - ys;
- data[6] = yz * nc + xs;
- data[10] = z * z * nc + c;
+ data[kScaleX] = x * x * nc + c;
+ data[kSkewX] = xy * nc - zs;
+ data[8] = zx * nc + ys;
+ data[kSkewY] = xy * nc + zs;
+ data[kScaleY] = y * y * nc + c;
+ data[9] = yz * nc - xs;
+ data[2] = zx * nc - ys;
+ data[6] = yz * nc + xs;
+ data[kScaleZ] = z * z * nc + c;
+
+ mSimpleMatrix = false;
}
void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
@@ -196,39 +218,89 @@
set(i, 2, z);
set(i, 3, w);
}
+
+ mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
}
void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
loadIdentity();
- data[0] = 2.0f / (right - left);
- data[5] = 2.0f / (top - bottom);
- data[10] = -2.0f / (far - near);
- data[12] = -(right + left) / (right - left);
- data[13] = -(top + bottom) / (top - bottom);
- data[14] = -(far + near) / (far - near);
+ data[kScaleX] = 2.0f / (right - left);
+ data[kScaleY] = 2.0f / (top - bottom);
+ data[kScaleZ] = -2.0f / (far - near);
+ data[kTranslateX] = -(right + left) / (right - left);
+ data[kTranslateY] = -(top + bottom) / (top - bottom);
+ data[kTranslateZ] = -(far + near) / (far - near);
}
#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
+void Matrix4::mapPoint(float& x, float& y) const {
+ if (mSimpleMatrix) {
+ MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]);
+ MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]);
+ return;
+ }
+
+ float dx = x * data[kScaleX] + y * data[kSkewX] + data[kTranslateX];
+ float dy = x * data[kSkewY] + y * data[kScaleY] + data[kTranslateY];
+ float dz = x * data[kPerspective0] + y * data[kPerspective1] + data[kPerspective2];
+ if (dz) dz = 1.0f / dz;
+
+ x = dx * dz;
+ y = dy * dz;
+}
+
void Matrix4::mapRect(Rect& r) const {
- const float sx = data[0];
- const float sy = data[5];
+ if (mSimpleMatrix) {
+ MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
+ MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
+ MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]);
+ MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]);
+ return;
+ }
- const float tx = data[12];
- const float ty = data[13];
+ float vertices[] = {
+ r.left, r.top,
+ r.right, r.top,
+ r.right, r.bottom,
+ r.left, r.bottom
+ };
- MUL_ADD_STORE(r.left, sx, tx);
- MUL_ADD_STORE(r.right, sx, tx);
- MUL_ADD_STORE(r.top, sy, ty);
- MUL_ADD_STORE(r.bottom, sy, ty);
+ float x, y, z;
+
+ for (int i = 0; i < 8; i+= 2) {
+ float px = vertices[i];
+ float py = vertices[i + 1];
+
+ x = px * data[kScaleX] + py * data[kSkewX] + data[kTranslateX];
+ y = px * data[kSkewY] + py * data[kScaleY] + data[kTranslateY];
+ z = px * data[kPerspective0] + py * data[kPerspective1] + data[kPerspective2];
+ if (z) z = 1.0f / z;
+
+ vertices[i] = x * z;
+ vertices[i + 1] = y * z;
+ }
+
+ r.left = r.right = vertices[0];
+ r.top = r.bottom = vertices[1];
+
+ for (int i = 2; i < 8; i += 2) {
+ x = vertices[i];
+ y = vertices[i + 1];
+
+ if (x < r.left) r.left = x;
+ else if (x > r.right) r.right = x;
+ if (y < r.top) r.top = y;
+ else if (y > r.bottom) r.bottom = y;
+ }
}
void Matrix4::dump() const {
- LOGD("Matrix4[");
- LOGD(" %f %f %f %f", data[0], data[4], data[ 8], data[12]);
- LOGD(" %f %f %f %f", data[1], data[5], data[ 9], data[13]);
- LOGD(" %f %f %f %f", data[2], data[6], data[10], data[14]);
- LOGD(" %f %f %f %f", data[3], data[7], data[11], data[15]);
+ LOGD("Matrix4[simple=%d", mSimpleMatrix);
+ LOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
+ LOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
+ LOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
+ LOGD(" %f %f %f %f", data[kPerspective0], data[kPerspective1], data[11], data[kPerspective2]);
LOGD("]");
}
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index b8a4da7..c247a67 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -32,6 +32,20 @@
public:
float data[16];
+ enum Entry {
+ kScaleX = 0,
+ kSkewY = 1,
+ kPerspective0 = 3,
+ kSkewX = 4,
+ kScaleY = 5,
+ kPerspective1 = 7,
+ kScaleZ = 10,
+ kTranslateX = 12,
+ kTranslateY = 13,
+ kTranslateZ = 14,
+ kPerspective2 = 15
+ };
+
Matrix4() {
loadIdentity();
}
@@ -90,10 +104,8 @@
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
- /**
- * Does not apply rotations!
- */
void mapRect(Rect& r) const;
+ void mapPoint(float& x, float& y) const;
float getTranslateX();
float getTranslateY();
@@ -101,6 +113,8 @@
void dump() const;
private:
+ bool mSimpleMatrix;
+
inline float get(int i, int j) const {
return data[i * 4 + j];
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5d30b1a..35e17bfb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -41,6 +41,7 @@
#define DEFAULT_PATH_CACHE_SIZE 6.0f
#define DEFAULT_PATCH_CACHE_SIZE 100
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define DEFAULT_DROP_SHADOW_CACHE_SIZE 1.0f
#define REQUIRED_TEXTURE_UNITS_COUNT 3
@@ -107,7 +108,8 @@
mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)),
- mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
+ mPatchCache(DEFAULT_PATCH_CACHE_SIZE),
+ mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
LOGD("Create OpenGLRenderer");
char property[PROPERTY_VALUE_MAX];
@@ -139,9 +141,18 @@
LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
}
+ if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting drop shadow cache size to %sMB", property);
+ mDropShadowCache.setMaxSize(MB(atof(property)));
+ } else {
+ LOGD(" Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
+ }
+ mDropShadowCache.setFontRenderer(mFontRenderer);
+
mCurrentProgram = NULL;
mShader = NULL;
mColorFilter = NULL;
+ mHasShadow = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -160,7 +171,10 @@
mTextureCache.clear();
mLayerCache.clear();
mGradientCache.clear();
+ mPathCache.clear();
mPatchCache.clear();
+ mProgramCache.clear();
+ mDropShadowCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -169,17 +183,17 @@
void OpenGLRenderer::setViewport(int width, int height) {
glViewport(0, 0, width, height);
-
mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
mWidth = width;
mHeight = height;
mFirstSnapshot->height = height;
+ mFirstSnapshot->viewport.set(0, 0, width, height);
}
void OpenGLRenderer::prepare() {
mSnapshot = new Snapshot(mFirstSnapshot);
- mSaveCount = 0;
+ mSaveCount = 1;
glDisable(GL_SCISSOR_TEST);
@@ -205,19 +219,17 @@
}
void OpenGLRenderer::restore() {
- if (mSaveCount == 0) return;
-
- if (restoreSnapshot()) {
+ if (mSaveCount > 1 && restoreSnapshot()) {
setScissorFromClip();
}
}
void OpenGLRenderer::restoreToCount(int saveCount) {
- if (saveCount <= 0 || saveCount > mSaveCount) return;
+ if (saveCount < 1) saveCount = 1;
bool restoreClip = false;
- while (mSaveCount != saveCount - 1) {
+ while (mSaveCount > saveCount) {
restoreClip |= restoreSnapshot();
}
@@ -228,7 +240,7 @@
int OpenGLRenderer::saveSnapshot() {
mSnapshot = new Snapshot(mSnapshot);
- return ++mSaveCount;
+ return mSaveCount++;
}
bool OpenGLRenderer::restoreSnapshot() {
@@ -240,6 +252,8 @@
sp<Snapshot> previous = mSnapshot->previous;
if (restoreOrtho) {
+ Rect& r = previous->viewport;
+ glViewport(r.left, r.top, r.right, r.bottom);
mOrthoMatrix.load(current->orthoMatrix);
}
@@ -247,42 +261,17 @@
composeLayer(current, previous);
}
- mSnapshot = previous;
- mSaveCount--;
-
- return restoreClip;
-}
-
-void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
- if (!current->layer) {
- LOGE("Attempting to compose a layer that does not exist");
- return;
+ bool skip = mSnapshot->skip;
+ if (!skip) {
+ mSaveCount--;
}
+ mSnapshot = previous;
- // Unbind current FBO and restore previous one
- // Most of the time, previous->fbo will be 0 to bind the default buffer
- glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
-
- // Restore the clip from the previous snapshot
- const Rect& clip = previous->clipRect;
- glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
-
- Layer* layer = current->layer;
- const Rect& rect = layer->layer;
-
- drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->texture, layer->alpha, layer->mode, layer->blend);
-
- LayerSize size(rect.getWidth(), rect.getHeight());
- // Failing to add the layer to the cache should happen only if the
- // layer is too large
- if (!mLayerCache.put(size, layer)) {
- LAYER_LOGD("Deleting layer");
-
- glDeleteFramebuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
-
- delete layer;
+ if (!skip) {
+ return restoreClip;
+ } else {
+ bool restorePreviousClip = restoreSnapshot();
+ return restoreClip || restorePreviousClip;
}
}
@@ -341,32 +330,71 @@
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
- // Save the layer in the snapshot
- snapshot->flags |= Snapshot::kFlagIsLayer;
layer->mode = mode;
layer->alpha = alpha / 255.0f;
layer->layer.set(left, top, right, bottom);
+ // Save the layer in the snapshot
+ snapshot->flags |= Snapshot::kFlagIsLayer;
snapshot->layer = layer;
snapshot->fbo = layer->fbo;
// Creates a new snapshot to draw into the FBO
saveSnapshot();
- // TODO: This doesn't preserve other transformations (check Skia first)
+ mSaveCount--;
+
+ mSnapshot->skip = true;
mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
mSnapshot->setClip(0.0f, 0.0f, right - left, bottom - top);
+ mSnapshot->viewport.set(0.0f, 0.0f, right - left, bottom - top);
mSnapshot->height = bottom - top;
+
setScissorFromClip();
mSnapshot->flags = Snapshot::kFlagDirtyOrtho | Snapshot::kFlagClipSet;
mSnapshot->orthoMatrix.load(mOrthoMatrix);
// Change the ortho projection
- mOrthoMatrix.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
+ glViewport(0, 0, right - left, bottom - top);
+ // Don't flip the FBO, it will get flipped when drawing back to the framebuffer
+ mOrthoMatrix.loadOrtho(0.0f, right - left, 0.0f, bottom - top, -1.0f, 1.0f);
return true;
}
+void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+ if (!current->layer) {
+ LOGE("Attempting to compose a layer that does not exist");
+ return;
+ }
+
+ // Unbind current FBO and restore previous one
+ // Most of the time, previous->fbo will be 0 to bind the default buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+
+ // Restore the clip from the previous snapshot
+ const Rect& clip = previous->clipRect;
+ glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
+
+ Layer* layer = current->layer;
+ const Rect& rect = layer->layer;
+
+ drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
+ layer->texture, layer->alpha, layer->mode, layer->blend);
+
+ LayerSize size(rect.getWidth(), rect.getHeight());
+ // Failing to add the layer to the cache should happen only if the
+ // layer is too large
+ if (!mLayerCache.put(size, layer)) {
+ LAYER_LOGD("Deleting layer");
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+
+ delete layer;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Transforms
///////////////////////////////////////////////////////////////////////////////
@@ -435,6 +463,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -451,6 +480,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -466,6 +496,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -491,6 +522,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -544,7 +576,7 @@
return;
}
- float length;
+ float length = -1.0f;
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
length = paint->measureText(text, bytesCount);
@@ -562,58 +594,39 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
+ mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+ if (mHasShadow) {
+ glActiveTexture(gTextureUnits[0]);
+ const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount,
+ count, mShadowRadius);
+ const AutoTexture autoCleanup(shadow);
+
+ setupShadow(shadow, x, y, mode);
+
+ // Draw the mesh
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+ }
+
uint32_t color = paint->getColor();
const GLfloat a = alpha / 255.0f;
const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
- mModelView.loadIdentity();
-
GLuint textureUnit = 0;
- // Needs to be set prior to calling FontRenderer::getTexture()
glActiveTexture(gTextureUnits[textureUnit]);
- ProgramDescription description;
- description.hasTexture = true;
- description.hasAlpha8Texture = true;
- if (mShader) {
- mShader->describe(description, mExtensions);
- }
- if (mColorFilter) {
- mColorFilter->describe(description, mExtensions);
- }
+ setupTextureAlpha8(mFontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
+ mode, false, true);
- useProgram(mProgramCache.get(description));
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- // Text is always blended, no need to check the shader
- chooseBlending(true, mode);
- bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
- glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
-
- int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
- glEnableVertexAttribArray(texCoordsSlot);
-
- // Always premultiplied
- glUniform4f(mCurrentProgram->color, r, g, b, a);
-
- textureUnit++;
- // Setup attributes and uniforms required by the shaders
- if (mShader) {
- mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
- }
- if (mColorFilter) {
- mColorFilter->setupProgram(mCurrentProgram);
- }
-
- // TODO: Implement scale properly
const Rect& clip = mSnapshot->getLocalClip();
- mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(texCoordsSlot);
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+
+ drawTextDecorations(text, bytesCount, length, x, y, paint);
}
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -634,55 +647,14 @@
const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
- // Describe the required shaders
- ProgramDescription description;
- description.hasTexture = true;
- description.hasAlpha8Texture = true;
- if (mShader) {
- mShader->describe(description, mExtensions);
- }
- if (mColorFilter) {
- mColorFilter->describe(description, mExtensions);
- }
+ const float x = texture->left - texture->offset;
+ const float y = texture->top - texture->offset;
- // Build and use the appropriate shader
- useProgram(mProgramCache.get(description));
-
- // Setup the blending mode
- chooseBlending(true, mode);
- bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
- glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
-
- int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
- glEnableVertexAttribArray(texCoordsSlot);
-
- // Setup attributes
- glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].position[0]);
- glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].texture[0]);
-
- // Setup uniforms
- mModelView.loadTranslate(texture->left - texture->offset,
- texture->top - texture->offset, 0.0f);
- mModelView.scale(texture->width, texture->height, 1.0f);
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- glUniform4f(mCurrentProgram->color, r, g, b, a);
-
- textureUnit++;
- // Setup attributes and uniforms required by the shaders
- if (mShader) {
- mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
- }
- if (mColorFilter) {
- mColorFilter->setupProgram(mCurrentProgram);
- }
+ setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true);
// Draw the mesh
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
- glDisableVertexAttribArray(texCoordsSlot);
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
}
///////////////////////////////////////////////////////////////////////////////
@@ -713,9 +685,153 @@
}
///////////////////////////////////////////////////////////////////////////////
+// Drop shadow
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShadow() {
+ mHasShadow = false;
+}
+
+void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
+ mHasShadow = true;
+ mShadowRadius = radius;
+ mShadowDx = dx;
+ mShadowDy = dy;
+ mShadowColor = color;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
+void OpenGLRenderer::setupShadow(const ShadowTexture* texture, float x, float y,
+ SkXfermode::Mode mode) {
+ const float sx = x - texture->left + mShadowDx;
+ const float sy = y - texture->top + mShadowDy;
+
+ const GLfloat a = ((mShadowColor >> 24) & 0xFF) / 255.0f;
+ const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((mShadowColor >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f;
+
+ GLuint textureUnit = 0;
+ setupTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, true, false);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(const Texture* texture, GLuint& textureUnit,
+ float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
+ bool transforms, bool applyFilters) {
+ setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit,
+ x, y, r, g, b, a, mode, transforms, applyFilters);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters) {
+ // Describe the required shaders
+ ProgramDescription description;
+ description.hasTexture = true;
+ description.hasAlpha8Texture = true;
+
+ if (applyFilters) {
+ if (mShader) {
+ mShader->describe(description, mExtensions);
+ }
+ if (mColorFilter) {
+ mColorFilter->describe(description, mExtensions);
+ }
+ }
+
+ // Build and use the appropriate shader
+ useProgram(mProgramCache.get(description));
+
+ // Setup the blending mode
+ chooseBlending(true, mode);
+ bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
+ glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
+
+ int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+ glEnableVertexAttribArray(texCoordsSlot);
+
+ // Setup attributes
+ glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, &mMeshVertices[0].position[0]);
+ glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, &mMeshVertices[0].texture[0]);
+
+ // Setup uniforms
+ if (transforms) {
+ mModelView.loadTranslate(x, y, 0.0f);
+ mModelView.scale(width, height, 1.0f);
+ } else {
+ mModelView.loadIdentity();
+ }
+ mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+ glUniform4f(mCurrentProgram->color, r, g, b, a);
+
+ textureUnit++;
+ if (applyFilters) {
+ // Setup attributes and uniforms required by the shaders
+ if (mShader) {
+ mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+ }
+ if (mColorFilter) {
+ mColorFilter->setupProgram(mCurrentProgram);
+ }
+ }
+}
+
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+ float x, float y, SkPaint* paint) {
+ // Handle underline and strike-through
+ uint32_t flags = paint->getFlags();
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ float underlineWidth = length;
+ // If length is > 0.0f, we already measured the text for the text alignment
+ if (length <= 0.0f) {
+ underlineWidth = paint->measureText(text, bytesCount);
+ }
+
+ float offsetX = 0;
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ offsetX = underlineWidth * 0.5f;
+ break;
+ case SkPaint::kRight_Align:
+ offsetX = underlineWidth;
+ break;
+ default:
+ break;
+ }
+
+ if (underlineWidth > 0.0f) {
+ float textSize = paint->getTextSize();
+ float height = textSize * kStdUnderline_Thickness;
+
+ float left = x - offsetX;
+ float top = 0.0f;
+ float right = left + underlineWidth;
+ float bottom = 0.0f;
+
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ top = y + textSize * kStdUnderline_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ top = y + textSize * kStdStrikeThru_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+ }
+ }
+}
+
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode, bool ignoreTransform) {
// If a shader is set, preserve only the alpha
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 76783e9..49143a5 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -45,6 +45,7 @@
#include "SkiaShader.h"
#include "SkiaColorFilter.h"
#include "PathCache.h"
+#include "TextDropShadowCache.h"
namespace android {
namespace uirenderer {
@@ -101,6 +102,9 @@
void resetColorFilter();
void setupColorFilter(SkiaColorFilter* filter);
+ void resetShadow();
+ void setupShadow(float radius, float dx, float dy, int color);
+
void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
private:
@@ -222,6 +226,73 @@
GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
/**
+ * Prepares the renderer to draw the specified shadow.
+ *
+ * @param texture The shadow texture
+ * @param x The x coordinate of the shadow
+ * @param y The y coordinate of the shadow
+ * @param mode The blending mode
+ */
+ void setupShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode);
+
+ /**
+ * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
+ *
+ * @param texture The texture to render with
+ * @param textureUnit The texture unit to use, may be modified
+ * @param x The x coordinate of the rectangle to draw
+ * @param y The y coordinate of the rectangle to draw
+ * @param r The red component of the color
+ * @param g The green component of the color
+ * @param b The blue component of the color
+ * @param a The alpha component of the color
+ * @param mode The blending mode
+ * @param transforms True if the matrix passed to the shader should be multiplied
+ * by the model-view matrix
+ * @param applyFilters Whether or not to take color filters and
+ * shaders into account
+ */
+ void setupTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y,
+ float r, float g, float b, float a, SkXfermode::Mode mode, bool transforms,
+ bool applyFilters);
+
+ /**
+ * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
+ *
+ * @param texture The texture to render with
+ * @param width The width of the texture
+ * @param height The height of the texture
+ * @param textureUnit The texture unit to use, may be modified
+ * @param x The x coordinate of the rectangle to draw
+ * @param y The y coordinate of the rectangle to draw
+ * @param r The red component of the color
+ * @param g The green component of the color
+ * @param b The blue component of the color
+ * @param a The alpha component of the color
+ * @param mode The blending mode
+ * @param transforms True if the matrix passed to the shader should be multiplied
+ * by the model-view matrix
+ * @param applyFilters Whether or not to take color filters and
+ * shaders into account
+ */
+ void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters);
+
+ /**
+ * Draws text underline and strike-through if needed.
+ *
+ * @param text The text to decor
+ * @param bytesCount The number of bytes in the text
+ * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param x The x coordinate where the text will be drawn
+ * @param y The y coordinate where the text will be drawn
+ * @param paint The paint to draw the text with
+ */
+ void drawTextDecorations(const char* text, int bytesCount, float length,
+ float x, float y, SkPaint* paint);
+
+ /**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
* back to default is achieved by calling:
*
@@ -304,6 +375,13 @@
// Font renderer
FontRenderer mFontRenderer;
+ // Drop shadow
+ bool mHasShadow;
+ float mShadowRadius;
+ float mShadowDx;
+ float mShadowDy;
+ int mShadowColor;
+
// Various caches
TextureCache mTextureCache;
LayerCache mLayerCache;
@@ -311,6 +389,7 @@
ProgramCache mProgramCache;
PathCache mPathCache;
PatchCache mPatchCache;
+ TextDropShadowCache mDropShadowCache;
}; // class OpenGLRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 4a01ffa..10440ea 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -162,10 +162,11 @@
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel());
+ // Textures are Alpha8
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
texture->blend = true;
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap.rowBytesAsPixels(), texture->height, 0,
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index dbae38e..6528d91 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -51,6 +51,8 @@
LOGE("Error while linking shaders: %s", log);
delete log;
}
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
glDeleteProgram(id);
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8a97b4c..39fe85a 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -137,7 +137,7 @@
// PorterDuff snippets
///////////////////////////////////////////////////////////////////////////////
-const char* gPorterDuff[12] = {
+const char* gBlendOps[18] = {
// Clear
"return vec4(0.0, 0.0, 0.0, 0.0);\n",
// Src
@@ -161,8 +161,26 @@
// DstAtop
"return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
// Xor
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, 1.0, "
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
"src.a + dst.a - 2.0 * src.a * dst.a);\n",
+ // Add
+ "return min(src + dst, 1.0);\n",
+ // Multiply
+ "return src * dst;\n",
+ // Screen
+ "return src + dst - src * dst;\n",
+ // Overlay
+ "return clamp(vec4(mix("
+ "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "step(dst.a, 2.0 * dst.rgb)), "
+ "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
+ // Darken
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+ // Lighten
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
};
///////////////////////////////////////////////////////////////////////////////
@@ -292,10 +310,10 @@
// Generate required functions
if (description.hasGradient && description.hasBitmap) {
- generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+ generateBlend(shader, "blendShaders", description.shadersMode);
}
if (description.colorOp == ProgramDescription::kColorBlend) {
- generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+ generateBlend(shader, "blendColors", description.colorMode);
}
if (description.isBitmapNpot) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
@@ -354,13 +372,12 @@
return shader;
}
-void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
- SkXfermode::Mode mode) {
+void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
shader.append("\nvec4 ");
shader.append(name);
shader.append("(vec4 src, vec4 dst) {\n");
shader.append(" ");
- shader.append(gPorterDuff[mode]);
+ shader.append(gBlendOps[mode]);
shader.append("}\n");
}
@@ -376,6 +393,9 @@
}
shader.append(" return vec2(");
switch (wrapS) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.x");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.x, 1.0)");
break;
@@ -385,6 +405,9 @@
}
shader.append(", ");
switch (wrapT) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.y");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.y, 1.0)");
break;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index a1a4a0e..fa4b8c4 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,9 +57,9 @@
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
-// Support only the 12 Porter-Duff modes for now
-#define PROGRAM_MAX_XFERMODE 0xC
-#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
@@ -177,7 +177,7 @@
Program* generateProgram(const ProgramDescription& description, programid key);
String8 generateVertexShader(const ProgramDescription& description);
String8 generateFragmentShader(const ProgramDescription& description);
- void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+ void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
void printLongString(const String8& shader) const;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 915b0be..7514b6f 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -27,6 +27,7 @@
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
// These properties are defined in pixels
#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 42c0621..c7a01b1 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -89,7 +89,8 @@
description.hasBitmap = true;
// The driver does not support non-power of two mirrored/repeated
// textures, so do it ourselves
- if (!extensions.hasNPot() && !isPowerOfTwo(width) && !isPowerOfTwo(height)) {
+ if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
+ (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
description.isBitmapNpot = true;
description.bitmapWrapS = gTileModes[mTileX];
description.bitmapWrapT = gTileModes[mTileY];
@@ -163,7 +164,8 @@
Rect start(mBounds[0], mBounds[1], mBounds[2], mBounds[3]);
if (mMatrix) {
mat4 shaderMatrix(*mMatrix);
- shaderMatrix.mapRect(start);
+ shaderMatrix.mapPoint(start.left, start.top);
+ shaderMatrix.mapPoint(start.right, start.bottom);
}
snapshot.transform.mapRect(start);
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 399ae68..77650a3 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -42,7 +42,7 @@
*/
class Snapshot: public LightRefBase<Snapshot> {
public:
- Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0) { }
+ Snapshot(): skip(false), flags(0), previous(NULL), layer(NULL), fbo(0) { }
/**
* Copies the specified snapshot. Only the transform and clip rectangle
@@ -54,10 +54,12 @@
height(s->height),
transform(s->transform),
clipRect(s->clipRect),
+ skip(false),
flags(0),
previous(s),
layer(NULL),
- fbo(s->fbo) {
+ fbo(s->fbo),
+ viewport(s->viewport) {
if ((s->flags & Snapshot::kFlagClipSet) &&
!(s->flags & Snapshot::kFlagDirtyLocalClip)) {
localClip.set(s->localClip);
@@ -137,8 +139,10 @@
if (flags & Snapshot::kFlagDirtyLocalClip) {
mat4 inverse;
inverse.loadInverse(transform);
+
localClip.set(clipRect);
inverse.mapRect(localClip);
+
flags &= ~Snapshot::kFlagDirtyLocalClip;
}
return localClip;
@@ -162,6 +166,12 @@
Rect clipRect;
/**
+ * This snapshot should be skipped. Snapshots marked as skipped are
+ * created by the renderer and should be hidden from the user.
+ */
+ bool skip;
+
+ /**
* Dirty flags.
*/
int flags;
@@ -178,6 +188,11 @@
GLuint fbo;
/**
+ * Current viewport.
+ */
+ Rect viewport;
+
+ /**
* Contains the previous ortho matrix.
*/
mat4 orthoMatrix;
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
new file mode 100644
index 0000000..aab5bd4
--- /dev/null
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "TextDropShadowCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize):
+ mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(maxByteSize) {
+ mCache.setOnEntryRemovedListener(this);
+}
+
+TextDropShadowCache::~TextDropShadowCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TextDropShadowCache::getSize() {
+ return mSize;
+}
+
+uint32_t TextDropShadowCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TextDropShadowCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) {
+ const uint32_t size = texture->width * texture->height;
+ mSize -= size;
+
+ if (texture) {
+ glDeleteTextures(1, &texture->id);
+ delete texture;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void TextDropShadowCache::clear() {
+ mCache.clear();
+}
+
+ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
+ int numGlyphs, uint32_t radius) {
+ ShadowText entry(paint, radius, len, text);
+ ShadowTexture* texture = mCache.get(entry);
+
+ if (!texture) {
+ FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0,
+ len, numGlyphs, radius);
+
+ texture = new ShadowTexture;
+ texture->left = shadow.penX;
+ texture->top = shadow.penY;
+ texture->width = shadow.width;
+ texture->height = shadow.height;
+ texture->generation = 0;
+ texture->blend = true;
+
+ const uint32_t size = shadow.width * shadow.height;
+ // Don't even try to cache a bitmap that's bigger than the cache
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ mCache.removeOldest();
+ }
+ }
+
+ glGenTextures(1, &texture->id);
+
+ glBindTexture(GL_TEXTURE_2D, texture->id);
+ // Textures are Alpha8
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (size < mMaxSize) {
+ mSize += size;
+ mCache.put(entry, texture);
+ } else {
+ texture->cleanup = true;
+ }
+
+ // Cleanup shadow
+ delete[] shadow.image;
+ }
+
+ return texture;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
new file mode 100644
index 0000000..5fbe9e5
--- /dev/null
+++ b/libs/hwui/TextDropShadowCache.h
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
+#define ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkPaint.h>
+
+#include "GenerationCache.h"
+#include "FontRenderer.h"
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct ShadowText {
+ ShadowText() { }
+
+ ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText):
+ paint(paint), radius(radius), len(len) {
+ text = new char[len];
+ memcpy(text, srcText, len);
+
+ hash = 0;
+ uint32_t multiplier = 1;
+ for (uint32_t i = 0; i < len; i++) {
+ hash += text[i] * multiplier;
+ uint32_t shifted = multiplier << 5;
+ multiplier = shifted - multiplier;
+ }
+ }
+
+ ShadowText(const ShadowText& shadow):
+ paint(shadow.paint), radius(shadow.radius), len(shadow.len), hash(shadow.hash) {
+ text = new char[shadow.len];
+ memcpy(text, shadow.text, shadow.len);
+ }
+
+ ~ShadowText() {
+ delete[] text;
+ }
+
+ SkPaint* paint;
+ uint32_t radius;
+ uint32_t len;
+ uint32_t hash;
+ char *text;
+
+ bool operator<(const ShadowText& rhs) const {
+ if (len < rhs.len) return true;
+ else if (len == rhs.len) {
+ if (radius < rhs.radius) return true;
+ else if (radius == rhs.radius) {
+ if (paint < rhs.paint) return true;
+ else if (paint == rhs.paint) {
+ if (hash < rhs.hash) return true;
+ if (hash == rhs.hash) {
+ return strncmp(text, rhs.text, len) < 0;
+ }
+ }
+ }
+ }
+ return false;
+ }
+}; // struct ShadowText
+
+/**
+ * Alpha texture used to represent a shadow.
+ */
+struct ShadowTexture: public Texture {
+ ShadowTexture(): Texture() {
+ }
+
+ float left;
+ float top;
+}; // struct ShadowTexture
+
+class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> {
+public:
+ TextDropShadowCache(uint32_t maxByteSize);
+ ~TextDropShadowCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(ShadowText& text, ShadowTexture*& texture);
+
+ ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
+ int numGlyphs, uint32_t radius);
+
+ /**
+ * Clears the cache. This causes all textures to be deleted.
+ */
+ void clear();
+
+ void setFontRenderer(FontRenderer& fontRenderer) {
+ mRenderer = &fontRenderer;
+ }
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ GenerationCache<ShadowText, ShadowTexture*> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+ FontRenderer* mRenderer;
+}; // class TextDropShadowCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
deleted file mode 100755
index e91bfb4..0000000
--- a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 24ef547..4a4d837 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -44,7 +44,6 @@
private ScriptC_Threshold mScript;
private ScriptC_Vertical_blur mScriptVBlur;
private ScriptC_Horizontal_blur mScriptHBlur;
- private ScriptC_Levels mScriptLevels;
private int mRadius = 0;
private SeekBar mRadiusSeekBar;
@@ -73,7 +72,8 @@
@SuppressWarnings({"FieldCanBeLocal"})
private Allocation mOutPixelsAllocation;
@SuppressWarnings({"FieldCanBeLocal"})
- private Allocation mScratchPixelsAllocation;
+ private Allocation mScratchPixelsAllocation1;
+ private Allocation mScratchPixelsAllocation2;
private SurfaceView mSurfaceView;
private ImageView mDisplayView;
@@ -261,29 +261,29 @@
}
else if(seekBar == mInBlackSeekBar) {
mInBlack = (float)progress;
- mScriptLevels.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
}
else if(seekBar == mOutBlackSeekBar) {
mOutBlack = (float)progress;
- mScriptLevels.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
}
else if(seekBar == mInWhiteSeekBar) {
mInWhite = (float)progress + 127.0f;
- mScriptLevels.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
}
else if(seekBar == mOutWhiteSeekBar) {
mOutWhite = (float)progress + 127.0f;
- mScriptLevels.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
}
else if(seekBar == mGammaSeekBar) {
mGamma = (float)progress/100.0f;
mGamma = Math.max(mGamma, 0.1f);
mGamma = 1.0f / mGamma;
- mScriptLevels.invoke_setGamma(mGamma);
+ mScriptVBlur.invoke_setGamma(mGamma);
}
else if(seekBar == mSaturationSeekBar) {
mSaturation = (float)progress / 50.0f;
- mScriptLevels.invoke_setSaturation(mSaturation);
+ mScriptVBlur.invoke_setSaturation(mSaturation);
}
long t = java.lang.System.currentTimeMillis();
@@ -373,28 +373,32 @@
mInPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapIn);
mOutPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapOut);
- mScratchPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapScratch);
+
+ Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+ tb.add(android.renderscript.Dimension.X, mBitmapIn.getWidth());
+ tb.add(android.renderscript.Dimension.Y, mBitmapIn.getHeight());
+ mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+ mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
mScriptVBlur = new ScriptC_Vertical_blur(mRS, getResources(), R.raw.vertical_blur, false);
mScriptHBlur = new ScriptC_Horizontal_blur(mRS, getResources(), R.raw.horizontal_blur, false);
- mScriptLevels = new ScriptC_Levels(mRS, getResources(), R.raw.levels, false);
mScript = new ScriptC_Threshold(mRS, getResources(), R.raw.threshold, false);
mScript.set_width(mBitmapIn.getWidth());
mScript.set_height(mBitmapIn.getHeight());
mScript.set_radius(mRadius);
- mScriptLevels.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
- mScriptLevels.invoke_setGamma(mGamma);
- mScriptLevels.invoke_setSaturation(mSaturation);
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setGamma(mGamma);
+ mScriptVBlur.invoke_setSaturation(mSaturation);
mScript.bind_InPixel(mInPixelsAllocation);
mScript.bind_OutPixel(mOutPixelsAllocation);
- mScript.bind_ScratchPixel(mScratchPixelsAllocation);
+ mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+ mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
mScript.set_vBlurScript(mScriptVBlur);
mScript.set_hBlurScript(mScriptHBlur);
- mScript.set_levelsScript(mScriptLevels);
}
private Bitmap loadBitmap(int resource) {
@@ -420,15 +424,15 @@
long t = java.lang.System.currentTimeMillis();
- mScript.invoke_filterBenchmark();
+ mScript.invoke_filter();
mRS.finish();
t = java.lang.System.currentTimeMillis() - t;
android.util.Log.v("Img", "Renderscript frame time core ms " + t);
- long javaTime = javaFilter();
- mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
- //mBenchmarkResult.setText("RS: " + t + " ms");
+ //long javaTime = javaFilter();
+ //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+ mBenchmarkResult.setText("RS: " + t + " ms");
mRadius = oldRadius;
mScript.set_radius(mRadius);
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
index 4ed5aba4..cfffac8 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
@@ -3,48 +3,28 @@
#include "ip.rsh"
void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
- uchar4 *output = (uchar4 *)v_out;
+ float4 *output = (float4 *)v_out;
const FilterStruct *fs = (const FilterStruct *)usrData;
- const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, 0, y);
+ const float4 *input = (const float4 *)rsGetElementAt(fs->ain, 0, y);
float3 blurredPixel = 0;
- float3 currentPixel = 0;
-
const float *gPtr = fs->gaussian;
if ((x > fs->radius) && (x < (fs->width - fs->radius))) {
- const uchar4 *i = input + (x - fs->radius);
+ const float4 *i = input + (x - fs->radius);
for(int r = -fs->radius; r <= fs->radius; r ++) {
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += i->xyz * gPtr[0];
gPtr++;
i++;
}
} else {
for(int r = -fs->radius; r <= fs->radius; r ++) {
// Stepping left and right away from the pixel
- int validW = x + r;
- // Clamp to zero and width max() isn't exposed for ints yet
- if(validW < 0) {
- validW = 0;
- }
- if(validW > fs->width - 1) {
- validW = fs->width - 1;
- }
- //int validW = rsClamp(w + r, 0, width - 1);
-
- currentPixel.x = (float)(input[validW].x);
- currentPixel.y = (float)(input[validW].y);
- currentPixel.z = (float)(input[validW].z);
-
- blurredPixel += currentPixel * gPtr[0];
+ int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1));
+ blurredPixel += input[validW].xyz * gPtr[0];
gPtr++;
}
}
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
+ output->xyz = blurredPixel;
}
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh
index 34213f5..1d7a719 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh
@@ -2,7 +2,7 @@
#define MAX_RADIUS 25
-typedef struct {
+typedef struct FilterStruct_s {
rs_allocation ain;
float *gaussian; //[MAX_RADIUS * 2 + 1];
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs
deleted file mode 100644
index bb8a6fc..0000000
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-#pragma version(1)
-
-#include "ip.rsh"
-
-
-static float inBlack;
-static float outBlack;
-static float inWhite;
-static float outWhite;
-static float3 gamma;
-static float saturation;
-
-static float inWMinInB;
-static float outWMinOutB;
-static float overInWMinInB;
-static rs_matrix3x3 colorMat;
-
-//#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel, inBlack, outBlack, inWhite, outWhite, gamma, saturation, InPixel, OutPixel, ScratchPixel, vBlurScript, hBlurScript)
-#pragma rs export_func(setLevels, setSaturation, setGamma);
-
-void setLevels(float iBlk, float oBlk, float iWht, float oWht) {
- inBlack = iBlk;
- outBlack = oBlk;
- inWhite = iWht;
- outWhite = oWht;
-
- inWMinInB = inWhite - inBlack;
- outWMinOutB = outWhite - outBlack;
- overInWMinInB = 1.f / inWMinInB;
-}
-
-void setSaturation(float sat) {
- saturation = sat;
-
- // Saturation
- // Linear weights
- //float rWeight = 0.3086f;
- //float gWeight = 0.6094f;
- //float bWeight = 0.0820f;
-
- // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
- float rWeight = 0.299f;
- float gWeight = 0.587f;
- float bWeight = 0.114f;
-
- float oneMinusS = 1.0f - saturation;
- rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
- rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
- rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
- rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
- rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
- rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
- rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
- rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
- rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
-}
-
-void setGamma(float g) {
- gamma = (float3)g;
-}
-
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
- const uchar4 *input = v_in;
- uchar4 *output = v_out;
-
- float3 currentPixel = 0;
-
- //currentPixel.xyz = convert_float3(input.xyz);
- currentPixel.x = (float)(input->x);
- currentPixel.y = (float)(input->y);
- currentPixel.z = (float)(input->z);
-
- float3 temp = rsMatrixMultiply(&colorMat, currentPixel);
- temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB;
- if (gamma.x != 1.0f)
- temp = pow(temp, (float3)gamma);
- currentPixel = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f);
-
- //output.xyz = convert_uchar3(currentPixel.xyz);
- output->x = (uint8_t)currentPixel.x;
- output->y = (uint8_t)currentPixel.y;
- output->z = (uint8_t)currentPixel.z;
- output->w = input->w;
-}
-
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
index 8e198c7..33945a5 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -8,14 +8,14 @@
uchar4 * InPixel;
uchar4 * OutPixel;
-uchar4 * ScratchPixel;
+float4 * ScratchPixel1;
+float4 * ScratchPixel2;
-#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel, vBlurScript, hBlurScript, levelsScript)
-#pragma rs export_func(filter, filterBenchmark);
+#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel1, ScratchPixel2, vBlurScript, hBlurScript)
+#pragma rs export_func(filter);
rs_script vBlurScript;
rs_script hBlurScript;
-rs_script levelsScript;
const int CMD_FINISHED = 1;
@@ -64,7 +64,21 @@
}
-static void blur() {
+static void copyInput() {
+ RS_DEBUG_MARKER;
+ rs_allocation ain = rsGetAllocation(InPixel);
+ uint32_t dimx = rsAllocationGetDimX(ain);
+ uint32_t dimy = rsAllocationGetDimY(ain);
+ for(uint32_t y = 0; y < dimy; y++) {
+ for(uint32_t x = 0; x < dimx; x++) {
+ ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
+ }
+ }
+ RS_DEBUG_MARKER;
+}
+
+void filter() {
+ copyInput();
computeGaussianWeights();
FilterStruct fs;
@@ -73,28 +87,11 @@
fs.height = height;
fs.radius = radius;
- fs.ain = rsGetAllocation(InPixel);
- rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel), &fs);
+ fs.ain = rsGetAllocation(ScratchPixel1);
+ rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs);
- fs.ain = rsGetAllocation(ScratchPixel);
+ fs.ain = rsGetAllocation(ScratchPixel2);
rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs);
-}
-
-void filter() {
- //RS_DEBUG(radius);
-
- if(radius > 0) {
- blur();
- rsForEach(levelsScript, rsGetAllocation(OutPixel), rsGetAllocation(OutPixel), 0);
- } else {
- rsForEach(levelsScript, rsGetAllocation(InPixel), rsGetAllocation(OutPixel), 0);
- }
-
- rsSendToClientBlocking(CMD_FINISHED);
-}
-
-void filterBenchmark() {
- blur();
rsSendToClientBlocking(CMD_FINISHED);
}
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
index 008c8b5..d901d2a 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -2,58 +2,91 @@
#include "ip.rsh"
+static float inBlack;
+static float outBlack;
+static float inWhite;
+static float outWhite;
+static float3 gamma;
+static float saturation;
+
+static float inWMinInB;
+static float outWMinOutB;
+static float overInWMinInB;
+static rs_matrix3x3 colorMat;
+
+#pragma rs export_func(setLevels, setSaturation, setGamma);
+
+void setLevels(float iBlk, float oBlk, float iWht, float oWht) {
+ inBlack = iBlk;
+ outBlack = oBlk;
+ inWhite = iWht;
+ outWhite = oWht;
+
+ inWMinInB = inWhite - inBlack;
+ outWMinOutB = outWhite - outBlack;
+ overInWMinInB = 1.f / inWMinInB;
+}
+
+void setSaturation(float sat) {
+ saturation = sat;
+
+ // Saturation
+ // Linear weights
+ //float rWeight = 0.3086f;
+ //float gWeight = 0.6094f;
+ //float bWeight = 0.0820f;
+
+ // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+ float rWeight = 0.299f;
+ float gWeight = 0.587f;
+ float bWeight = 0.114f;
+
+ float oneMinusS = 1.0f - saturation;
+ rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
+ rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
+ rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
+}
+
+void setGamma(float g) {
+ gamma = (float3)g;
+}
+
void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
uchar4 *output = (uchar4 *)v_out;
const FilterStruct *fs = (const FilterStruct *)usrData;
- const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, x, 0);
+ const float4 *input = (const float4 *)rsGetElementAt(fs->ain, x, 0);
float3 blurredPixel = 0;
- float3 currentPixel = 0;
-
const float *gPtr = fs->gaussian;
if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
- const uchar4 *i = input + ((y - fs->radius) * fs->width);
+ const float4 *i = input + ((y - fs->radius) * fs->width);
for(int r = -fs->radius; r <= fs->radius; r ++) {
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += i->xyz * gPtr[0];
gPtr++;
i += fs->width;
}
} else {
for(int r = -fs->radius; r <= fs->radius; r ++) {
- #if 1
- int validH = y + r;
- // Clamp to zero and width
- if(validH < 0) {
- validH = 0;
- }
- if(validH > fs->height - 1) {
- validH = fs->height - 1;
- }
-
- const uchar4 *i = input + validH * fs->width;
- //const uchar4 *i = (const uchar4 *)rsGetElementAt(fs->ain, x, validH);
-
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
+ const float4 *i = input + validH * fs->width;
+ blurredPixel += i->xyz * gPtr[0];
gPtr++;
- #else
- int validH = rsClamp(y + r, 0, height - 1);
- validH -= y;
- uchar4 *i = input + validH * width + x;
- blurredPixel.xyz += convert_float3(i->xyz) * gPtr[0];
- gPtr++;
- #endif
}
}
- //output->xyz = convert_uchar3(blurredPixel.xyz);
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
+ float3 temp = rsMatrixMultiply(&colorMat, blurredPixel);
+ temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB;
+ if (gamma.x != 1.0f)
+ temp = pow(temp, (float3)gamma);
+ temp = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f);
+
+ output->xyz = convert_uchar3(temp);
+ //output->w = input->w;
}
diff --git a/libs/rs/java/ModelViewer/res/drawable/robot.png b/libs/rs/java/ModelViewer/res/drawable/robot.png
index 7c85e56..f7353fd 100644
--- a/libs/rs/java/ModelViewer/res/drawable/robot.png
+++ b/libs/rs/java/ModelViewer/res/drawable/robot.png
Binary files differ
diff --git a/libs/rs/java/SceneGraph/Android.mk b/libs/rs/java/SceneGraph/Android.mk
new file mode 100644
index 0000000..5520446
--- /dev/null
+++ b/libs/rs/java/SceneGraph/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := SceneGraph
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/SceneGraph/AndroidManifest.xml b/libs/rs/java/SceneGraph/AndroidManifest.xml
new file mode 100644
index 0000000..8a8f87a
--- /dev/null
+++ b/libs/rs/java/SceneGraph/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.scenegraph">
+ <application android:label="SceneGraph">
+ <activity android:name="SceneGraph"
+ android:theme="@android:style/Theme.Black.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/rs/java/SceneGraph/res/drawable/robot.png b/libs/rs/java/SceneGraph/res/drawable/robot.png
new file mode 100644
index 0000000..f7353fd
--- /dev/null
+++ b/libs/rs/java/SceneGraph/res/drawable/robot.png
Binary files differ
diff --git a/libs/rs/java/SceneGraph/res/raw/robot.a3d b/libs/rs/java/SceneGraph/res/raw/robot.a3d
new file mode 100644
index 0000000..2d7d32b
--- /dev/null
+++ b/libs/rs/java/SceneGraph/res/raw/robot.a3d
Binary files differ
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraph.java b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraph.java
new file mode 100644
index 0000000..5daa4ac
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraph.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.scenegraph;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class SceneGraph extends Activity {
+
+ private SceneGraphView mView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mView = new SceneGraphView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mView.onPause();
+ }
+
+}
+
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphRS.java b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphRS.java
new file mode 100644
index 0000000..3db4a2b6
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphRS.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.scenegraph;
+
+import java.io.Writer;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+
+public class SceneGraphRS {
+
+ private final int STATE_LAST_FOCUS = 1;
+
+ int mWidth;
+ int mHeight;
+ int mRotation;
+
+ public SceneGraphRS() {
+ }
+
+ public void init(RenderScriptGL rs, Resources res, int width, int height) {
+ mRS = rs;
+ mRes = res;
+ mWidth = width;
+ mHeight = height;
+ mRotation = 0;
+ initRS();
+ }
+
+ private Resources mRes;
+ private RenderScriptGL mRS;
+ private Sampler mSampler;
+ private ProgramStore mPSBackground;
+ private ProgramFragment mPFBackground;
+ private ProgramVertex mPVBackground;
+ private ProgramVertex.MatrixAllocation mPVA;
+
+ private Allocation mGridImage;
+ private Allocation mAllocPV;
+
+ private Mesh mMesh;
+
+ private Font mItalic;
+ private Allocation mTextAlloc;
+
+ private ScriptC_Scenegraph mScript;
+ private ScriptC_Transform mTransformScript;
+
+ int mLastX;
+ int mLastY;
+
+ public void touchEvent(int x, int y) {
+ int dx = mLastX - x;
+ if(Math.abs(dx) > 50 || Math.abs(dx) < 3) {
+ dx = 0;
+ }
+
+ mRotation -= dx;
+ if(mRotation > 360) {
+ mRotation -= 360;
+ }
+ if(mRotation < 0) {
+ mRotation += 360;
+ }
+
+ mScript.set_gRotate(-(float)mRotation);
+
+ mLastX = x;
+ mLastY = y;
+ }
+
+ private void initPFS() {
+ ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null);
+
+ b.setDepthFunc(ProgramStore.DepthFunc.LESS);
+ b.setDitherEnable(false);
+ b.setDepthMask(true);
+ mPSBackground = b.create();
+
+ mScript.set_gPFSBackground(mPSBackground);
+ }
+
+ private void initPF() {
+ Sampler.Builder bs = new Sampler.Builder(mRS);
+ bs.setMin(Sampler.Value.LINEAR);
+ bs.setMag(Sampler.Value.LINEAR);
+ bs.setWrapS(Sampler.Value.CLAMP);
+ bs.setWrapT(Sampler.Value.WRAP);
+ mSampler = bs.create();
+
+ ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
+ b.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
+ ProgramFragment.Builder.Format.RGBA, 0);
+ mPFBackground = b.create();
+ mPFBackground.bindSampler(mSampler, 0);
+
+ mScript.set_gPFBackground(mPFBackground);
+ }
+
+ private void initPV() {
+ ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+ mPVBackground = pvb.create();
+
+ mPVA = new ProgramVertex.MatrixAllocation(mRS);
+ mPVBackground.bindAllocation(mPVA);
+ mPVA.setupProjectionNormalized(mWidth, mHeight);
+
+ mScript.set_gPVBackground(mPVBackground);
+ }
+
+ private void loadImage() {
+ mGridImage = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true);
+ mGridImage.uploadToTexture(0);
+
+ mScript.set_gTGrid(mGridImage);
+ }
+
+ private void initTextAllocation() {
+ String allocString = "Displaying file: R.raw.robot";
+ mTextAlloc = Allocation.createFromString(mRS, allocString);
+ mScript.set_gTextAlloc(mTextAlloc);
+ }
+
+ SgTransform mRootTransform;
+ SgTransform mGroup1;
+
+ SgTransform mRobot1;
+ SgTransform mRobot2;
+
+ void initTransformHierarchy() {
+ mRootTransform = new SgTransform(mRS);
+
+ mGroup1 = new SgTransform(mRS);
+ mRootTransform.addChild(mGroup1);
+
+ mRobot1 = new SgTransform(mRS);
+ mRobot2 = new SgTransform(mRS);
+
+ mGroup1.addChild(mRobot1);
+ mGroup1.addChild(mRobot2);
+
+ mGroup1.setTransform(0, new Float4(0.0f, 0.0f, 5.0f, 0.0f), TransformType.TRANSLATE);
+ mGroup1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 15.0f), TransformType.ROTATE);
+
+ mRobot1.setTransform(0, new Float4(-2.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE);
+ mRobot1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 20.0f), TransformType.ROTATE);
+ mRobot1.setTransform(2, new Float4(0.2f, 0.2f, 0.2f, 0.0f), TransformType.SCALE);
+
+ mRobot2.setTransform(0, new Float4(2.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE);
+ mRobot2.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, -20.0f), TransformType.ROTATE);
+ mRobot2.setTransform(2, new Float4(0.3f, 0.3f, 0.3f, 0.0f), TransformType.SCALE);
+ }
+
+ private void initRS() {
+
+ mScript = new ScriptC_Scenegraph(mRS, mRes, R.raw.scenegraph, true);
+ mTransformScript = new ScriptC_Transform(mRS, mRes, R.raw.transform, false);
+ mTransformScript.set_transformScript(mTransformScript);
+
+ mScript.set_gTransformRS(mTransformScript);
+
+ initPFS();
+ initPF();
+ initPV();
+
+ loadImage();
+
+ FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+ FileA3D.IndexEntry entry = model.getIndexEntry(0);
+ if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+ Log.e("rs", "could not load model");
+ }
+ else {
+ mMesh = (Mesh)entry.getObject();
+ mScript.set_gTestMesh(mMesh);
+ }
+
+ mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8);
+ mScript.set_gItalic(mItalic);
+
+ initTextAllocation();
+
+ initTransformHierarchy();
+
+ Log.v("========SceneGraph========", "transform hierarchy initialized");
+
+ mScript.bind_gRootNode(mRootTransform.getField());
+
+ mScript.bind_gGroup(mGroup1.mParent.mChildField);
+ mScript.bind_gRobot1(mRobot1.mParent.mChildField);
+ mScript.set_gRobot1Index(mRobot1.mIndexInParentGroup);
+ mScript.bind_gRobot2(mRobot2.mParent.mChildField);
+ mScript.set_gRobot2Index(mRobot2.mIndexInParentGroup);
+
+ mRS.contextBindRootScript(mScript);
+ }
+}
+
+
+
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphView.java b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphView.java
new file mode 100644
index 0000000..ae94869
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SceneGraphView.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.scenegraph;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class SceneGraphView extends RSSurfaceView {
+
+ public SceneGraphView(Context context) {
+ super(context);
+ //setFocusable(true);
+ }
+
+ private RenderScriptGL mRS;
+ private SceneGraphRS mRender;
+
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+ if (mRS == null) {
+ mRS = createRenderScript(true);
+ mRS.contextSetSurface(w, h, holder.getSurface());
+ mRender = new SceneGraphRS();
+ mRender.init(mRS, getResources(), w, h);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if(mRS != null) {
+ mRS = null;
+ destroyRenderScript();
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ // break point at here
+ // this method doesn't work when 'extends View' include 'extends ScrollView'.
+ return super.onKeyDown(keyCode, event);
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev)
+ {
+ boolean ret = true;
+ int act = ev.getAction();
+ if (act == ev.ACTION_UP) {
+ ret = false;
+ }
+
+ mRender.touchEvent((int)ev.getX(), (int)ev.getY());
+ return ret;
+ }
+}
+
+
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/SgTransform.java b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SgTransform.java
new file mode 100644
index 0000000..e81f1a7
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/SgTransform.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.scenegraph;
+
+import java.io.Writer;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+enum TransformType {
+
+ NONE(0),
+ TRANSLATE(1),
+ ROTATE(2),
+ SCALE(3);
+
+ int mID;
+ TransformType(int id) {
+ mID = id;
+ }
+}
+
+public class SgTransform {
+
+
+ ScriptField_SgTransform mTransformField;
+ ScriptField_SgTransform mChildField;
+ public ScriptField_SgTransform.Item mTransformData;
+
+ Float4[] mTransforms;
+ TransformType[] mTransformTypes;
+
+ RenderScript mRS;
+
+ Vector mChildren;
+ SgTransform mParent;
+ int mIndexInParentGroup;
+
+ public void setParent(SgTransform parent, int parentIndex) {
+ mParent = parent;
+ mIndexInParentGroup = parentIndex;
+ }
+
+ public void addChild(SgTransform child) {
+ mChildren.add(child);
+ child.setParent(this, mChildren.size() - 1);
+ }
+
+ public void setTransform(int index, Float4 value, TransformType type) {
+ mTransforms[index] = value;
+ mTransformTypes[index] = type;
+ }
+
+ void initData() {
+ int numTransforms = 16;
+ mTransforms = new Float4[numTransforms];
+ mTransformTypes = new TransformType[numTransforms];
+ for(int i = 0; i < numTransforms; i ++) {
+ mTransforms[i] = new Float4(0, 0, 0, 0);
+ mTransformTypes[i] = TransformType.NONE;
+ }
+ }
+
+ void setData() {
+
+ mTransformData.globalMat_Row0 = new Float4(1, 0, 0, 0);
+ mTransformData.globalMat_Row1 = new Float4(0, 1, 0, 0);
+ mTransformData.globalMat_Row2 = new Float4(0, 0, 1, 0);
+ mTransformData.globalMat_Row3 = new Float4(0, 0, 0, 1);
+
+ mTransformData.localMat_Row0 = new Float4(1, 0, 0, 0);
+ mTransformData.localMat_Row1 = new Float4(0, 1, 0, 0);
+ mTransformData.localMat_Row2 = new Float4(0, 0, 1, 0);
+ mTransformData.localMat_Row3 = new Float4(0, 0, 0, 1);
+
+ mTransformData.transforms0 = mTransforms[0];
+ mTransformData.transforms1 = mTransforms[1];
+ mTransformData.transforms2 = mTransforms[2];
+ mTransformData.transforms3 = mTransforms[3];
+ mTransformData.transforms4 = mTransforms[4];
+ mTransformData.transforms5 = mTransforms[5];
+ mTransformData.transforms6 = mTransforms[6];
+ mTransformData.transforms7 = mTransforms[7];
+ mTransformData.transforms8 = mTransforms[8];
+ mTransformData.transforms9 = mTransforms[9];
+ mTransformData.transforms10 = mTransforms[10];
+ mTransformData.transforms11 = mTransforms[11];
+ mTransformData.transforms12 = mTransforms[12];
+ mTransformData.transforms13 = mTransforms[13];
+ mTransformData.transforms14 = mTransforms[14];
+ mTransformData.transforms15 = mTransforms[15];
+
+ mTransformData.transformType0 = mTransformTypes[0].mID;
+ mTransformData.transformType1 = mTransformTypes[1].mID;
+ mTransformData.transformType2 = mTransformTypes[2].mID;
+ mTransformData.transformType3 = mTransformTypes[3].mID;
+ mTransformData.transformType4 = mTransformTypes[4].mID;
+ mTransformData.transformType5 = mTransformTypes[5].mID;
+ mTransformData.transformType6 = mTransformTypes[6].mID;
+ mTransformData.transformType7 = mTransformTypes[7].mID;
+ mTransformData.transformType8 = mTransformTypes[8].mID;
+ mTransformData.transformType9 = mTransformTypes[9].mID;
+ mTransformData.transformType10 = mTransformTypes[10].mID;
+ mTransformData.transformType11 = mTransformTypes[11].mID;
+ mTransformData.transformType12 = mTransformTypes[12].mID;
+ mTransformData.transformType13 = mTransformTypes[13].mID;
+ mTransformData.transformType14 = mTransformTypes[14].mID;
+ mTransformData.transformType15 = mTransformTypes[15].mID;
+
+ mTransformData.isDirty = 1;
+ mTransformData.children = null;
+
+ }
+
+ public SgTransform(RenderScript rs) {
+ mRS = rs;
+ mTransformData = new ScriptField_SgTransform.Item();
+ mChildren = new Vector();
+ initData();
+ }
+
+ public ScriptField_SgTransform.Item getData() {
+ setData();
+ if(mChildren.size() != 0) {
+ mChildField = new ScriptField_SgTransform(mRS, mChildren.size());
+ mTransformData.children = mChildField.getAllocation();
+
+ for(int i = 0; i < mChildren.size(); i ++) {
+ SgTransform child = (SgTransform)mChildren.get(i);
+ mChildField.set(child.getData(), i, false);
+ }
+ mChildField.copyAll();
+ }
+
+ return mTransformData;
+ }
+
+ public ScriptField_SgTransform getField() {
+ mTransformField = new ScriptField_SgTransform(mRS, 1);
+ mTransformField.set(getData(), 0, true);
+ return mTransformField;
+ }
+}
+
+
+
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/scenegraph.rs b/libs/rs/java/SceneGraph/src/com/android/scenegraph/scenegraph.rs
new file mode 100644
index 0000000..e6ae6df
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/scenegraph.rs
@@ -0,0 +1,92 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "rs_graphics.rsh"
+#include "transform_def.rsh"
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+rs_allocation gTGrid;
+rs_mesh gTestMesh;
+
+rs_program_store gPFSBackground;
+
+float gRotate;
+
+rs_font gItalic;
+rs_allocation gTextAlloc;
+
+rs_script gTransformRS;
+
+SgTransform *gGroup;
+SgTransform *gRobot1;
+int gRobot1Index;
+SgTransform *gRobot2;
+int gRobot2Index;
+
+SgTransform *gRootNode;
+
+#pragma rs export_var(gPVBackground, gPFBackground, gTGrid, gTestMesh, gPFSBackground, gRotate, gItalic, gTextAlloc, gTransformRS, gGroup, gRobot1, gRobot1Index, gRobot2, gRobot2Index, gRootNode)
+
+float gDT;
+int64_t gLastTime;
+
+void init() {
+ gRotate = 0.0f;
+}
+
+int root(int launchID) {
+
+ gGroup->transforms1.w += 0.5f;
+ gGroup->isDirty = 1;
+
+ SgTransform *robot1Ptr = gRobot1 + gRobot1Index;
+
+ robot1Ptr->transforms1.w -= 1.5f;
+ robot1Ptr->isDirty = 1;
+
+ SgTransform *robot2Ptr = gRobot2 + gRobot2Index;
+ robot2Ptr->transforms1.w += 2.5f;
+ robot2Ptr->isDirty = 1;
+
+ rsForEach(gTransformRS, gRootNode->children, gRootNode->children, 0);
+
+ rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ rsgClearDepth(1.0f);
+
+ rsgBindProgramVertex(gPVBackground);
+
+ rsgBindProgramFragment(gPFBackground);
+ rsgBindProgramStore(gPFSBackground);
+ rsgBindTexture(gPFBackground, 0, gTGrid);
+
+ rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot1Ptr->globalMat_Row0);
+ rsgDrawMesh(gTestMesh);
+
+ rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot2Ptr->globalMat_Row0);
+ rsgDrawMesh(gTestMesh);
+
+ color(0.3f, 0.3f, 0.3f, 1.0f);
+ rsgDrawText("Renderscript transform test", 30, 695);
+
+ rsgBindFont(gItalic);
+ rsgDrawText(gTextAlloc, 30, 730);
+
+ return 10;
+}
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform.rs b/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform.rs
new file mode 100644
index 0000000..a62d12b
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform.rs
@@ -0,0 +1,102 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#include "transform_def.rsh"
+
+rs_script transformScript;
+
+#pragma rs export_var(transformScript)
+
+typedef struct {
+ int changed;
+ rs_matrix4x4 *mat;
+} ParentData;
+
+void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
+ rs_matrix4x4 temp;
+
+ switch(type) {
+ case TRANSFORM_TRANSLATE:
+ rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
+ break;
+ case TRANSFORM_ROTATE:
+ rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
+ break;
+ case TRANSFORM_SCALE:
+ rsMatrixLoadScale(&temp, data.x, data.y, data.z);
+ break;
+ }
+ rsMatrixMultiply(mat, &temp);
+}
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+
+ SgTransform *data = (SgTransform *)v_out;
+ const ParentData *parent = (const ParentData *)usrData;
+
+ //rsDebug("Transform data", (int)data);
+ //rsDebug("Entering parent", (int)parent);
+
+ rs_matrix4x4 *localMat = (rs_matrix4x4*)&data->localMat_Row0;
+ rs_matrix4x4 *globalMat = (rs_matrix4x4*)&data->globalMat_Row0;
+
+ ParentData toChild;
+ toChild.changed = 0;
+ toChild.mat = globalMat;
+
+ //rsDebug("Transform is dirty", data->isDirty);
+
+ // Refresh matrices if dirty
+ if(data->isDirty) {
+ data->isDirty = 0;
+ toChild.changed = 1;
+
+ // Reset our local matrix
+ rsMatrixLoadIdentity(localMat);
+
+ float4 *transformSource = &data->transforms0;
+ int *transformTypes = &data->transformType0;
+
+ for(int i = 0; i < 16; i ++) {
+ if(transformTypes[i] == TRANSFORM_NONE) {
+ break;
+ }
+ //rsDebug("Transform adding transformation", transformTypes[i]);
+ appendTransformation(transformTypes[i], transformSource[i], localMat);
+ }
+ }
+
+ //rsDebug("Transform checking parent", (int)0);
+
+ if(parent) {
+ if(parent->changed) {
+ toChild.changed = 1;
+
+ rsMatrixLoad(globalMat, parent->mat);
+ rsMatrixMultiply(globalMat, localMat);
+ }
+ }
+ else {
+ rsMatrixLoad(globalMat, localMat);
+ }
+
+ //rsDebug("Transform calling self with child ", (int)data->children.p);
+ if(data->children.p) {
+ rsForEach(transformScript, data->children, data->children, (void*)&toChild);
+ }
+}
diff --git a/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform_def.rsh b/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform_def.rsh
new file mode 100644
index 0000000..10aac37
--- /dev/null
+++ b/libs/rs/java/SceneGraph/src/com/android/scenegraph/transform_def.rsh
@@ -0,0 +1,73 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.scenegraph)
+
+#define TRANSFORM_NONE 0
+#define TRANSFORM_TRANSLATE 1
+#define TRANSFORM_ROTATE 2
+#define TRANSFORM_SCALE 3
+
+typedef struct {
+ float4 globalMat_Row0;
+ float4 globalMat_Row1;
+ float4 globalMat_Row2;
+ float4 globalMat_Row3;
+
+ float4 localMat_Row0;
+ float4 localMat_Row1;
+ float4 localMat_Row2;
+ float4 localMat_Row3;
+
+ float4 transforms0;
+ float4 transforms1;
+ float4 transforms2;
+ float4 transforms3;
+ float4 transforms4;
+ float4 transforms5;
+ float4 transforms6;
+ float4 transforms7;
+ float4 transforms8;
+ float4 transforms9;
+ float4 transforms10;
+ float4 transforms11;
+ float4 transforms12;
+ float4 transforms13;
+ float4 transforms14;
+ float4 transforms15;
+
+ int transformType0;
+ int transformType1;
+ int transformType2;
+ int transformType3;
+ int transformType4;
+ int transformType5;
+ int transformType6;
+ int transformType7;
+ int transformType8;
+ int transformType9;
+ int transformType10;
+ int transformType11;
+ int transformType12;
+ int transformType13;
+ int transformType14;
+ int transformType15;
+
+ int isDirty;
+
+ rs_allocation children;
+
+} SgTransform;
diff --git a/libs/rs/java/tests/Android.mk b/libs/rs/java/tests/Android.mk
new file mode 100644
index 0000000..6c992d5
--- /dev/null
+++ b/libs/rs/java/tests/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RSTest
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/tests/AndroidManifest.xml b/libs/rs/java/tests/AndroidManifest.xml
new file mode 100644
index 0000000..bc144ab
--- /dev/null
+++ b/libs/rs/java/tests/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.rs.test">
+ <application
+ android:label="_RS_Test"
+ android:icon="@drawable/test_pattern">
+ <activity android:name="RSTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/rs/java/tests/res/drawable/test_pattern.png b/libs/rs/java/tests/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/libs/rs/java/tests/res/drawable/test_pattern.png
Binary files differ
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTest.java b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java
new file mode 100644
index 0000000..121793d
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.test;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class RSTest extends Activity {
+ //EventListener mListener = new EventListener();
+
+ private static final String LOG_TAG = "libRS_jni";
+ private static final boolean DEBUG = false;
+ private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+ private RSTestView mView;
+
+ // get the current looper (from your Activity UI thread for instance
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mView = new RSTestView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mView.onPause();
+ }
+
+ static void log(String message) {
+ if (LOG_ENABLED) {
+ Log.v(LOG_TAG, message);
+ }
+ }
+
+
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
new file mode 100644
index 0000000..c1a16dd
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+
+public class RSTestCore {
+ public static final int PART_COUNT = 50000;
+
+ public RSTestCore() {
+ }
+
+ private Resources mRes;
+ private RenderScriptGL mRS;
+
+ private ScriptC_Test_root mRootScript;
+
+ private boolean fp_mad() {
+ ScriptC_Fp_mad s = new ScriptC_Fp_mad(mRS, mRes, R.raw.fp_mad, true);
+ s.invoke_doTest(0, 0);
+ return true;
+ }
+
+ //private ScriptC_Fountain mScript;
+ public void init(RenderScriptGL rs, Resources res, int width, int height) {
+ mRS = rs;
+ mRes = res;
+
+ mRootScript = new ScriptC_Test_root(mRS, mRes, R.raw.test_root, true);
+
+ fp_mad();
+
+
+ /*
+ ProgramFragment.Builder pfb = new ProgramFragment.Builder(rs);
+ pfb.setVaryingColor(true);
+ rs.contextBindProgramFragment(pfb.create());
+
+ ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
+
+ Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+ smb.addVertexAllocation(points.getAllocation());
+ smb.addIndexType(Primitive.POINT);
+ Mesh sm = smb.create();
+
+ mScript = new ScriptC_Fountain(mRS, mRes, R.raw.fountain, true);
+ mScript.set_partMesh(sm);
+ mScript.bind_point(points);
+ mRS.contextBindRootScript(mScript);
+ */
+ }
+
+ public void newTouchPosition(float x, float y, float pressure, int id) {
+ }
+}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
new file mode 100644
index 0000000..7ae0c08
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.test;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RSTestView extends RSSurfaceView {
+
+ public RSTestView(Context context) {
+ super(context);
+ //setFocusable(true);
+ }
+
+ private RenderScriptGL mRS;
+ private RSTestCore mRender;
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+ if (mRS == null) {
+ mRS = createRenderScript(false);
+ mRS.contextSetSurface(w, h, holder.getSurface());
+ mRender = new RSTestCore();
+ mRender.init(mRS, getResources(), w, h);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if(mRS != null) {
+ mRS = null;
+ destroyRenderScript();
+ }
+ }
+
+/*
+ @Override
+ public boolean onTouchEvent(MotionEvent ev)
+ {
+ int act = ev.getActionMasked();
+ if (act == ev.ACTION_UP) {
+ mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
+ return false;
+ } else if (act == MotionEvent.ACTION_POINTER_UP) {
+ // only one pointer going up, we can get the index like this
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ mRender.newTouchPosition(0, 0, 0, pointerId);
+ }
+ int count = ev.getHistorySize();
+ int pcount = ev.getPointerCount();
+
+ for (int p=0; p < pcount; p++) {
+ int id = ev.getPointerId(p);
+ mRender.newTouchPosition(ev.getX(p),
+ ev.getY(p),
+ ev.getPressure(p),
+ id);
+
+ for (int i=0; i < count; i++) {
+ mRender.newTouchPosition(ev.getHistoricalX(p, i),
+ ev.getHistoricalY(p, i),
+ ev.getHistoricalPressure(p, i),
+ id);
+ }
+ }
+ return true;
+ }
+ */
+}
+
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
new file mode 100644
index 0000000..494ff35
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
@@ -0,0 +1,173 @@
+#include "shared.rsh"
+
+const int TEST_COUNT = 1;
+
+#pragma rs export_var(g_results)
+#pragma rs export_func(doTest)
+
+
+static float data_f1[1025];
+static float4 data_f4[1025];
+
+static void test_mad4(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~1 billion ops
+ for (int ct=0; ct < 1000 * (1000 / 80); ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f4[i] = (data_f4[i] * 0.02f +
+ data_f4[i+1] * 0.04f +
+ data_f4[i+2] * 0.05f +
+ data_f4[i+3] * 0.1f +
+ data_f4[i+4] * 0.2f +
+ data_f4[i+5] * 0.2f +
+ data_f4[i+6] * 0.1f +
+ data_f4[i+7] * 0.05f +
+ data_f4[i+8] * 0.04f +
+ data_f4[i+9] * 0.02f + 1.f);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_mad4 M ops", 1000.f / time);
+}
+
+static void test_mad(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~1 billion ops
+ for (int ct=0; ct < 1000 * (1000 / 20); ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f1[i] = (data_f1[i] * 0.02f +
+ data_f1[i+1] * 0.04f +
+ data_f1[i+2] * 0.05f +
+ data_f1[i+3] * 0.1f +
+ data_f1[i+4] * 0.2f +
+ data_f1[i+5] * 0.2f +
+ data_f1[i+6] * 0.1f +
+ data_f1[i+7] * 0.05f +
+ data_f1[i+8] * 0.04f +
+ data_f1[i+9] * 0.02f + 1.f);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_mad M ops", 1000.f / time);
+}
+
+static void test_norm(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~10 M ops
+ for (int ct=0; ct < 1000 * 10; ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f4[i] = normalize(data_f4[i]);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_norm M ops", 10.f / time);
+}
+
+static void test_sincos4(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~10 M ops
+ for (int ct=0; ct < 1000 * 10 / 4; ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f4[i] = sin(data_f4[i]) * cos(data_f4[i]);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_sincos4 M ops", 10.f / time);
+}
+
+static void test_sincos(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~10 M ops
+ for (int ct=0; ct < 1000 * 10; ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f1[i] = sin(data_f1[i]) * cos(data_f1[i]);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_sincos M ops", 10.f / time);
+}
+
+static void test_clamp(uint32_t index) {
+ start();
+
+ // Do ~100 M ops
+ for (int ct=0; ct < 1000 * 100; ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f1[i] = clamp(data_f1[i], -1.f, 1.f);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_clamp M ops", 100.f / time);
+
+ start();
+ // Do ~100 M ops
+ for (ct=0; ct < 1000 * 100; ct++) {
+ for (int i=0; i < (1000); i++) {
+ if (data_f1[i] < -1.f) data_f1[i] = -1.f;
+ if (data_f1[i] > -1.f) data_f1[i] = 1.f;
+ }
+ }
+
+ time = end(index);
+ rsDebug("fp_clamp ref M ops", 100.f / time);
+}
+
+static void test_clamp4(uint32_t index) {
+ start();
+
+ float total = 0;
+ // Do ~100 M ops
+ for (int ct=0; ct < 1000 * 100 /4; ct++) {
+ for (int i=0; i < (1000); i++) {
+ data_f4[i] = clamp(data_f4[i], -1.f, 1.f);
+ }
+ }
+
+ float time = end(index);
+ rsDebug("fp_clamp4 M ops", 100.f / time);
+}
+
+void doTest(uint32_t index, int test_num) {
+ for (int x=0; x < 1025; x++) {
+ data_f1[x] = (x & 0xf) * 0.1f;
+ data_f4[x].x = (x & 0xf) * 0.1f;
+ data_f4[x].y = (x & 0xf0) * 0.1f;
+ data_f4[x].z = (x & 0x33) * 0.1f;
+ data_f4[x].w = (x & 0x77) * 0.1f;
+ }
+
+ test_mad4(index);
+ test_mad(index);
+
+ for (x=0; x < 1025; x++) {
+ data_f1[x] = (x & 0xf) * 0.1f + 1.f;
+ data_f4[x].x = (x & 0xf) * 0.1f + 1.f;
+ data_f4[x].y = (x & 0xf0) * 0.1f + 1.f;
+ data_f4[x].z = (x & 0x33) * 0.1f + 1.f;
+ data_f4[x].w = (x & 0x77) * 0.1f + 1.f;
+ }
+
+ test_norm(index);
+ test_sincos4(index);
+ test_sincos(index);
+ test_clamp4(index);
+ test_clamp(index);
+}
+
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
new file mode 100644
index 0000000..1773e47
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
@@ -0,0 +1,25 @@
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+typedef struct TestResult_s {
+ rs_allocation name;
+ bool pass;
+ float score;
+ int64_t time;
+} TestResult;
+TestResult *g_results;
+
+static int64_t g_time;
+
+static void start(void) {
+ g_time = rsUptimeMillis();
+}
+
+static float end(uint32_t idx) {
+ int64_t t = rsUptimeMillis() - g_time;
+ //g_results[idx].time = t;
+ //rsDebug("test time", (int)t);
+ return ((float)t) / 1000.f;
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/test_root.rs b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs
new file mode 100644
index 0000000..72b391dd
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs
@@ -0,0 +1,26 @@
+// Fountain test script
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+
+typedef struct TestResult {
+ rs_allocation name;
+ bool pass;
+ float score;
+} TestResult_t;
+TestResult_t *results;
+
+#pragma rs export_var(results)
+//#pragma rs export_func(addParticles)
+
+int root() {
+
+ return 0;
+}
+
+
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 571b145..781dbea 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -438,36 +438,6 @@
ret RsProgramVertex
}
-LightBegin {
- }
-
-LightSetLocal {
- param bool isLocal
- }
-
-LightSetMonochromatic {
- param bool isMono
- }
-
-LightCreate {
- ret RsLight light
- }
-
-
-LightSetPosition {
- param RsLight light
- param float x
- param float y
- param float z
- }
-
-LightSetColor {
- param RsLight light
- param float r
- param float g
- param float b
- }
-
FileA3DCreateFromAssetStream {
param const void * data
param size_t len
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 6560101..7e44fea 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -34,6 +34,9 @@
init(rsc, type);
mPtr = malloc(mType->getSizeBytes());
+ if (mType->getElement()->getHasReferences()) {
+ memset(mPtr, 0, mType->getSizeBytes());
+ }
if (!mPtr) {
LOGE("Allocation::Allocation, alloc failure");
}
@@ -234,6 +237,12 @@
LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes);
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, sizeBytes / mType->getElement()->getSizeBytes());
+ decRefs(mPtr, sizeBytes / mType->getElement()->getSizeBytes());
+ }
+
memcpy(mPtr, data, size);
sendDirty();
mUploadDefered = true;
@@ -256,6 +265,12 @@
mType->dumpLOGV("type info");
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, count);
+ decRefs(ptr, count);
+ }
+
memcpy(ptr, data, size);
sendDirty();
mUploadDefered = true;
@@ -279,6 +294,10 @@
for (uint32_t line=yoff; line < (yoff+h); line++) {
uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(src, w);
+ decRefs(dst, w);
+ }
memcpy(dst, src, lineSize);
src += lineSize;
dst += destW * eSize;
@@ -375,7 +394,8 @@
alloc->setName(name.string(), name.size());
// Read in all of our allocation data
- stream->loadByteArray(alloc->getPtr(), dataSize);
+ alloc->data(stream->getPtr() + stream->getPos(), dataSize);
+ stream->reset(stream->getPos() + dataSize);
return alloc;
}
@@ -387,6 +407,32 @@
}
}
+void Allocation::incRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->incRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
+void Allocation::decRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->decRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
/////////////////
//
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 8273165..177d5a4 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -81,6 +81,9 @@
bool getIsTexture() const {return mIsTexture;}
bool getIsBufferObject() const {return mIsVertexBuffer;}
+ void incRefs(const void *ptr, size_t ct) const;
+ void decRefs(const void *ptr, size_t ct) const;
+
protected:
void sendDirty() const;
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 8e509ad..fbaa75f 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -161,6 +161,10 @@
mBits = mTypeBits * mVectorSize;
}
+bool Component::isReference() const
+{
+ return (mType >= RS_TYPE_ELEMENT);
+}
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
index 15fd5dd..a775051 100644
--- a/libs/rs/rsComponent.h
+++ b/libs/rs/rsComponent.h
@@ -51,6 +51,8 @@
void serialize(OStream *stream) const;
void loadFromStream(IStream *stream);
+ bool isReference() const;
+
protected:
RsDataType mType;
RsDataKind mKind;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 37b8bd6..2602dd4 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -34,6 +34,7 @@
mAllocLine = __LINE__;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
@@ -53,6 +54,7 @@
delete [] mFields;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
size_t Element::getSizeBits() const
@@ -68,15 +70,6 @@
return total;
}
-size_t Element::getFieldOffsetBits(uint32_t componentNumber) const
-{
- size_t offset = 0;
- for (uint32_t ct = 0; ct < componentNumber; ct++) {
- offset += mFields[ct].e->mBits;
- }
- return offset;
-}
-
void Element::dumpLOGV(const char *prefix) const
{
ObjectBase::dumpLOGV(prefix);
@@ -121,14 +114,22 @@
Element *elem = new Element(rsc);
elem->mComponent.loadFromStream(stream);
elem->mBits = elem->mComponent.getBits();
+ elem->mHasReference = elem->mComponent.isReference();
elem->mFieldCount = stream->loadU32();
if(elem->mFieldCount) {
+ uint32_t offset = 0;
elem->mFields = new ElementField_t [elem->mFieldCount];
for(uint32_t ct = 0; ct < elem->mFieldCount; ct ++) {
stream->loadString(&elem->mFields[ct].name);
Element *fieldElem = Element::createFromStream(rsc, stream);
elem->mFields[ct].e.set(fieldElem);
+ elem->mFields[ct].offsetBits = offset;
+ offset += fieldElem->getSizeBits();
+ // Check if our sub-elements have references
+ if(fieldElem->mHasReference) {
+ elem->mHasReference = true;
+ }
}
}
@@ -193,6 +194,7 @@
Element *e = new Element(rsc);
e->mComponent.set(dt, dk, isNorm, vecSize);
e->mBits = e->mComponent.getBits();
+ e->mHasReference = e->mComponent.isReference();
rsc->mStateElement.mElements.push(e);
return e;
}
@@ -223,9 +225,16 @@
Element *e = new Element(rsc);
e->mFields = new ElementField_t [count];
e->mFieldCount = count;
+ size_t bits = 0;
for (size_t ct=0; ct < count; ct++) {
e->mFields[ct].e.set(ein[ct]);
e->mFields[ct].name.setTo(nin[ct], lengths[ct]);
+ e->mFields[ct].offsetBits = bits;
+ bits += ein[ct]->getSizeBits();
+
+ if (ein[ct]->mHasReference) {
+ e->mHasReference = true;
+ }
}
rsc->mStateElement.mElements.push(e);
@@ -251,6 +260,43 @@
return s;
}
+void Element::incRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ if (ob) ob->incSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->incRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
+
+void Element::decRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ if (ob) ob->decSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->decRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
ElementState::ElementState()
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 90e7cc8..b5dad7a2 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -40,9 +40,11 @@
return (getSizeBits() + 7) >> 3;
}
- size_t getFieldOffsetBits(uint32_t componentNumber) const;
+ size_t getFieldOffsetBits(uint32_t componentNumber) const {
+ return mFields[componentNumber].offsetBits;
+ }
size_t getFieldOffsetBytes(uint32_t componentNumber) const {
- return (getFieldOffsetBits(componentNumber) + 7) >> 3;
+ return mFields[componentNumber].offsetBits >> 3;
}
uint32_t getFieldCount() const {return mFieldCount;}
@@ -66,6 +68,10 @@
static const Element * create(Context *rsc, size_t count, const Element **,
const char **, const size_t * lengths);
+ void incRefs(const void *) const;
+ void decRefs(const void *) const;
+ bool getHasReferences() const {return mHasReference;}
+
protected:
// deallocate any components that are part of this element.
void clear();
@@ -73,9 +79,11 @@
typedef struct {
String8 name;
ObjectBaseRef<const Element> e;
+ uint32_t offsetBits;
} ElementField_t;
ElementField_t *mFields;
size_t mFieldCount;
+ bool mHasReference;
Element(Context *);
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 833bee0..1ef9c93 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -130,15 +130,7 @@
// Move to the next character in the array
index = nextIndex;
- CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
-
- if(cachedGlyph == NULL) {
- cachedGlyph = cacheGlyph((uint32_t)utfChar);
- }
- // Is the glyph still in texture cache?
- if(!cachedGlyph->mIsValid) {
- updateGlyphCache(cachedGlyph);
- }
+ CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
// If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if(cachedGlyph->mIsValid) {
@@ -154,6 +146,20 @@
}
}
+Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
+
+ CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
+ if(cachedGlyph == NULL) {
+ cachedGlyph = cacheGlyph((uint32_t)utfChar);
+ }
+ // Is the glyph still in texture cache?
+ if(!cachedGlyph->mIsValid) {
+ updateGlyphCache(cachedGlyph);
+ }
+
+ return cachedGlyph;
+}
+
void Font::updateGlyphCache(CachedGlyphInfo *glyph)
{
FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
@@ -225,6 +231,7 @@
bool isInitialized = newFont->init(name, fontSize, dpi);
if(isInitialized) {
activeFonts.push(newFont);
+ rsc->mStateFont.precacheLatin(newFont);
return newFont;
}
@@ -422,6 +429,8 @@
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
@@ -611,12 +620,33 @@
}
}
+uint32_t FontState::getRemainingCacheCapacity() {
+ uint32_t remainingCapacity = 0;
+ float totalPixels = 0;
+ for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+ remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
+ totalPixels += mCacheLines[i]->mMaxWidth;
+ }
+ remainingCapacity = (remainingCapacity * 100) / totalPixels;
+ return remainingCapacity;
+}
+
+void FontState::precacheLatin(Font *font) {
+ // Remaining capacity is measured in %
+ uint32_t remainingCapacity = getRemainingCacheCapacity();
+ uint32_t precacheIdx = 0;
+ while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
+ font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
+ remainingCapacity = getRemainingCacheCapacity();
+ precacheIdx ++;
+ }
+}
+
+
void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
{
checkInit();
- //String8 text8(text);
-
// Render code here
Font *currentFont = mRSC->getFont();
if(!currentFont) {
@@ -636,6 +666,12 @@
issueDrawCommand();
mCurrentQuadIndex = 0;
}
+
+ // We store a string with letters in a rough frequency of occurrence
+ mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
+ mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
+ mLatinPrecache += String8(",.?!()-+@;:`'");
+ mLatinPrecache += String8("0123456789");
}
void FontState::renderText(const char *text, int x, int y)
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index ab229be..defe38b 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -93,6 +93,7 @@
bool mHasKerning;
DefaultKeyedVector<uint32_t, CachedGlyphInfo* > mCachedGlyphs;
+ CachedGlyphInfo* getCachedUTFChar(int32_t utfChar);
CachedGlyphInfo *cacheGlyph(uint32_t glyph);
void updateGlyphCache(CachedGlyphInfo *glyph);
@@ -129,9 +130,11 @@
uint32_t mMaxWidth;
uint32_t mCurrentRow;
uint32_t mCurrentCol;
+ bool mDirty;
CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) :
- mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol) {
+ mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol),
+ mDirty(false) {
}
bool fitBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
@@ -143,6 +146,7 @@
*retOriginX = mCurrentCol;
*retOriginY = mCurrentRow;
mCurrentCol += bitmap->width;
+ mDirty = true;
return true;
}
@@ -151,6 +155,10 @@
};
Vector<CacheTextureLine*> mCacheLines;
+ uint32_t getRemainingCacheCapacity();
+
+ void precacheLatin(Font *font);
+ String8 mLatinPrecache;
Context *mRSC;
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index 9026578..810e4ff 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -224,6 +224,49 @@
return mesh;
}
+void Mesh::computeBBox() {
+ float *posPtr = NULL;
+ uint32_t vectorSize = 0;
+ uint32_t stride = 0;
+ uint32_t numVerts = 0;
+ // First we need to find the position ptr and stride
+ for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
+ const Type *bufferType = mVertexBuffers[ct]->getType();
+ const Element *bufferElem = bufferType->getElement();
+
+ for (uint32_t ct=0; ct < bufferElem->getFieldCount(); ct++) {
+ if(strcmp(bufferElem->getFieldName(ct), "position") == 0) {
+ vectorSize = bufferElem->getField(ct)->getComponent().getVectorSize();
+ stride = bufferElem->getSizeBytes() / sizeof(float);
+ uint32_t offset = bufferElem->getFieldOffsetBytes(ct);
+ posPtr = (float*)((uint8_t*)mVertexBuffers[ct]->getPtr() + offset);
+ numVerts = bufferType->getDimX();
+ break;
+ }
+ }
+ if(posPtr) {
+ break;
+ }
+ }
+
+ mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 1e6;
+ mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = -1e6;
+ if(!posPtr) {
+ LOGE("Unable to compute bounding box");
+ mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 0.0f;
+ mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = 0.0f;
+ return;
+ }
+
+ for(uint32_t i = 0; i < numVerts; i ++) {
+ for(uint32_t v = 0; v < vectorSize; v ++) {
+ mBBoxMin[v] = rsMin(mBBoxMin[v], posPtr[v]);
+ mBBoxMax[v] = rsMax(mBBoxMax[v], posPtr[v]);
+ }
+ posPtr += stride;
+ }
+}
+
MeshContext::MeshContext()
{
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 765a971..ed01c38 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -61,6 +61,11 @@
virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
static Mesh *createFromStream(Context *rsc, IStream *stream);
+ // Bounding volumes
+ float mBBoxMin[3];
+ float mBBoxMax[3];
+ void computeBBox();
+
protected:
};
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 60de04a..aee4133 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -318,6 +318,12 @@
mDirty = true;
}
+void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const
+{
+ float *f = static_cast<float *>(mConstants[0]->getPtr());
+ memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
+}
+
void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
{
float *f = static_cast<float *>(mConstants[0]->getPtr());
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 1c8b9c8..a17c9ea 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -43,6 +43,7 @@
void addLight(const Light *);
void setProjectionMatrix(const rsc_Matrix *) const;
+ void getProjectionMatrix(rsc_Matrix *) const;
void setModelviewMatrix(const rsc_Matrix *) const;
void setTextureMatrix(const rsc_Matrix *) const;
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 455ece7..0a20344 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -56,6 +56,8 @@
char * mScriptText;
uint32_t mScriptTextLength;
+
+ bool mIsThreadable;
};
Enviroment_t mEnviroment;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 7c7b037..a140e22 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -278,7 +278,7 @@
}
- if ((rsc->getWorkerPoolSize() > 1) &&
+ if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable &&
((mtls.dimY * mtls.dimZ * mtls.dimArray) > 1)) {
//LOGE("launch 1");
@@ -350,10 +350,12 @@
static BCCvoid* symbolLookup(BCCvoid* pContext, const BCCchar* name)
{
const ScriptCState::SymbolTable_t *sym;
+ ScriptC *s = (ScriptC *)pContext;
sym = ScriptCState::lookupSymbol(name);
if (sym) {
return sym->mPtr;
}
+ s->mEnviroment.mIsThreadable = false;
sym = ScriptCState::lookupSymbolCL(name);
if (sym) {
return sym->mPtr;
@@ -371,8 +373,9 @@
LOGV("ScriptCState::runCompiler ");
s->mBccScript = bccCreateScript();
+ s->mEnviroment.mIsThreadable = true;
bccScriptBitcode(s->mBccScript, s->mEnviroment.mScriptText, s->mEnviroment.mScriptTextLength);
- bccRegisterSymbolCallback(s->mBccScript, symbolLookup, NULL);
+ bccRegisterSymbolCallback(s->mBccScript, symbolLookup, s);
bccCompileScript(s->mBccScript);
bccGetScriptLabel(s->mBccScript, "root", (BCCvoid**) &s->mProgram.mRoot);
bccGetScriptLabel(s->mBccScript, "init", (BCCvoid**) &s->mProgram.mInit);
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index ac32810..5a2b6ba 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -239,7 +239,7 @@
return a->getType()->getDimFaces();
}
-const void * SC_getElementAtX(RsAllocation va, uint32_t x)
+static const void * SC_getElementAtX(RsAllocation va, uint32_t x)
{
const Allocation *a = static_cast<const Allocation *>(va);
const Type *t = a->getType();
@@ -247,7 +247,7 @@
return &p[t->getElementSizeBytes() * x];
}
-const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
+static const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
{
const Allocation *a = static_cast<const Allocation *>(va);
const Type *t = a->getType();
@@ -255,7 +255,7 @@
return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
}
-const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
+static const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
{
const Allocation *a = static_cast<const Allocation *>(va);
const Type *t = a->getType();
@@ -263,6 +263,20 @@
return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
}
+static void SC_setObject(void **vdst, void * vsrc) {
+ static_cast<ObjectBase *>(vsrc)->incSysRef();
+ static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ *vdst = vsrc;
+}
+static void SC_clearObject(void **vdst) {
+ static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ *vdst = NULL;
+}
+static bool SC_isObject(RsAllocation vsrc) {
+ return vsrc != NULL;
+}
+
+
static void SC_debugF(const char *s, float f) {
LOGE("%s %f, 0x%08x", s, f, *((int *) (&f)));
@@ -405,6 +419,11 @@
{ "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY },
{ "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ },
+ { "_Z11rsSetObjectP13rs_allocation13rs_allocation", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject },
+ { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject },
+
+
// Debug
{ "_Z7rsDebugPKcf", (void *)&SC_debugF },
{ "_Z7rsDebugPKcff", (void *)&SC_debugFv2 },
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index f5e59534..a7877cdb 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -117,6 +117,12 @@
pf->setConstantColor(r, g, b, a);
}
+static void SC_vpGetProjectionMatrix(rsc_Matrix *m)
+{
+ GET_TLS();
+ rsc->getVertex()->getProjectionMatrix(m);
+}
+
//////////////////////////////////////////////////////////////////////////////
// Drawing
@@ -251,6 +257,20 @@
sm->renderPrimitiveRange(rsc, primIndex, start, len);
}
+static void SC_meshComputeBoundingBox(RsMesh vsm, float *minX, float *minY, float *minZ,
+ float *maxX, float *maxY, float *maxZ)
+{
+ GET_TLS();
+ Mesh *sm = static_cast<Mesh *>(vsm);
+ sm->computeBBox();
+ *minX = sm->mBBoxMin[0];
+ *minY = sm->mBBoxMin[1];
+ *minZ = sm->mBBoxMin[2];
+ *maxX = sm->mBBoxMax[0];
+ *maxY = sm->mBBoxMax[1];
+ *maxZ = sm->mBBoxMax[2];
+}
+
//////////////////////////////////////////////////////////////////////////////
//
@@ -373,6 +393,8 @@
{ "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix },
{ "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix },
+ { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix },
+
{ "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor },
{ "_Z11rsgGetWidthv", (void *)&SC_getWidth },
@@ -390,6 +412,7 @@
{ "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh },
{ "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive },
{ "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange },
+ { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox },
{ "_Z13rsgClearColorffff", (void *)&SC_ClearColor },
{ "_Z13rsgClearDepthf", (void *)&SC_ClearDepth },
diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh
index 85f3b25..0bfb3b9 100644
--- a/libs/rs/scriptc/rs_core.rsh
+++ b/libs/rs/scriptc/rs_core.rsh
@@ -602,6 +602,257 @@
m->m[2] = temp;
}
+/////////////////////////////////////////////////////
+// quaternion ops
+/////////////////////////////////////////////////////
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) {
+ q->w = w;
+ q->x = x;
+ q->y = y;
+ q->z = z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w = rhs->w;
+ q->x = rhs->x;
+ q->y = rhs->y;
+ q->z = rhs->z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, float s) {
+ q->w *= s;
+ q->x *= s;
+ q->y *= s;
+ q->z *= s;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w;
+ q->x = q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x;
+ q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->z + q->w*rhs->y;
+ q->z = q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z;
+}
+
+static void
+rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w *= rhs->w;
+ q->x *= rhs->x;
+ q->y *= rhs->y;
+ q->z *= rhs->z;
+}
+
+static void
+rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) {
+ rot *= (float)(M_PI / 180.0f) * 0.5f;
+ float c = cos(rot);
+ float s = sin(rot);
+
+ q->w = c;
+ q->x = x * s;
+ q->y = y * s;
+ q->z = z * s;
+}
+
+static void
+rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) {
+ const float len = x*x + y*y + z*z;
+ if (len != 1) {
+ const float recipLen = 1.f / sqrt(len);
+ x *= recipLen;
+ y *= recipLen;
+ z *= recipLen;
+ }
+ rsQuaternionLoadRotateUnit(q, rot, x, y, z);
+}
+
+static void
+rsQuaternionConjugate(rs_quaternion *q) {
+ q->x = -q->x;
+ q->y = -q->y;
+ q->z = -q->z;
+}
+
+static float
+rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) {
+ return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z;
+}
+
+static void
+rsQuaternionNormalize(rs_quaternion *q) {
+ const float len = rsQuaternionDot(q, q);
+ if (len != 1) {
+ const float recipLen = 1.f / sqrt(len);
+ rsQuaternionMultiply(q, recipLen);
+ }
+}
+
+static void
+rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) {
+ if(t <= 0.0f) {
+ rsQuaternionSet(q, q0);
+ return;
+ }
+ if(t >= 1.0f) {
+ rsQuaternionSet(q, q1);
+ return;
+ }
+
+ rs_quaternion tempq0, tempq1;
+ rsQuaternionSet(&tempq0, q0);
+ rsQuaternionSet(&tempq1, q1);
+
+ float angle = rsQuaternionDot(q0, q1);
+ if(angle < 0) {
+ rsQuaternionMultiply(&tempq0, -1.0f);
+ angle *= -1.0f;
+ }
+
+ float scale, invScale;
+ if (angle + 1.0f > 0.05f) {
+ if (1.0f - angle >= 0.05f) {
+ float theta = acos(angle);
+ float invSinTheta = 1.0f / sin(theta);
+ scale = sin(theta * (1.0f - t)) * invSinTheta;
+ invScale = sin(theta * t) * invSinTheta;
+ }
+ else {
+ scale = 1.0f - t;
+ invScale = t;
+ }
+ }
+ else {
+ rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w);
+ scale = sin(M_PI * (0.5f - t));
+ invScale = sin(M_PI * t);
+ }
+
+ rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale,
+ tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale);
+}
+
+static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) {
+ float x2 = 2.0f * q->x * q->x;
+ float y2 = 2.0f * q->y * q->y;
+ float z2 = 2.0f * q->z * q->z;
+ float xy = 2.0f * q->x * q->y;
+ float wz = 2.0f * q->w * q->z;
+ float xz = 2.0f * q->x * q->z;
+ float wy = 2.0f * q->w * q->y;
+ float wx = 2.0f * q->w * q->x;
+ float yz = 2.0f * q->y * q->z;
+
+ m->m[0] = 1.0f - y2 - z2;
+ m->m[1] = xy - wz;
+ m->m[2] = xz + wy;
+ m->m[3] = 0.0f;
+
+ m->m[4] = xy + wz;
+ m->m[5] = 1.0f - x2 - z2;
+ m->m[6] = yz - wx;
+ m->m[7] = 0.0f;
+
+ m->m[8] = xz - wy;
+ m->m[9] = yz - wx;
+ m->m[10] = 1.0f - x2 - y2;
+ m->m[11] = 0.0f;
+
+ m->m[12] = 0.0f;
+ m->m[13] = 0.0f;
+ m->m[14] = 0.0f;
+ m->m[15] = 1.0f;
+}
+
+/////////////////////////////////////////////////////
+// utility funcs
+/////////////////////////////////////////////////////
+void __attribute__((overloadable))
+rsExtractFrustumPlanes(const rs_matrix4x4 *modelViewProj,
+ float4 *left, float4 *right,
+ float4 *top, float4 *bottom,
+ float4 *near, float4 *far) {
+ // x y z w = a b c d in the plane equation
+ left->x = modelViewProj->m[3] + modelViewProj->m[0];
+ left->y = modelViewProj->m[7] + modelViewProj->m[4];
+ left->z = modelViewProj->m[11] + modelViewProj->m[8];
+ left->w = modelViewProj->m[15] + modelViewProj->m[12];
+
+ right->x = modelViewProj->m[3] - modelViewProj->m[0];
+ right->y = modelViewProj->m[7] - modelViewProj->m[4];
+ right->z = modelViewProj->m[11] - modelViewProj->m[8];
+ right->w = modelViewProj->m[15] - modelViewProj->m[12];
+
+ top->x = modelViewProj->m[3] - modelViewProj->m[1];
+ top->y = modelViewProj->m[7] - modelViewProj->m[5];
+ top->z = modelViewProj->m[11] - modelViewProj->m[9];
+ top->w = modelViewProj->m[15] - modelViewProj->m[13];
+
+ bottom->x = modelViewProj->m[3] + modelViewProj->m[1];
+ bottom->y = modelViewProj->m[7] + modelViewProj->m[5];
+ bottom->z = modelViewProj->m[11] + modelViewProj->m[9];
+ bottom->w = modelViewProj->m[15] + modelViewProj->m[13];
+
+ near->x = modelViewProj->m[3] + modelViewProj->m[2];
+ near->y = modelViewProj->m[7] + modelViewProj->m[6];
+ near->z = modelViewProj->m[11] + modelViewProj->m[10];
+ near->w = modelViewProj->m[15] + modelViewProj->m[14];
+
+ far->x = modelViewProj->m[3] - modelViewProj->m[2];
+ far->y = modelViewProj->m[7] - modelViewProj->m[6];
+ far->z = modelViewProj->m[11] - modelViewProj->m[10];
+ far->w = modelViewProj->m[15] - modelViewProj->m[14];
+
+ float len = length(left->xyz);
+ *left /= len;
+ len = length(right->xyz);
+ *right /= len;
+ len = length(top->xyz);
+ *top /= len;
+ len = length(bottom->xyz);
+ *bottom /= len;
+ len = length(near->xyz);
+ *near /= len;
+ len = length(far->xyz);
+ *far /= len;
+}
+
+bool __attribute__((overloadable))
+rsIsSphereInFrustum(float4 *sphere,
+ float4 *left, float4 *right,
+ float4 *top, float4 *bottom,
+ float4 *near, float4 *far) {
+
+ float distToCenter = dot(left->xyz, sphere->xyz) + left->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(right->xyz, sphere->xyz) + right->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(top->xyz, sphere->xyz) + top->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(bottom->xyz, sphere->xyz) + bottom->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(near->xyz, sphere->xyz) + near->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(far->xyz, sphere->xyz) + far->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ return true;
+}
+
/////////////////////////////////////////////////////
// int ops
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index fd0491c..c0b2d2d 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -27,6 +27,9 @@
rsgProgramVertexLoadTextureMatrix(const rs_matrix4x4 *);
extern void __attribute__((overloadable))
+ rsgProgramVertexGetProjectionMatrix(rs_matrix4x4 *);
+
+extern void __attribute__((overloadable))
rsgProgramFragmentConstantColor(rs_program_fragment, float, float, float, float);
extern uint __attribute__((overloadable))
@@ -77,6 +80,21 @@
extern void __attribute__((overloadable))
rsgFontColor(float, float, float, float);
+extern void __attribute__((overloadable))
+ rsgMeshComputeBoundingBox(rs_mesh mesh, float *minX, float *minY, float *minZ,
+ float *maxX, float *maxY, float *maxZ);
+void __attribute__((overloadable))
+rsgMeshComputeBoundingBox(rs_mesh mesh, float3 *bBoxMin, float3 *bBoxMax) {
+ float x1, y1, z1, x2, y2, z2;
+ rsgMeshComputeBoundingBox(mesh, &x1, &y1, &z1, &x2, &y2, &z2);
+ bBoxMin->x = x1;
+ bBoxMin->y = y1;
+ bBoxMin->z = z1;
+ bBoxMax->x = x2;
+ bBoxMax->y = y2;
+ bBoxMax->z = z2;
+}
+
///////////////////////////////////////////////////////
// misc
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 45f6bf4..c842ef1 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -29,6 +29,77 @@
#include "rs_cl.rsh"
#include "rs_core.rsh"
+extern void __attribute__((overloadable))
+ rsSetObject(rs_element *dst, rs_element src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_type *dst, rs_type src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_allocation *dst, rs_allocation src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_sampler *dst, rs_sampler src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_script *dst, rs_script src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_mesh *dst, rs_mesh src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_program_fragment *dst, rs_program_fragment src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_program_vertex *dst, rs_program_vertex src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_program_raster *dst, rs_program_raster src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_program_store *dst, rs_program_store src);
+extern void __attribute__((overloadable))
+ rsSetObject(rs_font *dst, rs_font src);
+
+extern void __attribute__((overloadable))
+ rsClearObject(rs_element *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_type *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_allocation *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_sampler *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_script *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_mesh *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_program_fragment *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_program_vertex *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_program_raster *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_program_store *dst);
+extern void __attribute__((overloadable))
+ rsClearObject(rs_font *dst);
+
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_element);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_type);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_allocation);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_sampler);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_script);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_mesh);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_program_fragment);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_program_vertex);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_program_raster);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_program_store);
+extern bool __attribute__((overloadable))
+ rsIsObject(rs_font);
+
+
+
// Allocations
// Return the rs_allocation associated with a bound data
@@ -112,8 +183,19 @@
extern void __attribute__((overloadable))
rsSendToClientBlocking(int cmdID, const void *data, uint len);
+
// Script to Script
+enum rs_for_each_strategy {
+ RS_FOR_EACH_STRATEGY_SERIAL,
+ RS_FOR_EACH_STRATEGY_DONT_CARE,
+ RS_FOR_EACH_STRATEGY_DST_LINEAR,
+ RS_FOR_EACH_STRATEGY_TILE_SMALL,
+ RS_FOR_EACH_STRATEGY_TILE_MEDIUM,
+ RS_FOR_EACH_STRATEGY_TILE_LARGE
+};
+
typedef struct rs_script_call {
+ enum rs_for_each_strategy strategy;
uint32_t xStart;
uint32_t xEnd;
uint32_t yStart;
@@ -122,7 +204,6 @@
uint32_t zEnd;
uint32_t arrayStart;
uint32_t arrayEnd;
-
} rs_script_call_t;
extern void __attribute__((overloadable))
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 69e1aed..dd42972 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -14,17 +14,18 @@
typedef uint32_t uint;
typedef uint64_t ulong;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_element;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_type;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_allocation;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_sampler;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_script;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_mesh;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_fragment;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_vertex;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_raster;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_store;
-typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_font;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_element;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_type;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_allocation;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_sampler;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_script;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_fragment;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_vertex;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_raster;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_store;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_font;
+
typedef float float2 __attribute__((ext_vector_type(2)));
typedef float float3 __attribute__((ext_vector_type(3)));
@@ -67,6 +68,7 @@
float m[4];
} rs_matrix2x2;
+typedef float4 rs_quaternion;
#define RS_PACKED __attribute__((packed, aligned(4)))
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 124f7b3..b1284fe 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -60,7 +60,6 @@
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
-#define id_to_index(id) ((id&ID_MASK)+1)
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
@@ -87,7 +86,7 @@
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+ , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -151,9 +150,9 @@
struct input_absinfo info;
- if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
LOGW("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
+ axis, device->name.string(), device->fd);
return -errno;
}
@@ -182,7 +181,7 @@
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -205,8 +204,7 @@
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
#if 0
for (size_t i=0; i<=KEY_MAX; i++) {
LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -242,7 +240,7 @@
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -638,6 +636,7 @@
return -1;
}
+ device->fd = fd;
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 6618702..5f5a4ac 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -1080,6 +1080,14 @@
1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
}
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
// --- TouchInputMapper ---
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 60a0d82..e09e755 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -232,6 +232,12 @@
}
}
+void AssetManager::getConfiguration(ResTable_config* outConfig) const
+{
+ AutoMutex _l(mLock);
+ *outConfig = *mConfig;
+}
+
/*
* Open an asset.
*
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index fe49300..adedf0c 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -156,9 +156,9 @@
return false;
}
- if (footerSize < kFooterMinSize) {
- LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
- footerSize, kFooterMinSize);
+ if (footerSize < (kFooterMinSize - kFooterTagSize)) {
+ LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize - kFooterTagSize);
return false;
}
}
@@ -169,6 +169,8 @@
return false;
}
+ mFooterStart = fileOffset;
+
char* scanBuf = (char*)malloc(footerSize);
if (scanBuf == NULL) {
LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
@@ -293,4 +295,38 @@
return true;
}
+bool ObbFile::removeFrom(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_RDWR);
+ if (fd < 0) {
+ goto out;
+ }
+ success = removeFrom(fd);
+ close(fd);
+
+out:
+ if (!success) {
+ LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno));
+ }
+ return success;
+}
+
+bool ObbFile::removeFrom(int fd)
+{
+ if (fd < 0) {
+ return false;
+ }
+
+ if (!readFrom(fd)) {
+ return false;
+ }
+
+ ftruncate(fd, mFooterStart);
+
+ return true;
+}
+
}
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java
index aed29c3..35038fa 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -101,15 +101,15 @@
public static final int STATE_INITIALIZED = 1;
// to keep in sync with
- // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
- /**
- * Event id for engine state change notification.
- */
- public static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ // frameworks/base/include/media/AudioEffect.h
/**
* Event id for engine control ownership change notification.
*/
- public static final int NATIVE_EVENT_CONTROL_STATUS = 1;
+ public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
+ /**
+ * Event id for engine state change notification.
+ */
+ public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
/**
* Event id for engine parameter change notification.
*/
@@ -795,7 +795,7 @@
// Interface definitions
// --------------------
/**
- * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
* when a the enabled state of the effect engine was changed by the controlling application.
*/
public interface OnEnableStatusChangeListener {
@@ -922,7 +922,6 @@
if (effect == null) {
return;
}
-
if (effect.mNativeEventHandler != null) {
Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
arg2, obj);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9212708..41d2cc5 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -239,6 +239,9 @@
// independently change its priority)
private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
+ // Used to alter media button redirection when the phone is ringing.
+ private boolean mIsRinging = false;
+
// Devices currently connected
private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
@@ -1956,11 +1959,16 @@
private final static Object mAudioFocusLock = new Object();
+ private final static Object mRingingLock = new Object();
+
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
//Log.v(TAG, " CALL_STATE_RINGING");
+ synchronized(mRingingLock) {
+ mIsRinging = true;
+ }
int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
if (ringVolume > 0) {
requestAudioFocus(AudioManager.STREAM_RING,
@@ -1970,12 +1978,18 @@
}
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
//Log.v(TAG, " CALL_STATE_OFFHOOK");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
requestAudioFocus(AudioManager.STREAM_RING,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
null, null /* both allowed to be null only for this clientId */,
IN_VOICE_COMM_FOCUS_ID /*clientId*/);
} else if (state == TelephonyManager.CALL_STATE_IDLE) {
//Log.v(TAG, " CALL_STATE_IDLE");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
}
}
@@ -2243,9 +2257,11 @@
// 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(mRingingLock) {
+ if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
+ (getMode() == AudioSystem.MODE_RINGTONE) ) {
+ return;
+ }
}
synchronized(mRCStack) {
if (!mRCStack.empty()) {
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 37f9f2c..88cce46 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -220,6 +220,50 @@
return -1;
}
+ private int[] getSupportedPlaybackFormats() {
+ return new int[] {
+ Mtp.Object.FORMAT_ASSOCIATION,
+ Mtp.Object.FORMAT_MP3,
+ Mtp.Object.FORMAT_MPEG,
+ Mtp.Object.FORMAT_EXIF_JPEG,
+ Mtp.Object.FORMAT_TIFF_EP,
+ Mtp.Object.FORMAT_GIF,
+ Mtp.Object.FORMAT_JFIF,
+ Mtp.Object.FORMAT_PNG,
+ Mtp.Object.FORMAT_TIFF,
+ Mtp.Object.FORMAT_WMA,
+ Mtp.Object.FORMAT_OGG,
+ Mtp.Object.FORMAT_AAC,
+ Mtp.Object.FORMAT_MP4_CONTAINER,
+ Mtp.Object.FORMAT_MP2,
+ Mtp.Object.FORMAT_3GP_CONTAINER,
+ Mtp.Object.FORMAT_ABSTRACT_AV_PLAYLIST,
+ Mtp.Object.FORMAT_WPL_PLAYLIST,
+ Mtp.Object.FORMAT_M3U_PLAYLIST,
+ Mtp.Object.FORMAT_PLS_PLAYLIST,
+ };
+ }
+
+ private int[] getSupportedCaptureFormats() {
+ // no capture formats yet
+ return null;
+ }
+
+ private int[] getSupportedObjectProperties(int handle) {
+ return new int[] {
+ Mtp.Object.PROPERTY_STORAGE_ID,
+ Mtp.Object.PROPERTY_OBJECT_FORMAT,
+ Mtp.Object.PROPERTY_OBJECT_SIZE,
+ Mtp.Object.PROPERTY_OBJECT_FILE_NAME,
+ Mtp.Object.PROPERTY_PARENT_OBJECT,
+ };
+ }
+
+ private int[] getSupportedDeviceProperties() {
+ // no device properties yet
+ return null;
+ }
+
private int getObjectProperty(int handle, int property,
long[] outIntValue, char[] outStringValue) {
Log.d(TAG, "getObjectProperty: " + property);
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index c113ffe..f69b8ad 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -318,6 +318,15 @@
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
const sp<Surface> native_surface = get_surface(env, surface);
+
+ // The application may misbehave and
+ // the preview surface becomes unavailable
+ if (native_surface.get() == 0) {
+ LOGE("Application lost the surface");
+ jniThrowException(env, "java/io/IOException", "invalid preview surface");
+ return;
+ }
+
LOGI("prepare: surface=%p (identity=%d)", native_surface.get(), native_surface->getIdentity());
if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) {
return;
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index bfdc872..abbea30 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -40,6 +40,10 @@
static jmethodID method_endSendObject;
static jmethodID method_getObjectList;
static jmethodID method_getNumObjects;
+static jmethodID method_getSupportedPlaybackFormats;
+static jmethodID method_getSupportedCaptureFormats;
+static jmethodID method_getSupportedObjectProperties;
+static jmethodID method_getSupportedDeviceProperties;
static jmethodID method_getObjectProperty;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
@@ -87,6 +91,13 @@
MtpObjectFormat format,
MtpObjectHandle parent);
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats();
+ virtual MtpObjectFormatList* getSupportedCaptureFormats();
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties();
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet);
@@ -190,6 +201,66 @@
(jint)storageID, (jint)format, (jint)parent);
}
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedPlaybackFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedCaptureFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedObjectProperties, (jint)format);
+ if (!array)
+ return NULL;
+ MtpObjectPropertyList* list = new MtpObjectPropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedDeviceProperties);
+ if (!array)
+ return NULL;
+ MtpDevicePropertyList* list = new MtpDevicePropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
@@ -460,6 +531,26 @@
LOGE("Can't find getNumObjects");
return -1;
}
+ method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
+ if (method_getSupportedPlaybackFormats == NULL) {
+ LOGE("Can't find getSupportedPlaybackFormats");
+ return -1;
+ }
+ method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
+ if (method_getSupportedCaptureFormats == NULL) {
+ LOGE("Can't find getSupportedCaptureFormats");
+ return -1;
+ }
+ method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
+ if (method_getSupportedObjectProperties == NULL) {
+ LOGE("Can't find getSupportedObjectProperties");
+ return -1;
+ }
+ method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
+ if (method_getSupportedDeviceProperties == NULL) {
+ LOGE("Can't find getSupportedDeviceProperties");
+ return -1;
+ }
method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
if (method_getObjectProperty == NULL) {
LOGE("Can't find getObjectProperty");
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 3cdf48a..0f3e245 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -218,7 +218,7 @@
return mIEffect->disable();
}
}
- return INVALID_OPERATION;
+ return NO_ERROR;
}
status_t AudioEffect::command(uint32_t cmdCode,
@@ -231,7 +231,22 @@
return INVALID_OPERATION;
}
- return mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = *(status_t *)replyData;
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ if (cmdCode == EFFECT_CMD_ENABLE) {
+ android_atomic_or(1, &mEnabled);
+ }
+ if (cmdCode == EFFECT_CMD_DISABLE) {
+ android_atomic_and(~1, &mEnabled);
+ }
+ return status;
}
@@ -347,7 +362,11 @@
{
LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
- mEnabled = enabled;
+ if (enabled) {
+ android_atomic_or(1, &mEnabled);
+ } else {
+ android_atomic_and(~1, &mEnabled);
+ }
if (mCbf) {
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
}
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 4eb63e8..947ff34 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -23,6 +23,7 @@
#include <camera/ICamera.h>
#include <media/IMediaRecorderClient.h>
#include <media/IMediaRecorder.h>
+#include <unistd.h>
namespace android {
@@ -373,6 +374,7 @@
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
reply->writeInt32(setOutputFile(fd, offset, length));
+ ::close(fd);
return NO_ERROR;
} break;
case SET_VIDEO_SIZE: {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 5756e53..a616aae 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -56,11 +56,6 @@
StagefrightRecorder::~StagefrightRecorder() {
LOGV("Destructor");
stop();
-
- if (mOutputFd >= 0) {
- ::close(mOutputFd);
- mOutputFd = -1;
- }
}
status_t StagefrightRecorder::init() {
@@ -736,7 +731,9 @@
encMeta->setInt32(kKeyChannelCount, mAudioChannels);
encMeta->setInt32(kKeySampleRate, mSampleRate);
encMeta->setInt32(kKeyBitRate, mAudioBitRate);
- encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ if (mAudioTimeScale > 0) {
+ encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -1037,7 +1034,9 @@
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
enc_meta->setInt32(kKeyColorFormat, colorFormat);
- enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ if (mVideoTimeScale > 0) {
+ enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ }
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -1120,7 +1119,9 @@
meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
- meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ if (mMovieTimeScale > 0) {
+ meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ }
if (mTrackEveryTimeDurationUs > 0) {
meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -1158,6 +1159,11 @@
mFlags = 0;
}
+ if (mOutputFd >= 0) {
+ ::close(mOutputFd);
+ mOutputFd = -1;
+ }
+
return OK;
}
@@ -1191,9 +1197,9 @@
mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
- mMovieTimeScale = 1000;
- mAudioTimeScale = 1000;
- mVideoTimeScale = 1000;
+ mMovieTimeScale = -1;
+ mAudioTimeScale = -1;
+ mVideoTimeScale = -1;
mCameraId = 0;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 77a1476..404762f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -81,6 +81,7 @@
libstagefright_httplive \
libstagefright_rtsp \
libstagefright_id3 \
+ libstagefright_g711dec \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 99978e8..c8dfede 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -35,7 +35,8 @@
: mStarted(false),
mCollectStats(false),
mPrevSampleTimeUs(0),
- mNumLostFrames(0),
+ mTotalLostFrames(0),
+ mPrevLostBytes(0),
mGroup(NULL) {
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
@@ -108,7 +109,8 @@
mStarted = false;
if (mCollectStats) {
- LOGI("Total lost audio frames: %lld", mNumLostFrames);
+ LOGI("Total lost audio frames: %lld",
+ mTotalLostFrames + (mPrevLostBytes >> 1));
}
return OK;
@@ -186,10 +188,11 @@
// Insert null frames when lost frames are detected.
int64_t timestampUs = mPrevSampleTimeUs;
uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
+ numLostBytes += mPrevLostBytes;
#if 0
// Simulate lost frames
- numLostBytes = ((rand() * 1.0 / RAND_MAX)) * kMaxBufferSize;
- numLostBytes &= 0xFFFFFFFE; // Alignment request
+ numLostBytes = ((rand() * 1.0 / RAND_MAX)) * 2 * kMaxBufferSize;
+ numLostBytes &= 0xFFFFFFFE; // Alignment requirement
// Reduce the chance to lose
if (rand() * 1.0 / RAND_MAX >= 0.05) {
@@ -197,13 +200,18 @@
}
#endif
if (numLostBytes > 0) {
- // Not expect too many lost frames!
- CHECK(numLostBytes <= kMaxBufferSize);
+ if (numLostBytes > kMaxBufferSize) {
+ mPrevLostBytes = numLostBytes - kMaxBufferSize;
+ numLostBytes = kMaxBufferSize;
+ }
- timestampUs += (1000000LL * numLostBytes >> 1) / sampleRate;
+ CHECK_EQ(numLostBytes & 1, 0);
+ timestampUs += ((1000000LL * (numLostBytes >> 1)) +
+ (sampleRate >> 1)) / sampleRate;
+
CHECK(timestampUs > mPrevSampleTimeUs);
if (mCollectStats) {
- mNumLostFrames += (numLostBytes >> 1);
+ mTotalLostFrames += (numLostBytes >> 1);
}
if ((err = skipFrame(timestampUs, options)) == -1) {
buffer->release();
@@ -240,7 +248,7 @@
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
CHECK(timestampUs > mPrevSampleTimeUs);
- if (mNumLostFrames == 0) {
+ if (mTotalLostFrames == 0) {
CHECK_EQ(mPrevSampleTimeUs,
mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e426fca..f2653cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -854,18 +854,6 @@
status_t AwesomePlayer::initVideoDecoder() {
uint32_t flags = 0;
-#if 0
- if (mRTPSession != NULL) {
- // XXX hack.
-
- const char *mime;
- CHECK(mVideoTrack->getFormat()->findCString(kKeyMIMEType, &mime));
- if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- flags |= OMXCodec::kPreferSoftwareCodecs;
- }
- }
-#endif
-
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
@@ -1019,6 +1007,12 @@
int64_t latenessUs = nowUs - timeUs;
+ if (mRTPSession != NULL) {
+ // We'll completely ignore timestamps for gtalk videochat
+ // and we'll play incoming video as fast as we get it.
+ latenessUs = 0;
+ }
+
if (latenessUs > 40000) {
// We're more than 40ms late.
LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
@@ -1222,7 +1216,7 @@
MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
return setDataSource_l(extractor);
- } else if (!strcmp("rtsp://gtalk", mUri.string())) {
+ } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
if (mLooper == NULL) {
mLooper = new ALooper;
mLooper->start(
@@ -1231,6 +1225,22 @@
PRIORITY_HIGHEST);
}
+ const char *startOfCodecString = &mUri.string()[13];
+ const char *startOfSlash1 = strchr(startOfCodecString, '/');
+ if (startOfSlash1 == NULL) {
+ return BAD_VALUE;
+ }
+ const char *startOfWidthString = &startOfSlash1[1];
+ const char *startOfSlash2 = strchr(startOfWidthString, '/');
+ if (startOfSlash2 == NULL) {
+ return BAD_VALUE;
+ }
+ const char *startOfHeightString = &startOfSlash2[1];
+
+ String8 codecString(startOfCodecString, startOfSlash1 - startOfCodecString);
+ String8 widthString(startOfWidthString, startOfSlash2 - startOfWidthString);
+ String8 heightString(startOfHeightString);
+
#if 0
mRTPPusher = new UDPPusher("/data/misc/rtpout.bin", 5434);
mLooper->registerHandler(mRTPPusher);
@@ -1257,8 +1267,8 @@
"a=rtpmap:97 AMR/8000/1\r\n"
"a=fmtp:97 octet-align\r\n";
#elif 1
- // My GTalk H.264 SDP
- static const char *raw =
+ String8 sdp;
+ sdp.appendFormat(
"v=0\r\n"
"o=- 64 233572944 IN IP4 127.0.0.0\r\n"
"s=QuickTime\r\n"
@@ -1268,24 +1278,16 @@
"m=video 5434 RTP/AVP 97\r\n"
"c=IN IP4 127.0.0.1\r\n"
"b=AS:30\r\n"
- "a=rtpmap:97 H264/90000\r\n"
- "a=cliprect:0,0,200,320\r\n"
- "a=framesize:97 320-200\r\n";
-#else
- // GTalk H263 SDP
- static const char *raw =
- "v=0\r\n"
- "o=- 64 233572944 IN IP4 127.0.0.0\r\n"
- "s=QuickTime\r\n"
- "t=0 0\r\n"
- "a=range:npt=0-315\r\n"
- "a=isma-compliance:2,2.0,2\r\n"
- "m=video 5434 RTP/AVP 98\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "b=AS:30\r\n"
- "a=rtpmap:98 H263-1998/90000\r\n"
- "a=cliprect:0,0,200,320\r\n"
- "a=framesize:98 320-200\r\n";
+ "a=rtpmap:97 %s/90000\r\n"
+ "a=cliprect:0,0,%s,%s\r\n"
+ "a=framesize:97 %s-%s\r\n",
+
+ codecString.string(),
+ heightString.string(), widthString.string(),
+ widthString.string(), heightString.string()
+ );
+ const char *raw = sdp.string();
+
#endif
sp<ASessionDescription> desc = new ASessionDescription;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 5e7dd5c..9ccd140 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -260,6 +260,7 @@
void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
LOGV("signalBufferReturned: %p", buffer->data());
+ Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
if ((*it)->pointer() == buffer->data()) {
@@ -327,6 +328,7 @@
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+
return OK;
}
}
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 23d8f56..ba99501 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -149,10 +149,10 @@
LOGV("threadTimeLapseEntry: taking picture");
CHECK_EQ(OK, mCamera->takePicture());
mCameraIdle = false;
- sleep(mTimeBetweenTimeLapseFrameCaptureUs/1E6);
+ usleep(mTimeBetweenTimeLapseFrameCaptureUs);
} else {
LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little.");
- sleep(.01);
+ usleep(1E4);
}
}
}
@@ -187,6 +187,7 @@
if (mUseStillCameraForTimeLapse) {
void *dummy;
pthread_join(mThreadTimeLapse, &dummy);
+ CHECK_EQ(OK, mCamera->startPreview());
} else {
mCamera->stopRecording();
}
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 9c99866..ccc6a34 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -68,7 +68,7 @@
return UNKNOWN_ERROR;
}
- setReceiveTimeout(5); // Time out reads after 5 secs by default
+ setReceiveTimeout(30); // Time out reads after 30 secs by default
mState = CONNECTING;
@@ -158,7 +158,7 @@
// The workaround accepts both behaviours but could potentially break
// legitimate responses that use a single newline to "fold" headers, which is
// why it's not yet on by default.
-#define WORKAROUND_FOR_MISSING_CR 0
+#define WORKAROUND_FOR_MISSING_CR 1
status_t HTTPStream::receive_line(char *line, size_t size) {
if (mState != CONNECTED) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 0d8c3c6..f52ec1a 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,6 +38,9 @@
namespace android {
+static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
+static const uint8_t kNalUnitTypePicParamSet = 0x08;
+
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
@@ -69,6 +72,11 @@
bool mIsAudio;
bool mIsMPEG4;
int64_t mTrackDurationUs;
+
+ // For realtime applications, we need to adjust the media clock
+ // for video track based on the audio media clock
+ bool mIsRealTimeRecording;
+ int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMaxWriteTimeUs;
int32_t mTimeScale;
@@ -111,6 +119,20 @@
};
List<SttsTableEntry> mSttsTableEntries;
+ // Sequence parameter set or picture parameter set
+ struct AVCParamSet {
+ AVCParamSet(uint16_t length, const uint8_t *data)
+ : mLength(length), mData(data) {}
+
+ uint16_t mLength;
+ const uint8_t *mData;
+ };
+ List<AVCParamSet> mSeqParamSets;
+ List<AVCParamSet> mPicParamSets;
+ uint8_t mProfileIdc;
+ uint8_t mProfileCompatible;
+ uint8_t mLevelIdc;
+
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
bool mGotAllCodecSpecificData;
@@ -124,8 +146,15 @@
static void *ThreadWrapper(void *me);
void threadEntry();
+ const uint8_t *parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen);
+
status_t makeAVCCodecSpecificData(
const uint8_t *data, size_t size);
+ status_t copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
+ status_t parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -139,6 +168,12 @@
void getCodecSpecificDataFromInputFormatIfPossible();
+ // Determine the track time scale
+ // If it is an audio track, try to use the sampling rate as
+ // the time scale; however, if user chooses the overwrite
+ // value, the user-supplied time scale will be used.
+ void setTimeScale();
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -405,7 +440,7 @@
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
+ int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
beginBox("moov");
@@ -720,10 +755,6 @@
mReachedEOS(false) {
getCodecSpecificDataFromInputFormatIfPossible();
- if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
- mTimeScale = 1000;
- }
-
const char *mime;
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -731,6 +762,28 @@
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ setTimeScale();
+}
+
+void MPEG4Writer::Track::setTimeScale() {
+ LOGV("setTimeScale");
+ // Default time scale
+ mTimeScale = 90000;
+
+ if (mIsAudio) {
+ // Use the sampling rate as the default time scale for audio track.
+ int32_t sampleRate;
+ bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
+ CHECK(success);
+ mTimeScale = sampleRate;
+ }
+
+ // If someone would like to overwrite the timescale, use user-supplied value.
+ int32_t timeScale;
+ if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
+ mTimeScale = timeScale;
+ }
+
CHECK(mTimeScale > 0);
}
@@ -916,6 +969,7 @@
mDone = false;
mIsFirstChunk = true;
+ mDriftTimeUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
ChunkInfo info;
@@ -943,6 +997,14 @@
startTimeUs = 0;
}
+ mIsRealTimeRecording = true;
+ {
+ int32_t isNotRealTime;
+ if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
+ mIsRealTimeRecording = (isNotRealTime == 0);
+ }
+ }
+
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
@@ -1038,6 +1100,174 @@
}
}
+static void getNalUnitType(uint8_t byte, uint8_t* type) {
+ LOGV("getNalUnitType: %d", byte);
+
+ // nal_unit_type: 5-bit unsigned integer
+ *type = (byte & 0x1F);
+}
+
+static const uint8_t *findNextStartCode(
+ const uint8_t *data, size_t length) {
+
+ LOGV("findNextStartCode: %p %d", data, length);
+
+ size_t bytesLeft = length;
+ while (bytesLeft > 4 &&
+ memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
+ --bytesLeft;
+ }
+ if (bytesLeft <= 4) {
+ bytesLeft = 0; // Last parameter set
+ }
+ return &data[length - bytesLeft];
+}
+
+const uint8_t *MPEG4Writer::Track::parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
+
+ LOGV("parseParamSet");
+ CHECK(type == kNalUnitTypeSeqParamSet ||
+ type == kNalUnitTypePicParamSet);
+
+ const uint8_t *nextStartCode = findNextStartCode(data, length);
+ *paramSetLen = nextStartCode - data;
+ if (*paramSetLen == 0) {
+ LOGE("Param set is malformed, since its length is 0");
+ return NULL;
+ }
+
+ AVCParamSet paramSet(*paramSetLen, data);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (*paramSetLen < 4) {
+ LOGE("Seq parameter set malformed");
+ return NULL;
+ }
+ if (mSeqParamSets.empty()) {
+ mProfileIdc = data[1];
+ mProfileCompatible = data[2];
+ mLevelIdc = data[3];
+ } else {
+ if (mProfileIdc != data[1] ||
+ mProfileCompatible != data[2] ||
+ mLevelIdc != data[3]) {
+ LOGE("Inconsistent profile/level found in seq parameter sets");
+ return NULL;
+ }
+ }
+ mSeqParamSets.push_back(paramSet);
+ } else {
+ mPicParamSets.push_back(paramSet);
+ }
+ return nextStartCode;
+}
+
+status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+ LOGV("copyAVCCodecSpecificData");
+
+ // 2 bytes for each of the parameter set length field
+ // plus the 7 bytes for the header
+ if (size < 4 + 7) {
+ LOGE("Codec specific data length too short: %d", size);
+ return ERROR_MALFORMED;
+ }
+
+ mCodecSpecificDataSize = size;
+ mCodecSpecificData = malloc(size);
+ memcpy(mCodecSpecificData, data, size);
+ return OK;
+}
+
+status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+
+ LOGV("parseAVCCodecSpecificData");
+ // Data starts with a start code.
+ // SPS and PPS are separated with start codes.
+ // Also, SPS must come before PPS
+ uint8_t type = kNalUnitTypeSeqParamSet;
+ bool gotSps = false;
+ bool gotPps = false;
+ const uint8_t *tmp = data;
+ const uint8_t *nextStartCode = data;
+ size_t bytesLeft = size;
+ size_t paramSetLen = 0;
+ mCodecSpecificDataSize = 0;
+ while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
+ getNalUnitType(*(tmp + 4), &type);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (gotPps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotSps) {
+ gotSps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
+ } else if (type == kNalUnitTypePicParamSet) {
+ if (!gotSps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotPps) {
+ gotPps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
+ } else {
+ LOGE("Only SPS and PPS Nal units are expected");
+ return ERROR_MALFORMED;
+ }
+
+ if (nextStartCode == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ // Move on to find the next parameter set
+ bytesLeft -= nextStartCode - tmp;
+ tmp = nextStartCode;
+ mCodecSpecificDataSize += (2 + paramSetLen);
+ }
+
+ {
+ // Check on the number of seq parameter sets
+ size_t nSeqParamSets = mSeqParamSets.size();
+ if (nSeqParamSets == 0) {
+ LOGE("Cound not find sequence parameter set");
+ return ERROR_MALFORMED;
+ }
+
+ if (nSeqParamSets > 0x1F) {
+ LOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the number of pic parameter sets
+ size_t nPicParamSets = mPicParamSets.size();
+ if (nPicParamSets == 0) {
+ LOGE("Cound not find picture parameter set");
+ return ERROR_MALFORMED;
+ }
+ if (nPicParamSets > 0xFF) {
+ LOGE("Too many pic parameter sets (%d) found", nPicParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the profiles
+ // These profiles requires additional parameter set extensions
+ if (mProfileIdc == 100 || mProfileIdc == 110 ||
+ mProfileIdc == 122 || mProfileIdc == 144) {
+ LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
+ return BAD_VALUE;
+ }
+ }
+
+ return OK;
+}
status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
const uint8_t *data, size_t size) {
@@ -1048,50 +1278,67 @@
return ERROR_MALFORMED;
}
- if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
- LOGE("Must start with a start code");
+ if (size < 4) {
+ LOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
- size_t picParamOffset = 4;
- while (picParamOffset + 3 < size
- && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
- ++picParamOffset;
+ // Data is in the form of AVCCodecSpecificData
+ if (memcmp("\x00\x00\x00\x01", data, 4)) {
+ return copyAVCCodecSpecificData(data, size);
}
- if (picParamOffset + 3 >= size) {
- LOGE("Could not find start-code for pictureParameterSet");
+ if (parseAVCCodecSpecificData(data, size) != OK) {
return ERROR_MALFORMED;
}
- size_t seqParamSetLength = picParamOffset - 4;
- size_t picParamSetLength = size - picParamOffset - 4;
-
- mCodecSpecificDataSize =
- 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
-
+ // ISO 14496-15: AVC file format
+ mCodecSpecificDataSize += 7; // 7 more bytes in the header
mCodecSpecificData = malloc(mCodecSpecificDataSize);
uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[0] = 1;
- header[1] = 0x42; // profile
- header[2] = 0x80;
- header[3] = 0x1e; // level
+ header[0] = 1; // version
+ header[1] = mProfileIdc; // profile indication
+ header[2] = mProfileCompatible; // profile compatibility
+ header[3] = mLevelIdc;
+ // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
#if USE_NALLEN_FOUR
header[4] = 0xfc | 3; // length size == 4 bytes
#else
header[4] = 0xfc | 1; // length size == 2 bytes
#endif
- header[5] = 0xe0 | 1;
- header[6] = seqParamSetLength >> 8;
- header[7] = seqParamSetLength & 0xff;
- memcpy(&header[8], &data[4], seqParamSetLength);
- header += 8 + seqParamSetLength;
- header[0] = 1;
- header[1] = picParamSetLength >> 8;
- header[2] = picParamSetLength & 0xff;
- memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
+ // 3-bit '111' followed by 5-bit numSequenceParameterSets
+ int nSequenceParamSets = mSeqParamSets.size();
+ header[5] = 0xe0 | nSequenceParamSets;
+ header += 6;
+ for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
+ it != mSeqParamSets.end(); ++it) {
+ // 16-bit sequence parameter set length
+ uint16_t seqParamSetLength = it->mLength;
+ header[0] = seqParamSetLength >> 8;
+ header[1] = seqParamSetLength & 0xff;
+
+ // SPS NAL unit (sequence parameter length bytes)
+ memcpy(&header[2], it->mData, seqParamSetLength);
+ header += (2 + seqParamSetLength);
+ }
+
+ // 8-bit nPictureParameterSets
+ int nPictureParamSets = mPicParamSets.size();
+ header[0] = nPictureParamSets;
+ header += 1;
+ for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
+ it != mPicParamSets.end(); ++it) {
+ // 16-bit picture parameter set length
+ uint16_t picParamSetLength = it->mLength;
+ header[0] = picParamSetLength >> 8;
+ header[1] = picParamSetLength & 0xff;
+
+ // PPS Nal unit (picture parameter set length bytes)
+ memcpy(&header[2], it->mData, picParamSetLength);
+ header += (2 + picParamSetLength);
+ }
return OK;
}
@@ -1113,10 +1360,16 @@
int32_t nZeroLengthFrames = 0;
int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
int64_t lastDurationUs = 0; // Between the previous two samples in ms
+ int64_t currDurationTicks = 0; // Timescale based ticks
+ int64_t lastDurationTicks = 0; // Timescale based ticks
int32_t sampleCount = 1; // Sample count in the current stts table entry
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
int64_t timestampUs;
+
+ int64_t wallClockTimeUs = 0;
+ int64_t lastWallClockTimeUs = 0;
+
sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
@@ -1168,91 +1421,6 @@
mGotAllCodecSpecificData = true;
continue;
- } else if (!mGotAllCodecSpecificData &&
- count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
- // The TI mpeg4 encoder does not properly set the
- // codec-specific-data flag.
-
- const uint8_t *data =
- (const uint8_t *)buffer->data() + buffer->range_offset();
-
- const size_t size = buffer->range_length();
-
- size_t offset = 0;
- while (offset + 3 < size) {
- if (data[offset] == 0x00 && data[offset + 1] == 0x00
- && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
- break;
- }
-
- ++offset;
- }
-
- // CHECK(offset + 3 < size);
- if (offset + 3 >= size) {
- // XXX assume the entire first chunk of data is the codec specific
- // data.
- offset = size;
- }
-
- mCodecSpecificDataSize = offset;
- mCodecSpecificData = malloc(offset);
- memcpy(mCodecSpecificData, data, offset);
-
- buffer->set_range(buffer->range_offset() + offset, size - offset);
-
- if (size == offset) {
- buffer->release();
- buffer = NULL;
-
- continue;
- }
-
- mGotAllCodecSpecificData = true;
- } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
- // The TI video encoder does not flag codec specific data
- // as such and also splits up SPS and PPS across two buffers.
-
- const uint8_t *data =
- (const uint8_t *)buffer->data() + buffer->range_offset();
-
- size_t size = buffer->range_length();
-
- CHECK(count == 2 || mCodecSpecificData == NULL);
-
- size_t offset = mCodecSpecificDataSize;
- mCodecSpecificDataSize += size + 4;
- mCodecSpecificData =
- realloc(mCodecSpecificData, mCodecSpecificDataSize);
-
- memcpy((uint8_t *)mCodecSpecificData + offset,
- "\x00\x00\x00\x01", 4);
-
- memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
-
- buffer->release();
- buffer = NULL;
-
- if (count == 2) {
- void *tmp = mCodecSpecificData;
- size = mCodecSpecificDataSize;
- mCodecSpecificData = NULL;
- mCodecSpecificDataSize = 0;
-
- status_t err = makeAVCCodecSpecificData(
- (const uint8_t *)tmp, size);
- free(tmp);
- tmp = NULL;
- CHECK_EQ(OK, err);
-
- mGotAllCodecSpecificData = true;
- }
-
- continue;
- }
-
- if (!mGotAllCodecSpecificData) {
- mGotAllCodecSpecificData = true;
}
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -1305,6 +1473,33 @@
}
timestampUs -= previousPausedDurationUs;
+ if (mIsRealTimeRecording && !mIsAudio) {
+ // The minor adjustment on the timestamp is heuristic/experimental
+ // We are adjusting the timestamp to reduce the fluctuation of the duration
+ // of neighboring samples. This in turn helps reduce the track header size,
+ // especially, the number of entries in the "stts" box.
+ if (mNumSamples > 1) {
+ int64_t durationUs = timestampUs + mOwner->getDriftTimeUs() - lastTimestampUs;
+ int64_t diffUs = (durationUs > lastDurationUs)
+ ? durationUs - lastDurationUs
+ : lastDurationUs - durationUs;
+ if (diffUs <= 5000) { // XXX: Magic number 5ms
+ timestampUs = lastTimestampUs + lastDurationUs;
+ } else {
+ timestampUs += mOwner->getDriftTimeUs();
+ }
+ }
+ }
+ CHECK(timestampUs >= 0);
+ if (mNumSamples > 1) {
+ if (timestampUs <= lastTimestampUs) {
+ LOGW("Drop a frame, since it arrives too late!");
+ copy->release();
+ copy = NULL;
+ continue;
+ }
+ }
+
LOGV("time stamp: %lld and previous paused duration %lld",
timestampUs, previousPausedDurationUs);
if (timestampUs > mTrackDurationUs) {
@@ -1314,7 +1509,16 @@
mSampleSizes.push_back(sampleSize);
++mNumSamples;
if (mNumSamples > 2) {
- if (lastDurationUs != timestampUs - lastTimestampUs) {
+ // We need to use the time scale based ticks, rather than the
+ // timestamp itself to determine whether we have to use a new
+ // stts entry, since we may have rounding errors.
+ // The calculation is intended to reduce the accumulated
+ // rounding errors.
+ currDurationTicks =
+ ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+
+ if (currDurationTicks != lastDurationTicks) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
@@ -1329,7 +1533,16 @@
previousSampleSize = sampleSize;
}
lastDurationUs = timestampUs - lastTimestampUs;
+ lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
+ if (mIsRealTimeRecording && mIsAudio) {
+ wallClockTimeUs = systemTime() / 1000;
+ int64_t wallClockDurationUs = wallClockTimeUs - lastWallClockTimeUs;
+ if (mNumSamples > 2) {
+ mOwner->addDriftTimeUs(lastDurationUs - wallClockDurationUs);
+ }
+ lastWallClockTimeUs = wallClockTimeUs;
+ }
if (isSync != 0) {
mStssTableEntries.push_back(mNumSamples);
@@ -1555,6 +1768,18 @@
}
}
+void MPEG4Writer::addDriftTimeUs(int64_t driftTimeUs) {
+ LOGV("addDriftTimeUs: %lld us", driftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ mDriftTimeUs += driftTimeUs;
+}
+
+int64_t MPEG4Writer::getDriftTimeUs() {
+ LOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ return mDriftTimeUs;
+}
+
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
LOGV("bufferChunk");
@@ -1585,7 +1810,6 @@
LOGV("%s track time scale: %d",
mIsAudio? "Audio": "Video", mTimeScale);
-
time_t now = time(NULL);
int32_t mvhdTimeScale = mOwner->getTimeScale();
int64_t trakDurationUs = getDurationUs();
@@ -1753,6 +1977,8 @@
mOwner->writeInt32(samplerate << 16);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
mOwner->beginBox("esds");
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt8(0x03); // ES_DescrTag
@@ -1833,6 +2059,8 @@
CHECK(23 + mCodecSpecificDataSize < 128);
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->beginBox("esds");
mOwner->writeInt32(0); // version=0, flags=0
@@ -1877,6 +2105,8 @@
mOwner->endBox(); // d263
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->beginBox("avcC");
mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
mOwner->endBox(); // avcC
@@ -1894,10 +2124,18 @@
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSttsTableEntries.size());
+ int64_t prevTimestampUs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
- int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+
+ // Make sure that we are calculating the sample duration the exactly
+ // same way as we made decision on how to create stts entries.
+ int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+ int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
mOwner->writeInt32(dur);
}
mOwner->endBox(); // stts
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 39d264c..7648d42 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -32,6 +32,8 @@
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
+const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4f3bffd..4741b1d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -26,6 +26,7 @@
#include "include/AMRWBEncoder.h"
#include "include/AVCDecoder.h"
#include "include/AVCEncoder.h"
+#include "include/G711Decoder.h"
#include "include/M4vH263Decoder.h"
#include "include/M4vH263Encoder.h"
#include "include/MP3Decoder.h"
@@ -77,6 +78,7 @@
FACTORY_CREATE(AMRWBDecoder)
FACTORY_CREATE(AACDecoder)
FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
FACTORY_CREATE(M4vH263Decoder)
FACTORY_CREATE(VorbisDecoder)
FACTORY_CREATE(VPXDecoder)
@@ -124,6 +126,7 @@
FACTORY_REF(AMRWBDecoder)
FACTORY_REF(AACDecoder)
FACTORY_REF(AVCDecoder)
+ FACTORY_REF(G711Decoder)
FACTORY_REF(M4vH263Decoder)
FACTORY_REF(VorbisDecoder)
FACTORY_REF(VPXDecoder)
@@ -155,6 +158,8 @@
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
+ { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
@@ -1680,6 +1685,14 @@
MediaBuffer *buffer = info->mMediaBuffer;
+ if (msg.u.extended_buffer_data.range_offset
+ + msg.u.extended_buffer_data.range_length
+ > buffer->size()) {
+ CODEC_LOGE(
+ "Codec lied about its buffer size requirements, "
+ "sending a buffer larger than the originally "
+ "advertised size in FILL_BUFFER_DONE!");
+ }
buffer->set_range(
msg.u.extended_buffer_data.range_offset,
msg.u.extended_buffer_data.range_length);
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 39b1b96..8d820c0 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -31,7 +31,11 @@
namespace android {
-static uint16_t WAVE_FORMAT_PCM = 1;
+enum {
+ WAVE_FORMAT_PCM = 1,
+ WAVE_FORMAT_ALAW = 6,
+ WAVE_FORMAT_MULAW = 7,
+};
static uint32_t U32_LE_AT(const uint8_t *ptr) {
return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
@@ -45,6 +49,7 @@
WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size);
@@ -63,6 +68,7 @@
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
+ uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -108,7 +114,7 @@
return new WAVSource(
mDataSource, mTrackMeta,
- mBitsPerSample, mDataOffset, mDataSize);
+ mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
sp<MetaData> WAVExtractor::getTrackMetaData(
@@ -160,8 +166,10 @@
return NO_INIT;
}
- uint16_t format = U16_LE_AT(formatSpec);
- if (format != WAVE_FORMAT_PCM) {
+ mWaveFormat = U16_LE_AT(formatSpec);
+ if (mWaveFormat != WAVE_FORMAT_PCM
+ && mWaveFormat != WAVE_FORMAT_ALAW
+ && mWaveFormat != WAVE_FORMAT_MULAW) {
return ERROR_UNSUPPORTED;
}
@@ -178,9 +186,17 @@
mBitsPerSample = U16_LE_AT(&formatSpec[14]);
- if (mBitsPerSample != 8 && mBitsPerSample != 16
- && mBitsPerSample != 24) {
- return ERROR_UNSUPPORTED;
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample != 8 && mBitsPerSample != 16
+ && mBitsPerSample != 24) {
+ return ERROR_UNSUPPORTED;
+ }
+ } else {
+ CHECK(mWaveFormat == WAVE_FORMAT_MULAW
+ || mWaveFormat == WAVE_FORMAT_ALAW);
+ if (mBitsPerSample != 8) {
+ return ERROR_UNSUPPORTED;
+ }
}
mValidFormat = true;
@@ -190,7 +206,23 @@
mDataSize = chunkSize;
mTrackMeta = new MetaData;
- mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+
+ switch (mWaveFormat) {
+ case WAVE_FORMAT_PCM:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ break;
+ case WAVE_FORMAT_ALAW:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+ break;
+ default:
+ CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW);
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+ break;
+ }
+
mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
@@ -217,10 +249,12 @@
WAVSource::WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size)
: mDataSource(dataSource),
mMeta(meta),
+ mWaveFormat(waveFormat),
mSampleRate(0),
mNumChannels(0),
mBitsPerSample(bitsPerSample),
@@ -312,43 +346,45 @@
buffer->set_range(0, n);
- if (mBitsPerSample == 8) {
- // Convert 8-bit unsigned samples to 16-bit signed.
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample == 8) {
+ // Convert 8-bit unsigned samples to 16-bit signed.
- MediaBuffer *tmp;
- CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+ MediaBuffer *tmp;
+ CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
- // The new buffer holds the sample number of samples, but each
- // one is 2 bytes wide.
- tmp->set_range(0, 2 * n);
+ // The new buffer holds the sample number of samples, but each
+ // one is 2 bytes wide.
+ tmp->set_range(0, 2 * n);
- int16_t *dst = (int16_t *)tmp->data();
- const uint8_t *src = (const uint8_t *)buffer->data();
- while (n-- > 0) {
- *dst++ = ((int16_t)(*src) - 128) * 256;
- ++src;
+ int16_t *dst = (int16_t *)tmp->data();
+ const uint8_t *src = (const uint8_t *)buffer->data();
+ while (n-- > 0) {
+ *dst++ = ((int16_t)(*src) - 128) * 256;
+ ++src;
+ }
+
+ buffer->release();
+ buffer = tmp;
+ } else if (mBitsPerSample == 24) {
+ // Convert 24-bit signed samples to 16-bit signed.
+
+ const uint8_t *src =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+ int16_t *dst = (int16_t *)src;
+
+ size_t numSamples = buffer->range_length() / 3;
+ for (size_t i = 0; i < numSamples; ++i) {
+ int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
+ x = (x << 8) >> 8; // sign extension
+
+ x = x >> 8;
+ *dst++ = (int16_t)x;
+ src += 3;
+ }
+
+ buffer->set_range(buffer->range_offset(), 2 * numSamples);
}
-
- buffer->release();
- buffer = tmp;
- } else if (mBitsPerSample == 24) {
- // Convert 24-bit signed samples to 16-bit signed.
-
- const uint8_t *src =
- (const uint8_t *)buffer->data() + buffer->range_offset();
- int16_t *dst = (int16_t *)src;
-
- size_t numSamples = buffer->range_length() / 3;
- for (size_t i = 0; i < numSamples; ++i) {
- int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
- x = (x << 8) >> 8; // sign extension
-
- x = x >> 8;
- *dst++ = (int16_t)x;
- src += 3;
- }
-
- buffer->set_range(buffer->range_offset(), 2 * numSamples);
}
size_t bytesPerSample = mBitsPerSample >> 3;
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index d5eb156..389180c 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -98,6 +98,7 @@
: mSource(source),
mMeta(meta),
mNumInputFrames(-1),
+ mPrevTimestampUs(-1),
mStarted(false),
mInputBuffer(NULL),
mInputFrameData(NULL),
@@ -337,14 +338,18 @@
MediaBuffer *outputBuffer;
CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
- uint8_t *outPtr = (uint8_t *) outputBuffer->data();
- uint32_t dataLength = outputBuffer->size();
+
+ // Add 4 bytes for the start code 0x00000001
+ uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
+ uint32_t dataLength = outputBuffer->size() - 4;
int32_t type;
AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
- // Return SPS and PPS for the first two buffers
- if (!mSpsPpsHeaderReceived) {
+ // Combine SPS and PPS and place them in the very first output buffer
+ // SPS and PPS are separated by start code 0x00000001
+ // Assume that we have exactly one SPS and exactly one PPS.
+ while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) {
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_WRONG_STATE) {
mSpsPpsHeaderReceived = true;
@@ -352,11 +357,22 @@
} else {
switch (type) {
case AVC_NALTYPE_SPS:
- case AVC_NALTYPE_PPS:
- LOGV("%s received",
- (type == AVC_NALTYPE_SPS)? "SPS": "PPS");
++mNumInputFrames;
- outputBuffer->set_range(0, dataLength);
+ memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0, dataLength + 4);
+ outPtr += (dataLength + 4); // 4 bytes for next start code
+ dataLength = outputBuffer->size() -
+ (outputBuffer->range_length() + 4);
+ break;
+ case AVC_NALTYPE_PPS:
+ ++mNumInputFrames;
+ memcpy(((uint8_t *) outputBuffer->data()) +
+ outputBuffer->range_length(),
+ "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0,
+ dataLength + outputBuffer->range_length() + 4);
+ outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1);
+ outputBuffer->meta_data()->setInt64(kKeyTime, 0);
*out = outputBuffer;
return OK;
default:
@@ -378,10 +394,34 @@
outputBuffer->release();
return err;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ // When the timestamp of the current sample is the same as
+ // that of the previous sample, the encoding of the sample
+ // is bypassed, and the output length is set to 0.
+ if (mNumInputFrames >= 1 && mPrevTimestampUs == timeUs) {
+ // Frame arrives too late
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ outputBuffer->set_range(0, 0);
+ *out = outputBuffer;
+ return OK;
+ }
+
+ // Don't accept out-of-order samples
+ CHECK(mPrevTimestampUs < timeUs);
+ mPrevTimestampUs = timeUs;
+
AVCFrameIO videoInput;
memset(&videoInput, 0, sizeof(videoInput));
videoInput.height = ((mVideoHeight + 15) >> 4) << 4;
diff --git a/media/libstagefright/codecs/g711/Android.mk b/media/libstagefright/codecs/g711/Android.mk
new file mode 100644
index 0000000..2e431205
--- /dev/null
+++ b/media/libstagefright/codecs/g711/Android.mk
@@ -0,0 +1,4 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
new file mode 100644
index 0000000..cfb9fe4
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ G711Decoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+
+LOCAL_MODULE := libstagefright_g711dec
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
new file mode 100644
index 0000000..4414e4e
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "G711Decoder"
+#include <utils/Log.h>
+
+#include "G711Decoder.h"
+
+#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>
+
+static const size_t kMaxNumSamplesPerFrame = 16384;
+
+namespace android {
+
+G711Decoder::G711Decoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mStarted(false),
+ mBufferGroup(NULL) {
+}
+
+G711Decoder::~G711Decoder() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t G711Decoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ mIsMLaw = false;
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
+ mIsMLaw = true;
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(
+ new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t)));
+
+ mSource->start();
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t G711Decoder::stop() {
+ CHECK(mStarted);
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ mSource->stop();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> G711Decoder::getFormat() {
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t numChannels;
+ int32_t sampleRate;
+
+ CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, numChannels);
+ meta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+
+ meta->setCString(kKeyDecoderComponent, "G711Decoder");
+
+ return meta;
+}
+
+status_t G711Decoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ status_t err;
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ CHECK(seekTimeUs >= 0);
+ } else {
+ seekTimeUs = -1;
+ }
+
+ MediaBuffer *inBuffer;
+ err = mSource->read(&inBuffer, options);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (inBuffer->range_length() > kMaxNumSamplesPerFrame) {
+ LOGE("input buffer too large (%d).", inBuffer->range_length());
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ int64_t timeUs;
+ CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ const uint8_t *inputPtr =
+ (const uint8_t *)inBuffer->data() + inBuffer->range_offset();
+
+ MediaBuffer *outBuffer;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK);
+
+ if (mIsMLaw) {
+ DecodeMLaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ } else {
+ DecodeALaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ }
+
+ // Each 8-bit byte is converted into a 16-bit sample.
+ outBuffer->set_range(0, inBuffer->range_length() * 2);
+
+ outBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ *out = outBuffer;
+
+ return OK;
+}
+
+// static
+void G711Decoder::DecodeALaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+}
+
+// static
+void G711Decoder::DecodeMLaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 5002442e..a011137 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -69,6 +69,7 @@
mMeta(meta),
mNumInputFrames(-1),
mNextModTimeUs(0),
+ mPrevTimestampUs(-1),
mStarted(false),
mInputBuffer(NULL),
mInputFrameData(NULL),
@@ -294,10 +295,23 @@
outputBuffer->release();
return UNKNOWN_ERROR;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
- if (mNextModTimeUs > timeUs) {
- LOGV("mNextModTimeUs %lld > timeUs %lld", mNextModTimeUs, timeUs);
+
+ // When the timestamp of the current sample is the same as that
+ // of the previous sample, encoding of the current sample is
+ // bypassed, and the output length of the sample is set to 0
+ if (mNumInputFrames >= 1 &&
+ (mNextModTimeUs > timeUs || mPrevTimestampUs == timeUs)) {
+ // Frame arrives too late
outputBuffer->set_range(0, 0);
*out = outputBuffer;
mInputBuffer->release();
@@ -305,6 +319,10 @@
return OK;
}
+ // Don't accept out-of-order samples
+ CHECK(mPrevTimestampUs < timeUs);
+ mPrevTimestampUs = timeUs;
+
// Color convert to OMX_COLOR_FormatYUV420Planar if necessary
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
uint8_t *inPtr = (uint8_t *) mInputBuffer->data();
diff --git a/media/libstagefright/include/AVCEncoder.h b/media/libstagefright/include/AVCEncoder.h
index 4fe2e30..83e1f97 100644
--- a/media/libstagefright/include/AVCEncoder.h
+++ b/media/libstagefright/include/AVCEncoder.h
@@ -64,6 +64,7 @@
int32_t mVideoBitRate;
int32_t mVideoColorFormat;
int64_t mNumInputFrames;
+ int64_t mPrevTimestampUs;
status_t mInitCheck;
bool mStarted;
bool mSpsPpsHeaderReceived;
diff --git a/media/libstagefright/include/G711Decoder.h b/media/libstagefright/include/G711Decoder.h
new file mode 100644
index 0000000..8b5143a
--- /dev/null
+++ b/media/libstagefright/include/G711Decoder.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef G711_DECODER_H_
+
+#define G711_DECODER_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct G711Decoder : public MediaSource {
+ G711Decoder(const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~G711Decoder();
+
+private:
+ sp<MediaSource> mSource;
+ bool mStarted;
+ bool mIsMLaw;
+
+ MediaBufferGroup *mBufferGroup;
+
+ static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+ static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+ G711Decoder(const G711Decoder &);
+ G711Decoder &operator=(const G711Decoder &);
+};
+
+} // namespace android
+
+#endif // G711_DECODER_H_
diff --git a/media/libstagefright/include/M4vH263Encoder.h b/media/libstagefright/include/M4vH263Encoder.h
index dd146f4..dbe9fd0 100644
--- a/media/libstagefright/include/M4vH263Encoder.h
+++ b/media/libstagefright/include/M4vH263Encoder.h
@@ -59,6 +59,7 @@
int32_t mVideoColorFormat;
int64_t mNumInputFrames;
int64_t mNextModTimeUs;
+ int64_t mPrevTimestampUs;
status_t mInitCheck;
bool mStarted;
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 9384942..3e847b9 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -43,6 +43,7 @@
sp<DataSource> mDataSource;
status_t mInitCheck;
bool mValidFormat;
+ uint16_t mWaveFormat;
uint16_t mNumChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 8b59998..2818041 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -110,7 +110,7 @@
buffer->data()[0] = 0x00;
buffer->data()[1] = 0x00;
} else {
- buffer->setRange(2, buffer->size() - 2);
+ buffer->setRange(buffer->offset() + 2, buffer->size() - 2);
}
mPackets.push_back(buffer);
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index a577704..224b4bf 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -356,24 +356,10 @@
if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin();
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
- mediaBuffer->meta_data()->setInt64(kKeyNTPTime, ntpTime);
- if (mFirstAccessUnit) {
- mFirstAccessUnit = false;
- mFirstAccessUnitNTP = ntpTime;
- }
- if (ntpTime > mFirstAccessUnitNTP) {
- ntpTime -= mFirstAccessUnitNTP;
- } else {
- ntpTime = 0;
- }
-
- int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -390,10 +376,29 @@
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
- // LOG(VERBOSE) << "discarding damaged AU";
+ LOG(VERBOSE) << "discarding damaged AU";
return;
}
+ uint64_t ntpTime;
+ CHECK(buffer->meta()->findInt64(
+ "ntp-time", (int64_t *)&ntpTime));
+
+ if (mFirstAccessUnit) {
+ mFirstAccessUnit = false;
+ mFirstAccessUnitNTP = ntpTime;
+ }
+
+ if (ntpTime > mFirstAccessUnitNTP) {
+ ntpTime -= mFirstAccessUnitNTP;
+ } else {
+ ntpTime = 0;
+ }
+
+ int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+ buffer->meta()->setInt64("timeUs", timeUs);
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 5bd306b..469af3e 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -28,8 +28,6 @@
#include <arpa/inet.h>
#include <sys/socket.h>
-#define IGNORE_RTCP_TIME 0
-
namespace android {
static const size_t kMaxUDPSize = 1500;
@@ -61,8 +59,9 @@
struct sockaddr_in mRemoteRTCPAddr;
};
-ARTPConnection::ARTPConnection()
- : mPollEventPending(false),
+ARTPConnection::ARTPConnection(uint32_t flags)
+ : mFlags(flags),
+ mPollEventPending(false),
mLastReceiverReportTimeUs(-1) {
}
@@ -280,7 +279,10 @@
sp<ARTPSource> source = s->mSources.valueAt(i);
source->addReceiverReport(buffer);
- source->addFIR(buffer);
+
+ if (mFlags & kRegularlyRequestFIR) {
+ source->addFIR(buffer);
+ }
}
if (buffer->size() > 0) {
@@ -405,13 +407,11 @@
buffer->setInt32Data(u16at(&data[2]));
buffer->setRange(payloadOffset, size - payloadOffset);
-#if IGNORE_RTCP_TIME
- if (!source->timeEstablished()) {
+ if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) {
source->timeUpdate(rtpTime, 0);
- source->timeUpdate(rtpTime + 20, 0x100000000ll);
+ source->timeUpdate(rtpTime + 90000, 0x100000000ll);
CHECK(source->timeEstablished());
}
-#endif
source->processRTPPacket(buffer);
@@ -533,9 +533,9 @@
sp<ARTPSource> source = findSource(s, id);
-#if !IGNORE_RTCP_TIME
- source->timeUpdate(rtpTime, ntpTime);
-#endif
+ if ((mFlags & kFakeTimestamps) == 0) {
+ source->timeUpdate(rtpTime, ntpTime);
+ }
return 0;
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index 49839ad..c535199 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -28,7 +28,12 @@
struct ASessionDescription;
struct ARTPConnection : public AHandler {
- ARTPConnection();
+ enum Flags {
+ kFakeTimestamps = 1,
+ kRegularlyRequestFIR = 2,
+ };
+
+ ARTPConnection(uint32_t flags = 0);
void addStream(
int rtpSocket, int rtcpSocket,
@@ -56,6 +61,8 @@
static const int64_t kSelectTimeoutUs;
+ uint32_t mFlags;
+
struct StreamInfo;
List<StreamInfo> mStreams;
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 0e0f45a..e082078 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -40,7 +40,10 @@
mDesc = desc;
- mRTPConn = new ARTPConnection;
+ mRTPConn = new ARTPConnection(
+ ARTPConnection::kFakeTimestamps
+ | ARTPConnection::kRegularlyRequestFIR);
+
looper()->registerHandler(mRTPConn);
for (size_t i = 1; i < mDesc->countTracks(); ++i) {
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index e08183e..225f6e8 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -98,7 +98,7 @@
mNTPTime[mNumTimes] = ntpTime;
mRTPTime[mNumTimes++] = rtpTime;
- if (mNumTimes == 2) {
+ if (timeEstablished()) {
for (List<sp<ABuffer> >::iterator it = mQueue.begin();
it != mQueue.end(); ++it) {
sp<AMessage> meta = (*it)->meta();
@@ -112,13 +112,6 @@
}
bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
-#if 1
- if (mNumTimes != 2) {
- // Drop incoming packets until we've established a time base.
- return false;
- }
-#endif
-
uint32_t seqNum = (uint32_t)buffer->int32Data();
if (mNumTimes == 2) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index cc23856..d6dd597 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -43,7 +43,7 @@
#if 1
mRTPAddr.sin_addr.s_addr = INADDR_ANY;
#else
- mRTPAddr.sin_addr.s_addr = inet_addr("172.19.19.74");
+ mRTPAddr.sin_addr.s_addr = inet_addr("172.19.18.246");
#endif
mRTPAddr.sin_port = htons(5634);
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index c159e20..9bfd00f 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -266,6 +266,13 @@
putUInt16(*values++);
}
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+ size_t count = (values ? values->size() : 0);
+ putUInt32(count);
+ for (size_t i = 0; i < count; i++)
+ putUInt16((*values)[i]);
+}
+
void MtpDataPacket::putAInt32(const int32_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index e8314d7..b458286 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -74,6 +74,7 @@
void putAUInt8(const uint8_t* values, int count);
void putAInt16(const int16_t* values, int count);
void putAUInt16(const uint16_t* values, int count);
+ void putAUInt16(const UInt16List* values);
void putAInt32(const int32_t* values, int count);
void putAUInt32(const uint32_t* values, int count);
void putAUInt32(const UInt32List* list);
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 02bb0d9..17823df 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -51,6 +51,13 @@
MtpObjectFormat format,
MtpObjectHandle parent) = 0;
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats() = 0;
+ virtual MtpObjectFormatList* getSupportedCaptureFormats() = 0;
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format) = 0;
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0;
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) = 0;
diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h
index 3935d33..5b53e31 100644
--- a/media/mtp/MtpDebug.h
+++ b/media/mtp/MtpDebug.h
@@ -17,7 +17,7 @@
#ifndef _MTP_DEBUG_H
#define _MTP_DEBUG_H
-#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#include <utils/Log.h>
#include "MtpTypes.h"
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 082d924..30abfb8 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -78,46 +78,6 @@
MTP_EVENT_OBJECT_REMOVED,
};
-static const MtpObjectProperty kSupportedObjectProperties[] = {
- MTP_PROPERTY_STORAGE_ID,
- MTP_PROPERTY_OBJECT_FORMAT,
- MTP_PROPERTY_OBJECT_SIZE,
- MTP_PROPERTY_OBJECT_FILE_NAME,
- MTP_PROPERTY_PARENT_OBJECT,
-};
-
-static const MtpObjectFormat kSupportedPlaybackFormats[] = {
- // MTP_FORMAT_UNDEFINED,
- MTP_FORMAT_ASSOCIATION,
- // MTP_FORMAT_TEXT,
- // MTP_FORMAT_HTML,
- MTP_FORMAT_MP3,
- //MTP_FORMAT_AVI,
- MTP_FORMAT_MPEG,
- // MTP_FORMAT_ASF,
- MTP_FORMAT_EXIF_JPEG,
- MTP_FORMAT_TIFF_EP,
- // MTP_FORMAT_BMP,
- MTP_FORMAT_GIF,
- MTP_FORMAT_JFIF,
- MTP_FORMAT_PNG,
- MTP_FORMAT_TIFF,
- MTP_FORMAT_WMA,
- MTP_FORMAT_OGG,
- MTP_FORMAT_AAC,
- // MTP_FORMAT_FLAC,
- // MTP_FORMAT_WMV,
- MTP_FORMAT_MP4_CONTAINER,
- MTP_FORMAT_MP2,
- MTP_FORMAT_3GP_CONTAINER,
- // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
- MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
- MTP_FORMAT_WPL_PLAYLIST,
- MTP_FORMAT_M3U_PLAYLIST,
- // MTP_FORMAT_MPL_PLAYLIST,
- MTP_FORMAT_PLS_PLAYLIST,
-};
-
MtpServer::MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm)
: mFD(fd),
@@ -354,6 +314,10 @@
MtpStringBuffer string;
char prop_value[PROPERTY_VALUE_MAX];
+ MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+ MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+ MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
// fill in device info
mData.putUInt16(MTP_STANDARD_VERSION);
mData.putUInt32(6); // MTP Vendor Extension ID
@@ -365,10 +329,9 @@
sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
mData.putAUInt16(kSupportedEventCodes,
sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
- mData.putEmptyArray(); // Device Properties Supported
- mData.putEmptyArray(); // Capture Formats
- mData.putAUInt16(kSupportedPlaybackFormats,
- sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
+ mData.putAUInt16(deviceProperties); // Device Properties Supported
+ mData.putAUInt16(captureFormats); // Capture Formats
+ mData.putAUInt16(playbackFormats); // Playback Formats
// FIXME
string.set("Google, Inc.");
mData.putString(string); // Manufacturer
@@ -383,6 +346,10 @@
string.set(prop_value);
mData.putString(string); // Serial Number
+ delete playbackFormats;
+ delete captureFormats;
+ delete deviceProperties;
+
return MTP_RESPONSE_OK;
}
@@ -443,8 +410,9 @@
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpObjectFormat format = mRequest.getParameter(1);
- mData.putAUInt16(kSupportedObjectProperties,
- sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
+ MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+ mData.putAUInt16(properties);
+ delete properties;
return MTP_RESPONSE_OK;
}
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index 2a895a7..7e3c009 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -78,6 +78,7 @@
typedef Vector<int32_t> Int32List;
typedef Vector<int64_t> Int64List;
+typedef UInt16List MtpObjectPropertyList;
typedef UInt16List MtpDevicePropertyList;
typedef UInt16List MtpObjectFormatList;
typedef UInt32List MtpObjectHandleList;
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 246f9fc..f70a145 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -51,4 +51,9 @@
android:label="MediaRecorder stress tests InstrumentationRunner">
</instrumentation>
+ <instrumentation android:name=".MediaFrameworkPowerTestRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="Media Power tests InstrumentationRunner">
+ </instrumentation>
+
</manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
new file mode 100755
index 0000000..34db4db
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
@@ -0,0 +1,50 @@
+/*
+ * 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.mediaframeworktest;
+
+import com.android.mediaframeworktest.power.MediaPlayerPowerTest;
+
+import junit.framework.TestSuite;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+
+/**
+ * Instrumentation Test Runner for all MediaPlayer tests.
+ *
+ * Running all tests:
+ *
+ * adb shell am instrument \
+ * -w com.android.mediaframeworktest/.MediaFrameworkPowerTestRunner
+ */
+
+public class MediaFrameworkPowerTestRunner extends InstrumentationTestRunner {
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(MediaPlayerPowerTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return MediaFrameworkPowerTestRunner.class.getClassLoader();
+ }
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 3e33951..c7f461e 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -25,6 +25,11 @@
import com.android.mediaframeworktest.functional.SimTonesTest;
import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest;
import com.android.mediaframeworktest.functional.MediaAudioManagerTest;
+import com.android.mediaframeworktest.functional.MediaAudioEffectTest;
+import com.android.mediaframeworktest.functional.MediaBassBoostTest;
+import com.android.mediaframeworktest.functional.MediaEqualizerTest;
+import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
+import com.android.mediaframeworktest.functional.MediaVisualizerTest;
import junit.framework.TestSuite;
import android.test.InstrumentationTestRunner;
@@ -55,6 +60,11 @@
suite.addTestSuite(MediaMimeTest.class);
suite.addTestSuite(MediaPlayerInvokeTest.class);
suite.addTestSuite(MediaAudioManagerTest.class);
+ suite.addTestSuite(MediaAudioEffectTest.class);
+ suite.addTestSuite(MediaBassBoostTest.class);
+ suite.addTestSuite(MediaEqualizerTest.class);
+ suite.addTestSuite(MediaVirtualizerTest.class);
+ suite.addTestSuite(MediaVisualizerTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 9a48c92..ca6e999 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -35,6 +35,7 @@
public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav";
public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr";
public static final String OGG = "/sdcard/media_api/music/Revelation.ogg";
+ public static final String SINE_200_1000 = "/sdcard/media_api/music/sine_200+1000Hz_44K_mo.wav";
public static final int MP3CBR_LENGTH = 71000;
public static final int MP3VBR_LENGTH = 71000;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
new file mode 100644
index 0000000..fd939ae
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
@@ -0,0 +1,1492 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.EnvironmentalReverb;
+import android.media.Equalizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaAudioEffectTest";
+
+ private AudioEffect mEffect = null;
+ private boolean mHasControl = false;
+ private boolean mIsEnabled = false;
+ private int mParameterChanged = -1;
+ private MediaPlayer mMediaPlayer = null;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private int mError = 0;
+ private final Object lock = new Object();
+
+ public MediaAudioEffectTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // AUDIOEFFECT TESTS:
+ //----------------------------------
+
+ //-----------------------------------------------------------------
+ // 0 - static methods
+ //----------------------------------
+
+ //Test case 0.0: test queryEffects() and available effects
+ @LargeTest
+ public void test0_0QueryEffects() throws Exception {
+
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+
+ assertTrue("test0_0QueryEffects: number of effects < 4: "+desc.length, (desc.length >= 4));
+
+ boolean hasEQ = false;
+ boolean hasBassBoost = false;
+ boolean hasVirtualizer = false;
+ boolean hasEnvReverb = false;
+
+ for (int i = 0; i < desc.length; i++) {
+ if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+ hasEQ = true;
+ } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+ hasBassBoost = true;
+ } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+ hasVirtualizer = true;
+ }
+ else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+ hasEnvReverb = true;
+ }
+ }
+ assertTrue("test0_0QueryEffects: equalizer not found", hasEQ);
+ assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost);
+ assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer);
+ assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - constructor
+ //----------------------------------
+
+ //Test case 1.0: test constructor from effect type and get effect ID
+ @LargeTest
+ public void test1_0ConstructorFromType() throws Exception {
+ boolean result = true;
+ String msg = "test1_0ConstructorFromType()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+": no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(desc[0].mType,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ assertTrue(msg +": invalid effect ID", (effect.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ result = false;
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test constructor from effect uuid
+ @LargeTest
+ public void test1_1ConstructorFromUuid() throws Exception {
+ boolean result = true;
+ String msg = "test1_1ConstructorFromUuid()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+"no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
+ desc[0].mUuid,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test constructor failure from unknown type
+ @LargeTest
+ public void test1_2ConstructorUnknownType() throws Exception {
+ boolean result = false;
+ String msg = "test1_2ConstructorUnknownType()";
+
+ try {
+ AudioEffect effect = new AudioEffect(UUID.randomUUID(),
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ msg = msg.concat(": could create random AudioEffect");
+ if (effect != null) {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ result = true;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test getEnabled() failure when called on released effect
+ @LargeTest
+ public void test1_3GetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_3GetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.getEnabled();
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.4: test contructor on mediaPlayer audio session
+ @LargeTest
+ public void test1_4InsertOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_4InsertOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ mp.getAudioSessionId());
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ loge(msg, ": effect.setEnabled");
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ }
+
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.5: test auxiliary effect attachement on MediaPlayer
+ @LargeTest
+ public void test1_5AuxiliaryOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_5AuxiliaryOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ mp.attachAuxEffect(effect.getId());
+ mp.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.6: test auxiliary effect attachement failure before setDatasource
+ @LargeTest
+ public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception {
+ boolean result = false;
+ String msg = "test1_6AuxiliaryOnMediaPlayerFailure()";
+
+ try {
+ createMediaPlayerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized); // mMediaPlayer has been initialized?
+ mError = 0;
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ synchronized(lock) {
+ try {
+ mMediaPlayer.attachAuxEffect(effect.getId());
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Attach effect: wait was interrupted.");
+ }
+ }
+ assertTrue(msg + ": no error on attachAuxEffect", mError != 0);
+ result = true;
+ effect.release();
+ terminateMediaPlayerLooper();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //Test case 1.7: test auxiliary effect attachement on AudioTrack
+ @LargeTest
+ public void test1_7AuxiliaryOnAudioTrack() throws Exception {
+ boolean result = false;
+ String msg = "test1_7AuxiliaryOnAudioTrack()";
+
+ try {
+ AudioTrack track = new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioTrack.getMinBufferSize(44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT),
+ AudioTrack.MODE_STREAM);
+ assertNotNull(msg + ": could not create AudioTrack", track);
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+
+ track.attachAuxEffect(effect.getId());
+ track.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ track.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - enable/ disable
+ //----------------------------------
+
+
+ //Test case 2.0: test setEnabled() and getEnabled() in valid state
+ @LargeTest
+ public void test2_0SetEnabledGetEnabled() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SetEnabledGetEnabled()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ effect.setEnabled(true);
+ assertTrue(msg + ": invalid state from getEnabled", effect.getEnabled());
+ effect.setEnabled(false);
+ assertFalse(msg + ": invalid state to getEnabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": setEnabled() in wrong state");
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test setEnabled() throws exception after release
+ @LargeTest
+ public void test2_1SetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test2_1SetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 3 - set parameters
+ //----------------------------------
+
+ //Test case 3.0: test setParameter(byte[], byte[])
+ @LargeTest
+ public void test3_0SetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_0SetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.1: test setParameter(int, int)
+ @LargeTest
+ public void test3_1SetParameterIntInt() throws Exception {
+ boolean result = false;
+ String msg = "test3_1SetParameterIntInt()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.2: test setParameter(int, short)
+ @LargeTest
+ public void test3_2SetParameterIntShort() throws Exception {
+ boolean result = false;
+ String msg = "test3_2SetParameterIntShort()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.3: test setParameter(int, byte[])
+ @LargeTest
+ public void test3_3SetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_3SetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.4: test setParameter(int[], int[])
+ @LargeTest
+ public void test3_4SetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_4SetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ value[0] = 0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.5: test setParameter(int[], short[])
+ @LargeTest
+ public void test3_5SetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_5SetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ value[0] = (short)0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.6: test setParameter(int[], byte[])
+ @LargeTest
+ public void test3_6SetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_6SetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = shortToByteArray((short)0);
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.7: test setParameter() throws exception after release()
+ @LargeTest
+ public void test3_7SetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test3_7SetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 4 - get parameters
+ //----------------------------------
+
+ //Test case 4.0: test getParameter(byte[], byte[])
+ @LargeTest
+ public void test4_0GetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_0GetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = new byte[2];
+ if (effect.getParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.1: test getParameter(int, int[])
+ @LargeTest
+ public void test4_1GetParameterIntIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_1GetParameterIntIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] value = new int[1];
+ if (effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.2: test getParameter(int, short[])
+ @LargeTest
+ public void test4_2GetParameterIntShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_2GetParameterIntShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ short[] value = new short[1];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.3: test getParameter(int, byte[])
+ @LargeTest
+ public void test4_3GetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_3GetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = new byte[2];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.4: test getParameter(int[], int[])
+ @LargeTest
+ public void test4_4GetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_4GetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.5: test getParameter(int[], short[])
+ @LargeTest
+ public void test4_5GetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_5GetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.6: test getParameter(int[], byte[])
+ @LargeTest
+ public void test4_6GetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_6GetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = new byte[2];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.7: test getParameter() throws exception after release()
+ @LargeTest
+ public void test4_7GetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test4_7GetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ short[] value = new short[1];
+ effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 5 priority and listeners
+ //----------------------------------
+
+ //Test case 5.0: test control passed to higher priority client
+ @LargeTest
+ public void test5_0setEnabledLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_0setEnabledLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ assertTrue(msg + ": Effect2 does not have control", effect2.hasControl());
+ assertFalse(msg + ": Effect1 has control", effect1.hasControl());
+ assertTrue(msg + ": Effect1 can enable",
+ effect1.setEnabled(true) == AudioEffect.ERROR_INVALID_OPERATION);
+ assertFalse(msg + ": Effect1 has enabled", effect2.getEnabled());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.1: test control passed to higher priority client
+ @LargeTest
+ public void test5_1setParameterLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_1setParameterLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ int status = effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ assertEquals(msg + ": Effect2 setParameter failed",
+ AudioEffect.SUCCESS, status);
+
+ status = effect1.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)1);
+ assertEquals(msg + ": Effect1 setParameter did not fail",
+ AudioEffect.ERROR_INVALID_OPERATION, status);
+
+ short[] value = new short[1];
+ status = effect2.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ assertEquals(msg + ": Effect2 getParameter failed",
+ AudioEffect.SUCCESS, status);
+ assertEquals(msg + ": Effect1 changed parameter",
+ (short)0, value[0]);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.2: test control status listener
+ @LargeTest
+ public void test5_2ControlStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_2ControlStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ mHasControl = true;
+ createListenerLooper(true, false, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ synchronized(lock) {
+ try {
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": effect control not lost by effect1", mHasControl);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.3: test enable status listener
+ @LargeTest
+ public void test5_3EnableStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_3EnableStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, true, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ mEffect.setEnabled(true);
+ mIsEnabled = true;
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ assertTrue(msg + ": effect not enabled", effect2.getEnabled());
+ synchronized(lock) {
+ try {
+ effect2.setEnabled(false);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": enable status not updated", mIsEnabled);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.4: test parameter changed listener
+ @LargeTest
+ public void test5_4ParameterChangedListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_4ParameterChangedListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, false, true);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ synchronized(lock) {
+ try {
+ mParameterChanged = -1;
+ effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertEquals(msg + ": parameter change not received",
+ Equalizer.PARAM_CURRENT_PRESET, mParameterChanged);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 6 command method
+ //----------------------------------
+
+
+ //Test case 6.0: test command method
+ @LargeTest
+ public void test6_0Command() throws Exception {
+ boolean result = false;
+ String msg = "test6_0Command()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ byte[] cmd = new byte[0];
+ byte[] reply = new byte[4];
+ int status = effect.command(3, cmd, reply);
+ assertEquals(msg + ": command failed", AudioEffect.SUCCESS, status);
+ assertTrue(msg + ": effect not enabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": command in illegal state");
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ /*
+ * Initializes the message looper so that the MediaPlayer object can
+ * receive the callback messages.
+ */
+ private void createMediaPlayerLooper() {
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mMediaPlayer.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer player, int what, int extra) {
+ synchronized(lock) {
+ mError = what;
+ lock.notify();
+ }
+ return true;
+ }
+ });
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer player) {
+ synchronized(lock) {
+ lock.notify();
+ }
+ }
+ });
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the message looper thread.
+ */
+ private void terminateMediaPlayerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ }
+ }
+
+ /*
+ * Initializes the message looper fro effect listener
+ */
+ class ListenerThread extends Thread {
+ boolean mControl;
+ boolean mEnable;
+ boolean mParameter;
+
+ public ListenerThread(boolean control, boolean enable, boolean parameter) {
+ super();
+ mControl = control;
+ mEnable = enable;
+ mParameter = parameter;
+ }
+ }
+ private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+
+ new ListenerThread(control, enable, parameter) {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mEffect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull("could not create AudioEffect", mEffect);
+
+ if (mControl) {
+ mEffect.setControlStatusListener(new AudioEffect.OnControlStatusChangeListener() {
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mHasControl = controlGranted;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mEnable) {
+ mEffect.setEnableStatusListener(new AudioEffect.OnEnableStatusChangeListener() {
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mIsEnabled = enabled;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mParameter) {
+ mEffect.setParameterListener(new AudioEffect.OnParameterChangeListener() {
+ public void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mParameterChanged = byteArrayToInt(param);
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mEffect != null) {
+ mEffect.release();
+ mEffect = null;
+ }
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf) {
+ return byteArrayToInt(valueBuf, 0);
+
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+
+ protected byte[] intToByteArray(int value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putInt(value);
+ return converter.array();
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf) {
+ return byteArrayToShort(valueBuf, 0);
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ protected byte[] shortToByteArray(short value) {
+ ByteBuffer converter = ByteBuffer.allocate(2);
+ converter.order(ByteOrder.nativeOrder());
+ short sValue = (short) value;
+ converter.putShort(sValue);
+ return converter.array();
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
new file mode 100644
index 0000000..8a68c5e
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -0,0 +1,334 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.BassBoost;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaBassBoostTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private BassBoost mBassBoost = null;
+ private int mSession = -1;
+
+ public MediaBassBoostTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseBassBoost();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // BASS BOOST TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ BassBoost bb = null;
+ try {
+ bb = new BassBoost(0, 0);
+ assertNotNull(msg + ": could not create BassBoost", bb);
+ try {
+ assertTrue(msg +": invalid effect ID", (bb.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": BassBoost not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": BassBoost not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (bb != null) {
+ bb.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getBassBoost(0);
+ try {
+ if (mBassBoost.getStrengthSupported()) {
+ mBassBoost.setStrength((short)TEST_STRENGTH);
+ short strength = mBassBoost.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mBassBoost.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getBassBoost(0);
+ try {
+ BassBoost.Settings settings = mBassBoost.getProperties();
+ String str = settings.toString();
+ settings = new BassBoost.Settings(str);
+ mBassBoost.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual bass boost influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getBassBoost(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mBassBoost.setStrength((short)1000);
+ mBassBoost.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // MIN_ENERGY_RATIO_2 times higher with bassboost on.
+ assertTrue(msg + ": bass boost has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseBassBoost();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getBassBoost(int session) {
+ if (mBassBoost == null || session != mSession) {
+ if (session != mSession && mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ try {
+ mBassBoost = new BassBoost(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getBassBoost() BassBoost not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getBassBoost() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mBassBoost", mBassBoost);
+ }
+
+ private void releaseBassBoost() {
+ if (mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
new file mode 100644
index 0000000..e46887b
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -0,0 +1,397 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Equalizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaEqualizerTest";
+ private final static int MIN_NUMBER_OF_BANDS = 4;
+ private final static int MIN_BAND_LEVEL = -1500;
+ private final static int MAX_BAND_LEVEL = 1500;
+ private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
+ private final static int MIN_NUMBER_OF_PRESETS = 4;
+ private Equalizer mEqualizer = null;
+ private int mSession = -1;
+
+ public MediaEqualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseEqualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // EQUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Equalizer eq = null;
+ try {
+ eq = new Equalizer(0, 0);
+ assertNotNull(msg + ": could not create Equalizer", eq);
+ try {
+ assertTrue(msg +": invalid effect ID", (eq.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Equalizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (eq != null) {
+ eq.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test setBandLevel() and getBandLevel()
+ @LargeTest
+ public void test1_0BandLevel() throws Exception {
+ boolean result = false;
+ String msg = "test1_0BandLevel()";
+ getEqualizer(0);
+ try {
+ short numBands = mEqualizer.getNumberOfBands();
+ assertTrue(msg + ": not enough bands", numBands >= MIN_NUMBER_OF_BANDS);
+
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ assertTrue(msg + ": min level too high", levelRange[0] <= MIN_BAND_LEVEL);
+ assertTrue(msg + ": max level too low", levelRange[1] >= MAX_BAND_LEVEL);
+
+ mEqualizer.setBandLevel((short)0, levelRange[1]);
+ short level = mEqualizer.getBandLevel((short)0);
+ // 10% margin on actual level compared to requested level
+ assertTrue(msg + ": setBandLevel failed",
+ ((float)level > (float)levelRange[1] * 0.9f) &&
+ ((float)level < (float)levelRange[1] * 1.1f));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test band frequency
+ @LargeTest
+ public void test1_1BandFrequency() throws Exception {
+ boolean result = false;
+ String msg = "test1_1BandFrequency()";
+ getEqualizer(0);
+ try {
+ short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ);
+ assertTrue(msg + ": getBand failed", band >= 0);
+ int[] freqRange = mEqualizer.getBandFreqRange(band);
+ assertTrue(msg + ": getBandFreqRange failed",
+ (freqRange[0] <= TEST_FREQUENCY_MILLIHERTZ) &&
+ (freqRange[1] >= TEST_FREQUENCY_MILLIHERTZ));
+ int freq = mEqualizer.getCenterFreq(band);
+ assertTrue(msg + ": getCenterFreq failed",
+ (freqRange[0] <= freq) && (freqRange[1] >= freq));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test presets
+ @LargeTest
+ public void test1_2Presets() throws Exception {
+ boolean result = false;
+ String msg = "test1_2Presets()";
+ getEqualizer(0);
+ try {
+ short numPresets = mEqualizer.getNumberOfPresets();
+ assertTrue(msg + ": getNumberOfPresets failed", numPresets >= MIN_NUMBER_OF_PRESETS);
+ mEqualizer.usePreset((short)(numPresets - 1));
+ short preset = mEqualizer.getCurrentPreset();
+ assertEquals(msg + ": usePreset failed", preset, (short)(numPresets - 1));
+ String name = mEqualizer.getPresetName(preset);
+ assertNotNull(msg + ": getPresetName failed", name);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test properties
+ @LargeTest
+ public void test1_3Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_3Properties()";
+ getEqualizer(0);
+ try {
+ Equalizer.Settings settings = mEqualizer.getProperties();
+ String str = settings.toString();
+ settings = new Equalizer.Settings(str);
+ mEqualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test that the equalizer actually alters the sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getEqualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // measure reference energy around 1kHz
+ int refEnergy = probe.capture(1000);
+ short band = mEqualizer.getBand(1000000);
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ mEqualizer.setBandLevel(band, levelRange[0]);
+ mEqualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy = probe.capture(1000);
+ assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseEqualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getEqualizer(int session) {
+ if (mEqualizer == null || session != mSession) {
+ if (session != mSession && mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ try {
+ mEqualizer = new Equalizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getEqualizer() Equalizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getEqualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mEqualizer", mEqualizer);
+ }
+
+ private void releaseEqualizer() {
+ if (mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
new file mode 100644
index 0000000..6b8ae44
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Virtualizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVirtualizerTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private Virtualizer mVirtualizer = null;
+ private int mSession = -1;
+
+ public MediaVirtualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVirtualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VIRTUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Virtualizer virtualizer = null;
+ try {
+ virtualizer = new Virtualizer(0, 0);
+ assertNotNull(msg + ": could not create Virtualizer", virtualizer);
+ try {
+ assertTrue(msg +": invalid effect ID", (virtualizer.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Virtualizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Virtualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (virtualizer != null) {
+ virtualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getVirtualizer(0);
+ try {
+ if (mVirtualizer.getStrengthSupported()) {
+ mVirtualizer.setStrength((short)TEST_STRENGTH);
+ short strength = mVirtualizer.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mVirtualizer.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getVirtualizer(0);
+ try {
+ Virtualizer.Settings settings = mVirtualizer.getProperties();
+ String str = settings.toString();
+ settings = new Virtualizer.Settings(str);
+ mVirtualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual virtualizer influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVirtualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mVirtualizer.setStrength((short)1000);
+ mVirtualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // four times higher with virtualizer on.
+ // NOTE: this is what is observed with current virtualizer implementation and the test
+ // audio file but is not the primary effect of the virtualizer. A better way would
+ // be to have a stereo PCM capture and check that a strongly paned input is centered
+ // when output. However, we cannot capture stereo with the visualizer.
+ assertTrue(msg + ": virtiualizer has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVirtualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getVirtualizer(int session) {
+ if (mVirtualizer == null || session != mSession) {
+ if (session != mSession && mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ try {
+ mVirtualizer = new Virtualizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVirtualizer() Virtualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVirtualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVirtualizer", mVirtualizer);
+ }
+
+ private void releaseVirtualizer() {
+ if (mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
new file mode 100644
index 0000000..26fdbfe
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
@@ -0,0 +1,505 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVisualizerTest";
+ private final static int MIN_CAPTURE_RATE_MAX = 20000;
+ private final static int MIN_SAMPLING_RATE = 8000000;
+ private final static int MAX_SAMPLING_RATE = 48000000;
+ private final static int MIN_CAPTURE_SIZE_MAX = 1024;
+ private final static int MAX_CAPTURE_SIZE_MIN = 128;
+
+ private Visualizer mVisualizer = null;
+ private int mSession = -1;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private final Object lock = new Object();
+ private byte[] mWaveform = null;
+ private byte[] mFft = null;
+ private boolean mCaptureWaveform = false;
+ private boolean mCaptureFft = false;
+
+ public MediaVisualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVisualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VISUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Visualizer visualizer = null;
+ try {
+ visualizer = new Visualizer(0);
+ assertNotNull(msg + ": could not create Visualizer", visualizer);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Visualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (visualizer != null) {
+ visualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: check capture rate and sampling rate
+ @LargeTest
+ public void test1_0CaptureRates() throws Exception {
+ boolean result = false;
+ String msg = "test1_0CaptureRates()";
+ getVisualizer(0);
+ try {
+ int captureRate = mVisualizer.getMaxCaptureRate();
+ assertTrue(msg +": insufficient max capture rate",
+ captureRate >= MIN_CAPTURE_RATE_MAX);
+ int samplingRate = mVisualizer.getSamplingRate();
+ assertTrue(msg +": invalid sampling rate",
+ samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: check capture size
+ @LargeTest
+ public void test1_1CaptureSize() throws Exception {
+ boolean result = false;
+ String msg = "test1_1CaptureSize()";
+ getVisualizer(0);
+ try {
+ int[] range = mVisualizer.getCaptureSizeRange();
+ assertTrue(msg +": insufficient min capture size",
+ range[0] <= MAX_CAPTURE_SIZE_MIN);
+ assertTrue(msg +": insufficient min capture size",
+ range[1] >= MIN_CAPTURE_SIZE_MAX);
+ mVisualizer.setCaptureSize(range[0]);
+ assertEquals(msg +": insufficient min capture size",
+ range[0], mVisualizer.getCaptureSize());
+ mVisualizer.setCaptureSize(range[1]);
+ assertEquals(msg +": insufficient min capture size",
+ range[1], mVisualizer.getCaptureSize());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - check capture
+ //----------------------------------
+
+ //Test case 2.0: test capture in polling mode
+ @LargeTest
+ public void test2_0PollingCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_0PollingCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVisualizer(mp.getAudioSessionId());
+ mVisualizer.setEnabled(true);
+ // check capture on silence
+ byte[] data = new byte[mVisualizer.getCaptureSize()];
+ mVisualizer.getWaveForm(data);
+ int energy = computeEnergy(data, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // check capture on sound
+ mVisualizer.getWaveForm(data);
+ energy = computeEnergy(data, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test capture with listener
+ @LargeTest
+ public void test2_1ListenerCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_1ListenerCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+
+ getVisualizer(mp.getAudioSessionId());
+ createListenerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+
+ mVisualizer.setEnabled(true);
+
+ // check capture on silence
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ int energy = computeEnergy(mWaveform, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+
+ // check capture on sound
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ energy = computeEnergy(mWaveform, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ terminateListenerLooper();
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private int computeEnergy(byte[] data, boolean unsigned) {
+ int energy = 0;
+ if (data.length != 0) {
+ for (int i = 0; i < data.length; i++) {
+ int tmp;
+ // convert from unsigned 8 bit to signed 16 bit
+ if (unsigned) {
+ tmp = ((int)data[i] & 0xFF) - 128;
+ } else {
+ tmp = (int)data[i];
+ }
+ energy += tmp*tmp;
+ }
+ energy /= data.length;
+ }
+ return energy;
+ }
+
+ private void getVisualizer(int session) {
+ if (mVisualizer == null || session != mSession) {
+ if (session != mSession && mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ try {
+ mVisualizer = new Visualizer(session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVisualizer", mVisualizer);
+ }
+
+ private void releaseVisualizer() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+
+ private void createListenerLooper() {
+
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ if (mVisualizer != null) {
+ mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+ public void onWaveFormDataCapture(
+ Visualizer visualizer, byte[] waveform, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureWaveform) {
+ mWaveform = waveform;
+ lock.notify();
+ }
+ }
+ }
+ }
+
+ public void onFftDataCapture(
+ Visualizer visualizer, byte[] fft, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureFft) {
+ mFft = fft;
+ lock.notify();
+ }
+ }
+ }
+ }
+ },
+ 10000,
+ true,
+ true);
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
new file mode 100644
index 0000000..9e91740
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mediaframeworktest.power;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.media.MediaPlayer;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Junit / Instrumentation test case for the power measurment the media player
+ */
+public class MediaPlayerPowerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerPowerTest";
+ private String MP3_POWERTEST =
+ Environment.getExternalStorageDirectory().toString() + "/power_sample_mp3.mp3";
+ private String MP3_STREAM = "http://75.17.48.204:10088/power_media/power_sample_mp3.mp3";
+ private String OGG_STREAM = "http://75.17.48.204:10088/power_media/power_sample_ogg.mp3";
+ private String AAC_STREAM = "http://75.17.48.204:10088/power_media/power_sample_aac.mp3";
+
+ public MediaPlayerPowerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ getActivity();
+ super.setUp();
+
+ }
+
+ public void audioPlayback(String filePath) {
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(filePath);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200000);
+ mp.stop();
+ mp.release();
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ assertTrue("MP3 Playback", false);
+ }
+ }
+
+ // A very simple test case which start the audio player.
+ // Power measurment will be done in other application.
+ public void testPowerLocalMP3Playback() throws Exception {
+ audioPlayback(MP3_POWERTEST);
+ }
+
+ public void testPowerStreamMP3Playback() throws Exception {
+ audioPlayback(MP3_STREAM);
+ }
+
+ public void testPowerStreamOGGPlayback() throws Exception {
+ audioPlayback(OGG_STREAM);
+ }
+
+ public void testPowerStreamAACPlayback() throws Exception {
+ audioPlayback(AAC_STREAM);
+ }
+}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 950a1e9..bd2b27a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,6 +7,7 @@
#
LOCAL_SRC_FILES:= \
asset_manager.cpp \
+ configuration.cpp \
input.cpp \
looper.cpp \
native_activity.cpp \
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 36c381e..3f7c1b6 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "NAsset"
#include <utils/Log.h>
-#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
#include <utils/AssetManager.h>
#include <utils/AssetDir.h>
#include <utils/Asset.h>
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
new file mode 100644
index 0000000..d76164f
--- /dev/null
+++ b/native/android/configuration.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Configuration"
+#include <utils/Log.h>
+
+#include <utils/AssetManager.h>
+
+#include <android_runtime/android_content_res_Configuration.h>
+
+using namespace android;
+
+AConfiguration* AConfiguration_new() {
+ AConfiguration* config = new AConfiguration;
+ memset(config, 0, sizeof(AConfiguration));
+ return config;
+}
+
+void AConfiguration_delete(AConfiguration* config) {
+ delete config;
+}
+
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
+ ((AssetManager*)am)->getConfiguration(out);
+}
+
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) {
+ *dest = *src;
+}
+
+int32_t AConfiguration_getMcc(AConfiguration* config) {
+ return config->mcc;
+}
+
+int32_t AConfiguration_getMnc(AConfiguration* config) {
+ return config->mnc;
+}
+
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage) {
+ outLanguage[0] = config->language[0];
+ outLanguage[1] = config->language[1];
+}
+
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry) {
+ outCountry[0] = config->country[0];
+ outCountry[1] = config->country[1];
+}
+
+int32_t AConfiguration_getOrientation(AConfiguration* config) {
+ return config->orientation;
+}
+
+int32_t AConfiguration_getTouchscreen(AConfiguration* config) {
+ return config->touchscreen;
+}
+
+int32_t AConfiguration_getDensity(AConfiguration* config) {
+ return config->density;
+}
+
+int32_t AConfiguration_getKeyboard(AConfiguration* config) {
+ return config->keyboard;
+}
+
+int32_t AConfiguration_getNavigation(AConfiguration* config) {
+ return config->navigation;
+}
+
+int32_t AConfiguration_getKeysHidden(AConfiguration* config) {
+ return config->inputFlags&ResTable_config::MASK_KEYSHIDDEN;
+}
+
+int32_t AConfiguration_getNavHidden(AConfiguration* config) {
+ return (config->inputFlags&ResTable_config::MASK_NAVHIDDEN)
+ >> ResTable_config::SHIFT_NAVHIDDEN;
+}
+
+int32_t AConfiguration_getSdkVersion(AConfiguration* config) {
+ return config->sdkVersion;
+}
+
+int32_t AConfiguration_getScreenSize(AConfiguration* config) {
+ return config->screenLayout&ResTable_config::MASK_SCREENSIZE;
+}
+
+int32_t AConfiguration_getScreenLong(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_SCREENLONG)
+ >> ResTable_config::SHIFT_SCREENLONG;
+}
+
+int32_t AConfiguration_getUiModeType(AConfiguration* config) {
+ return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
+}
+
+int32_t AConfiguration_getUiModeNight(AConfiguration* config) {
+ return (config->uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
+ >> ResTable_config::SHIFT_UI_MODE_NIGHT;
+
+}
+
+// ----------------------------------------------------------------------
+
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
+ config->mcc = mcc;
+}
+
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc) {
+ config->mnc = mnc;
+}
+
+void AConfiguration_setLanguage(AConfiguration* config, const char* language) {
+ config->language[0] = language[0];
+ config->language[1] = language[1];
+}
+
+void AConfiguration_setCountry(AConfiguration* config, const char* country) {
+ config->country[0] = country[0];
+ config->country[1] = country[1];
+}
+
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation) {
+ config->orientation = orientation;
+}
+
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen) {
+ config->touchscreen = touchscreen;
+}
+
+void AConfiguration_setDensity(AConfiguration* config, int32_t density) {
+ config->density = density;
+}
+
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard) {
+ config->keyboard = keyboard;
+}
+
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation) {
+ config->navigation = navigation;
+}
+
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_KEYSHIDDEN)
+ | (keysHidden&ResTable_config::MASK_KEYSHIDDEN);
+}
+
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_NAVHIDDEN)
+ | ((navHidden<<ResTable_config::SHIFT_NAVHIDDEN)&ResTable_config::MASK_NAVHIDDEN);
+}
+
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion) {
+ config->sdkVersion = sdkVersion;
+}
+
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | (screenSize&ResTable_config::MASK_SCREENSIZE);
+}
+
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
+}
+
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);
+}
+
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ((uiModeNight<<ResTable_config::SHIFT_UI_MODE_NIGHT)&ResTable_config::MASK_UI_MODE_NIGHT);
+
+}
+
+// ----------------------------------------------------------------------
+
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
+ return (config1->diff(*config2));
+}
+
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested) {
+ return base->match(*requested);
+}
+
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested) {
+ return base->isBetterThan(*test, requested);
+}
diff --git a/native/copy-to-ndk.sh b/native/copy-to-ndk.sh
new file mode 100644
index 0000000..4f5a16a
--- /dev/null
+++ b/native/copy-to-ndk.sh
@@ -0,0 +1,55 @@
+# Take care of copying current header files over to the correct
+# location in the NDK.
+
+copyndkheaders() {
+ local CURR_PLATFORM=android-9
+ local ALL_PLATFORMS="$CURR_PLATFORM android-8 android-5 android-4 android-3"
+
+ local SRC_HEADERS=$ANDROID_BUILD_TOP/frameworks/base/native/include/android
+ local NDK_PLATFORMS=$ANDROID_BUILD_TOP/development/ndk/platforms
+ local DST_HEADERS=$NDK_PLATFORMS/$CURR_PLATFORM
+
+ local SRC_LIB_ANDROID=$ANDROID_PRODUCT_OUT/system/lib/libandroid.so
+ local DST_LIB_ANDROID=$NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/lib/libandroid.so
+
+ local didsomething=""
+
+ #echo "SRC_HEADERS: $SRC_HEADERS"
+
+ for i in $(cd $SRC_HEADERS; ls *.h); do
+ local src=$SRC_HEADERS/$i
+ local changed=""
+ for j in $ALL_PLATFORMS; do
+ local dst=$NDK_PLATFORMS/$j/arch-arm/usr/include/android/$i
+ if [ "$changed" == "" -a -e $dst ]; then
+ #echo "Exists: $dst"
+ if diff $src $dst >/dev/null; then
+ echo "$i: has not changed from $j" >/dev/null
+ changed="false"
+ else
+ changed="true"
+ echo "$i: has changed from $j" >/dev/null
+ fi
+ fi
+ done
+ if [ "$changed" == "true" -o "$changed" == "" ]; then
+ echo "Updating: $i"
+ cp $src $NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/include/android/$i
+ didsomething="true"
+ fi
+ done
+
+ if diff $SRC_LIB_ANDROID $DST_LIB_ANDROID >/dev/null; then
+ echo "libandroid.so: has not changed" >/dev/null
+ else
+ echo "Updating: $DST_LIB_ANDROID"
+ cp $SRC_LIB_ANDROID $DST_LIB_ANDROID
+ didsomething="true"
+ fi
+ if [ "$didsomething" != "" ]; then
+ echo "Headers changed... rebuilding platforms."
+ sh $ANDROID_BUILD_TOP/ndk/build/tools/build-platforms.sh
+ fi
+}
+
+copyndkheaders
diff --git a/native/include/android/asset_manager.h b/native/include/android/asset_manager.h
index 89989f8..4fa0ef3 100644
--- a/native/include/android/asset_manager.h
+++ b/native/include/android/asset_manager.h
@@ -18,8 +18,6 @@
#ifndef ANDROID_ASSET_MANAGER_H
#define ANDROID_ASSET_MANAGER_H
-#include <jni.h>
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -43,14 +41,6 @@
/**
- * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
- * object. Note that the caller is responsible for obtaining and holding a VM reference
- * to the jobject to prevent its being garbage collected while the native object is
- * in use.
- */
-AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
-
-/**
* Open the named directory within the asset hierarchy. The directory can then
* be inspected with the AAssetDir functions. To open the top-level directory,
* pass in "" as the dirName.
diff --git a/native/include/android/asset_manager_jni.h b/native/include/android/asset_manager_jni.h
new file mode 100644
index 0000000..aec2d3c
--- /dev/null
+++ b/native/include/android/asset_manager_jni.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_ASSET_MANAGER_JNI_H
+#define ANDROID_ASSET_MANAGER_JNI_H
+
+#include <android/asset_manager.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
+ * object. Note that the caller is responsible for obtaining and holding a VM reference
+ * to the jobject to prevent its being garbage collected while the native object is
+ * in use.
+ */
+AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_ASSET_MANAGER_JNI_H
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
new file mode 100644
index 0000000..79b9b1e
--- /dev/null
+++ b/native/include/android/configuration.h
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_CONFIGURATION_H
+#define ANDROID_CONFIGURATION_H
+
+#include <android/asset_manager.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AConfiguration;
+typedef struct AConfiguration AConfiguration;
+
+enum {
+ ACONFIGURATION_ORIENTATION_ANY = 0x0000,
+ ACONFIGURATION_ORIENTATION_PORT = 0x0001,
+ ACONFIGURATION_ORIENTATION_LAND = 0x0002,
+ ACONFIGURATION_ORIENTATION_SQUARE = 0x0003,
+
+ ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000,
+ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001,
+ ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002,
+ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003,
+
+ ACONFIGURATION_DENSITY_DEFAULT = 0,
+ ACONFIGURATION_DENSITY_LOW = 120,
+ ACONFIGURATION_DENSITY_MEDIUM = 160,
+ ACONFIGURATION_DENSITY_HIGH = 240,
+ ACONFIGURATION_DENSITY_NONE = 0xffff,
+
+ ACONFIGURATION_KEYBOARD_ANY = 0x0000,
+ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001,
+ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002,
+ ACONFIGURATION_KEYBOARD_12KEY = 0x0003,
+
+ ACONFIGURATION_NAVIGATION_ANY = 0x0000,
+ ACONFIGURATION_NAVIGATION_NONAV = 0x0001,
+ ACONFIGURATION_NAVIGATION_DPAD = 0x0002,
+ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003,
+ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004,
+
+ ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
+ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
+ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
+
+ ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
+ ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
+
+ ACONFIGURATION_SCREENSIZE_ANY = 0x00,
+ ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
+ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
+ ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
+ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
+
+ ACONFIGURATION_SCREENLONG_ANY = 0x00,
+ ACONFIGURATION_SCREENLONG_NO = 0x1,
+ ACONFIGURATION_SCREENLONG_YES = 0x2,
+
+ ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
+ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
+ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+
+ ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10,
+ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20,
+
+ ACONFIGURATION_MCC = 0x0001,
+ ACONFIGURATION_MNC = 0x0002,
+ ACONFIGURATION_LOCALE = 0x0004,
+ ACONFIGURATION_TOUCHSCREEN = 0x0008,
+ ACONFIGURATION_KEYBOARD = 0x0010,
+ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
+ ACONFIGURATION_NAVIGATION = 0x0040,
+ ACONFIGURATION_ORIENTATION = 0x0080,
+ ACONFIGURATION_DENSITY = 0x0100,
+ ACONFIGURATION_SCREEN_SIZE = 0x0200,
+ ACONFIGURATION_VERSION = 0x0400,
+ ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
+ ACONFIGURATION_UI_MODE = 0x1000,
+};
+
+/**
+ * Create a new AConfiguration, initialized with no values set.
+ */
+AConfiguration* AConfiguration_new();
+
+/**
+ * Free an AConfiguration that was previously created with
+ * AConfiguration_new().
+ */
+void AConfiguration_delete(AConfiguration* config);
+
+/**
+ * Create and return a new AConfiguration based on the current configuration in
+ * use in the given AssetManager.
+ */
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am);
+
+/**
+ * Copy the contents of 'src' to 'dest'.
+ */
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src);
+
+/**
+ * Return the current MCC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMcc(AConfiguration* config);
+
+/**
+ * Set the current MCC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc);
+
+/**
+ * Return the current MNC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMnc(AConfiguration* config);
+
+/**
+ * Set the current MNC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc);
+
+/**
+ * Return the current language code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a language is not set, they will be 0.
+ */
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage);
+
+/**
+ * Set the current language code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setLanguage(AConfiguration* config, const char* language);
+
+/**
+ * Return the current country code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a country is not set, they will be 0.
+ */
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry);
+
+/**
+ * Set the current country code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setCountry(AConfiguration* config, const char* country);
+
+/**
+ * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration.
+ */
+int32_t AConfiguration_getOrientation(AConfiguration* config);
+
+/**
+ * Set the current orientation in the configuration.
+ */
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation);
+
+/**
+ * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration.
+ */
+int32_t AConfiguration_getTouchscreen(AConfiguration* config);
+
+/**
+ * Set the current touchscreen in the configuration.
+ */
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen);
+
+/**
+ * Return the current ACONFIGURATION_DENSITY_* set in the configuration.
+ */
+int32_t AConfiguration_getDensity(AConfiguration* config);
+
+/**
+ * Set the current density in the configuration.
+ */
+void AConfiguration_setDensity(AConfiguration* config, int32_t density);
+
+/**
+ * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration.
+ */
+int32_t AConfiguration_getKeyboard(AConfiguration* config);
+
+/**
+ * Set the current keyboard in the configuration.
+ */
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard);
+
+/**
+ * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration.
+ */
+int32_t AConfiguration_getNavigation(AConfiguration* config);
+
+/**
+ * Set the current navigation in the configuration.
+ */
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation);
+
+/**
+ * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getKeysHidden(AConfiguration* config);
+
+/**
+ * Set the current keys hidden in the configuration.
+ */
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden);
+
+/**
+ * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getNavHidden(AConfiguration* config);
+
+/**
+ * Set the current nav hidden in the configuration.
+ */
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden);
+
+/**
+ * Return the current SDK (API) version set in the configuration.
+ */
+int32_t AConfiguration_getSdkVersion(AConfiguration* config);
+
+/**
+ * Set the current SDK version in the configuration.
+ */
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion);
+
+/**
+ * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenSize(AConfiguration* config);
+
+/**
+ * Set the current screen size in the configuration.
+ */
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize);
+
+/**
+ * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenLong(AConfiguration* config);
+
+/**
+ * Set the current screen long in the configuration.
+ */
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeType(AConfiguration* config);
+
+/**
+ * Set the current UI mode type in the configuration.
+ */
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeNight(AConfiguration* config);
+
+/**
+ * Set the current UI mode night in the configuration.
+ */
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
+
+/**
+ * Perform a diff between two configurations. Returns a bit mask of
+ * ACONFIGURATION_* constants, each bit set meaning that configuration element
+ * is different between them.
+ */
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2);
+
+/**
+ * Determine whether 'base' is a valid configuration for use within the
+ * environment 'requested'. Returns 0 if there are any values in 'base'
+ * that conflict with 'requested'. Returns 1 if it does not conflict.
+ */
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested);
+
+/**
+ * Determine whether the configuration in 'test' is better than the existing
+ * configuration in 'base'. If 'requested' is non-NULL, this decision is based
+ * on the overall configuration given there. If it is NULL, this decision is
+ * simply based on which configuration is more specific. Returns non-0 if
+ * 'test' is better than 'base'.
+ *
+ * This assumes you have already filtered the configurations with
+ * AConfiguration_match().
+ */
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_CONFIGURATION_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index ee4204d..d74e1ce 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -197,6 +197,12 @@
void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect);
/**
+ * The current device AConfiguration has changed. The new configuration can
+ * be retrieved from assetManager.
+ */
+ void (*onConfigurationChanged)(ANativeActivity* activity);
+
+ /**
* The system is running low on memory. Use this callback to release
* resources you do not need, to help the system avoid killing more
* important processes.
@@ -208,7 +214,9 @@
* This is the function that must be in the native code to instantiate the
* application's native activity. It is called with the activity instance (see
* above); if the code is being instantiated from a previously saved instance,
- * the savedState will be non-NULL and point to the saved data.
+ * the savedState will be non-NULL and point to the saved data. You must make
+ * any copy of this data you need -- it will be released after you return from
+ * this function.
*/
typedef void ANativeActivity_createFunc(ANativeActivity* activity,
void* savedState, size_t savedStateSize);
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 7599d7e..ad03d0e 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -36,12 +36,23 @@
typedef struct ANativeWindow ANativeWindow;
typedef struct ANativeWindow_Buffer {
+ // The number of pixels that are show horizontally.
int32_t width;
+
+ // The number of pixels that are shown vertically.
int32_t height;
+
+ // The number of *pixels* that a line in the buffer takes in
+ // memory. This may be >= width.
int32_t stride;
+
+ // The format of the buffer. One of WINDOW_FORMAT_*
int32_t format;
+
+ // The actual bits.
void* bits;
+ // Do not touch.
uint32_t reserved[6];
} ANativeWindow_Buffer;
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 315a2a36..94b60a1 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1409,7 +1409,9 @@
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso && cnx->egl.eglGetProcAddress) {
found = true;
- cnx->hooks[i]->ext.extensions[slot] =
+ // Extensions are independent of the bound context
+ cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] =
cnx->egl.eglGetProcAddress(procname);
}
}
@@ -1421,7 +1423,6 @@
}
pthread_mutex_unlock(&gInitDriverMutex);
-
return addr;
}
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 23837ef..dcf8735c 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -27,9 +27,12 @@
// ----------------------------------------------------------------------------
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
#undef GL_EXTENSION
#undef GL_EXTENSION_NAME
+#undef GL_EXTENSION_ARRAY
+#undef GL_EXTENSION_LIST
+#undef GET_TLS
#if defined(__arm__)
@@ -60,7 +63,7 @@
: \
);
- #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
+ #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
#define GL_EXTENSION(_n) \
void API_ENTRY(GL_EXTENSION_NAME(_n))() { \
@@ -78,97 +81,40 @@
#endif
-GL_EXTENSION(0)
-GL_EXTENSION(1)
-GL_EXTENSION(2)
-GL_EXTENSION(3)
-GL_EXTENSION(4)
-GL_EXTENSION(5)
-GL_EXTENSION(6)
-GL_EXTENSION(7)
-GL_EXTENSION(8)
-GL_EXTENSION(9)
-GL_EXTENSION(10)
-GL_EXTENSION(11)
-GL_EXTENSION(12)
-GL_EXTENSION(13)
-GL_EXTENSION(14)
-GL_EXTENSION(15)
+#define GL_EXTENSION_LIST(name) \
+ name(0) name(1) name(2) name(3) \
+ name(4) name(5) name(6) name(7) \
+ name(8) name(9) name(10) name(11) \
+ name(12) name(13) name(14) name(15) \
+ name(16) name(17) name(18) name(19) \
+ name(20) name(21) name(22) name(23) \
+ name(24) name(25) name(26) name(27) \
+ name(28) name(29) name(30) name(31) \
+ name(32) name(33) name(34) name(35) \
+ name(36) name(37) name(38) name(39) \
+ name(40) name(41) name(42) name(43) \
+ name(44) name(45) name(46) name(47) \
+ name(48) name(49) name(50) name(51) \
+ name(52) name(53) name(54) name(55) \
+ name(56) name(57) name(58) name(59) \
+ name(60) name(61) name(62) name(63)
-GL_EXTENSION(16)
-GL_EXTENSION(17)
-GL_EXTENSION(18)
-GL_EXTENSION(19)
-GL_EXTENSION(20)
-GL_EXTENSION(21)
-GL_EXTENSION(22)
-GL_EXTENSION(23)
-GL_EXTENSION(24)
-GL_EXTENSION(25)
-GL_EXTENSION(26)
-GL_EXTENSION(27)
-GL_EXTENSION(28)
-GL_EXTENSION(29)
-GL_EXTENSION(30)
-GL_EXTENSION(31)
-GL_EXTENSION(32)
-GL_EXTENSION(33)
-GL_EXTENSION(34)
-GL_EXTENSION(35)
-GL_EXTENSION(36)
-GL_EXTENSION(37)
-GL_EXTENSION(38)
-GL_EXTENSION(39)
-GL_EXTENSION(40)
-GL_EXTENSION(41)
-GL_EXTENSION(42)
-GL_EXTENSION(43)
-GL_EXTENSION(44)
-GL_EXTENSION(45)
-GL_EXTENSION(46)
-GL_EXTENSION(47)
+GL_EXTENSION_LIST( GL_EXTENSION )
-GL_EXTENSION(48)
-GL_EXTENSION(49)
-GL_EXTENSION(50)
-GL_EXTENSION(51)
-GL_EXTENSION(52)
-GL_EXTENSION(53)
-GL_EXTENSION(54)
-GL_EXTENSION(55)
-GL_EXTENSION(56)
-GL_EXTENSION(57)
-GL_EXTENSION(58)
-GL_EXTENSION(59)
-GL_EXTENSION(60)
-GL_EXTENSION(61)
-GL_EXTENSION(62)
-GL_EXTENSION(63)
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3),
- GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7),
- GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
- GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
- GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
- GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
- GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
- GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
- GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
- GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
- GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
- GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
- GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
- GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
- GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
- GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+ GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
};
+#undef GET_TLS
+#undef GL_EXTENSION_LIST
+#undef GL_EXTENSION_ARRAY
#undef GL_EXTENSION_NAME
#undef GL_EXTENSION
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e..a12edf2 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -39,6 +39,8 @@
#undef CALL_GL_API
#undef CALL_GL_API_RETURN
+#define DEBUG_CALL_GL_API 0
+
#if USE_FAST_TLS_KEY
#ifdef HAVE_ARM_TLS_REGISTER
@@ -73,10 +75,24 @@
#define API_ENTRY(_api) _api
+#if DEBUG_CALL_GL_API
+
#define CALL_GL_API(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
- _c->_api(__VA_ARGS__)
-
+ _c->_api(__VA_ARGS__); \
+ GLenum status = GL_NO_ERROR; \
+ while ((status = glGetError()) != GL_NO_ERROR) { \
+ LOGD("[" #_api "] 0x%x", status); \
+ }
+
+#else
+
+ #define CALL_GL_API(_api, ...) \
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+ _c->_api(__VA_ARGS__);
+
+#endif
+
#define CALL_GL_API_RETURN(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
return _c->_api(__VA_ARGS__)
diff --git a/packages/DefaultContainerService/res/values-cs/strings.xml b/packages/DefaultContainerService/res/values-cs/strings.xml
index 0179e85..216d715 100644
--- a/packages/DefaultContainerService/res/values-cs/strings.xml
+++ b/packages/DefaultContainerService/res/values-cs/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-da/strings.xml b/packages/DefaultContainerService/res/values-da/strings.xml
index 0179e85..5243028 100644
--- a/packages/DefaultContainerService/res/values-da/strings.xml
+++ b/packages/DefaultContainerService/res/values-da/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Hjælp til pakkeadgang"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-de/strings.xml b/packages/DefaultContainerService/res/values-de/strings.xml
index 5d12956..216d715 100644
--- a/packages/DefaultContainerService/res/values-de/strings.xml
+++ b/packages/DefaultContainerService/res/values-de/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-el/strings.xml b/packages/DefaultContainerService/res/values-el/strings.xml
index b0b5794..a4d8144 100644
--- a/packages/DefaultContainerService/res/values-el/strings.xml
+++ b/packages/DefaultContainerService/res/values-el/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Βοηθός πρόσβασης πακέτου"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-es-rUS/strings.xml b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
index cf893de..670c2c5 100644
--- a/packages/DefaultContainerService/res/values-es-rUS/strings.xml
+++ b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Asist. p/acceder al paq."</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-es/strings.xml b/packages/DefaultContainerService/res/values-es/strings.xml
index 6817520..022c461 100644
--- a/packages/DefaultContainerService/res/values-es/strings.xml
+++ b/packages/DefaultContainerService/res/values-es/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Ayudante acceso a paquete"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
index 3b4a90d..5c458bc 100644
--- a/packages/DefaultContainerService/res/values-fr/strings.xml
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Aide accès au package"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-it/strings.xml b/packages/DefaultContainerService/res/values-it/strings.xml
index 55bd6e5..216d715 100644
--- a/packages/DefaultContainerService/res/values-it/strings.xml
+++ b/packages/DefaultContainerService/res/values-it/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-ja/strings.xml b/packages/DefaultContainerService/res/values-ja/strings.xml
index dc1dfea..2f57e4e 100644
--- a/packages/DefaultContainerService/res/values-ja/strings.xml
+++ b/packages/DefaultContainerService/res/values-ja/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"パッケージアクセス支援ツール"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-ko/strings.xml b/packages/DefaultContainerService/res/values-ko/strings.xml
index 0179e85..0304972 100644
--- a/packages/DefaultContainerService/res/values-ko/strings.xml
+++ b/packages/DefaultContainerService/res/values-ko/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"패키지 액세스 도움말"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-nb/strings.xml b/packages/DefaultContainerService/res/values-nb/strings.xml
index 0179e85..637f54d 100644
--- a/packages/DefaultContainerService/res/values-nb/strings.xml
+++ b/packages/DefaultContainerService/res/values-nb/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Hjelpeprogram for pakketilgang"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-nl/strings.xml b/packages/DefaultContainerService/res/values-nl/strings.xml
index 0179e85..9ece040 100644
--- a/packages/DefaultContainerService/res/values-nl/strings.xml
+++ b/packages/DefaultContainerService/res/values-nl/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Helper voor pakkettoegang"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-pl/strings.xml b/packages/DefaultContainerService/res/values-pl/strings.xml
index 0c96f3d..216d715 100644
--- a/packages/DefaultContainerService/res/values-pl/strings.xml
+++ b/packages/DefaultContainerService/res/values-pl/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
index 0179e85..5c03669 100644
--- a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder pacotes"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-pt/strings.xml b/packages/DefaultContainerService/res/values-pt/strings.xml
index 00b90de..5fbd949 100644
--- a/packages/DefaultContainerService/res/values-pt/strings.xml
+++ b/packages/DefaultContainerService/res/values-pt/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Assistente de pacote"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-ru/strings.xml b/packages/DefaultContainerService/res/values-ru/strings.xml
index 0179e85..ccb0c53 100644
--- a/packages/DefaultContainerService/res/values-ru/strings.xml
+++ b/packages/DefaultContainerService/res/values-ru/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Мастер доступа к пакетам"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-sv/strings.xml b/packages/DefaultContainerService/res/values-sv/strings.xml
index b097814..097a709 100644
--- a/packages/DefaultContainerService/res/values-sv/strings.xml
+++ b/packages/DefaultContainerService/res/values-sv/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Hjälp med paketåtkomst"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-tr/strings.xml b/packages/DefaultContainerService/res/values-tr/strings.xml
index afd870f..12ea674 100644
--- a/packages/DefaultContainerService/res/values-tr/strings.xml
+++ b/packages/DefaultContainerService/res/values-tr/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"Paket Erişim Yardımcısı"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
index 4f99d1b..65928b1 100644
--- a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"软件包访问帮助程序"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
index 38870f6..9a43509 100644
--- a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
+++ b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
@@ -20,5 +20,5 @@
<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>
+ <string name="service_name" msgid="4841491635055379553">"套件存取輔助程式"</string>
</resources>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f1c6532..c6e0a24 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -24,6 +24,8 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.res.ObbInfo;
+import android.content.res.ObbScanner;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
@@ -142,6 +144,10 @@
public boolean checkFreeStorage(boolean external, Uri fileUri) {
return checkFreeStorageInner(external, fileUri);
}
+
+ public ObbInfo getObbInfo(String filename) {
+ return ObbScanner.getObbInfo(filename);
+ }
};
public DefaultContainerService() {
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_0.png b/packages/SystemUI/res/drawable-hdpi/battery_0.png
new file mode 100644
index 0000000..f4103a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_100.png b/packages/SystemUI/res/drawable-hdpi/battery_100.png
new file mode 100644
index 0000000..061cbe5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_20.png b/packages/SystemUI/res/drawable-hdpi/battery_20.png
new file mode 100644
index 0000000..0064027
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_40.png b/packages/SystemUI/res/drawable-hdpi/battery_40.png
new file mode 100644
index 0000000..10de0e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_60.png b/packages/SystemUI/res/drawable-hdpi/battery_60.png
new file mode 100644
index 0000000..aa2b8ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_80.png b/packages/SystemUI/res/drawable-hdpi/battery_80.png
new file mode 100644
index 0000000..fe231f0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
new file mode 100644
index 0000000..d809b84
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
new file mode 100644
index 0000000..5cc007c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_0.png b/packages/SystemUI/res/drawable-hdpi/signal_0.png
new file mode 100644
index 0000000..00e36c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_100.png b/packages/SystemUI/res/drawable-hdpi/signal_100.png
new file mode 100644
index 0000000..96e52ff
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_20.png b/packages/SystemUI/res/drawable-hdpi/signal_20.png
new file mode 100644
index 0000000..c0f652a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_40.png b/packages/SystemUI/res/drawable-hdpi/signal_40.png
new file mode 100644
index 0000000..995dd8e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_60.png b/packages/SystemUI/res/drawable-hdpi/signal_60.png
new file mode 100644
index 0000000..51e31ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_80.png b/packages/SystemUI/res/drawable-hdpi/signal_80.png
new file mode 100644
index 0000000..afa656e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..92fc7f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..77f09ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
new file mode 100644
index 0000000..e375ee9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
new file mode 100644
index 0000000..f62502b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
new file mode 100644
index 0000000..e887fb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
new file mode 100644
index 0000000..58159d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
new file mode 100644
index 0000000..6e857b5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
new file mode 100644
index 0000000..b5d36cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
new file mode 100644
index 0000000..75cc409
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_20.png b/packages/SystemUI/res/drawable-mdpi/battery_20.png
new file mode 100644
index 0000000..c0d0030
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_40.png b/packages/SystemUI/res/drawable-mdpi/battery_40.png
new file mode 100644
index 0000000..e301c08
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_60.png b/packages/SystemUI/res/drawable-mdpi/battery_60.png
new file mode 100644
index 0000000..0fde1fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_80.png b/packages/SystemUI/res/drawable-mdpi/battery_80.png
new file mode 100644
index 0000000..15c4e1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
new file mode 100644
index 0000000..7ab1f26
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
new file mode 100644
index 0000000..08f7a4d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_0.png b/packages/SystemUI/res/drawable-mdpi/signal_0.png
new file mode 100644
index 0000000..6533677
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
new file mode 100644
index 0000000..e8976a2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_20.png b/packages/SystemUI/res/drawable-mdpi/signal_20.png
new file mode 100644
index 0000000..651e2a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_40.png b/packages/SystemUI/res/drawable-mdpi/signal_40.png
new file mode 100644
index 0000000..6ba7906
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_60.png b/packages/SystemUI/res/drawable-mdpi/signal_60.png
new file mode 100644
index 0000000..6d2e812
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_80.png b/packages/SystemUI/res/drawable-mdpi/signal_80.png
new file mode 100644
index 0000000..a152623
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..4d19717
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..830cbd5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
new file mode 100644
index 0000000..eb87532
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
new file mode 100644
index 0000000..3bfc83e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
new file mode 100644
index 0000000..0d8479c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
new file mode 100644
index 0000000..8f1d26c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
new file mode 100644
index 0000000..22636d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/battery.xml b/packages/SystemUI/res/drawable/battery.xml
new file mode 100644
index 0000000..c2294d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/battery.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:maxLevel="1" android:drawable="@drawable/battery_0" />
+ <item android:maxLevel="5">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_0" android:duration="250" />
+ <item android:drawable="@drawable/battery_20" android:duration="250" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="20" android:drawable="@drawable/battery_20" />
+ <item android:maxLevel="40" android:drawable="@drawable/battery_40" />
+ <item android:maxLevel="60" android:drawable="@drawable/battery_60" />
+ <item android:maxLevel="80" android:drawable="@drawable/battery_80" />
+ <item android:maxLevel="101" android:drawable="@drawable/battery_100" />
+</level-list>
+
diff --git a/packages/SystemUI/res/drawable/battery_charging.xml b/packages/SystemUI/res/drawable/battery_charging.xml
new file mode 100644
index 0000000..2fd0c6d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/battery_charging.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:maxLevel="20">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_0" android:duration="1000" />
+ <item android:drawable="@drawable/battery_20" android:duration="1000" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="40">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_20" android:duration="1000" />
+ <item android:drawable="@drawable/battery_40" android:duration="1000" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="60">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_40" android:duration="1000" />
+ <item android:drawable="@drawable/battery_60" android:duration="1000" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="80">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_60" android:duration="1000" />
+ <item android:drawable="@drawable/battery_80" android:duration="1000" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="92">
+ <animation-list android:oneshot="false">
+ <item android:drawable="@drawable/battery_80" android:duration="1000" />
+ <item android:drawable="@drawable/battery_100" android:duration="1000" />
+ </animation-list>
+ </item>
+ <item android:maxLevel="101" android:drawable="@drawable/battery_100" />
+</level-list>
+
diff --git a/packages/SystemUI/res/drawable/button_frame.xml b/packages/SystemUI/res/drawable/button_frame.xml
new file mode 100644
index 0000000..5db39a5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/button_frame.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/button_frame_pressed" />
+ <item android:drawable="@drawable/button_frame_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/signal.xml b/packages/SystemUI/res/drawable/signal.xml
new file mode 100644
index 0000000..8b4f56b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/signal.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:maxLevel="01" android:drawable="@drawable/signal_0" />
+ <item android:maxLevel="20" android:drawable="@drawable/signal_20" />
+ <item android:maxLevel="40" android:drawable="@drawable/signal_40" />
+ <item android:maxLevel="60" android:drawable="@drawable/signal_60" />
+ <item android:maxLevel="80" android:drawable="@drawable/signal_80" />
+ <item android:maxLevel="101" android:drawable="@drawable/signal_100" />
+
+</level-list>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_expand.xml b/packages/SystemUI/res/drawable/status_bar_expand.xml
new file mode 100644
index 0000000..f966920
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_expand.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/status_bar_expand_pressed" />
+ <item android:drawable="@drawable/status_bar_expand_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 9ae2c25..ffb1571 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -39,13 +39,13 @@
android:onClick="notificationIconsClicked"
android:background="@drawable/status_bar_icon_tray"
>
- <view
+ <ImageView
class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView"
- android:id="@+id/more"
+ android:id="@+id/expand"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:src="@drawable/stat_notify_more"
- android:layout_marginLeft="10dip"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_bar_expand"
+ android:onClick="notificationIconsClicked"
/>
<view
class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
@@ -64,39 +64,47 @@
</com.android.systemui.statusbar.tablet.NotificationIconArea>
- <RelativeLayout android:id="@+id/systemInfo"
- android:layout_width="200dip"
+ <LinearLayout android:id="@+id/ticker"
+ android:layout_width="300dip"
android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:clickable="true"
- android:onClick="systemInfoClicked"
+ android:paddingLeft="6dip"
+ android:animationCache="false"
+ android:layout_alignLeft="@id/notificationIcons"
+ android:layout_alignTop="@id/notificationIcons"
+ android:orientation="horizontal"
+ android:visibility="gone"
>
- <com.android.systemui.statusbar.Clock
- android:id="@+id/clock"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
+ <ImageView android:id="@+id/tickerIcon"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:singleLine="true"
- android:textSize="16sp"
- android:textStyle="bold"
- android:padding="6dip"
+ android:layout_height="match_parent"
+ android:layout_marginRight="8dip"
/>
- <ImageView
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
+ android:layout_width="0dip"
+ android:layout_weight="1"
android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_empty"
- />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_full"
- />
- </RelativeLayout>
+ android:paddingTop="2dip"
+ android:paddingRight="10dip">
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </com.android.systemui.statusbar.TickerView>
+ </LinearLayout>
+
+ <include layout="@layout/status_bar_center"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ />
<com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
android:layout_width="wrap_content"
@@ -125,60 +133,5 @@
android:src="@drawable/status_bar_home"
systemui:keyCode="3"
/>
-
-<!--
-
- <LinearLayout android:id="@+id/ticker"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="6dip"
- android:animationCache="false"
- android:orientation="horizontal" >
- <ImageSwitcher android:id="@+id/tickerIcon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginRight="8dip"
- >
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- </ImageSwitcher>
- <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingTop="2dip"
- android:paddingRight="10dip">
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- </com.android.systemui.statusbar.TickerView>
- </LinearLayout>
-
- <com.android.systemui.statusbar.DateView android:id="@+id/date"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:gravity="center_vertical|left"
- android:paddingLeft="6px"
- android:paddingRight="6px"
- android:background="@drawable/status_bar_background"
- />
--->
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
new file mode 100644
index 0000000..775fea0
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<!-- Center of status bar: System info display, system info panel trigger -->
+<RelativeLayout android:id="@+id/systemInfo"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:clickable="true"
+ android:onClick="systemInfoClicked"
+ >
+ <com.android.systemui.statusbar.Clock
+ style="@*android:style/TextAppearance.StatusBar.Icon"
+ android:id="@+id/clock"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:padding="2dip"
+ />
+ <ImageView
+ android:id="@+id/battery"
+ android:layout_width="50dip"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/battery"
+ />
+ <ImageView
+ android:id="@+id/signal"
+ android:layout_width="50dip"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/signal"
+ />
+</RelativeLayout>
+
+
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index 84a718a..3489eec 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -46,17 +46,23 @@
android:background="@android:drawable/divider_horizontal_dark"
/>
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
+ <ScrollView
+ android:id="@+id/notificationScroller"
android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:animationCache="false"
- android:orientation="vertical"
- android:background="@drawable/status_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
+ android:layout_width="match_parent"
>
- </LinearLayout>
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|bottom"
+ android:animationCache="false"
+ android:orientation="vertical"
+ android:background="@drawable/status_bar_background"
+ android:clickable="true"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
index 2222d08..e5b2f53 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -2,7 +2,7 @@
<!--
/* apps/common/assets/default/default/skins/StatusBar.xml
**
-** Copyright 2006, The Android Open Source Project
+** Copyright 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.
@@ -18,35 +18,120 @@
*/
-->
-<!-- android:background="@drawable/status_bar_closed_default_background" -->
-<FrameLayout
+<com.android.systemui.statusbar.tablet.SystemPanel
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="300dip"
- android:layout_width="400dip"
- android:paddingLeft="8dip"
- android:paddingRight="8dip"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
android:background="#FF000000"
+ android:orientation="vertical"
>
-
- <RelativeLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animationCache="false"
- android:background="@drawable/status_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
- >
- <TextView
- android:id="@+id/systemPanelDummy"
+ <TextView android:id="@+id/settings_button"
+ style="?android:attr/textAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:layout_marginTop="2dip"
+ android:layout_marginBottom="1dip"
+ android:layout_marginRight="10dip"
+ android:padding="8dip"
+ android:textSize="20sp"
+ android:text="@string/system_panel_settings_button"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1sp"
+ android:background="@android:drawable/divider_horizontal_dark"
+ />
+
+ <LinearLayout
+ android:padding="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <ImageButton android:id="@+id/brightness"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:textColor="#FFCCCCCC"
- android:textSize="18sp"
+ android:src="@drawable/system_panel_brightness_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/sound"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_sound_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/orientation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_orientation_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/airplane"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_airplane_default"
+ android:background="@drawable/button_frame"
+ />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <ImageView android:id="@+id/battery_meter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:src="@drawable/dots_empty"
+ />
+
+ <TextView android:id="@+id/battery_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:layout_below="@id/battery_meter"
+ />
+
+ <com.android.systemui.statusbar.Clock
+ style="@*android:style/TextAppearance.StatusBar.Icon"
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:padding="2dip"
+ android:layout_centerHorizontal="true"
+ />
+
+ <TextView android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/clock"
+ />
+
+ <ImageView android:id="@+id/signal_meter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/signal"
+ />
+
+ <TextView android:id="@+id/signal_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:layout_below="@id/signal_meter"
/>
</RelativeLayout>
-</FrameLayout>
+
+</com.android.systemui.statusbar.tablet.SystemPanel>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ba3a3d1..2df3b6d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -51,4 +51,7 @@
power usage activity to find out what drained the battery. -->
<string name="battery_low_why">Battery use</string>
+ <!-- Name of the button that links to the Settings app. [MAXCHARS=NONE] -->
+ <string name="system_panel_settings_button">Settings</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 5add6de..91b583b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -1086,12 +1086,12 @@
}
private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
+ MyTicker(Context context, View sb) {
super(context, sb);
}
@Override
- void tickerStarting() {
+ public void tickerStarting() {
mTicking = true;
mIcons.setVisibility(View.GONE);
mTickerView.setVisibility(View.VISIBLE);
@@ -1103,7 +1103,7 @@
}
@Override
- void tickerDone() {
+ public void tickerDone() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
@@ -1114,7 +1114,7 @@
}
}
- void tickerHalting() {
+ public void tickerHalting() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 07e8653..e7b0509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -141,7 +141,7 @@
}
};
- Ticker(Context context, StatusBarView sb) {
+ public Ticker(Context context, View sb) {
mContext = context;
mTickerView = sb.findViewById(R.id.ticker);
@@ -163,7 +163,7 @@
}
- void addEntry(StatusBarNotification n) {
+ public void addEntry(StatusBarNotification n) {
int initialCount = mSegments.size();
// If what's being displayed has the same text and icon, just drop it
@@ -185,11 +185,11 @@
final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
// If there's already a notification schedule for this package and id, remove it.
- for (int i=0; i<initialCount; i++) {
+ for (int i=0; i<mSegments.size(); i++) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
// just update that one to use this new data instead
- mSegments.remove(i);
+ mSegments.remove(i--); // restart iteration here
}
}
@@ -212,7 +212,7 @@
}
}
- void removeEntry(StatusBarNotification n) {
+ public void removeEntry(StatusBarNotification n) {
for (int i=mSegments.size()-1; i>=0; i--) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
@@ -221,13 +221,13 @@
}
}
- void halt() {
+ public void halt() {
mHandler.removeCallbacks(mAdvanceTicker);
mSegments.clear();
tickerHalting();
}
- void reflowText() {
+ public void reflowText() {
if (mSegments.size() > 0) {
Segment seg = mSegments.get(0);
CharSequence text = seg.getText();
@@ -266,8 +266,8 @@
mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
}
- abstract void tickerStarting();
- abstract void tickerDone();
- abstract void tickerHalting();
+ public abstract void tickerStarting();
+ public abstract void tickerDone();
+ public abstract void tickerHalting();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
index 9749ae4..8140811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
@@ -34,5 +34,9 @@
super.onSizeChanged(w, h, oldw, oldh);
mTicker.reflowText();
}
+
+ public void setTicker(Ticker t) {
+ mTicker = t;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
index 3c7b130..7c7d74c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
@@ -31,24 +31,16 @@
public class NotificationIconArea extends LinearLayout {
private static final String TAG = "NotificationIconArea";
- MoreView mMoreView;
IconLayout mIconLayout;
DraggerView mDraggerView;
public NotificationIconArea(Context context, AttributeSet attrs) {
super(context, attrs);
- mMoreView = (MoreView) findViewById(R.id.more);
mIconLayout = (IconLayout)findViewById(R.id.icons);
mDraggerView = (DraggerView) findViewById(R.id.handle);
}
- static class MoreView extends ImageView {
- public MoreView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-
static class IconLayout extends LinearLayout {
public IconLayout(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
new file mode 100644
index 0000000..c864daa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -0,0 +1,309 @@
+/*
+ * 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.systemui.statusbar.tablet;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.media.AudioManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.systemui.statusbar.*;
+import com.android.systemui.R;
+
+public class SystemPanel extends LinearLayout {
+ private static final String TAG = "SystemPanel";
+
+ private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5;
+ private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+ private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
+
+
+ private TabletStatusBarService mBar;
+ private boolean mAirplaneMode;
+
+ private ImageButton mBrightnessButton;
+ private ImageButton mSoundButton;
+ private ImageButton mOrientationButton;
+ private ImageButton mAirplaneButton;
+
+ private ImageView mBatteryMeter;
+ private ImageView mSignalMeter;
+
+ private TextView mBatteryText;
+ private TextView mSignalText;
+
+ private final AudioManager mAudioManager;
+
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+ mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+ } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ // hack for now
+ mBar.updateBatteryDisplay(intent.getIntExtra("level", 0),
+ (intent.getIntExtra("plugged", 0) != 0));
+ }
+ }
+ };
+
+ public void setBar(TabletStatusBarService bar) {
+ mBar = bar;
+ }
+
+ public void setBatteryLevel(int level, boolean plugged) {
+ mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
+ mBatteryMeter.setImageLevel(level);
+ mBatteryText.setText(String.format("Battery: %d%%", level));
+ }
+
+ public SystemPanel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SystemPanel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // get notified of phone state changes
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ public void onAttachedToWindow() {
+ TextView settingsButton = (TextView)findViewById(R.id.settings_button);
+ settingsButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ getContext().startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mBar.animateCollapse();
+ }});
+
+ mBrightnessButton = (ImageButton)findViewById(R.id.brightness);
+ mBrightnessButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ rotateBrightness();
+ }
+ });
+
+ mSoundButton = (ImageButton)findViewById(R.id.sound);
+ mSoundButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ setSilentMode(!getSilentMode());
+ mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+ }
+ });
+ mOrientationButton = (ImageButton)findViewById(R.id.orientation);
+ mOrientationButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Toast.makeText(getContext(), "Orientation control not implemented; please adjust neck angle.", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ mAirplaneButton = (ImageButton)findViewById(R.id.airplane);
+ mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+ mAirplaneButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ boolean newMode = !getAirplaneMode();
+ Toast.makeText(getContext(), "Attempting to turn "
+ + (newMode ? "on" : "off") + " airplane mode (flaky).",
+ Toast.LENGTH_SHORT).show();
+ setAirplaneMode(newMode);
+ }
+ });
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ getContext().registerReceiver(mReceiver, filter);
+
+ mBatteryMeter = (ImageView)findViewById(R.id.battery_meter);
+ mBatteryMeter.setImageResource(R.drawable.battery);
+ mBatteryMeter.setImageLevel(0);
+ mSignalMeter = (ImageView)findViewById(R.id.signal_meter);
+ mBatteryMeter.setImageResource(R.drawable.signal);
+ mBatteryMeter.setImageLevel(0);
+
+ mBatteryText = (TextView)findViewById(R.id.battery_info);
+ mSignalText = (TextView)findViewById(R.id.signal_info);
+ }
+
+ public void onDetachedFromWindow() {
+ getContext().unregisterReceiver(mReceiver);
+ }
+
+ // ----------------------------------------------------------------------
+
+// private boolean isAutoBrightness() {
+// Context context = getContext();
+// try {
+// IPowerManager power = IPowerManager.Stub.asInterface(
+// ServiceManager.getService("power"));
+// if (power != null) {
+// int brightnessMode = Settings.System.getInt(context.getContentResolver(),
+// Settings.System.SCREEN_BRIGHTNESS_MODE);
+// return brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+// }
+// } catch (RemoteException e) {
+// } catch (Settings.SettingNotFoundException e) {
+// }
+// return false;
+// }
+
+ private void rotateBrightness() {
+ int alpha = 0xFF;
+ Context context = getContext();
+ try {
+ IPowerManager power = IPowerManager.Stub.asInterface(
+ ServiceManager.getService("power"));
+ if (power != null) {
+ ContentResolver cr = context.getContentResolver();
+ int brightness = Settings.System.getInt(cr,
+ Settings.System.SCREEN_BRIGHTNESS);
+ int brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ //Only get brightness setting if available
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)) {
+ brightnessMode = Settings.System.getInt(cr,
+ Settings.System.SCREEN_BRIGHTNESS_MODE);
+ }
+
+ // Rotate AUTO -> MINIMUM -> DEFAULT -> MAXIMUM
+ // Technically, not a toggle...
+ if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+ brightness = MINIMUM_BACKLIGHT;
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ alpha = 0x40;
+ } else if (brightness < DEFAULT_BACKLIGHT) {
+ brightness = DEFAULT_BACKLIGHT;
+ alpha = 0xC0;
+ } else if (brightness < MAXIMUM_BACKLIGHT) {
+ brightness = MAXIMUM_BACKLIGHT;
+ alpha = 0xFF;
+ } else {
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+ brightness = MINIMUM_BACKLIGHT;
+ alpha = 0x60;
+ }
+
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)) {
+ // Set screen brightness mode (automatic or manual)
+ Settings.System.putInt(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ brightnessMode);
+ } else {
+ // Make sure we set the brightness if automatic mode isn't available
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ }
+ if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
+ power.setBacklightBrightness(brightness);
+ Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness);
+ }
+ }
+ } catch (RemoteException e) {
+ } catch (Settings.SettingNotFoundException e) {
+ }
+
+ mBrightnessButton.setAlpha(alpha);
+ }
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+ mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
+ if (mAirplaneButton != null) {
+ mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+ }
+ }
+ };
+
+ private boolean getAirplaneMode() {
+ return mAirplaneMode;
+ }
+
+ private void setAirplaneMode(boolean on) {
+ Settings.System.putInt(
+ mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON,
+ on ? 1 : 0);
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("state", on);
+ getContext().sendBroadcast(intent);
+ }
+
+ boolean getSilentMode() {
+ return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ }
+
+ void setSilentMode(boolean on) {
+ if (on) {
+ mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.VIBRATE_IN_SILENT, 1) == 1)
+ ? AudioManager.RINGER_MODE_VIBRATE
+ : AudioManager.RINGER_MODE_SILENT);
+ } else {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index b1c4ee3..b0ffaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,30 +16,34 @@
package com.android.systemui.statusbar.tablet;
+import android.animation.Animator;
+import android.app.ActivityManagerNative;
import android.app.Notification;
+import android.app.PendingIntent;
import android.app.Service;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.widget.RemoteViews;
-import android.app.ActivityManagerNative;
-import android.app.PendingIntent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.graphics.Rect;
-import android.os.RemoteException;
import android.view.WindowManagerImpl;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
@@ -53,26 +57,40 @@
public static final boolean DEBUG = false;
public static final String TAG = "TabletStatusBar";
- View mStatusBarView;
- NotificationIconArea mNotificationIconArea;
+
int mIconSize;
H mHandler = new H();
+ // tracking all current notifications
+ private NotificationData mNotns = new NotificationData();
+
+ View mStatusBarView;
+ NotificationIconArea mNotificationIconArea;
+
View mNotificationPanel;
- View mSystemPanel;
+ SystemPanel mSystemPanel;
ViewGroup mPile;
TextView mClearButton;
+ ImageView mBatteryMeter;
+ ImageView mSignalMeter;
+
NotificationIconArea.IconLayout mIconLayout;
- private NotificationData mNotns = new NotificationData();
-
+ KickerController mKicker;
+ View mKickerView;
+ boolean mTicking;
+ boolean mExpandedVisible;
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
protected void addPanelWindows() {
mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
- mSystemPanel = View.inflate(this, R.layout.sysbar_panel_system, null);
+ mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
mNotificationPanel.setVisibility(View.GONE);
mSystemPanel.setVisibility(View.GONE);
@@ -86,8 +104,8 @@
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -98,12 +116,12 @@
WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
lp = new WindowManager.LayoutParams(
- 400, // ViewGroup.LayoutParams.WRAP_CONTENT,
- 200, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ 500, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -112,23 +130,12 @@
lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
WindowManagerImpl.getDefault().addView(mSystemPanel, lp);
-
- // Lorem ipsum, Dolores
- TextView tv = ((TextView) mSystemPanel.findViewById(R.id.systemPanelDummy));
- if (tv != null) tv.setText("System status: great");
-
- mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
- mPile.removeAllViews();
-
- mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
+ mSystemPanel.setBar(this);
}
@Override
public void onCreate() {
super.onCreate(); // will add the main bar view
-
- addPanelWindows();
}
protected View makeStatusBarView() {
@@ -145,6 +152,24 @@
// where the icons go
mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+ mKicker = new KickerController((Context)this, mStatusBarView);
+
+ // System info (center)
+ mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
+ mSignalMeter = (ImageView) sb.findViewById(R.id.signal);
+
+ // Add the windows
+ addPanelWindows();
+
+ mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
+ mPile.removeAllViews();
+
+ ScrollView scroller = (ScrollView)mPile.getParent();
+ scroller.setFillViewport(true);
+
+ mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+
return sb;
}
@@ -162,10 +187,12 @@
case MSG_OPEN_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "opening notifications panel");
mNotificationPanel.setVisibility(View.VISIBLE);
+ mExpandedVisible = true;
break;
case MSG_CLOSE_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "closing notifications panel");
mNotificationPanel.setVisibility(View.GONE);
+ mExpandedVisible = false;
break;
case MSG_OPEN_SYSTEM_PANEL:
if (DEBUG) Slog.d(TAG, "opening system panel");
@@ -178,25 +205,33 @@
}
}
}
+
+ StatusBarIcon mBatterySBI;
+ StatusBarIcon mSignalSBI;
+ public void updateBatteryDisplay(int level, boolean plugged) {
+ if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged"));
+ mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
+ mBatteryMeter.setImageLevel(level);
+ mSystemPanel.setBatteryLevel(level, plugged);
+ }
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
}
public void updateIcon(String slot, int index, int viewIndex,
StatusBarIcon old, StatusBarIcon icon) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
}
public void removeIcon(String slot, int index, int viewIndex) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
}
public void addNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
addNotificationViews(key, notification);
-
- // TODO: kicker; immersive mode
+ // tick()
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
@@ -274,7 +309,127 @@
}
public void disable(int state) {
- // TODO
+ /*
+ final int old = mDisabled;
+ final int diff = state ^ old;
+ mDisabled = state;
+
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ void performDisableActions(int net) {
+ /*
+ int old = mDisabled;
+ int diff = net ^ old;
+ mDisabled = net;
+
+ // act accordingly
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ private void tick(StatusBarNotification n) {
+ // Show the ticker if one is requested. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+ if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mKicker.addEntry(n);
+ }
+ }
+ }
+
+ private class KickerController {
+ View mView;
+ ImageView mKickerIcon;
+ TextSwitcher mKickerText;
+
+ public KickerController(Context context, View sb) {
+ mView = sb.findViewById(R.id.ticker);
+ mKickerIcon = (ImageView) mView.findViewById(R.id.tickerIcon);
+ mKickerText = (TextSwitcher) mView.findViewById(R.id.tickerText);
+ }
+
+ public void halt() {
+ tickerHalting();
+ }
+
+ public void addEntry(StatusBarNotification n) {
+ mKickerIcon.setImageResource(n.notification.icon);
+ mKickerText.setCurrentText(n.notification.tickerText);
+ tickerStarting();
+ }
+
+ public void tickerStarting() {
+ mTicking = true;
+ mIconLayout.setVisibility(View.GONE);
+ mKickerView.setVisibility(View.VISIBLE);
+ }
+
+ public void tickerDone() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
+
+ public void tickerHalting() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
}
public void animateExpand() {
@@ -405,8 +560,9 @@
}
StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
- NotificationData list = mNotns;
- ViewGroup parent = mPile;
+ if (DEBUG) {
+ Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+ }
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
@@ -422,19 +578,13 @@
}
// Construct the expanded view.
NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
- if (!inflateViews(entry, parent)) {
+ if (!inflateViews(entry, mPile)) {
handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ notification);
return null;
}
- // Add the expanded view.
- final int viewIndex = list.add(entry);
- if (parent != null) parent.addView(entry.row, viewIndex);
// Add the icon.
-// final int iconIndex = 0; // XXX: sort into ongoing and regular buckets
-// mIconLayout.addView(iconView, iconIndex,
-// new LinearLayout.LayoutParams(mIconSize, mIconSize));
-
+ mNotns.add(entry);
refreshIcons();
return iconView;
@@ -443,13 +593,25 @@
private void refreshIcons() {
// XXX: need to implement a new limited linear layout class
// to avoid removing & readding everything
- mIconLayout.removeAllViews();
+
int N = mNotns.size();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+
+ if (DEBUG) {
+ Slog.d(TAG, "refreshing icons (" + N + " notifications, mIconLayout="
+ + mIconLayout + ", mPile=" + mPile);
+ }
+
+ mIconLayout.removeAllViews();
for (int i=0; i<4; i++) {
if (i>=N) break;
mIconLayout.addView(mNotns.get(N-i-1).icon, i, params);
}
+
+ mPile.removeAllViews();
+ for (int i=0; i<N; i++) {
+ mPile.addView(mNotns.get(N-i-1).row);
+ }
}
private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index ba1d7f5..70a4b20 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -113,7 +113,15 @@
flags, PixelFormat.TRANSLUCENT);
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
- lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+
+ if (mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation)) {
+ Log.d(TAG, "Rotation sensor for lock screen On!");
+ lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
+ } else {
+ Log.d(TAG, "Rotation sensor for lock screen Off!");
+ lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ }
+
lp.setTitle("Keyguard");
mWindowLayoutParams = lp;
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 8693294..822be46 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -645,7 +645,9 @@
// Show LockScreen first for any screen other than Pattern unlock.
final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
== DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- if (isSecure() && usingLockPattern) {
+
+ boolean showSlidingTab = getResources().getBoolean(R.bool.config_enableSlidingTabFirst);
+ if (isSecure() && (usingLockPattern || !showSlidingTab)) {
return Mode.UnlockScreen;
} else {
return Mode.LockScreen;
@@ -688,7 +690,7 @@
private void showTimeoutDialog() {
int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
- int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;;
+ int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
if(getUnlockMode() == UnlockMode.Password) {
if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index b3707b0..f8c0aba 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -59,12 +59,7 @@
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardScreenCallback mCallback;
- private TextView mCarrier;
private SlidingTab mSelector;
- private TextView mTime;
- private TextView mDate;
- private TextView mStatus1;
- private TextView mStatus2;
private TextView mScreenLocked;
private TextView mEmergencyCallText;
private Button mEmergencyCallButton;
@@ -93,6 +88,8 @@
private java.text.DateFormat mTimeFormat;
private boolean mEnableMenuKeyInLockScreen;
+ private StatusView mStatusView;
+
/**
* The status of this lock screen.
*/
@@ -195,14 +192,7 @@
inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true);
}
- mCarrier = (TextView) findViewById(R.id.carrier);
- // Required for Marquee to work
- mCarrier.setSelected(true);
- mCarrier.setTextColor(0xffffffff);
-
- mDate = (TextView) findViewById(R.id.date);
- mStatus1 = (TextView) findViewById(R.id.status1);
- mStatus2 = (TextView) findViewById(R.id.status2);
+ mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
mScreenLocked = (TextView) findViewById(R.id.screenLocked);
mSelector = (SlidingTab) findViewById(R.id.tab_selector);
@@ -427,38 +417,11 @@
}
private void refreshTimeAndDateDisplay() {
- mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+ mStatusView.refreshTimeAndDateDisplay();
}
private void updateStatusLines() {
- if (!mStatus.showStatusLines()
- || (mCharging == null && mNextAlarm == null)) {
- mStatus1.setVisibility(View.INVISIBLE);
- mStatus2.setVisibility(View.INVISIBLE);
- } else if (mCharging != null && mNextAlarm == null) {
- // charging only
- mStatus1.setVisibility(View.VISIBLE);
- mStatus2.setVisibility(View.INVISIBLE);
-
- mStatus1.setText(mCharging);
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
- } else if (mNextAlarm != null && mCharging == null) {
- // next alarm only
- mStatus1.setVisibility(View.VISIBLE);
- mStatus2.setVisibility(View.INVISIBLE);
-
- mStatus1.setText(mNextAlarm);
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
- } else if (mCharging != null && mNextAlarm != null) {
- // both charging and next alarm
- mStatus1.setVisibility(View.VISIBLE);
- mStatus2.setVisibility(View.VISIBLE);
-
- mStatus1.setText(mCharging);
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
- mStatus2.setText(mNextAlarm);
- mStatus2.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
- }
+ mStatusView.updateStatusLines(mStatus.showStatusLines(), mCharging, mChargingIcon, mAlarmIcon);
}
/** {@inheritDoc} */
@@ -508,7 +471,7 @@
switch (status) {
case Normal:
// text
- mCarrier.setText(
+ mStatusView.setCarrierText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
mUpdateMonitor.getTelephonySpn()));
@@ -524,7 +487,7 @@
case NetworkLocked:
// The carrier string shows both sim card status (i.e. No Sim Card) and
// carrier's name and/or "Emergency Calls Only" status
- mCarrier.setText(
+ mStatusView.setCarrierText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
getContext().getText(R.string.lockscreen_network_locked_message)));
@@ -537,7 +500,7 @@
break;
case SimMissing:
// text
- mCarrier.setText(R.string.lockscreen_missing_sim_message_short);
+ mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short);
mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions);
// layout
@@ -548,7 +511,7 @@
break;
case SimMissingLocked:
// text
- mCarrier.setText(
+ mStatusView.setCarrierText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
getContext().getText(R.string.lockscreen_missing_sim_message_short)));
@@ -562,7 +525,7 @@
break;
case SimLocked:
// text
- mCarrier.setText(
+ mStatusView.setCarrierText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
getContext().getText(R.string.lockscreen_sim_locked_message)));
@@ -574,7 +537,7 @@
break;
case SimPukLocked:
// text
- mCarrier.setText(
+ mStatusView.setCarrierText(
getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
getContext().getText(R.string.lockscreen_sim_puk_locked_message)));
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index 60cd56c..9db86aa 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -30,6 +30,7 @@
import android.telephony.TelephonyManager;
import android.text.method.DigitsKeyListener;
import android.text.method.TextKeyListener;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -50,6 +51,7 @@
public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener {
+ private static final String TAG = "PasswordUnlockScreen";
private final KeyguardUpdateMonitor mUpdateMonitor;
private final KeyguardScreenCallback mCallback;
@@ -59,12 +61,15 @@
private Button mEmergencyCallButton;
private LockPatternUtils mLockPatternUtils;
private PasswordEntryKeyboardView mKeyboardView;
+ private PasswordEntryKeyboardView mKeyboardViewAlpha;
private PasswordEntryKeyboardHelper mKeyboardHelper;
+ private PasswordEntryKeyboardHelper mKeyboardHelperAlpha;
private int mCreationOrientation;
private int mCreationHardKeyboardHidden;
private CountDownTimer mCountdownTimer;
- private TextView mTitle;
+
+ private StatusView mStatusView;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
@@ -88,25 +93,43 @@
layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
}
+ mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
+
final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
+ mKeyboardViewAlpha = (PasswordEntryKeyboardView) findViewById(R.id.keyboardAlpha);
mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
mPasswordEntry.setOnEditorActionListener(this);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
mEmergencyCallButton.setOnClickListener(this);
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
- mTitle = (TextView) findViewById(R.id.enter_password_label);
- mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this);
- mKeyboardHelper.setKeyboardMode(mIsAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
- : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+ mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false);
+ if (mKeyboardViewAlpha == null || !mIsAlpha) {
+ mKeyboardHelper.setKeyboardMode(mIsAlpha ?
+ PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
+ : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+ mKeyboardView.setVisibility(
+ mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ ? View.INVISIBLE : View.VISIBLE);
+ } else {
+ mKeyboardHelperAlpha = new PasswordEntryKeyboardHelper(context, mKeyboardViewAlpha,
+ this, false);
+ mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+ mKeyboardHelperAlpha.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
+ mKeyboardView.setVisibility(View.GONE);
+ mKeyboardViewAlpha.setVisibility(
+ mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ ? View.INVISIBLE : View.VISIBLE);
+ mPasswordEntry.setWidth(mKeyboardViewAlpha.getLayoutParams().width);
+ }
- mKeyboardView.setVisibility(mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
- ? View.INVISIBLE : View.VISIBLE);
+ mPasswordEntry.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0,
+ 0, 0);
mPasswordEntry.requestFocus();
// This allows keyboards with overlapping qwerty/numeric keys to choose just the
@@ -115,11 +138,20 @@
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
} else {
mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mTitle.setText(R.string.keyguard_password_enter_pin_password_code);
+ mStatusView.setInstructionText(R.string.keyguard_password_enter_pin_password_code);
}
mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
com.android.internal.R.array.config_virtualKeyVibePattern : 0);
+ if (mKeyboardHelperAlpha != null) {
+ mKeyboardHelperAlpha.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
+ com.android.internal.R.array.config_virtualKeyVibePattern : 0);
+ }
+
+ // until we get an update...
+ mStatusView.setCarrierText(LockScreen.getCarrierString(
+ mUpdateMonitor.getTelephonyPlmn(),
+ mUpdateMonitor.getTelephonySpn()));
}
@Override
@@ -140,6 +172,9 @@
/** {@inheritDoc} */
public void onResume() {
+ // reset status
+ mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
+
// start fresh
mPasswordEntry.setText("");
resetStatusInfo();
@@ -179,9 +214,9 @@
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
- mTitle.setText(R.string.lockscreen_password_wrong);
+ mStatusView.setInstructionText(R.string.lockscreen_password_wrong);
} else if (entry.length() > 0) {
- mTitle.setText(R.string.lockscreen_password_wrong);
+ mStatusView.setInstructionText(R.string.lockscreen_password_wrong);
}
mPasswordEntry.setText("");
}
@@ -199,7 +234,7 @@
String instructions = getContext().getString(
R.string.lockscreen_too_many_failed_attempts_countdown,
secondsRemaining);
- mTitle.setText(instructions);
+ mStatusView.setInstructionText(instructions);
}
@Override
@@ -252,31 +287,40 @@
return false;
}
+ // ---------- InfoCallback
+
+ /** {@inheritDoc} */
+ public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+ mStatusView.onRefreshBatteryInfo(showBatteryInfo, pluggedIn, batteryLevel);
+ }
+
+ /** {@inheritDoc} */
+ public void onTimeChanged() {
+ mStatusView.onTimeChanged();
+ }
+
+ /** {@inheritDoc} */
+ public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+ mStatusView.onRefreshCarrierInfo(plmn, spn);
+ }
+
+ /** {@inheritDoc} */
+ public void onRingerModeChanged(int state) {
+ // not currently used
+ }
+
+ // ---------- SimStateCallback
+
+ /** {@inheritDoc} */
public void onPhoneStateChanged(String newState) {
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
}
- public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
-
- }
-
- public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
-
- }
-
- public void onRingerModeChanged(int state) {
-
- }
-
- public void onTimeChanged() {
-
- }
-
private void resetStatusInfo() {
if(mIsAlpha) {
- mTitle.setText(R.string.keyguard_password_enter_password_code);
+ mStatusView.setInstructionText(R.string.keyguard_password_enter_password_code);
} else {
- mTitle.setText(R.string.keyguard_password_enter_pin_password_code);
+ mStatusView.setInstructionText(R.string.keyguard_password_enter_pin_password_code);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 418e243..35fa3e5 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -25,9 +25,6 @@
import android.view.ViewGroup;
import android.view.MotionEvent;
import android.widget.Button;
-import android.widget.TextView;
-import android.text.format.DateFormat;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import com.android.internal.telephony.IccCard;
@@ -37,7 +34,6 @@
import com.android.internal.widget.LockPatternView.Cell;
import java.util.List;
-import java.util.Date;
/**
* This is the screen that shows the 9 circle unlock widget and instructs
@@ -75,27 +71,7 @@
*/
private boolean mEnableFallback;
- private String mDateFormatString;
-
- private TextView mCarrier;
- private TextView mDate;
-
- // are we showing battery information?
- private boolean mShowingBatteryInfo = false;
-
- // last known plugged in state
- private boolean mPluggedIn = false;
-
- // last known battery level
- private int mBatteryLevel = 100;
-
- private String mNextAlarm = null;
-
- private String mInstructions = null;
- private TextView mStatus1;
- private TextView mStatusSep;
- private TextView mStatus2;
-
+ private StatusView mStatusView;
private LockPatternView mLockPatternView;
@@ -133,15 +109,18 @@
private void updateFooter(FooterMode mode) {
switch (mode) {
case Normal:
+ Log.d(TAG, "mode normal");
mFooterNormal.setVisibility(View.VISIBLE);
mFooterForgotPattern.setVisibility(View.GONE);
break;
case ForgotLockPattern:
+ Log.d(TAG, "mode ForgotLockPattern");
mFooterNormal.setVisibility(View.GONE);
mFooterForgotPattern.setVisibility(View.VISIBLE);
mForgotPatternButton.setVisibility(View.VISIBLE);
break;
case VerifyUnlocked:
+ Log.d(TAG, "mode VerifyUnlocked");
mFooterNormal.setVisibility(View.GONE);
mFooterForgotPattern.setVisibility(View.GONE);
}
@@ -180,24 +159,16 @@
mCreationOrientation = configuration.orientation;
LayoutInflater inflater = LayoutInflater.from(context);
+
if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
+ Log.d(TAG, "portrait mode");
inflater.inflate(R.layout.keyguard_screen_unlock_portrait, this, true);
} else {
+ Log.d(TAG, "landscape mode");
inflater.inflate(R.layout.keyguard_screen_unlock_landscape, this, true);
}
- mCarrier = (TextView) findViewById(R.id.carrier);
- mDate = (TextView) findViewById(R.id.date);
-
- mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
- refreshTimeAndDateDisplay();
-
- mStatus1 = (TextView) findViewById(R.id.status1);
- mStatusSep = (TextView) findViewById(R.id.statusSep);
- mStatus2 = (TextView) findViewById(R.id.status2);
-
- resetStatusInfo();
-
+ mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
@@ -249,15 +220,11 @@
updateMonitor.registerSimStateCallback(this);
setFocusableInTouchMode(true);
- // Required to get Marquee to work.
- mCarrier.setSelected(true);
- mCarrier.setTextColor(0xffffffff);
-
// until we get an update...
- mCarrier.setText(
- LockScreen.getCarrierString(
+ mStatusView.setCarrierText(LockScreen.getCarrierString(
mUpdateMonitor.getTelephonyPlmn(),
mUpdateMonitor.getTelephonySpn()));
+
}
private void refreshEmergencyButtonText() {
@@ -270,88 +237,6 @@
mEnableFallback = state;
}
- private void resetStatusInfo() {
- mInstructions = null;
- mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo();
- mPluggedIn = mUpdateMonitor.isDevicePluggedIn();
- mBatteryLevel = mUpdateMonitor.getBatteryLevel();
- mNextAlarm = mLockPatternUtils.getNextAlarm();
- updateStatusLines();
- }
-
- private void updateStatusLines() {
- if (mInstructions != null) {
- // instructions only
- mStatus1.setText(mInstructions);
- if (TextUtils.isEmpty(mInstructions)) {
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- } else {
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(
- R.drawable.ic_lock_idle_lock, 0, 0, 0);
- }
-
- mStatus1.setVisibility(View.VISIBLE);
- mStatusSep.setVisibility(View.GONE);
- mStatus2.setVisibility(View.GONE);
- } else if (mShowingBatteryInfo && mNextAlarm == null) {
- // battery only
- if (mPluggedIn) {
- if (mBatteryLevel >= 100) {
- mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
- } else {
- mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel));
- }
- } else {
- mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
- }
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
-
- mStatus1.setVisibility(View.VISIBLE);
- mStatusSep.setVisibility(View.GONE);
- mStatus2.setVisibility(View.GONE);
-
- } else if (mNextAlarm != null && !mShowingBatteryInfo) {
- // alarm only
- mStatus1.setText(mNextAlarm);
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
-
- mStatus1.setVisibility(View.VISIBLE);
- mStatusSep.setVisibility(View.GONE);
- mStatus2.setVisibility(View.GONE);
- } else if (mNextAlarm != null && mShowingBatteryInfo) {
- // both battery and next alarm
- mStatus1.setText(mNextAlarm);
- mStatusSep.setText("|");
- mStatus2.setText(getContext().getString(
- R.string.lockscreen_battery_short,
- Math.min(100, mBatteryLevel)));
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
- if (mPluggedIn) {
- mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
- } else {
- mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
-
- mStatus1.setVisibility(View.VISIBLE);
- mStatusSep.setVisibility(View.VISIBLE);
- mStatus2.setVisibility(View.VISIBLE);
- } else {
- // nothing specific to show; show general instructions
- mStatus1.setText(R.string.lockscreen_pattern_instructions);
- mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
-
- mStatus1.setVisibility(View.VISIBLE);
- mStatusSep.setVisibility(View.GONE);
- mStatus2.setVisibility(View.GONE);
- }
- }
-
-
- private void refreshTimeAndDateDisplay() {
- mDate.setText(DateFormat.format(mDateFormatString, new Date()));
- }
-
-
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// as long as the user is entering a pattern (i.e sending a touch
@@ -366,25 +251,21 @@
return result;
}
-
// ---------- InfoCallback
/** {@inheritDoc} */
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
- mShowingBatteryInfo = showBatteryInfo;
- mPluggedIn = pluggedIn;
- mBatteryLevel = batteryLevel;
- updateStatusLines();
+ mStatusView.onRefreshBatteryInfo(showBatteryInfo, pluggedIn, batteryLevel);
}
/** {@inheritDoc} */
public void onTimeChanged() {
- refreshTimeAndDateDisplay();
+ mStatusView.onTimeChanged();
}
/** {@inheritDoc} */
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
- mCarrier.setText(LockScreen.getCarrierString(plmn, spn));
+ mStatusView.onRefreshCarrierInfo(plmn, spn);
}
/** {@inheritDoc} */
@@ -444,8 +325,8 @@
/** {@inheritDoc} */
public void onResume() {
- // reset header
- resetStatusInfo();
+ // reset status
+ mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
// reset lock pattern
mLockPatternView.enableInput();
@@ -514,8 +395,8 @@
if (mLockPatternUtils.checkPattern(pattern)) {
mLockPatternView
.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mInstructions = "";
- updateStatusLines();
+ mStatusView.setInstructions("");
+ mStatusView.updateStatusLines();
mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
} else {
@@ -533,8 +414,9 @@
handleAttemptLockout(deadline);
} else {
// TODO mUnlockIcon.setVisibility(View.VISIBLE);
- mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
- updateStatusLines();
+ mStatusView.setInstructions(
+ getContext().getString(R.string.lockscreen_pattern_wrong));
+ mStatusView.updateStatusLines();
mLockPatternView.postDelayed(
mCancelPatternRunnable,
PATTERN_CLEAR_TIMEOUT_MS);
@@ -552,17 +434,18 @@
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) (millisUntilFinished / 1000);
- mInstructions = getContext().getString(
+ mStatusView.setInstructions(getContext().getString(
R.string.lockscreen_too_many_failed_attempts_countdown,
- secondsRemaining);
- updateStatusLines();
+ secondsRemaining));
+ mStatusView.updateStatusLines();
}
@Override
public void onFinish() {
mLockPatternView.setEnabled(true);
- mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions);
- updateStatusLines();
+ mStatusView.setInstructions(getContext().getString(
+ R.string.lockscreen_pattern_instructions));
+ mStatusView.updateStatusLines();
// TODO mUnlockIcon.setVisibility(View.VISIBLE);
mFailedPatternAttemptsSinceLastTimeout = 0;
if (mEnableFallback) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 73fa93c..f59f32c 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -406,7 +406,12 @@
@Override
public final void openPanel(int featureId, KeyEvent event) {
- openPanel(getPanelState(featureId, true), event);
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.showOverflowMenu();
+ } else {
+ openPanel(getPanelState(featureId, true), event);
+ }
}
private void openPanel(PanelFeatureState st, KeyEvent event) {
@@ -497,7 +502,10 @@
@Override
public final void closePanel(int featureId) {
- if (featureId == FEATURE_CONTEXT_MENU) {
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.hideOverflowMenu();
+ } else if (featureId == FEATURE_CONTEXT_MENU) {
closeContextMenu();
} else {
closePanel(getPanelState(featureId, true), true);
@@ -591,7 +599,7 @@
// The panel key was pushed, so set the chording key
mPanelChordingKey = keyCode;
mPanelMayLongPress = false;
-
+
PanelFeatureState st = getPanelState(featureId, true);
if (!st.isOpen) {
if (getContext().getResources().getConfiguration().keyboard
@@ -600,7 +608,6 @@
}
return preparePanel(st, event);
}
-
} else if (mPanelMayLongPress && mPanelChordingKey == keyCode
&& (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) {
// We have had a long press while in a state where this
@@ -635,25 +642,38 @@
}
boolean playSoundEffect = false;
- PanelFeatureState st = getPanelState(featureId, true);
- if (st.isOpen || st.isHandled) {
+ final PanelFeatureState st = getPanelState(featureId, true);
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ if (!mActionBar.isOverflowMenuShowing()) {
+ final Callback cb = getCallback();
+ if (cb != null &&
+ cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
+ playSoundEffect = mActionBar.showOverflowMenu();
+ }
+ } else {
+ playSoundEffect = mActionBar.hideOverflowMenu();
+ }
+ } else {
+ if (st.isOpen || st.isHandled) {
- // Play the sound effect if the user closed an open menu (and not if
- // they just released a menu shortcut)
- playSoundEffect = st.isOpen;
+ // Play the sound effect if the user closed an open menu (and not if
+ // they just released a menu shortcut)
+ playSoundEffect = st.isOpen;
- // Close menu
- closePanel(st, true);
+ // Close menu
+ closePanel(st, true);
- } else if (st.isPrepared) {
+ } else if (st.isPrepared) {
- // Write 'menu opened' to event log
- EventLog.writeEvent(50001, 0);
+ // Write 'menu opened' to event log
+ EventLog.writeEvent(50001, 0);
- // Show menu
- openPanel(st, event);
+ // Show menu
+ openPanel(st, event);
- playSoundEffect = true;
+ playSoundEffect = true;
+ }
}
if (playSoundEffect) {
@@ -833,6 +853,21 @@
}
private void reopenMenu(boolean toggleMenuMode) {
+ if (mActionBar != null) {
+ if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
+ final Callback cb = getCallback();
+ if (cb != null) {
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
+ mActionBar.showOverflowMenu();
+ }
+ }
+ } else {
+ mActionBar.hideOverflowMenu();
+ }
+ return;
+ }
+
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
// Save the future expanded mode state since closePanel will reset it
@@ -1379,12 +1414,8 @@
}
case KeyEvent.KEYCODE_MENU: {
- if (mActionBar != null && mActionBar.isOverflowReserved()) {
- mActionBar.showOverflowMenu();
- } else {
- onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
- event);
- }
+ onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
+ event);
return true;
}
@@ -2239,6 +2270,10 @@
requestFeature(FEATURE_ACTION_BAR);
}
+ if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
+ requestFeature(FEATURE_ACTION_BAR_OVERLAY);
+ }
+
if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
requestFeature(FEATURE_ACTION_MODE_OVERLAY);
}
@@ -2325,7 +2360,11 @@
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
- layoutResource = com.android.internal.R.layout.screen_action_bar;
+ if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
+ layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
+ } else {
+ layoutResource = com.android.internal.R.layout.screen_action_bar;
+ }
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
@@ -2420,7 +2459,9 @@
// being called in the middle of onCreate or similar.
mDecor.post(new Runnable() {
public void run() {
- invalidatePanelMenu(FEATURE_ACTION_BAR);
+ if (!isDestroyed()) {
+ invalidatePanelMenu(FEATURE_ACTION_BAR);
+ }
}
});
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index cb7fe06..a07c385 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -37,6 +37,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.LocalPowerManager;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -48,15 +49,20 @@
import com.android.internal.policy.PolicyManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.view.BaseInputHandler;
import com.android.internal.widget.PointerLocationView;
import android.util.Config;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
+import android.view.InputChannel;
+import android.view.InputQueue;
+import android.view.InputHandler;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowOrientationListener;
@@ -221,6 +227,17 @@
int mPointerLocationMode = 0;
PointerLocationView mPointerLocationView = null;
+ InputChannel mPointerLocationInputChannel;
+
+ private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
+ @Override
+ public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+ synchronized (mLock) {
+ mPointerLocationView.addTouchEvent(event);
+ }
+ }
+ };
// The current size of the screen.
int mW, mH;
@@ -617,8 +634,26 @@
WindowManagerImpl wm = (WindowManagerImpl)
mContext.getSystemService(Context.WINDOW_SERVICE);
wm.addView(addView, lp);
+
+ if (mPointerLocationInputChannel == null) {
+ try {
+ mPointerLocationInputChannel =
+ mWindowManager.monitorInput("PointerLocationView");
+ InputQueue.registerInputChannel(mPointerLocationInputChannel,
+ mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
+ ex);
+ }
+ }
}
if (removeView != null) {
+ if (mPointerLocationInputChannel != null) {
+ InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
+ mPointerLocationInputChannel.dispose();
+ mPointerLocationInputChannel = null;
+ }
+
WindowManagerImpl wm = (WindowManagerImpl)
mContext.getSystemService(Context.WINDOW_SERVICE);
wm.removeView(removeView);
@@ -732,20 +767,6 @@
: Configuration.KEYBOARDHIDDEN_YES;
}
- public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
- if (mPointerLocationView == null) {
- return;
- }
- synchronized (mLock) {
- if (mPointerLocationView == null) {
- return;
- }
- ev.offsetLocation(targetX, targetY);
- mPointerLocationView.addTouchEvent(ev);
- ev.offsetLocation(-targetX, -targetY);
- }
- }
-
/** {@inheritDoc} */
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
diff --git a/policy/src/com/android/internal/policy/impl/StatusView.java b/policy/src/com/android/internal/policy/impl/StatusView.java
new file mode 100644
index 0000000..3f08cfd
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/StatusView.java
@@ -0,0 +1,255 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.internal.policy.impl;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.Date;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+class StatusView {
+ private String mDateFormatString;
+
+ private TextView mCarrier;
+ private TextView mDate;
+
+ // are we showing battery information?
+ private boolean mShowingBatteryInfo = false;
+
+ // last known plugged in state
+ private boolean mPluggedIn = false;
+
+ // last known battery level
+ private int mBatteryLevel = 100;
+
+ private String mNextAlarm = null;
+
+ private String mInstructions = null;
+ private TextView mStatus1;
+ private TextView mStatus2;
+ private TextView mPropertyOf;
+
+ private boolean mHasStatus2;
+ private boolean mHasCarrier;
+ private boolean mHasDate;
+ private boolean mHasProperty;
+
+ private View mView;
+
+ private View findViewById(int id) {
+ return mView.findViewById(id);
+ }
+
+ private Context getContext() {
+ return mView.getContext();
+ }
+
+ void setInstructions(String instructions) {
+ mInstructions = instructions;
+ }
+
+ void setCarrierText(CharSequence carrierText) {
+ if (mCarrier != null) {
+ mCarrier.setText(carrierText);
+ }
+ }
+
+ void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+ mShowingBatteryInfo = showBatteryInfo;
+ mPluggedIn = pluggedIn;
+ mBatteryLevel = batteryLevel;
+ updateStatusLines();
+ }
+
+ void onTimeChanged() {
+ refreshTimeAndDateDisplay();
+ }
+
+ public void onRingerModeChanged(int state) {
+ }
+
+ void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+ setCarrierText(LockScreen.getCarrierString(plmn, spn));
+ }
+
+ public StatusView(View view, KeyguardUpdateMonitor updateMonitor,
+ LockPatternUtils lockPatternUtils) {
+ mView = view;
+ mCarrier = (TextView) findViewById(R.id.carrier);
+ mHasCarrier = (mCarrier != null);
+ mDate = (TextView) findViewById(R.id.date);
+ mHasDate = (mDate != null);
+ mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
+
+ refreshTimeAndDateDisplay();
+
+ mStatus1 = (TextView) findViewById(R.id.status1);
+ mStatus2 = (TextView) findViewById(R.id.status2);
+ mHasStatus2 = (mStatus2 != null);
+ mPropertyOf = (TextView) findViewById(R.id.propertyOf);
+ mHasProperty = (mPropertyOf != null);
+
+ resetStatusInfo(updateMonitor, lockPatternUtils);
+
+ // Required to get Marquee to work.
+ if (mHasCarrier) {
+ mCarrier.setSelected(true);
+ mCarrier.setTextColor(0xffffffff);
+ }
+
+ }
+
+ void resetStatusInfo(KeyguardUpdateMonitor updateMonitor, LockPatternUtils lockPatternUtils) {
+ mInstructions = null;
+ mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
+ mPluggedIn = updateMonitor.isDevicePluggedIn();
+ mBatteryLevel = updateMonitor.getBatteryLevel();
+ mNextAlarm = lockPatternUtils.getNextAlarm();
+ updateStatusLines();
+ }
+
+ void setInstructionText(int stringId) {
+ mStatus1.setText(stringId);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
+ mStatus1.setVisibility(View.VISIBLE);
+ }
+
+ void setInstructionText(String string) {
+ mStatus1.setText(string);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
+ mStatus1.setVisibility(View.VISIBLE);
+ }
+
+ void setCarrierText(int stringId) {
+ mCarrier.setText(stringId);
+ }
+ void setCarrierText(String string) {
+ mCarrier.setText(string);
+ }
+
+ /** Originated from PatternUnlockScreen **/
+ void updateStatusLines() {
+ if (mHasProperty) {
+ // TODO Get actual name & email
+ String name = "John Smith";
+ String email = "jsmith@gmail.com";
+ mPropertyOf.setText("Property of:\n" + name + "\n" + email);
+ mPropertyOf.setVisibility(View.VISIBLE);
+ }
+
+ if (!mHasStatus2) return;
+
+ if (mInstructions != null) {
+ // instructions only
+ mStatus1.setText(mInstructions);
+ if (TextUtils.isEmpty(mInstructions)) {
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ } else {
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_lock_idle_lock, 0, 0, 0);
+ }
+
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+ } else if (mShowingBatteryInfo && mNextAlarm == null) {
+ // battery only
+ if (mPluggedIn) {
+ if (mBatteryLevel >= 100) {
+ mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
+ } else {
+ mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in,
+ mBatteryLevel));
+ }
+ } else {
+ mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
+ }
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0,
+ 0, 0);
+
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+
+ } else if (mNextAlarm != null && !mShowingBatteryInfo) {
+ // alarm only
+ mStatus1.setText(mNextAlarm);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0,
+ 0, 0);
+
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+ } else if (mNextAlarm != null && mShowingBatteryInfo) {
+ // both battery and next alarm
+ mStatus1.setText(mNextAlarm);
+ mStatus2.setText(getContext().getString(
+ R.string.lockscreen_battery_short,
+ Math.min(100, mBatteryLevel)));
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0,
+ 0, 0);
+ if (mPluggedIn) {
+ mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging,
+ 0, 0, 0);
+ } else {
+ mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ }
+
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.VISIBLE);
+ } else {
+ // nothing specific to show; show general instructions
+ mStatus1.setText(R.string.lockscreen_pattern_instructions);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0,
+ 0, 0);
+
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ /** Originated from LockScreen **/
+ // TODO Merge with function above
+ void updateStatusLines(boolean showStatusLines, String charging, Drawable chargingIcon,
+ Drawable alarmIcon) {
+ if (!showStatusLines || (charging == null && mNextAlarm == null)) {
+ mStatus1.setVisibility(View.INVISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+ } else if (charging != null && mNextAlarm == null) {
+ // charging only
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+
+ mStatus1.setText(charging);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(chargingIcon, null, null, null);
+ } else if (mNextAlarm != null && charging == null) {
+ // next alarm only
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.INVISIBLE);
+
+ mStatus1.setText(mNextAlarm);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(alarmIcon, null, null, null);
+ } else if (charging != null && mNextAlarm != null) {
+ // both charging and next alarm
+ mStatus1.setVisibility(View.VISIBLE);
+ mStatus2.setVisibility(View.VISIBLE);
+
+ mStatus1.setText(charging);
+ mStatus1.setCompoundDrawablesWithIntrinsicBounds(chargingIcon, null, null, null);
+ mStatus2.setText(mNextAlarm);
+ mStatus2.setCompoundDrawablesWithIntrinsicBounds(alarmIcon, null, null, null);
+ }
+ }
+
+ void refreshTimeAndDateDisplay() {
+ if (mHasDate) {
+ mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+ }
+ }
+
+}
diff --git a/preloaded-classes b/preloaded-classes
index 33dba37..3317286 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -138,7 +138,6 @@
android.database.MatrixCursor
android.database.sqlite.SQLiteClosable
android.database.sqlite.SQLiteCompiledSql
-android.database.sqlite.SQLiteContentHelper
android.database.sqlite.SQLiteCursor
android.database.sqlite.SQLiteDatabase
android.database.sqlite.SQLiteDebug
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 252b42a..6e7633e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1675,6 +1675,9 @@
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
+ if (masterMute) {
+ masterVolume = 0;
+ }
#ifdef LVMX
bool tracksConnectedChanged = false;
bool stateChanged = false;
@@ -1750,7 +1753,7 @@
// compute volume for this track
int16_t left, right, aux;
- if (track->isMuted() || masterMute || track->isPausing() ||
+ if (track->isMuted() || track->isPausing() ||
mStreamTypes[track->type()].mute) {
left = right = aux = 0;
if (track->isPausing()) {
@@ -5351,7 +5354,7 @@
return;
}
- if (mState == ACTIVE || mState == STOPPING || mState == STOPPED) {
+ if (mState == ACTIVE || mState == STOPPING || mState == STOPPED || mState == RESTART) {
// do 32 bit to 16 bit conversion for auxiliary effect input buffer
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
@@ -6032,8 +6035,8 @@
AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
int sessionId)
: mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false),
- mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0),
- mNewLeftVolume(0), mNewRightVolume(0)
+ mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 30597320..f825df9 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -425,7 +425,7 @@
}
}
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
if (appWidgetIds == null) {
return;
}
@@ -437,7 +437,24 @@
synchronized (mAppWidgetIds) {
for (int i=0; i<N; i++) {
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- notifyAppWidgetViewDataChangedInstanceLocked(id, views, viewId);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ for (int i=0; i<N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
}
}
}
@@ -459,11 +476,17 @@
}
void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- id.views = views;
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate) id.views = views;
// is anyone listening?
if (id.host.callbacks != null) {
@@ -479,18 +502,16 @@
}
}
- void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, RemoteViews views, int viewId) {
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- id.views = views;
-
// is anyone listening?
if (id.host.callbacks != null) {
try {
// the lock is held, but this is a oneway call
- id.host.callbacks.viewDataChanged(id.appWidgetId, views, viewId);
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
} catch (RemoteException e) {
// It failed; remove the callback. No need to prune because
// we know that this host is still referenced by this instance.
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index cc1566b..859353a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -313,21 +313,21 @@
switch (mNetAttributes[netType].mRadio) {
case ConnectivityManager.TYPE_WIFI:
if (DBG) Slog.v(TAG, "Starting Wifi Service.");
- WifiStateTracker wst = new WifiStateTracker(context, mHandler);
- WifiService wifiService = new WifiService(context, wst);
+ WifiStateTracker wst = new WifiStateTracker();
+ WifiService wifiService = new WifiService(context);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- wifiService.startWifi();
+ wifiService.checkAndStartWifi();
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
- wst.startMonitoring();
+ wst.startMonitoring(context, mHandler);
//TODO: as part of WWS refactor, create only when needed
- mWifiWatchdogService = new WifiWatchdogService(context, wst);
+ mWifiWatchdogService = new WifiWatchdogService(context);
break;
case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
- netType, mNetAttributes[netType].mName);
- mNetTrackers[netType].startMonitoring();
+ mNetTrackers[netType] = new MobileDataStateTracker(netType,
+ mNetAttributes[netType].mName);
+ mNetTrackers[netType].startMonitoring(context, mHandler);
if (noMobileData) {
if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting");
mNetTrackers[netType].teardown();
@@ -1205,28 +1205,6 @@
sendConnectedBroadcast(info);
}
- private void handleScanResultsAvailable(NetworkInfo info) {
- int networkType = info.getType();
- if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
- info.getTypeName() + " network. Don't know how to handle.");
- }
-
- mNetTrackers[networkType].interpretScanResultsAvailable();
- }
-
- private void handleNotificationChange(boolean visible, int id,
- Notification notification) {
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- notificationManager.notify(id, notification);
- } else {
- notificationManager.cancel(id);
- }
- }
-
/**
* After a change in the connectivity state of a network. We're mainly
* concerned with making sure that the list of DNS servers is set up
@@ -1618,30 +1596,12 @@
handleConnect(info);
}
break;
-
- case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
- info = (NetworkInfo) msg.obj;
- handleScanResultsAvailable(info);
- break;
-
- case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2,
- (Notification) msg.obj);
- break;
-
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
// TODO - make this handle ip/proxy/gateway/dns changes
info = (NetworkInfo) msg.obj;
type = info.getType();
handleDnsConfigurationChange(type);
break;
- case NetworkStateTracker.EVENT_ROAMING_CHANGED:
- // fill me in
- break;
-
- case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
- // fill me in
- break;
case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
FeatureUser u = (FeatureUser)msg.obj;
u.expire();
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 9195123..c2c799b 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -75,7 +75,8 @@
int sw);
private static native boolean nativeHasKeys(int deviceId, int sourceMask,
int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(InputChannel inputChannel);
+ private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+ boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
private static native int nativeInjectInputEvent(InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
@@ -225,14 +226,38 @@
return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
}
+ /**
+ * Creates an input channel that will receive all input from the input dispatcher.
+ * @param inputChannelName The input channel name.
+ * @return The input channel.
+ */
+ public InputChannel monitorInput(String inputChannelName) {
+ if (inputChannelName == null) {
+ throw new IllegalArgumentException("inputChannelName must not be null.");
+ }
+
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+ nativeRegisterInputChannel(inputChannels[0], true);
+ inputChannels[0].dispose(); // don't need to retain the Java object reference
+ return inputChannels[1];
+ }
+
+ /**
+ * Registers an input channel so that it can be used as an input event target.
+ * @param inputChannel The input channel to register.
+ */
public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
- nativeRegisterInputChannel(inputChannel);
+ nativeRegisterInputChannel(inputChannel, false);
}
+ /**
+ * Unregisters an input channel.
+ * @param inputChannel The input channel to unregister.
+ */
public void unregisterInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 36b3a5e..c61baad 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -77,9 +77,12 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
/**
* This class provides a system service that manages input methods.
@@ -463,6 +466,9 @@
screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
+ mStatusBar = statusBar;
+ statusBar.setIconVisibility("ime", false);
+
buildInputMethodListLocked(mMethodList, mMethodMap);
final String enabledStr = Settings.Secure.getString(
@@ -506,9 +512,6 @@
}
}
- mStatusBar = statusBar;
- statusBar.setIconVisibility("ime", false);
-
mSettingsObserver = new SettingsObserver(mHandler);
updateFromSettingsLocked();
}
@@ -1508,21 +1511,22 @@
hideInputMethodMenuLocked();
int N = immis.size();
-
- mItems = new CharSequence[N];
- mIms = new InputMethodInfo[N];
-
- int j = 0;
+
+ final Map<CharSequence, InputMethodInfo> imMap =
+ new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+
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++;
+ imMap.put(property.loadLabel(pm), property);
}
-
+
+ N = imMap.size();
+ mItems = imMap.keySet().toArray(new CharSequence[N]);
+ mIms = imMap.values().toArray(new InputMethodInfo[N]);
+
int checkedItem = 0;
for (int i = 0; i < N; ++i) {
if (mIms[i].getId().equals(lastInputMethodId)) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ef5e9cc..d604886 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,34 +16,42 @@
package com.android.server;
+import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.ObbInfo;
-import android.content.res.ObbScanner;
import android.net.Uri;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.StorageResultCode;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
+import android.os.storage.StorageResultCode;
import android.util.Slog;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
/**
* MountService implements back-end services for platform storage
@@ -137,9 +145,77 @@
final private HashSet<String> mAsecMountSet = new HashSet<String>();
/**
- * Private hash of currently mounted filesystem images.
+ * Mounted OBB tracking information. Used to track the current state of all
+ * OBBs.
*/
- final private HashSet<String> mObbMountSet = new HashSet<String>();
+ final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+ final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+
+ class ObbState implements IBinder.DeathRecipient {
+ public ObbState(String filename, IObbActionListener token, int callerUid) {
+ this.filename = filename;
+ this.token = token;
+ this.callerUid = callerUid;
+ mounted = false;
+ }
+
+ // OBB source filename
+ String filename;
+
+ // Token of remote Binder caller
+ IObbActionListener token;
+
+ // Binder.callingUid()
+ public int callerUid;
+
+ // Whether this is mounted currently.
+ boolean mounted;
+
+ @Override
+ public void binderDied() {
+ ObbAction action = new UnmountObbAction(this, true);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ removeObbState(this);
+
+ token.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
+ // OBB Action Handler
+ final private ObbActionHandler mObbActionHandler;
+
+ // OBB action handler messages
+ private static final int OBB_RUN_ACTION = 1;
+ private static final int OBB_MCS_BOUND = 2;
+ private static final int OBB_MCS_UNBIND = 3;
+ private static final int OBB_MCS_RECONNECT = 4;
+ private static final int OBB_MCS_GIVE_UP = 5;
+
+ /*
+ * Default Container Service information
+ */
+ static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
+
+ final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
+
+ class DefaultContainerConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceConnected");
+ IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceDisconnected");
+ }
+ };
+
+ // Used in the ObbActionHandler
+ private IMediaContainerService mContainerService = null;
// Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1;
@@ -363,7 +439,7 @@
public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
}
@@ -917,6 +993,9 @@
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ // Add OBB Action Handler to MountService thread.
+ mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -1013,7 +1092,7 @@
private void setUmsEnabling(boolean enable) {
synchronized (mListeners) {
- mUmsEnabling = true;
+ mUmsEnabling = enable;
}
}
@@ -1351,12 +1430,16 @@
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
- private boolean isCallerOwnerOfPackage(String packageName) {
+ private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
final int callerUid = Binder.getCallingUid();
- return isUidOwnerOfPackage(packageName, callerUid);
+ return isUidOwnerOfPackageOrSystem(packageName, callerUid);
}
- private boolean isUidOwnerOfPackage(String packageName, int callerUid) {
+ private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
+ if (callerUid == android.os.Process.SYSTEM_UID) {
+ return true;
+ }
+
if (packageName == null) {
return false;
}
@@ -1375,12 +1458,6 @@
waitForReady();
warnOnNotMounted();
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
- }
-
try {
ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
String []tok = rsp.get(0).split(" ");
@@ -1392,7 +1469,7 @@
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
- throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
+ return null;
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -1400,95 +1477,390 @@
}
public boolean isObbMounted(String filename) {
- waitForReady();
- warnOnNotMounted();
-
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
- }
-
- synchronized (mObbMountSet) {
- return mObbMountSet.contains(filename);
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
}
}
- public int mountObb(String filename, String key) {
+ public void mountObb(String filename, String key, IObbActionListener token) {
waitForReady();
warnOnNotMounted();
- synchronized (mObbMountSet) {
- if (mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageMounted;
+ final ObbState obbState;
+
+ synchronized (mObbMounts) {
+ if (isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB file is already mounted");
}
+
+ if (mObbMounts.containsKey(token)) {
+ throw new IllegalArgumentException("You may only have one OBB mounted at a time");
+ }
+
+ final int callerUid = Binder.getCallingUid();
+ obbState = new ObbState(filename, token, callerUid);
+ addObbState(obbState);
}
- final int callerUid = Binder.getCallingUid();
-
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
- }
-
- if (key == null) {
- key = "none";
- }
-
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
+ token.asBinder().linkToDeath(obbState, 0);
+ } catch (RemoteException rex) {
+ Slog.e(TAG, "Failed to link to listener death");
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mObbMountSet) {
- mObbMountSet.add(filename);
- }
- }
- return rc;
+ MountObbAction action = new MountObbAction(obbState, key);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- public int unmountObb(String filename, boolean force) {
- waitForReady();
- warnOnNotMounted();
+ public void unmountObb(String filename, boolean force, IObbActionListener token) {
+ final ObbState obbState;
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ synchronized (mObbMounts) {
+ if (!isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB is not mounted");
+ }
+ obbState = mObbPathToStateMap.get(filename);
}
- synchronized (mObbMountSet) {
- if (!mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageNotMounted;
- }
- }
+ UnmountObbAction action = new UnmountObbAction(obbState, force);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
+
+ private void addObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.put(obbState.token, obbState);
+ mObbPathToStateMap.put(obbState.filename, obbState);
+ }
+ }
+
+ private void removeObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.remove(obbState.token);
+ mObbPathToStateMap.remove(obbState.filename);
+ }
+ }
+
+ private class ObbActionHandler extends Handler {
+ private boolean mBound = false;
+ private List<ObbAction> mActions = new LinkedList<ObbAction>();
+
+ ObbActionHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBB_RUN_ACTION: {
+ ObbAction action = (ObbAction) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
+
+ // If a bind was already initiated we don't really
+ // need to do anything. The pending install
+ // will be processed later on.
+ if (!mBound) {
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ action.handleError();
+ return;
+ } else {
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mActions.add(action);
+ }
+ } else {
+ // Already bound to the service. Just make
+ // sure we trigger off processing the first request.
+ if (mActions.size() == 0) {
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+
+ mActions.add(action);
+ }
+ break;
+ }
+ case OBB_MCS_BOUND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_BOUND");
+ if (msg.obj != null) {
+ mContainerService = (IMediaContainerService) msg.obj;
+ }
+ if (mContainerService == null) {
+ // Something seriously wrong. Bail out
+ Slog.e(TAG, "Cannot bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ } else if (mActions.size() > 0) {
+ ObbAction action = mActions.get(0);
+ if (action != null) {
+ action.execute(this);
+ }
+ } else {
+ // Should never happen ideally.
+ Slog.w(TAG, "Empty queue");
+ }
+ break;
+ }
+ case OBB_MCS_RECONNECT: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_RECONNECT");
+ if (mActions.size() > 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ }
+ }
+ break;
+ }
+ case OBB_MCS_UNBIND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_UNBIND");
+
+ // Delete pending install
+ if (mActions.size() > 0) {
+ mActions.remove(0);
+ }
+ if (mActions.size() == 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ } else {
+ // There are more pending requests in queue.
+ // Just post MCS_BOUND message to trigger processing
+ // of next pending install.
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+ break;
+ }
+ case OBB_MCS_GIVE_UP: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_GIVE_UP");
+ mActions.remove(0);
+ break;
+ }
+ }
+ }
+
+ private boolean connectToService() {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Trying to bind to DefaultContainerService");
+
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
+ mBound = true;
+ return true;
+ }
+ return false;
+ }
+
+ private void disconnectService() {
+ mContainerService = null;
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+
+ abstract class ObbAction {
+ private static final int MAX_RETRIES = 3;
+ private int mRetries;
+
+ ObbState mObbState;
+
+ ObbAction(ObbState obbState) {
+ mObbState = obbState;
+ }
+
+ public void execute(ObbActionHandler handler) {
+ try {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Starting to execute action: " + this.toString());
+ mRetries++;
+ if (mRetries > MAX_RETRIES) {
+ Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
+ handleError();
+ return;
+ } else {
+ handleExecute();
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_UNBIND");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_RECONNECT");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
+ } catch (Exception e) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Error handling OBB action", e);
+ handleError();
+ }
+ }
+
+ abstract void handleExecute() throws RemoteException;
+ abstract void handleError();
+ }
+
+ class MountObbAction extends ObbAction {
+ private String mKey;
+
+ MountObbAction(ObbState obbState, String key) {
+ super(obbState);
+ mKey = key;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ if (mKey == null) {
+ mKey = "none";
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
+ mObbState.callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "mounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
} else {
- rc = StorageResultCode.OperationFailedInternalError;
+ Slog.e(TAG, "Couldn't mount OBB file");
+
+ // We didn't succeed, so remove this from the mount-set.
+ removeObbState(mObbState);
}
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mObbMountSet) {
- mObbMountSet.remove(filename);
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
}
}
- return rc;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ class UnmountObbAction extends ObbAction {
+ private boolean mForceUnmount;
+
+ UnmountObbAction(ObbState obbState, boolean force) {
+ super(obbState);
+ mForceUnmount = force;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+
+ if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "unmounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ } else {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ }
+ }
+
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("UnmountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename != null ? mObbState.filename : "null");
+ sb.append(",force=");
+ sb.append(mForceUnmount);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+ sb.append('}');
+ return sb.toString();
+ }
}
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 67796c6..9d262b6 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -130,11 +130,11 @@
private boolean mBatteryFull;
private NotificationRecord mLedNotification;
- private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
- private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
- private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
- private static final int BATTERY_BLINK_ON = 125;
- private static final int BATTERY_BLINK_OFF = 2875;
+ private static int mBatteryLowARGB;
+ private static int mBatteryMediumARGB;
+ private static int mBatteryFullARGB;
+ private static int mBatteryLedOn;
+ private static int mBatteryLedOff;
private static String idDebugString(Context baseContext, String packageName, int id) {
Context c = null;
@@ -453,6 +453,17 @@
mDefaultNotificationLedOff = resources.getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
+ mBatteryLowARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+ mBatteryMediumARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+ mBatteryFullARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ mBatteryLedOn = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOn);
+ mBatteryLedOff = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOff);
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -1071,17 +1082,17 @@
// Battery low always shows, other states only show if charging.
if (mBatteryLow) {
if (mBatteryCharging) {
- mBatteryLight.setColor(BATTERY_LOW_ARGB);
+ mBatteryLight.setColor(mBatteryLowARGB);
} else {
// Flash when battery is low and not charging
- mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
- BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
}
} else if (mBatteryCharging) {
if (mBatteryFull) {
- mBatteryLight.setColor(BATTERY_FULL_ARGB);
+ mBatteryLight.setColor(mBatteryFullARGB);
} else {
- mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
+ mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
mBatteryLight.turnOff();
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2fb481c..4ee89cc 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -151,6 +151,7 @@
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
private final int MY_UID;
+ private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;
@@ -309,7 +310,7 @@
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, mTag);
+ MY_UID, MY_PID, mTag);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -434,11 +435,11 @@
}
}
- PowerManagerService()
- {
+ PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
- MY_UID = Binder.getCallingUid();
+ MY_UID = Process.myUid();
+ MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
// XXX remove this when the kernel doesn't timeout wake locks
@@ -573,13 +574,13 @@
private class WakeLock implements IBinder.DeathRecipient
{
- WakeLock(int f, IBinder b, String t, int u) {
+ WakeLock(int f, IBinder b, String t, int u, int p) {
super();
flags = f;
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = Binder.getCallingPid();
+ pid = p;
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -631,21 +632,23 @@
public void acquireWakeLock(int flags, IBinder lock, String tag) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
int acquireUid = -1;
+ int acquirePid = -1;
String acquireName = null;
int acquireType = -1;
@@ -657,7 +660,7 @@
WakeLock wl;
boolean newlock;
if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid);
+ wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
@@ -730,13 +733,14 @@
}
if (newlock) {
acquireUid = wl.uid;
+ acquirePid = wl.pid;
acquireName = wl.tag;
acquireType = wl.monitorType;
}
if (acquireType >= 0) {
try {
- mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+ mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
} catch (RemoteException e) {
// Ignore
}
@@ -756,6 +760,7 @@
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
int releaseUid;
+ int releasePid;
String releaseName;
int releaseType;
@@ -800,13 +805,14 @@
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
releaseUid = wl.uid;
+ releasePid = wl.pid;
releaseName = wl.tag;
releaseType = wl.monitorType;
if (releaseType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+ mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
} catch (RemoteException e) {
// Ignore
} finally {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 15080b2..064d47f 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -17,6 +17,8 @@
package com.android.server;
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
@@ -26,25 +28,30 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
+import android.net.wifi.WifiStateMachine;
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;
import android.net.DhcpInfo;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.os.Binder;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
@@ -72,7 +79,7 @@
private static final String TAG = "WifiService";
private static final boolean DBG = true;
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
private Context mContext;
@@ -123,10 +130,63 @@
private boolean mIsReceiverRegistered = false;
- WifiService(Context context, WifiStateTracker tracker) {
+
+ NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+
+ // Variables relating to the 'available networks' notification
+ /**
+ * The icon to show in the 'available networks' notification. This will also
+ * be the ID of the Notification given to the NotificationManager.
+ */
+ private static final int ICON_NETWORKS_AVAILABLE =
+ com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ /**
+ * When a notification is shown, we wait this amount before possibly showing it again.
+ */
+ private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /**
+ * Whether the user has set the setting to show the 'available networks' notification.
+ */
+ private boolean mNotificationEnabled;
+ /**
+ * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+ */
+ private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+ /**
+ * The {@link System#currentTimeMillis()} must be at least this value for us
+ * to show the notification again.
+ */
+ private long mNotificationRepeatTime;
+ /**
+ * The Notification object given to the NotificationManager.
+ */
+ private Notification mNotification;
+ /**
+ * Whether the notification is being shown, as set by us. That is, if the
+ * user cancels the notification, we will not receive the callback so this
+ * will still be true. We only guarantee if this is false, then the
+ * notification is not showing.
+ */
+ private boolean mNotificationShown;
+ /**
+ * The number of continuous scans that must occur before consider the
+ * supplicant in a scanning state. This allows supplicant to associate with
+ * remembered networks that are in the scan results.
+ */
+ private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+ /**
+ * The number of scans since the last network state change. When this
+ * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+ * supplicant to actually be scanning. When the network state changes to
+ * something other than scanning, we reset this to 0.
+ */
+ private int mNumScansSinceNetworkStateChange;
+
+
+ WifiService(Context context) {
mContext = context;
- mWifiStateTracker = tracker;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine = new WifiStateMachine(mContext);
+ mWifiStateMachine.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -164,6 +224,42 @@
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ // reset & clear notification on any wifi state change
+ resetNotification();
+ } else if (intent.getAction().equals(
+ WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ // reset & clear notification on a network connect & disconnect
+ switch(mNetworkInfo.getDetailedState()) {
+ case CONNECTED:
+ case DISCONNECTED:
+ resetNotification();
+ break;
+ }
+ } else if (intent.getAction().equals(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ checkAndSetNotification();
+ }
+ }
+ }, filter);
+
+ // Setting is in seconds
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+ mNotificationEnabledSettingObserver.register();
}
/**
@@ -172,7 +268,7 @@
*
* This function is used only at boot time
*/
- public void startWifi() {
+ public void checkAndStartWifi() {
/* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
boolean wifiEnabled = !isAirplaneModeOn()
&& (getPersistedWifiEnabled() || testAndClearWifiSavedState());
@@ -255,17 +351,13 @@
Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
}
- NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
/**
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean pingSupplicant() {
enforceAccessPermission();
- return mWifiStateTracker.pingSupplicant();
+ return mWifiStateMachine.pingSupplicant();
}
/**
@@ -274,7 +366,7 @@
*/
public boolean startScan(boolean forceActive) {
enforceChangePermission();
- return mWifiStateTracker.startScan(forceActive);
+ return mWifiStateMachine.startScan(forceActive);
}
private void enforceAccessPermission() {
@@ -304,7 +396,7 @@
enforceChangePermission();
if (DBG) {
- Slog.e(TAG, "Invoking mWifiStateTracker.setWifiEnabled\n");
+ Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
}
// set a flag if the user is enabling Wifi while in airplane mode
@@ -312,7 +404,7 @@
mAirplaneModeOverwridden.set(true);
}
- mWifiStateTracker.setWifiEnabled(enable);
+ mWifiStateMachine.setWifiEnabled(enable);
persistWifiEnabled(enable);
if (enable) {
@@ -338,7 +430,7 @@
*/
public int getWifiEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiState();
+ return mWifiStateMachine.getWifiState();
}
/**
@@ -362,7 +454,7 @@
setWifiApConfiguration(wifiConfig);
}
- mWifiStateTracker.setWifiApEnabled(wifiConfig, enabled);
+ mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
return true;
}
@@ -377,7 +469,7 @@
*/
public int getWifiApEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiApState();
+ return mWifiStateMachine.getWifiApState();
}
/**
@@ -427,7 +519,7 @@
*/
public boolean disconnect() {
enforceChangePermission();
- return mWifiStateTracker.disconnectCommand();
+ return mWifiStateMachine.disconnectCommand();
}
/**
@@ -436,7 +528,7 @@
*/
public boolean reconnect() {
enforceChangePermission();
- return mWifiStateTracker.reconnectCommand();
+ return mWifiStateMachine.reconnectCommand();
}
/**
@@ -445,7 +537,7 @@
*/
public boolean reassociate() {
enforceChangePermission();
- return mWifiStateTracker.reassociateCommand();
+ return mWifiStateMachine.reassociateCommand();
}
/**
@@ -454,7 +546,7 @@
*/
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
- return mWifiStateTracker.getConfiguredNetworks();
+ return mWifiStateMachine.getConfiguredNetworks();
}
/**
@@ -464,7 +556,7 @@
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
- return mWifiStateTracker.addOrUpdateNetwork(config);
+ return mWifiStateMachine.addOrUpdateNetwork(config);
}
/**
@@ -475,7 +567,7 @@
*/
public boolean removeNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.removeNetwork(netId);
+ return mWifiStateMachine.removeNetwork(netId);
}
/**
@@ -487,7 +579,7 @@
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- return mWifiStateTracker.enableNetwork(netId, disableOthers);
+ return mWifiStateMachine.enableNetwork(netId, disableOthers);
}
/**
@@ -498,7 +590,7 @@
*/
public boolean disableNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.disableNetwork(netId);
+ return mWifiStateMachine.disableNetwork(netId);
}
/**
@@ -511,7 +603,7 @@
* Make sure we have the latest information, by sending
* a status request to the supplicant.
*/
- return mWifiStateTracker.requestConnectionInfo();
+ return mWifiStateMachine.requestConnectionInfo();
}
/**
@@ -521,7 +613,7 @@
*/
public List<ScanResult> getScanResults() {
enforceAccessPermission();
- return mWifiStateTracker.getScanResultsList();
+ return mWifiStateMachine.getScanResultsList();
}
/**
@@ -533,7 +625,7 @@
public boolean saveConfiguration() {
boolean result = true;
enforceChangePermission();
- return mWifiStateTracker.saveConfig();
+ return mWifiStateMachine.saveConfig();
}
/**
@@ -576,7 +668,7 @@
numChannels);
}
- mWifiStateTracker.setNumAllowedChannels(numChannels);
+ mWifiStateMachine.setNumAllowedChannels(numChannels);
return true;
}
@@ -596,7 +688,7 @@
* Wi-Fi is not currently enabled), get the value from
* Settings.
*/
- numChannels = mWifiStateTracker.getNumAllowedChannels();
+ numChannels = mWifiStateMachine.getNumAllowedChannels();
if (numChannels < 0) {
numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
@@ -622,7 +714,76 @@
*/
public DhcpInfo getDhcpInfo() {
enforceAccessPermission();
- return mWifiStateTracker.getDhcpInfo();
+ return mWifiStateMachine.getDhcpInfo();
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#startWifi}
+ *
+ */
+ public void startWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a start issued, keep wifi alive until a stop issued irrespective
+ * of WifiLock & device idle status unless wifi enabled status is toggled
+ */
+
+ mWifiStateMachine.setDriverStart(true);
+ mWifiStateMachine.reconnectCommand();
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#stopWifi}
+ *
+ */
+ public void stopWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a stop is issued, wifi is brought up only by startWifi
+ * unless wifi enabled status is toggled
+ */
+ mWifiStateMachine.setDriverStart(false);
+ }
+
+
+ /**
+ * see {@link android.net.wifi.WifiManager#addToBlacklist}
+ *
+ */
+ public void addToBlacklist(String bssid) {
+ enforceChangePermission();
+
+ mWifiStateMachine.addToBlacklist(bssid);
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#clearBlacklist}
+ *
+ */
+ public void clearBlacklist() {
+ enforceChangePermission();
+
+ mWifiStateMachine.clearBlacklist();
+ }
+
+ public void connectNetworkWithId(int networkId) {
+ enforceChangePermission();
+ mWifiStateMachine.connectNetwork(networkId);
+ }
+
+ public void connectNetworkWithConfig(WifiConfiguration config) {
+ enforceChangePermission();
+ mWifiStateMachine.connectNetwork(config);
+ }
+
+ public void saveNetwork(WifiConfiguration config) {
+ enforceChangePermission();
+ mWifiStateMachine.saveNetwork(config);
+ }
+
+ public void forgetNetwork(int netId) {
+ enforceChangePermission();
+ mWifiStateMachine.forgetNetwork(netId);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -641,11 +802,11 @@
mAlarmManager.cancel(mIdleIntent);
mDeviceIdle = false;
mScreenOff = false;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine.enableRssiPolling(true);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
- mWifiStateTracker.enableRssiPolling(false);
+ mWifiStateMachine.enableRssiPolling(false);
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
* AND the "stay on while plugged in" setting doesn't match the
@@ -653,7 +814,7 @@
* or plugged in to AC).
*/
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
- WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+ WifiInfo info = mWifiStateMachine.requestConnectionInfo();
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
// we used to go to sleep immediately, but this caused some race conditions
// we don't have time to track down for this release. Delay instead,
@@ -704,7 +865,7 @@
isBluetoothPlaying = true;
}
}
- mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
+ mWifiStateMachine.setBluetoothScanMode(isBluetoothPlaying);
} else {
return;
@@ -771,21 +932,21 @@
/* Disable tethering when airplane mode is enabled */
if (airplaneMode) {
- mWifiStateTracker.setWifiApEnabled(null, false);
+ mWifiStateMachine.setWifiApEnabled(null, false);
}
if (wifiShouldBeEnabled) {
if (wifiShouldBeStarted) {
- mWifiStateTracker.setWifiEnabled(true);
- mWifiStateTracker.setScanOnlyMode(
+ mWifiStateMachine.setWifiEnabled(true);
+ mWifiStateMachine.setScanOnlyMode(
strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
- mWifiStateTracker.startWifi(true);
+ mWifiStateMachine.setDriverStart(true);
} else {
- mWifiStateTracker.requestCmWakeLock();
- mWifiStateTracker.disconnectAndStop();
+ mWifiStateMachine.requestCmWakeLock();
+ mWifiStateMachine.setDriverStart(false);
}
} else {
- mWifiStateTracker.setWifiEnabled(false);
+ mWifiStateMachine.setWifiEnabled(false);
}
}
@@ -832,17 +993,17 @@
+ ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("Wi-Fi is " + mWifiStateTracker.getWifiStateByName());
+ pw.println("Wi-Fi is " + mWifiStateMachine.getWifiStateByName());
pw.println("Stay-awake conditions: " +
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println();
pw.println("Internal state:");
- pw.println(mWifiStateTracker);
+ pw.println(mWifiStateMachine);
pw.println();
pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
if (scanResults != null && scanResults.size() != 0) {
pw.println(" BSSID Frequency RSSI Flags SSID");
for (ScanResult r : scanResults) {
@@ -1069,7 +1230,7 @@
if (mMulticasters.size() != 0) {
return;
} else {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
}
}
@@ -1084,7 +1245,7 @@
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- mWifiStateTracker.stopPacketFiltering();
+ mWifiStateMachine.stopPacketFiltering();
}
int uid = Binder.getCallingUid();
@@ -1121,7 +1282,7 @@
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
Long ident = Binder.clearCallingIdentity();
@@ -1140,4 +1301,144 @@
return (mMulticasters.size() > 0);
}
}
+
+ private void checkAndSetNotification() {
+ // If we shouldn't place a notification on available networks, then
+ // don't bother doing any of the following
+ if (!mNotificationEnabled) return;
+
+ State state = mNetworkInfo.getState();
+ if ((state == NetworkInfo.State.DISCONNECTED)
+ || (state == NetworkInfo.State.UNKNOWN)) {
+ // Look for an open network
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
+ if (scanResults != null) {
+ int numOpenNetworks = 0;
+ for (int i = scanResults.size() - 1; i >= 0; i--) {
+ ScanResult scanResult = scanResults.get(i);
+
+ if (TextUtils.isEmpty(scanResult.capabilities)) {
+ numOpenNetworks++;
+ }
+ }
+
+ if (numOpenNetworks > 0) {
+ if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+ /*
+ * We've scanned continuously at least
+ * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+ * probably does not have a remembered network in range,
+ * since otherwise supplicant would have tried to
+ * associate and thus resetting this counter.
+ */
+ setNotificationVisible(true, numOpenNetworks, false, 0);
+ }
+ return;
+ }
+ }
+ }
+
+ // No open networks in range, remove the notification
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Clears variables related to tracking whether a notification has been
+ * shown recently and clears the current notification.
+ */
+ private void resetNotification() {
+ mNotificationRepeatTime = 0;
+ mNumScansSinceNetworkStateChange = 0;
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Display or don't display a notification that there are open Wi-Fi networks.
+ * @param visible {@code true} if notification should be visible, {@code false} otherwise
+ * @param numNetworks the number networks seen
+ * @param force {@code true} to force notification to be shown/not-shown,
+ * even if it is already shown/not-shown.
+ * @param delay time in milliseconds after which the notification should be made
+ * visible or invisible.
+ */
+ private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+ int delay) {
+
+ // Since we use auto cancel on the notification, when the
+ // mNetworksAvailableNotificationShown is true, the notification may
+ // have actually been canceled. However, when it is false we know
+ // for sure that it is not being shown (it will not be shown any other
+ // place than here)
+
+ // If it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown && !force) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Message message;
+ if (visible) {
+
+ // Not enough time has passed to show the notification again
+ if (System.currentTimeMillis() < mNotificationRepeatTime) {
+ return;
+ }
+
+ if (mNotification == null) {
+ // Cache the Notification object.
+ mNotification = new Notification();
+ mNotification.when = 0;
+ mNotification.icon = ICON_NETWORKS_AVAILABLE;
+ mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+ }
+
+ CharSequence title = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available, numNetworks);
+ CharSequence details = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+ mNotification.tickerText = title;
+ mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+ mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+ notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
+ } else {
+ notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
+ }
+
+ mNotificationShown = visible;
+ }
+
+ private class NotificationEnabledSettingObserver extends ContentObserver {
+
+ public NotificationEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ mNotificationEnabled = getValue();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mNotificationEnabled = getValue();
+ resetNotification();
+ }
+
+ private boolean getValue() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ }
+ }
+
+
}
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index be14cd3..46d6bef 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -27,7 +27,6 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -77,7 +76,6 @@
private Context mContext;
private ContentResolver mContentResolver;
- private WifiStateTracker mWifiStateTracker;
private WifiManager mWifiManager;
/**
@@ -108,10 +106,9 @@
/** Whether the current AP check should be canceled. */
private boolean mShouldCancel;
- WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) {
+ WifiWatchdogService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
- mWifiStateTracker = wifiStateTracker;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
createThread();
@@ -752,7 +749,7 @@
// Black list this "bad" AP, this will cause an attempt to connect to another
blacklistAp(ap.bssid);
// Initiate an association to an alternate AP
- mWifiStateTracker.reassociateCommand();
+ mWifiManager.reassociate();
}
private void blacklistAp(String bssid) {
@@ -763,7 +760,7 @@
// Before taking action, make sure we should not cancel our processing
if (shouldCancel()) return;
- mWifiStateTracker.addToBlacklist(bssid);
+ mWifiManager.addToBlacklist(bssid);
if (D) {
myLogD("Blacklisting " + bssid);
@@ -858,7 +855,7 @@
* (and blacklisted them). Clear the blacklist so the AP with best
* signal is chosen.
*/
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
if (V) {
myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
@@ -929,7 +926,7 @@
* should revert anything done by the watchdog monitoring.
*/
private void handleReset() {
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
setIdleState(true);
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b1e8968..57f93c4 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -62,10 +62,14 @@
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -92,6 +96,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -115,6 +120,7 @@
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
+import android.view.Surface.OutOfResourcesException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
@@ -369,6 +375,7 @@
private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
+ Watermark mWatermark;
int mTransactionSequence = 0;
@@ -4373,11 +4380,19 @@
}
return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
-
+
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
}
+ public InputChannel monitorInput(String inputChannelName) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "monitorInput()")) {
+ throw new SecurityException("Requires READ_INPUT_STATE permission");
+ }
+ return mInputManager.monitorInput(inputChannelName);
+ }
+
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (mSystemBooted) {
@@ -7862,6 +7877,7 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -7871,7 +7887,6 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
- notifyFocusChanged();
}
}
} break;
@@ -8070,12 +8085,12 @@
if (oldHold != newHold) {
try {
if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid,
+ mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid,
+ mBatteryStats.noteStartWakelock(newHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
@@ -8516,12 +8531,6 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
}
- if (mFxSession == null) {
- mFxSession = new SurfaceSession();
- }
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
-
// Initialize state of exiting tokens.
for (i=mExitingTokens.size()-1; i>=0; i--) {
mExitingTokens.get(i).hasVisible = false;
@@ -8538,8 +8547,24 @@
float buttonBrightness = -1;
boolean focusDisplayed = false;
boolean animating = false;
+ boolean createWatermark = false;
+
+ if (mFxSession == null) {
+ mFxSession = new SurfaceSession();
+ createWatermark = true;
+ }
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
+
+ if (createWatermark) {
+ createWatermark();
+ }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(dw, dh);
+ }
+
try {
boolean wallpaperForceHidingChanged = false;
int repeats = 0;
@@ -10042,6 +10067,93 @@
mScreenFrozenLock.release();
}
+ static int getPropertyInt(String name, int defUnits, int defDps, DisplayMetrics dm) {
+ String str = SystemProperties.get(name);
+ try {
+ int val = Integer.parseInt(str);
+ return val;
+ } catch (Exception e) {
+ }
+ if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
+ return defDps;
+ }
+ int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
+ return val;
+ }
+
+ class Watermark {
+ Surface mSurface;
+ int mWidth;
+ int mHeight;
+ int mXPercent;
+ int mYPercent;
+
+ Watermark(SurfaceSession session, String text) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+
+ int fontSize = getPropertyInt("ro.watermark.height",
+ TypedValue.COMPLEX_UNIT_DIP, 48, dm);
+ mXPercent = getPropertyInt("ro.watermark.x",
+ TypedValue.COMPLEX_UNIT_PX, 50, dm);
+ mYPercent = getPropertyInt("ro.watermark.y",
+ TypedValue.COMPLEX_UNIT_PX, 99, dm);
+ int color = getPropertyInt("ro.watermark.color",
+ TypedValue.COMPLEX_UNIT_PX, 0x80ffffff, dm);
+ int shadowRadius = getPropertyInt("ro.watermark.shadow.radius",
+ TypedValue.COMPLEX_UNIT_PX, 5, dm);
+ int shadowDx = getPropertyInt("ro.watermark.shadow.dx",
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowDy = getPropertyInt("ro.watermark.shadow.dy",
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowColor = getPropertyInt("ro.watermark.shadow.color",
+ TypedValue.COMPLEX_UNIT_PX, 0xff000000, dm);
+
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setTextSize(fontSize);
+ paint.setColor(color);
+ paint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+ paint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+ FontMetricsInt fm = paint.getFontMetricsInt();
+ mHeight = fm.descent - fm.ascent + 20;
+ mWidth = (int)paint.measureText(text) + 20;
+
+ try {
+ mSurface = new Surface(session, 0,
+ "WatermarkSurface",
+ -1, mWidth, mHeight, PixelFormat.TRANSLUCENT, 0);
+ mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
+ Rect dirty = new Rect(0, 0, mWidth, mHeight);
+ Canvas c = mSurface.lockCanvas(dirty);
+ c.drawText(text, 10, -fm.ascent+10, paint);
+ mSurface.unlockCanvasAndPost(c);
+ mSurface.show();
+ } catch (OutOfResourcesException e) {
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ int availW = dw - mWidth;
+ int availH = dh - mHeight;
+ mSurface.setPosition((availW*mXPercent)/100,
+ (availH*mYPercent)/100);
+ }
+ }
+
+ void createWatermark() {
+ if (mWatermark != null) {
+ return;
+ }
+
+ String text = SystemProperties.get("ro.watermark.text");
+ if (text == null || text.length() <= 0) {
+ return;
+ }
+
+ mWatermark = new Watermark(mFxSession, text);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a32cd4c..55ec6aa 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.R;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -39,7 +38,6 @@
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
-import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
@@ -50,7 +48,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ResultInfo;
import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
@@ -95,7 +92,6 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -110,6 +106,7 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -200,6 +197,9 @@
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
+ // The rate at which we check for apps using excessive wake locks -- 15 mins.
+ static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -771,6 +771,11 @@
boolean mDidAppSwitch;
/**
+ * Last time (in realtime) at which we checked for wake lock usage.
+ */
+ long mLastWakeLockCheckTime;
+
+ /**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
@@ -915,6 +920,7 @@
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
AlertDialog mUidAlert;
@@ -1174,6 +1180,16 @@
} catch (RemoteException e) {
}
} break;
+ case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ checkExcessiveWakeLocksLocked(true);
+ removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ if (mSleeping) {
+ Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+ }
+ }
+ } break;
}
}
};
@@ -2560,6 +2576,11 @@
mProcDeaths[0]++;
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteProcessDiedLocked(app.info.uid, pid);
+ }
+
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
@@ -3577,7 +3598,11 @@
}
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
// Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
@@ -5381,6 +5406,12 @@
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
+
+ // Initialize the wake times of all processes.
+ checkExcessiveWakeLocksLocked(false);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
}
}
@@ -5430,6 +5461,7 @@
mWindowManager.setEventDispatching(true);
mSleeping = false;
mMainStack.resumeTopActivityLocked(null);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
}
}
@@ -11286,6 +11318,62 @@
}
}
+ final void checkExcessiveWakeLocksLocked(boolean doKills) {
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ if (mLastWakeLockCheckTime == 0) {
+ doKills = false;
+ }
+ if (stats.isScreenOn()) {
+ doKills = false;
+ }
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long timeSince = curRealtime - mLastWakeLockCheckTime;
+ mLastWakeLockCheckTime = curRealtime;
+ if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
+ doKills = false;
+ }
+ int i = mLruProcesses.size();
+ while (i > 0) {
+ i--;
+ ProcessRecord app = mLruProcesses.get(i);
+ if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+ long wtime;
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, curRealtime);
+ }
+ long timeUsed = wtime - app.lastWakeTime;
+ if (false) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Wake for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(timeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(timeUsed, sb);
+ sb.append(" (");
+ sb.append((timeUsed*100)/timeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ }
+ // If a process has held a wake lock for more
+ // than 50% of the time during this period,
+ // that sounds pad. Kill!
+ if (doKills && timeSince > 0
+ && ((timeUsed*100)/timeSince) >= 50) {
+ Slog.i(TAG, "Excessive wake lock in " + app.processName
+ + " (pid " + app.pid + "): held " + timeUsed
+ + " during " + timeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive wake lock");
+ Process.killProcessQuiet(app.pid);
+ } else {
+ app.lastWakeTime = wtime;
+ }
+ }
+ }
+ }
+
private final boolean updateOomAdjLocked(
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
@@ -11308,6 +11396,12 @@
// Likewise do a gc when an app is moving in to the
// background (such as a service stopping).
scheduleAppGcLocked(app);
+ // And note its current wake lock time.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
+ }
}
app.setRawAdj = app.curRawAdj;
}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 37da6f7..7314e04 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -93,31 +93,31 @@
return data;
}
- public void noteStartWakelock(int uid, String name, int type) {
+ public void noteStartWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+ mStats.noteStartWakeLocked(uid, pid, name, type);
}
}
- public void noteStopWakelock(int uid, String name, int type) {
+ public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+ mStats.noteStopWakeLocked(uid, pid, name, type);
}
}
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+ mStats.noteStartSensorLocked(uid, sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+ mStats.noteStopSensorLocked(uid, sensor);
}
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 18b1acb..6d1fbab 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -27,8 +26,9 @@
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -74,6 +74,7 @@
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
BroadcastRecord curReceiver;// receiver currently running in the app
+ long lastWakeTime; // How long proc held wake lock at last check
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
@@ -128,6 +129,8 @@
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
+ final long now = SystemClock.uptimeMillis();
+
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -157,8 +160,9 @@
pw.print(" curReceiver="); pw.println(curReceiver);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
- pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
- pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(prefix); pw.print("lastActivityTime=");
+ TimeUtils.formatDuration(lastActivityTime, now, pw);
+ pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -177,6 +181,12 @@
pw.print(" persistentActivities="); pw.println(persistentActivities);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+ pw.print(" lastRequestedGc=");
+ TimeUtils.formatDuration(lastRequestedGc, now, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(lastLowMemory, now, pw);
+ pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75365ad..ab5a78d 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -117,7 +118,10 @@
StartItem si = list.get(i);
pw.print(prefix); pw.print("#"); pw.print(i);
pw.print(" id="); pw.print(si.id);
- if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
if (si.deliveryCount != 0) {
pw.print(" dc="); pw.print(si.deliveryCount);
}
@@ -140,18 +144,26 @@
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
- if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
- pw.print(" dataDir="); pw.println(dataDir);
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
- pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
- pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" restartTime="); pw.println(restartTime);
+ pw.print(prefix); pw.print("createTime=");
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.println("");
+ pw.print(prefix); pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.println("");
if (startRequested || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -162,13 +174,15 @@
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay-now);
- pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
}
if (deliveredStarts.size() > 0) {
pw.print(prefix); pw.println("Delivered Starts:");
- dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ dumpStartList(pw, prefix, deliveredStarts, now);
}
if (pendingStarts.size() > 0) {
pw.print(prefix); pw.println("Pending Starts:");
@@ -213,7 +227,8 @@
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = lastActivity = SystemClock.uptimeMillis();
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index ea6aa94..c1165c7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,9 @@
// capabilities of the GPS engine
private int mEngineCapabilities;
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
@@ -635,6 +638,7 @@
mEnabled = native_init();
if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
}
@@ -839,7 +843,7 @@
sendMessage(INJECT_NTP_TIME, 0, null);
result = true;
} else if ("force_xtra_injection".equals(command)) {
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
xtraDownloadRequest();
result = true;
}
@@ -1372,7 +1376,7 @@
handleInjectNtpTime();
break;
case DOWNLOAD_XTRA_DATA:
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
handleDownloadXtraData();
}
break;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0982b32..ebe71ab 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -213,7 +213,7 @@
void setDisplayOrientation(int32_t displayId, int32_t orientation);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- jweak inputChannelObjWeak);
+ jweak inputChannelObjWeak, bool monitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
@@ -334,6 +334,7 @@
bool mWindowsReady;
Vector<InputWindow> mWindows;
Vector<InputWindow*> mWallpaperWindows;
+ Vector<sp<InputChannel> > mMonitoringChannels;
// Focus tracking for keys, trackball, etc.
InputWindow* mFocusedWindow;
@@ -382,6 +383,10 @@
static void addTarget(const InputWindow* window, int32_t targetFlags,
nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+ void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
@@ -492,7 +497,7 @@
}
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+ const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
if (! inputChannelObjWeak) {
LOGE("Could not create weak reference for input channel.");
@@ -519,9 +524,14 @@
status = mInputManager->registerInputChannel(inputChannel);
if (! status) {
+ // Success.
+ if (monitor) {
+ registerMonitoringChannel(inputChannel);
+ }
return OK;
}
+ // Failed!
{
AutoMutex _l(mInputChannelRegistryLock);
mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
@@ -552,6 +562,8 @@
env->DeleteWeakGlobalRef(inputChannelObjWeak);
+ unregisterMonitoringChannel(inputChannel);
+
return mInputManager->unregisterInputChannel(inputChannel);
}
@@ -829,6 +841,8 @@
env->DeleteLocalRef(inputChannelObjLocal);
}
+
+ unregisterMonitoringChannel(inputChannel);
}
bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
@@ -1429,7 +1443,9 @@
// If there is no currently touched window then fail.
if (! mTouchedWindow) {
- LOGW("Dropping event because there is no touched window to receive it.");
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched window to receive it.");
+#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
injectionPermission = INJECTION_PERMISSION_GRANTED;
break; // failed, exit wait loop
@@ -1587,6 +1603,8 @@
outTargets.clear();
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
+
+ addMonitoringTargetsLd(outTargets);
}
pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1631,6 +1649,8 @@
}
windowType = focusedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
} // release lock
pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1657,6 +1677,8 @@
}
windowType = touchedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
} // release lock
int32_t eventType;
@@ -1714,6 +1736,39 @@
android_server_PowerManagerService_userActivity(eventTime, eventType);
}
+void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+ mMonitoringChannels.push(inputChannel);
+ } // release lock
+}
+
+void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+ } // release lock
+}
+
+void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.timeout = -1;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
static void dumpMotionRange(String8& dump,
const char* name, const InputDeviceInfo::MotionRange* range) {
if (range) {
@@ -1805,6 +1860,11 @@
mWindows[i].ownerPid, mWindows[i].ownerUid,
mWindows[i].dispatchingTimeout / 1000000.0);
}
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
+ i, mMonitoringChannels[i]->getName().string());
+ }
}
// ----------------------------------------------------------------------------
@@ -2012,7 +2072,7 @@
}
static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
+ jobject inputChannelObj, jboolean monitor) {
if (checkInputManagerUnitialized(env)) {
return;
}
@@ -2026,15 +2086,17 @@
status_t status = gNativeInputManager->registerInputChannel(
- env, inputChannel, inputChannelObj);
+ env, inputChannel, inputChannelObj, monitor);
if (status) {
jniThrowRuntimeException(env, "Failed to register input channel. "
"Check logs for details.");
return;
}
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- android_server_InputManager_handleInputChannelDisposed, NULL);
+ if (! monitor) {
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+ android_server_InputManager_handleInputChannelDisposed, NULL);
+ }
}
static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
@@ -2149,7 +2211,7 @@
(void*) android_server_InputManager_nativeGetSwitchState },
{ "nativeHasKeys", "(II[I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
- { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
+ { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index b80dbc5..146c177 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -22,6 +22,7 @@
#include "jni.h"
#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/Timers.h>
#include "com_android_server_PowerManagerService.h"
namespace android {
@@ -43,6 +44,11 @@
static bool gScreenOn;
static bool gScreenBright;
+static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1];
+
+// Throttling interval for user activity calls.
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+
// ----------------------------------------------------------------------------
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -67,6 +73,21 @@
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
if (gPowerManagerServiceObj) {
+ // Throttle calls into user activity by event type.
+ // We're a little conservative about argument checking here in case the caller
+ // passes in bad data which could corrupt system state.
+ if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (eventTime > now) {
+ eventTime = now;
+ }
+
+ if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
+ return;
+ }
+ gLastEventTime[eventType] = eventTime;
+ }
+
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
@@ -136,6 +157,12 @@
GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
"userActivity", "(JZIZ)V");
+ // Initialize
+ for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) {
+ gLastEventTime[i] = LLONG_MIN;
+ }
+ gScreenOn = true;
+ gScreenBright = true;
return 0;
}
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 9b05f38..7c329b2 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -30,6 +30,8 @@
POWER_MANAGER_LONG_TOUCH_EVENT = 3,
POWER_MANAGER_TOUCH_UP_EVENT = 4,
POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+ POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
};
extern bool android_server_PowerManagerService_isScreenOn();
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index a14bfb5..79772ed 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@
clz.cpp.arm \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
+ DisplayHardware/HWComposer.cpp \
BlurFilter.cpp.arm \
GLExtensions.cpp \
Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 2eac0a8..166c528 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
#include "DisplayHardware/DisplayHardware.h"
-#include <hardware/copybit.h>
#include <hardware/overlay.h>
#include <hardware/gralloc.h>
#include "GLExtensions.h"
+#include "HWComposer.h"
using namespace android;
@@ -76,7 +76,7 @@
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy),
- mFlags(0)
+ mFlags(0), mHwc(0)
{
init(dpy);
}
@@ -262,6 +262,17 @@
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+
+ // initialize the H/W composer
+ mHwc = new HWComposer();
+ if (mHwc->initCheck() == NO_ERROR) {
+ mHwc->setFrameBuffer(mDisplay, mSurface);
+ }
+}
+
+HWComposer& DisplayHardware::getHwComposer() const {
+ return *mHwc;
}
/*
@@ -317,7 +328,12 @@
}
mPageFlipCount++;
- eglSwapBuffers(dpy, surface);
+
+ if (mHwc->initCheck() == NO_ERROR) {
+ mHwc->commit();
+ } else {
+ eglSwapBuffers(dpy, surface);
+ }
checkEGLErrors("eglSwapBuffers");
// for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 66bf521..f2cfd2d 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,12 +34,11 @@
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
-struct framebuffer_device_t;
-struct copybit_image_t;
namespace android {
class FramebufferNativeWindow;
+class HWComposer;
class DisplayHardware : public DisplayHardwareBase
{
@@ -80,6 +79,9 @@
uint32_t getPageFlipCount() const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+ // Hardware Composer
+ HWComposer& getHwComposer() const;
status_t compositionComplete() const;
@@ -107,6 +109,8 @@
GLint mMaxViewportDims;
GLint mMaxTextureSize;
+ HWComposer* mHwc;
+
sp<FramebufferNativeWindow> mNativeWindow;
overlay_control_device_t* mOverlayEngine;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
new file mode 100644
index 0000000..0291d78
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/hardware.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "HWComposer.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+HWComposer::HWComposer()
+ : mModule(0), mHwc(0), mList(0), mCapacity(0),
+ mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
+{
+ int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+ LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+ if (err == 0) {
+ err = hwc_open(mModule, &mHwc);
+ LOGE_IF(err, "%s device failed to initialize (%s)",
+ HWC_HARDWARE_COMPOSER, strerror(-err));
+ }
+}
+
+HWComposer::~HWComposer() {
+ free(mList);
+ if (mHwc) {
+ hwc_close(mHwc);
+ }
+}
+
+status_t HWComposer::initCheck() const {
+ return mHwc ? NO_ERROR : NO_INIT;
+}
+
+void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
+ mDpy = (hwc_display_t)dpy;
+ mSur = (hwc_surface_t)sur;
+}
+
+status_t HWComposer::createWorkList(size_t numLayers) {
+ if (mHwc) {
+ if (!mList || mCapacity < numLayers) {
+ free(mList);
+ size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
+ mList = (hwc_layer_list_t*)malloc(size);
+ mCapacity = numLayers;
+ }
+ mList->flags = HWC_GEOMETRY_CHANGED;
+ mList->numHwLayers = numLayers;
+ }
+ return NO_ERROR;
+}
+
+status_t HWComposer::prepare() const {
+ int err = mHwc->prepare(mHwc, mList);
+ return (status_t)err;
+}
+
+status_t HWComposer::commit() const {
+ int err = mHwc->set(mHwc, mDpy, mSur, mList);
+ mList->flags &= ~HWC_GEOMETRY_CHANGED;
+ return (status_t)err;
+}
+
+size_t HWComposer::getNumLayers() const {
+ return mList ? mList->numHwLayers : 0;
+}
+
+hwc_layer_t* HWComposer::getLayers() const {
+ return mList ? mList->hwLayers : 0;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
new file mode 100644
index 0000000..c5d5c2b
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSER_H
+#define ANDROID_SF_HWCOMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hwcomposer.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HWComposer
+{
+public:
+
+ HWComposer();
+ ~HWComposer();
+
+ status_t initCheck() const;
+
+ // tells the HAL what the framebuffer is
+ void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
+
+ // create a work list for numLayers layer
+ status_t createWorkList(size_t numLayers);
+
+ // Asks the HAL what it can do
+ status_t prepare() const;
+
+ // commits the list
+ status_t commit() const;
+
+
+ size_t getNumLayers() const;
+ hwc_layer_t* getLayers() const;
+
+private:
+ hw_module_t const* mModule;
+ hwc_composer_device_t* mHwc;
+ hwc_layer_list_t* mList;
+ size_t mCapacity;
+ hwc_display_t mDpy;
+ hwc_surface_t mSur;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993..3720e1669 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,6 +35,7 @@
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
#define DEBUG_RESIZE 0
@@ -177,6 +178,62 @@
return NO_ERROR;
}
+void Layer::setGeometry(hwc_layer_t* hwcl)
+{
+ hwcl->compositionType = HWC_FRAMEBUFFER;
+ hwcl->hints = 0;
+ hwcl->flags = 0;
+ hwcl->transform = 0;
+ hwcl->blending = HWC_BLENDING_NONE;
+
+ // we can't do alpha-fade with the hwc HAL
+ const State& s(drawingState());
+ if (s.alpha < 0xFF) {
+ hwcl->flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ // we can only handle simple transformation
+ if (mOrientation & Transform::ROT_INVALID) {
+ hwcl->flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ hwcl->transform = mOrientation;
+
+ if (needsBlending()) {
+ hwcl->blending = mPremultipliedAlpha ?
+ HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+ }
+
+ hwcl->displayFrame.left = mTransformedBounds.left;
+ hwcl->displayFrame.top = mTransformedBounds.top;
+ hwcl->displayFrame.right = mTransformedBounds.right;
+ hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+
+ hwcl->visibleRegionScreen.rects =
+ reinterpret_cast<hwc_rect_t const *>(
+ visibleRegionScreen.getArray(
+ &hwcl->visibleRegionScreen.numRects));
+}
+
+void Layer::setPerFrameData(hwc_layer_t* hwcl) {
+ sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+ if (buffer == NULL) {
+ // this situation can happen if we ran out of memory for instance.
+ // not much we can do. continue to use whatever texture was bound
+ // to this context.
+ hwcl->handle = NULL;
+ return;
+ }
+ hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+ // TODO: set the crop value properly
+ hwcl->sourceCrop.left = 0;
+ hwcl->sourceCrop.top = 0;
+ hwcl->sourceCrop.right = buffer->width;
+ hwcl->sourceCrop.bottom = buffer->height;
+}
+
void Layer::reloadTexture(const Region& dirty)
{
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e1d283b..188da6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,8 @@
bool isFixedSize() const;
// LayerBase interface
+ virtual void setGeometry(hwc_layer_t* hwcl);
+ virtual void setPerFrameData(hwc_layer_t* hwcl);
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index d5aa53f..043d54d 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -39,8 +39,11 @@
// ---------------------------------------------------------------------------
+int32_t LayerBase::sSequence = 1;
+
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
: dpy(display), contentDirty(false),
+ sequence(uint32_t(android_atomic_inc(&sSequence))),
mFlinger(flinger),
mNeedsFiltering(false),
mOrientation(0),
@@ -304,22 +307,17 @@
}
}
-void LayerBase::draw(const Region& inClip) const
+void LayerBase::setGeometry(hwc_layer_t* hwcl) {
+ hwcl->flags |= HWC_SKIP_LAYER;
+}
+
+void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
+ hwcl->compositionType = HWC_FRAMEBUFFER;
+ hwcl->handle = NULL;
+}
+
+void LayerBase::draw(const Region& clip) const
{
- // invalidate the region we'll update
- Region clip(inClip); // copy-on-write, so no-op most of the time
-
- // Remove the transparent area from the clipping region
- const State& s = drawingState();
- if (LIKELY(!s.transparentRegion.isEmpty())) {
- clip.subtract(transparentRegionScreen);
- if (clip.isEmpty()) {
- // usually this won't happen because this should be taken care of
- // by SurfaceFlinger::computeVisibleRegions()
- return;
- }
- }
-
// reset GL state
glEnable(GL_SCISSOR_TEST);
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 4288cf7..dd1cd05 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,6 +35,8 @@
#include <pixelflinger/pixelflinger.h>
+#include <hardware/hwcomposer.h>
+
#include "Transform.h"
namespace android {
@@ -53,6 +55,8 @@
class LayerBase : public RefBase
{
+ static int32_t sSequence;
+
public:
LayerBase(SurfaceFlinger* flinger, DisplayID display);
@@ -61,6 +65,7 @@
Region visibleRegionScreen;
Region transparentRegionScreen;
Region coveredRegionScreen;
+ int32_t sequence;
struct State {
uint32_t w;
@@ -105,6 +110,10 @@
virtual const char* getTypeId() const { return "LayerBase"; }
+ virtual void setGeometry(hwc_layer_t* hwcl);
+
+ virtual void setPerFrameData(hwc_layer_t* hwcl);
+
/**
* draw - performs some global clipping optimizations
* and calls onDraw().
@@ -210,12 +219,6 @@
inline const State& currentState() const { return mCurrentState; }
inline State& currentState() { return mCurrentState; }
- static int compareCurrentStateZ(
- sp<LayerBase> const * layerA,
- sp<LayerBase> const * layerB) {
- return layerA[0]->currentState().z - layerB[0]->currentState().z;
- }
-
int32_t getOrientation() const { return mOrientation; }
int tx() const { return mLeft; }
int ty() const { return mTop; }
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5f836366..e8b2ebf 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -495,7 +495,7 @@
const ISurface::BufferHeap& buffers(mBufferHeap);
uint32_t w = mLayer.mTransformedBounds.width();
uint32_t h = mLayer.mTransformedBounds.height();
- if (buffers.w * h != buffers.h * w) {
+ if (mLayer.getOrientation() & (Transform::ROT_90 | Transform::ROT_270)) {
int t = w; w = h; h = t;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3167c4c..d257897 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,6 +52,7 @@
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
/* ideally AID_GRAPHICS would be in a semi-public header
* or there would be a way to map a user/group name to its id
@@ -65,95 +66,6 @@
namespace android {
// ---------------------------------------------------------------------------
-SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
- : lookup(rhs.lookup), layers(rhs.layers)
-{
-}
-
-ssize_t SurfaceFlinger::LayerVector::indexOf(
- const sp<LayerBase>& key, size_t guess) const
-{
- if (guess<size() && lookup.keyAt(guess) == key)
- return guess;
- const ssize_t i = lookup.indexOfKey(key);
- if (i>=0) {
- const size_t idx = lookup.valueAt(i);
- LOGE_IF(layers[idx]!=key,
- "LayerVector[%p]: layers[%d]=%p, key=%p",
- this, int(idx), layers[idx].get(), key.get());
- return idx;
- }
- return i;
-}
-
-ssize_t SurfaceFlinger::LayerVector::add(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- size_t count = layers.size();
- ssize_t l = 0;
- ssize_t h = count-1;
- ssize_t mid;
- sp<LayerBase> const* a = layers.array();
- while (l <= h) {
- mid = l + (h - l)/2;
- const int c = cmp(a+mid, &layer);
- if (c == 0) { l = mid; break; }
- else if (c<0) { l = mid+1; }
- else { h = mid-1; }
- }
- size_t order = l;
- while (order<count && !cmp(&layer, a+order)) {
- order++;
- }
- count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= order) {
- lookup.editValueAt(i)++;
- }
- }
- layers.insertAt(layer, order);
- lookup.add(layer, order);
- return order;
-}
-
-ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
-{
- const ssize_t keyIndex = lookup.indexOfKey(layer);
- if (keyIndex >= 0) {
- const size_t index = lookup.valueAt(keyIndex);
- LOGE_IF(layers[index]!=layer,
- "LayerVector[%p]: layers[%u]=%p, layer=%p",
- this, int(index), layers[index].get(), layer.get());
- layers.removeItemsAt(index);
- lookup.removeItemsAt(keyIndex);
- const size_t count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= size_t(index)) {
- lookup.editValueAt(i)--;
- }
- }
- return index;
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t SurfaceFlinger::LayerVector::reorder(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- // XXX: it's a little lame. but oh well...
- ssize_t err = remove(layer);
- if (err >=0)
- err = add(layer, cmp);
- return err;
-}
-
-// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
@@ -165,6 +77,7 @@
mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
+ mHwWorkListDirty(false),
mDeferReleaseConsole(false),
mFreezeDisplay(false),
mFreezeCount(0),
@@ -457,6 +370,11 @@
// post surfaces (if needed)
handlePageFlip();
+ if (UNLIKELY(mHwWorkListDirty)) {
+ // build the h/w work list
+ handleWorkList();
+ }
+
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw() && !isFrozen())) {
// repaint the framebuffer (if needed)
@@ -521,6 +439,10 @@
{
Vector< sp<LayerBase> > ditchedLayers;
+ /*
+ * Perform and commit the transaction
+ */
+
{ // scope for the lock
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
@@ -528,9 +450,15 @@
handleTransactionLocked(transactionFlags, ditchedLayers);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
+ mHwWorkListDirty = true;
+ // here the transaction has been committed
}
- // do this without lock held
+ /*
+ * Clean-up all layers that went away
+ * (do this without the lock held)
+ */
+
const size_t count = ditchedLayers.size();
for (size_t i=0 ; i<count ; i++) {
if (ditchedLayers[i] != 0) {
@@ -764,8 +692,8 @@
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers = const_cast<LayerVector&>(
- mDrawingState.layersSortedByZ);
+ LayerVector& currentLayers(
+ const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -773,8 +701,22 @@
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+
+ /*
+ * rebuild the visible layer list
+ */
+ mVisibleLayersSortedByZ.clear();
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ size_t count = currentLayers.size();
+ mVisibleLayersSortedByZ.setCapacity(count);
+ for (size_t i=0 ; i<count ; i++) {
+ if (!currentLayers[i]->visibleRegionScreen.isEmpty())
+ mVisibleLayersSortedByZ.add(currentLayers[i]);
+ }
+
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
+ mHwWorkListDirty = true;
}
unlockPageFlip(currentLayers);
@@ -805,6 +747,20 @@
}
}
+void SurfaceFlinger::handleWorkList()
+{
+ mHwWorkListDirty = false;
+ HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+ if (hwc.initCheck() == NO_ERROR) {
+ const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
+ const size_t count = currentLayers.size();
+ hwc.createWorkList(count);
+ hwc_layer_t* const cur(hwc.getLayers());
+ for (size_t i=0 ; cur && i<count ; i++) {
+ currentLayers[i]->setGeometry(&cur[i]);
+ }
+ }
+}
void SurfaceFlinger::handleRepaint()
{
@@ -869,19 +825,77 @@
// draw something...
drawWormhole();
}
- const SurfaceFlinger& flinger(*this);
- const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
- const size_t count = drawingLayers.size();
- sp<LayerBase> const* const layers = drawingLayers.array();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer = layers[i];
- const Region& visibleRegion(layer->visibleRegionScreen);
- if (!visibleRegion.isEmpty()) {
- const Region clip(dirty.intersect(visibleRegion));
- if (!clip.isEmpty()) {
- layer->draw(clip);
+
+ status_t err = NO_ERROR;
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ size_t count = layers.size();
+
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ HWComposer& hwc(hw.getHwComposer());
+ hwc_layer_t* const cur(hwc.getLayers());
+
+ LOGE_IF(cur && hwc.getNumLayers() != count,
+ "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
+ hwc.getNumLayers(), count);
+
+ // just to be extra-safe, use the smallest count
+ if (hwc.initCheck() == NO_ERROR) {
+ count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
+ }
+
+ /*
+ * update the per-frame h/w composer data for each layer
+ * and build the transparent region of the FB
+ */
+ Region transparent;
+ if (cur) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(layers[i]);
+ layer->setPerFrameData(&cur[i]);
+ if (cur[i].hints & HWC_HINT_CLEAR_FB) {
+ if (!(layer->needsBlending())) {
+ transparent.orSelf(layer->visibleRegionScreen);
+ }
}
}
+ err = hwc.prepare();
+ LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+ }
+
+ /*
+ * clear the area of the FB that need to be transparent
+ */
+ transparent.andSelf(dirty);
+ if (!transparent.isEmpty()) {
+ glClearColor(0,0,0,0);
+ Region::const_iterator it = transparent.begin();
+ Region::const_iterator const end = transparent.end();
+ const int32_t height = hw.getHeight();
+ while (it != end) {
+ const Rect& r(*it++);
+ const GLint sy = height - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ }
+
+
+ /*
+ * and then, render the layers targeted at the framebuffer
+ */
+ for (size_t i=0 ; i<count ; i++) {
+ if (cur) {
+ if (!(cur[i].compositionType == HWC_FRAMEBUFFER) ||
+ cur[i].flags & HWC_SKIP_LAYER) {
+ // skip layers handled by the HAL
+ continue;
+ }
+ }
+ const sp<LayerBase>& layer(layers[i]);
+ const Region clip(dirty.intersect(layer->visibleRegionScreen));
+ if (!clip.isEmpty()) {
+ layer->draw(clip);
+ }
}
}
@@ -1029,8 +1043,7 @@
status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
{
- ssize_t i = mCurrentState.layersSortedByZ.add(
- layer, &LayerBase::compareCurrentStateZ);
+ ssize_t i = mCurrentState.layersSortedByZ.add(layer);
return (i < 0) ? status_t(i) : status_t(NO_ERROR);
}
@@ -1372,9 +1385,10 @@
flags |= eTraversalNeeded;
}
if (what & eLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setLayer(s.z)) {
- mCurrentState.layersSortedByZ.reorder(
- layer, &Layer::compareCurrentStateZ);
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
// AND transaction (list changed)
flags |= eTransactionNeeded|eTraversalNeeded;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8821e5c..8e286e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -40,9 +40,6 @@
#include "MessageQueue.h"
-struct copybit_device_t;
-struct overlay_device_t;
-
namespace android {
// ---------------------------------------------------------------------------
@@ -246,21 +243,19 @@
status_t setClientState(const sp<Client>& client,
int32_t count, const layer_state_t* states);
-
- class LayerVector {
+ class LayerVector : public SortedVector< sp<LayerBase> > {
public:
- inline LayerVector() { }
- LayerVector(const LayerVector&);
- inline size_t size() const { return layers.size(); }
- inline sp<LayerBase> const* array() const { return layers.array(); }
- ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t remove(const sp<LayerBase>&);
- ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const;
- inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
- private:
- KeyedVector< sp<LayerBase> , size_t> lookup;
- Vector< sp<LayerBase> > layers;
+ LayerVector() { }
+ LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
+ virtual int do_compare(const void* lhs, const void* rhs) const {
+ const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
+ const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
+ // sort layers by Z order
+ uint32_t lz = l->currentState().z;
+ uint32_t rz = r->currentState().z;
+ // then by sequence, so we get a stable ordering
+ return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
+ }
};
struct State {
@@ -301,6 +296,7 @@
void handlePageFlip();
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
+ void handleWorkList();
void handleRepaint();
void postFramebuffer();
void composeSurfaces(const Region& dirty);
@@ -375,10 +371,13 @@
Region mInvalidRegion;
Region mWormholeRegion;
bool mVisibleRegionsDirty;
+ bool mHwWorkListDirty;
bool mDeferReleaseConsole;
bool mFreezeDisplay;
int32_t mFreezeCount;
nsecs_t mFreezeDisplayTime;
+ Vector< sp<LayerBase> > mVisibleLayersSortedByZ;
+
// don't use a lock for these, we don't care
int mDebugRegion;
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 3b326df..0f448e0 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -190,7 +190,7 @@
return err;
}
- if (texture->target != GL_TEXTURE_2D)
+ if (texture->target != Texture::TEXTURE_2D)
return INVALID_OPERATION;
glBindTexture(GL_TEXTURE_2D, texture->name);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 11315ce..b0fa0f5 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1385,7 +1385,7 @@
* Normalize a phone number by removing the characters other than digits. If
* the given number has keypad letters, the letters will be converted to
* digits first.
- *
+ *
* @param phoneNumber
* the number to be normalized.
* @return the normalized number.
@@ -1397,9 +1397,9 @@
int len = phoneNumber.length();
for (int i = 0; i < len; i++) {
char c = phoneNumber.charAt(i);
- if (PhoneNumberUtils.isISODigit(c)) {
+ if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) {
sb.append(c);
- } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
+ } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
}
}
@@ -1779,6 +1779,15 @@
}
/**
+ * @hide
+ * @param number
+ * @return true if number contains @
+ */
+ public static boolean isUriNumber(String number) {
+ return number != null && number.contains("@");
+ }
+
+ /**
* This function handles the plus code conversion within NANP CDMA network
* If the number format is
* 1)+1NANP,remove +,
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 37ef912..0c63c37 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -304,9 +304,9 @@
int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly);
ret[1] = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ret[0] = 1;
ret[2] = MAX_USER_DATA_SEPTETS - septets;
@@ -318,9 +318,9 @@
ret[1] = messageBody.length();
if (octets > MAX_USER_DATA_BYTES) {
// 6 is the size of the user data header
- ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ret[0] = 1;
ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 1381a2d..819cfbe 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -21,11 +21,18 @@
import android.os.Handler;
import android.os.Message;
import android.os.RegistrantList;
+import android.util.Log;
+
import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+
+
/**
* @hide
*
@@ -45,6 +52,9 @@
*/
public final class CallManager {
+ private static final String LOG_TAG ="GSM";
+ private static final boolean LOCAL_DEBUG = true;
+
private static final int EVENT_DISCONNECT = 100;
private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
private static final int EVENT_NEW_RINGING_CONNECTION = 102;
@@ -163,6 +173,14 @@
}
/**
+ * Returns all the registered phone objects.
+ * @return all the registered phone objects.
+ */
+ public List<Phone> getAllPhones() {
+ return Collections.unmodifiableList(mPhones);
+ }
+
+ /**
* Get current coarse-grained voice call state.
* If the Call Manager has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
@@ -171,7 +189,7 @@
public Phone.State getState() {
Phone.State s = Phone.State.IDLE;
- for(Phone phone : mPhones) {
+ for (Phone phone : mPhones) {
if (phone.getState() == Phone.State.RINGING) {
return Phone.State.RINGING;
} else if (phone.getState() == Phone.State.OFFHOOK) {
@@ -182,6 +200,41 @@
}
/**
+ * @return the service state of CallManager, which represents the
+ * highest priority state of all the service states of phones
+ *
+ * The priority is defined as
+ *
+ * STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF
+ *
+ */
+
+ public int getServiceState() {
+ int resultState = ServiceState.STATE_OUT_OF_SERVICE;
+
+ for (Phone phone : mPhones) {
+ int serviceState = phone.getServiceState().getState();
+ if (serviceState == ServiceState.STATE_IN_SERVICE) {
+ // IN_SERVICE has the highest priority
+ resultState = serviceState;
+ break;
+ } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ // OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF
+ // Note: EMERGENCY_ONLY is not in use at this moment
+ if ( resultState == ServiceState.STATE_EMERGENCY_ONLY ||
+ resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ } else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
+ if (resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ }
+ }
+ return resultState;
+ }
+
+ /**
* Register phone to CallManager
* @param phone
* @return true if register successfully
@@ -202,11 +255,39 @@
}
/**
+ * return the default phone or null if no phone available
+ */
+ public Phone getDefaultPhone() {
+ return mDefaultPhone;
+ }
+
+ /**
+ * @return the phone associated with the foreground call
+ */
+ public Phone getFgPhone() {
+ return getActiveFgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the background call
+ */
+ public Phone getBgPhone() {
+ return getFirstActiveBgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the ringing call
+ */
+ public Phone getRingingPhone() {
+ return getFirstActiveRingingCall().getPhone();
+ }
+
+ /**
* unregister phone from CallManager
* @param phone
*/
public void unregisterPhone(Phone phone) {
- if (phone != null && !mPhones.contains(phone)) {
+ if (phone != null && mPhones.contains(phone)) {
mPhones.remove(phone);
mRingingCalls.remove(phone.getRingingCall());
mBackgroundCalls.remove(phone.getBackgroundCall());
@@ -282,9 +363,13 @@
if ( hasActiveFgCall() ) {
Phone activePhone = getActiveFgCall().getPhone();
- boolean hasBgCall = activePhone.getBackgroundCall().isIdle();
+ boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
boolean sameChannel = (activePhone == ringingPhone);
+ if (LOCAL_DEBUG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
+ }
+
if (sameChannel && hasBgCall) {
getActiveFgCall().hangup();
} else if (!sameChannel && !hasBgCall) {
@@ -313,8 +398,15 @@
}
/**
- * Places any active calls on hold, and makes any held calls
- * active. Switch occurs asynchronously and may fail.
+ * Places active call on hold, and makes held call active.
+ * Switch occurs asynchronously and may fail.
+ *
+ * There are 4 scenarios
+ * 1. only active call but no held call, aka, hold
+ * 2. no active call but only held call, aka, unhold
+ * 3. both active and held calls from same phone, aka, swap
+ * 4. active and held calls from different phones, aka, phone swap
+ *
* Final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
@@ -335,11 +427,13 @@
heldPhone = heldCall.getPhone();
}
- if (activePhone != heldPhone) {
+ if (activePhone != null) {
activePhone.switchHoldingAndActive();
}
- heldPhone.switchHoldingAndActive();
+ if (heldPhone != null && heldPhone != activePhone) {
+ heldPhone.switchHoldingAndActive();
+ }
}
/**
@@ -389,6 +483,24 @@
* handled asynchronously.
*/
public Connection dial(Phone phone, String dialString) throws CallStateException {
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + (activePhone != phone));
+ }
+
+ if (activePhone != phone) {
+ if (hasBgCall) {
+ Log.d(LOG_TAG, "Hangup");
+ getActiveFgCall().hangup();
+ } else {
+ Log.d(LOG_TAG, "Switch");
+ activePhone.switchHoldingAndActive();
+ }
+ }
+ }
return phone.dial(dialString);
}
@@ -966,7 +1078,7 @@
* @return list of ringing calls
*/
public ArrayList<Call> getRingingCalls() {
- return mBackgroundCalls;
+ return mRingingCalls;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 769b2fc8..784e87d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -179,6 +179,7 @@
static final int PHONE_TYPE_NONE = RILConstants.NO_PHONE;
static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE;
static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE;
+ static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE;
// Used for preferred network type
// Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 803b736..2e391cb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -24,6 +24,8 @@
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.sip.SipPhone;
+import com.android.internal.telephony.sip.SipPhoneFactory;
/**
* {@hide}
@@ -175,4 +177,13 @@
return phone;
}
}
+
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makeSipPhone(String sipUri) {
+ return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 86c86bb..a45cad1 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -111,7 +111,7 @@
}
/**
- * Retrieves the compelete voice mail number.
+ * Retrieves the complete voice mail number.
*
* @hide
*/
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 71a80e0..888f721 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -79,6 +79,7 @@
int NO_PHONE = 0;
int GSM_PHONE = 1;
int CDMA_PHONE = 2;
+ int SIP_PHONE = 3;
int CDM_TTY_MODE_DISABLED = 0;
int CDM_TTY_MODE_ENABLED = 1;
diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
index a636a4b..2f5d3ec 100644
--- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java
@@ -24,7 +24,6 @@
import android.util.Config;
import android.util.Log;
-
/**
* WAP push handler class.
*
@@ -59,7 +58,7 @@
*/
public int dispatchWapPdu(byte[] pdu) {
- if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
+ if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu));
int index = 0;
int transactionId = pdu[index++] & 0xFF;
@@ -68,7 +67,7 @@
if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) &&
(pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType);
return Intents.RESULT_SMS_HANDLED;
}
@@ -81,7 +80,7 @@
* So it will be encoded in no more than 5 octets.
*/
if (pduDecoder.decodeUintvarInteger(index) == false) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error.");
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
headerLength = (int)pduDecoder.getValue32();
@@ -102,136 +101,44 @@
* Length = Uintvar-integer
*/
if (pduDecoder.decodeContentType(index) == false) {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
+ if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error.");
return Intents.RESULT_SMS_GENERIC_ERROR;
}
- int binaryContentType;
+
String mimeType = pduDecoder.getValueString();
- if (mimeType == null) {
- binaryContentType = (int)pduDecoder.getValue32();
- // TODO we should have more generic way to map binaryContentType code to mimeType.
- switch (binaryContentType) {
- case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_MMS:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF:
- mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF;
- break;
- default:
- if (Config.LOGD) {
- Log.w(LOG_TAG,
- "Received PDU. Unsupported Content-Type = " + binaryContentType);
- }
- return Intents.RESULT_SMS_HANDLED;
- }
- } else {
- if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS;
- } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF)) {
- binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF;
- } else {
- if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType);
- return Intents.RESULT_SMS_HANDLED;
- }
- }
+
index += pduDecoder.getDecodedDataLength();
- boolean dispatchedByApplication = false;
- switch (binaryContentType) {
- case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO:
- dispatchWapPdu_PushCO(pdu, transactionId, pduType, headerStartIndex, headerLength);
- dispatchedByApplication = true;
- break;
- case WspTypeDecoder.CONTENT_TYPE_B_MMS:
- dispatchWapPdu_MMS(pdu, transactionId, pduType, headerStartIndex, headerLength);
- dispatchedByApplication = true;
- break;
- default:
- break;
- }
- if (dispatchedByApplication == false) {
- dispatchWapPdu_default(pdu, transactionId, pduType, mimeType,
- headerStartIndex, headerLength);
- }
- return Activity.RESULT_OK;
- }
-
- private void dispatchWapPdu_default(byte[] pdu, int transactionId, int pduType,
- String mimeType, int headerStartIndex, int headerLength) {
byte[] header = new byte[headerLength];
System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
- int dataIndex = headerStartIndex + headerLength;
- byte[] data;
- data = new byte[pdu.length - dataIndex];
- System.arraycopy(pdu, dataIndex, data, 0, data.length);
+ byte[] intentData;
+ String permission;
+
+ if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) {
+ intentData = pdu;
+ } else {
+ int dataIndex = headerStartIndex + headerLength;
+ intentData = new byte[pdu.length - dataIndex];
+ System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length);
+ }
+
+ if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) {
+ permission = "android.permission.RECEIVE_MMS";
+ } else {
+ permission = "android.permission.RECEIVE_WAP_PUSH";
+ }
Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
intent.setType(mimeType);
intent.putExtra("transactionId", transactionId);
intent.putExtra("pduType", pduType);
intent.putExtra("header", header);
- intent.putExtra("data", data);
+ intent.putExtra("data", intentData);
+ intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters());
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
+ mSmsDispatcher.dispatch(intent, permission);
+
+ return Activity.RESULT_OK;
}
-
- private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType,
- int headerStartIndex, int headerLength) {
- byte[] header = new byte[headerLength];
- System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
-
- Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
- intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO);
- intent.putExtra("transactionId", transactionId);
- intent.putExtra("pduType", pduType);
- intent.putExtra("header", header);
- intent.putExtra("data", pdu);
-
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH");
- }
-
- private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType,
- int headerStartIndex, int headerLength) {
- byte[] header = new byte[headerLength];
- System.arraycopy(pdu, headerStartIndex, header, 0, header.length);
- int dataIndex = headerStartIndex + headerLength;
- byte[] data = new byte[pdu.length - dataIndex];
- System.arraycopy(pdu, dataIndex, data, 0, data.length);
-
- Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION);
- intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS);
- intent.putExtra("transactionId", transactionId);
- intent.putExtra("pduType", pduType);
- intent.putExtra("header", header);
- intent.putExtra("data", data);
-
- mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS");
- }
-}
-
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 336bc82..6bf6b13 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -16,11 +16,12 @@
package com.android.internal.telephony;
+import java.util.HashMap;
/**
- * Implement the WSP data type decoder.
+ * Implement the WSP data type decoder.
*
- * @hide
+ * @hide
*/
public class WspTypeDecoder {
@@ -30,35 +31,176 @@
public static final int PDU_TYPE_PUSH = 0x06;
public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07;
- // TODO we should have mapping between those binary code and mime type string.
- // see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
+ private final static HashMap<Integer, String> WELL_KNOWN_MIME_TYPES =
+ new HashMap<Integer, String>();
- public static final int CONTENT_TYPE_B_DRM_RIGHTS_XML = 0x4a;
- public static final int CONTENT_TYPE_B_DRM_RIGHTS_WBXML = 0x4b;
- public static final int CONTENT_TYPE_B_PUSH_SI = 0x2e;
- public static final int CONTENT_TYPE_B_PUSH_SL = 0x30;
- public static final int CONTENT_TYPE_B_PUSH_CO = 0x32;
- public static final int CONTENT_TYPE_B_MMS = 0x3e;
- public static final int CONTENT_TYPE_B_VND_DOCOMO_PF = 0x0310;
+ private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS =
+ new HashMap<Integer, String>();
- public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML =
- "application/vnd.oma.drm.rights+xml";
- public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML =
- "application/vnd.oma.drm.rights+wbxml";
- public static final String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic";
- public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc";
- public static final String CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
- public static final String CONTENT_MIME_TYPE_B_MMS = "application/vnd.wap.mms-message";
- public static final String CONTENT_MIME_TYPE_B_VND_DOCOMO_PF = "application/vnd.docomo.pf";
+ private static final int Q_VALUE = 0x00;
- public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f;
+ static {
+ WELL_KNOWN_MIME_TYPES.put(0x00, "*/*");
+ WELL_KNOWN_MIME_TYPES.put(0x01, "text/*");
+ WELL_KNOWN_MIME_TYPES.put(0x02, "text/html");
+ WELL_KNOWN_MIME_TYPES.put(0x03, "text/plain");
+ WELL_KNOWN_MIME_TYPES.put(0x04, "text/x-hdml");
+ WELL_KNOWN_MIME_TYPES.put(0x05, "text/x-ttml");
+ WELL_KNOWN_MIME_TYPES.put(0x06, "text/x-vCalendar");
+ WELL_KNOWN_MIME_TYPES.put(0x07, "text/x-vCard");
+ WELL_KNOWN_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+ WELL_KNOWN_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+ WELL_KNOWN_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+ WELL_KNOWN_MIME_TYPES.put(0x0B, "multipart/*");
+ WELL_KNOWN_MIME_TYPES.put(0x0C, "multipart/mixed");
+ WELL_KNOWN_MIME_TYPES.put(0x0D, "multipart/form-data");
+ WELL_KNOWN_MIME_TYPES.put(0x0E, "multipart/byterantes");
+ WELL_KNOWN_MIME_TYPES.put(0x0F, "multipart/alternative");
+ WELL_KNOWN_MIME_TYPES.put(0x10, "application/*");
+ WELL_KNOWN_MIME_TYPES.put(0x11, "application/java-vm");
+ WELL_KNOWN_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+ WELL_KNOWN_MIME_TYPES.put(0x13, "application/x-hdmlc");
+ WELL_KNOWN_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+ WELL_KNOWN_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+ WELL_KNOWN_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+ WELL_KNOWN_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+ WELL_KNOWN_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x1C, "image/*");
+ WELL_KNOWN_MIME_TYPES.put(0x1D, "image/gif");
+ WELL_KNOWN_MIME_TYPES.put(0x1E, "image/jpeg");
+ WELL_KNOWN_MIME_TYPES.put(0x1F, "image/tiff");
+ WELL_KNOWN_MIME_TYPES.put(0x20, "image/png");
+ WELL_KNOWN_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+ WELL_KNOWN_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+ WELL_KNOWN_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+ WELL_KNOWN_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+ WELL_KNOWN_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+ WELL_KNOWN_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+ WELL_KNOWN_MIME_TYPES.put(0x27, "application/xml");
+ WELL_KNOWN_MIME_TYPES.put(0x28, "text/xml");
+ WELL_KNOWN_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+ WELL_KNOWN_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+ WELL_KNOWN_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+ WELL_KNOWN_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+ WELL_KNOWN_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+ WELL_KNOWN_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+ WELL_KNOWN_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+ WELL_KNOWN_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+ WELL_KNOWN_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+ WELL_KNOWN_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+ WELL_KNOWN_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+ WELL_KNOWN_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+ WELL_KNOWN_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x3C, "application/wml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x3D, "text/css");
+ WELL_KNOWN_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+ WELL_KNOWN_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+ WELL_KNOWN_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+ WELL_KNOWN_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+ WELL_KNOWN_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+ WELL_KNOWN_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+ WELL_KNOWN_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+ WELL_KNOWN_MIME_TYPES.put(0x4F, "audio/*");
+ WELL_KNOWN_MIME_TYPES.put(0x50, "video/*");
+ WELL_KNOWN_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x52, "application/mikey");
+ WELL_KNOWN_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+ WELL_KNOWN_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
+ WELL_KNOWN_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+ WELL_KNOWN_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+ WELL_KNOWN_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+ WELL_KNOWN_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+ WELL_KNOWN_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0305, "text/calendar");
+ WELL_KNOWN_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+ WELL_KNOWN_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+ WELL_KNOWN_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+ WELL_KNOWN_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+ WELL_KNOWN_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+ WELL_KNOWN_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+ WELL_KNOWN_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+ WELL_KNOWN_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+ WELL_KNOWN_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+ WELL_KNOWN_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+ WELL_KNOWN_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+ WELL_KNOWN_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+ WELL_KNOWN_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+ WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+ WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+ WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+ WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+ WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+ WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+ WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+ WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+ WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+ WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+ WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+ WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+ WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+ WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+ WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+ WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+ WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+ WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+ WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+ WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+ }
+
+ public static final String CONTENT_TYPE_B_PUSH_CO = "application/vnd.wap.coc";
+ public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message";
byte[] wspData;
int dataLength;
long unsigned32bit;
String stringValue;
+ HashMap<String, String> contentParameters;
+
public WspTypeDecoder(byte[] pdu) {
wspData = pdu;
}
@@ -69,17 +211,17 @@
* @param startIndex The starting position of the "Text-string" in this pdu
*
* @return false when error(not a Text-string) occur
- * return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * return value can be retrieved by getValueString() method length of data in pdu can be
+ * retrieved by getDecodedDataLength() method
*/
public boolean decodeTextString(int startIndex) {
int index = startIndex;
while (wspData[index] != 0) {
index++;
}
- dataLength = index - startIndex + 1;
+ dataLength = index - startIndex + 1;
if (wspData[startIndex] == 127) {
- stringValue = new String(wspData, startIndex+1, dataLength - 2);
+ stringValue = new String(wspData, startIndex + 1, dataLength - 2);
} else {
stringValue = new String(wspData, startIndex, dataLength - 1);
}
@@ -87,13 +229,33 @@
}
/**
+ * Decode the "Token-text" type for WSP pdu
+ *
+ * @param startIndex The starting position of the "Token-text" in this pdu
+ *
+ * @return always true
+ * return value can be retrieved by getValueString() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
+ */
+ public boolean decodeTokenText(int startIndex) {
+ int index = startIndex;
+ while (wspData[index] != 0) {
+ index++;
+ }
+ dataLength = index - startIndex + 1;
+ stringValue = new String(wspData, startIndex, dataLength - 1);
+
+ return true;
+ }
+
+ /**
* Decode the "Short-integer" type for WSP pdu
*
* @param startIndex The starting position of the "Short-integer" in this pdu
*
* @return false when error(not a Short-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeShortInteger(int startIndex) {
if ((wspData[startIndex] & 0x80) == 0) {
@@ -111,7 +273,7 @@
*
* @return false when error(not a Long-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeLongInteger(int startIndex) {
int lengthMultiOctet = wspData[startIndex] & 0xff;
@@ -120,10 +282,10 @@
return false;
}
unsigned32bit = 0;
- for (int i=1; i<=lengthMultiOctet; i++) {
- unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex+i] & 0xff);
+ for (int i = 1; i <= lengthMultiOctet; i++) {
+ unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex + i] & 0xff);
}
- dataLength = 1+lengthMultiOctet;
+ dataLength = 1 + lengthMultiOctet;
return true;
}
@@ -134,7 +296,7 @@
*
* @return false when error(not a Integer-Value) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeIntegerValue(int startIndex) {
if (decodeShortInteger(startIndex) == true) {
@@ -150,10 +312,10 @@
*
* @return false when error(not a Uintvar-integer) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeUintvarInteger(int startIndex) {
- int index = startIndex;
+ int index = startIndex;
unsigned32bit = 0;
while ((wspData[index] & 0x80) != 0) {
@@ -175,7 +337,7 @@
*
* @return false when error(not a Value-length) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeValueLength(int startIndex) {
if ((wspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) {
@@ -185,22 +347,22 @@
unsigned32bit = wspData[startIndex];
dataLength = 1;
} else {
- decodeUintvarInteger(startIndex+1);
- dataLength ++;
+ decodeUintvarInteger(startIndex + 1);
+ dataLength++;
}
return true;
}
/**
- * Decode the "Extension-media" type for WSP PDU.
- *
- * @param startIndex The starting position of the "Extension-media" in this PDU.
- *
- * @return false on error, such as if there is no Extension-media at startIndex.
- * Side-effects: updates stringValue (available with getValueString()), which will be
- * null on error. The length of the data in the PDU is available with getValue32(), 0
- * on error.
- */
+ * Decode the "Extension-media" type for WSP PDU.
+ *
+ * @param startIndex The starting position of the "Extension-media" in this PDU.
+ *
+ * @return false on error, such as if there is no Extension-media at startIndex.
+ * Side-effects: updates stringValue (available with
+ * getValueString()), which will be null on error. The length of the
+ * data in the PDU is available with getValue32(), 0 on error.
+ */
public boolean decodeExtensionMedia(int startIndex) {
int index = startIndex;
dataLength = 0;
@@ -212,7 +374,7 @@
index++;
}
- dataLength = index - startIndex + 1;
+ dataLength = index - startIndex + 1;
stringValue = new String(wspData, startIndex, dataLength - 1);
return rtrn;
@@ -225,7 +387,7 @@
*
* @return false when error(not a Constrained-encoding) occur
* return value can be retrieved first by getValueString() and second by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeConstrainedEncoding(int startIndex) {
if (decodeShortInteger(startIndex) == true) {
@@ -240,31 +402,162 @@
*
* @param startIndex The starting position of the "Content-type" in this pdu
*
- * @return false when error(not a Content-type) occur
- * return value can be retrieved first by getValueString() and second by getValue32()
- * method length of data in pdu can be retrieved by getValue32() method
+ * @return false when error(not a Content-type) occurs
+ * If a content type exists in the headers (either as inline string, or as well-known
+ * value), getValueString() will return it. If a 'well known value' is encountered that
+ * cannot be mapped to a string mime type, getValueString() will return null, and
+ * getValue32() will return the unknown content type value.
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
+ * Any content type parameters will be accessible via getContentParameters()
*/
public boolean decodeContentType(int startIndex) {
int mediaPrefixLength;
- long mediaFieldLength;
+ contentParameters = new HashMap<String, String>();
- if (decodeValueLength(startIndex) == false) {
- return decodeConstrainedEncoding(startIndex);
- }
- mediaPrefixLength = getDecodedDataLength();
- mediaFieldLength = getValue32();
- if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
- dataLength += mediaPrefixLength;
- stringValue = null;
- return true;
- }
- if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
- dataLength += mediaPrefixLength;
- return true;
+ try {
+ if (decodeValueLength(startIndex) == false) {
+ boolean found = decodeConstrainedEncoding(startIndex);
+ if (found) {
+ expandWellKnownMimeType();
+ }
+ return found;
+ }
+ int headersLength = (int) unsigned32bit;
+ mediaPrefixLength = getDecodedDataLength();
+ if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) {
+ dataLength += mediaPrefixLength;
+ int readLength = dataLength;
+ stringValue = null;
+ expandWellKnownMimeType();
+ long wellKnownValue = unsigned32bit;
+ String mimeType = stringValue;
+ if (readContentParameters(startIndex + dataLength,
+ (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+ dataLength += readLength;
+ unsigned32bit = wellKnownValue;
+ stringValue = mimeType;
+ return true;
+ }
+ return false;
+ }
+ if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) {
+ dataLength += mediaPrefixLength;
+ int readLength = dataLength;
+ expandWellKnownMimeType();
+ long wellKnownValue = unsigned32bit;
+ String mimeType = stringValue;
+ if (readContentParameters(startIndex + dataLength,
+ (headersLength - (dataLength - mediaPrefixLength)), 0)) {
+ dataLength += readLength;
+ unsigned32bit = wellKnownValue;
+ stringValue = mimeType;
+ return true;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //something doesn't add up
+ return false;
}
return false;
}
+ private boolean readContentParameters(int startIndex, int leftToRead, int accumulator) {
+
+ int totalRead = 0;
+
+ if (leftToRead > 0) {
+ byte nextByte = wspData[startIndex];
+ String value = null;
+ String param = null;
+ if ((nextByte & 0x80) == 0x00 && nextByte > 31) { // untyped
+ decodeTokenText(startIndex);
+ param = stringValue;
+ totalRead += dataLength;
+ } else { // typed
+ if (decodeIntegerValue(startIndex)) {
+ totalRead += dataLength;
+ int wellKnownParameterValue = (int) unsigned32bit;
+ param = WELL_KNOWN_PARAMETERS.get(wellKnownParameterValue);
+ if (param == null) {
+ param = "unassigned/0x" + Long.toHexString(wellKnownParameterValue);
+ }
+ // special case for the "Q" parameter, value is a uintvar
+ if (wellKnownParameterValue == Q_VALUE) {
+ if (decodeUintvarInteger(startIndex + totalRead)) {
+ totalRead += dataLength;
+ value = String.valueOf(unsigned32bit);
+ contentParameters.put(param, value);
+ return readContentParameters(startIndex + totalRead, leftToRead
+ - totalRead, accumulator + totalRead);
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ if (decodeNoValue(startIndex + totalRead)) {
+ totalRead += dataLength;
+ value = null;
+ } else if (decodeIntegerValue(startIndex + totalRead)) {
+ totalRead += dataLength;
+ int intValue = (int) unsigned32bit;
+ if (intValue == 0) {
+ value = "";
+ } else {
+ value = String.valueOf(intValue);
+ }
+ } else {
+ decodeTokenText(startIndex + totalRead);
+ totalRead += dataLength;
+ value = stringValue;
+ if (value.startsWith("\"")) {
+ // quoted string, so remove the quote
+ value = value.substring(1);
+ }
+ }
+ contentParameters.put(param, value);
+ return readContentParameters(startIndex + totalRead, leftToRead - totalRead,
+ accumulator + totalRead);
+
+ } else {
+ dataLength = accumulator;
+ return true;
+ }
+ }
+
+ /**
+ * Check if the next byte is No-Value
+ *
+ * @param startIndex The starting position of the "Content length" in this pdu
+ *
+ * @return true if and only if the next byte is 0x00
+ */
+ private boolean decodeNoValue(int startIndex) {
+ if (wspData[startIndex] == 0) {
+ dataLength = 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Populate stringValue with the mime type corresponding to the value in unsigned32bit
+ *
+ * Sets unsigned32bit to -1 if stringValue is already populated
+ */
+ private void expandWellKnownMimeType() {
+ if (stringValue == null) {
+ int binaryContentType = (int) unsigned32bit;
+ stringValue = WELL_KNOWN_MIME_TYPES.get(binaryContentType);
+ } else {
+ unsigned32bit = -1;
+ }
+ }
+
/**
* Decode the "Content length" type for WSP pdu
*
@@ -272,7 +565,7 @@
*
* @return false when error(not a Content length) occur
* return value can be retrieved by getValue32() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeContentLength(int startIndex) {
return decodeIntegerValue(startIndex);
@@ -285,7 +578,7 @@
*
* @return false when error(not a Content location) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeContentLocation(int startIndex) {
return decodeTextString(startIndex);
@@ -298,7 +591,8 @@
*
* @return false when error(not a X-Wap-Application-Id) occur
* return value can be retrieved first by getValueString() and second by getValue32()
- * method length of data in pdu can be retrieved by getValue32() method
+ * method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapApplicationId(int startIndex) {
if (decodeIntegerValue(startIndex) == true) {
@@ -315,7 +609,7 @@
*
* @return false when error(not a X-Wap-Content-URI) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapContentURI(int startIndex) {
return decodeTextString(startIndex);
@@ -328,7 +622,7 @@
*
* @return false when error(not a X-Wap-Initiator-URI) occur
* return value can be retrieved by getValueString() method
- * length of data in pdu can be retrieved by getValue32() method
+ * length of data in pdu can be retrieved by getDecodedDataLength() method
*/
public boolean decodeXWapInitiatorURI(int startIndex) {
return decodeTextString(startIndex);
@@ -354,4 +648,18 @@
public String getValueString() {
return stringValue;
}
+
+ /**
+ * Any parameters encountered as part of a decodeContentType() invocation.
+ *
+ * @return a map of content parameters keyed by their names, or null if
+ * decodeContentType() has not been called If any unassigned
+ * well-known parameters are encountered, the key of the map will be
+ * 'unassigned/0x...', where '...' is the hex value of the
+ * unassigned parameter. If a parameter has No-Value the value will be null.
+ *
+ */
+ public HashMap<String, String> getContentParameters() {
+ return contentParameters;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
index 0ba3e11..2eb6ccb 100644
--- a/telephony/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
@@ -58,7 +58,8 @@
SET_UP_EVENT_LIST(0x05),
SET_UP_IDLE_MODE_TEXT(0x28),
SET_UP_MENU(0x25),
- SET_UP_CALL(0x10);
+ SET_UP_CALL(0x10),
+ PROVIDE_LOCAL_INFORMATION(0x26);
private int mValue;
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index b916713..1e23e34 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.os.SystemProperties;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.CommandsInterface;
@@ -245,48 +246,46 @@
CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
switch (cmdParams.getCommandType()) {
- case SET_UP_MENU:
- if (removeMenu(cmdMsg.getMenu())) {
- mMenuCmd = null;
- } else {
- mMenuCmd = cmdMsg;
- }
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0,
- null);
- break;
- case DISPLAY_TEXT:
- // when application is not required to respond, send an immediate
- // response.
- if (!cmdMsg.geTextMessage().responseNeeded) {
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- }
- break;
- case REFRESH:
- // ME side only handles refresh commands which meant to remove IDLE
- // MODE TEXT.
- cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT
- .value();
- break;
- case SET_UP_IDLE_MODE_TEXT:
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- break;
- case LAUNCH_BROWSER:
- case SELECT_ITEM:
- case GET_INPUT:
- case GET_INKEY:
- case SEND_DTMF:
- case SEND_SMS:
- case SEND_SS:
- case SEND_USSD:
- case PLAY_TONE:
- case SET_UP_CALL:
- // nothing to do on telephony!
- break;
- default:
- CatLog.d(this, "Unsupported command");
- return;
+ case SET_UP_MENU:
+ if (removeMenu(cmdMsg.getMenu())) {
+ mMenuCmd = null;
+ } else {
+ mMenuCmd = cmdMsg;
+ }
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case DISPLAY_TEXT:
+ // when application is not required to respond, send an immediate response.
+ if (!cmdMsg.geTextMessage().responseNeeded) {
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ }
+ break;
+ case REFRESH:
+ // ME side only handles refresh commands which meant to remove IDLE
+ // MODE TEXT.
+ cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
+ break;
+ case SET_UP_IDLE_MODE_TEXT:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ return;
+ case LAUNCH_BROWSER:
+ case SELECT_ITEM:
+ case GET_INPUT:
+ case GET_INKEY:
+ case SEND_DTMF:
+ case SEND_SMS:
+ case SEND_SS:
+ case SEND_USSD:
+ case PLAY_TONE:
+ case SET_UP_CALL:
+ // nothing to do on telephony!
+ break;
+ default:
+ CatLog.d(this, "Unsupported command");
+ return;
}
mCurrntCmd = cmdMsg;
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
@@ -315,6 +314,11 @@
}
ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ Input cmdInput = null;
+ if (mCurrntCmd != null) {
+ cmdInput = mCurrntCmd.geInput();
+ }
+
// command details
int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
if (cmdDet.compRequired) {
@@ -327,7 +331,13 @@
buf.write(cmdDet.commandQualifier);
// device identities
- tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
+ // According to TS102.223/TS31.111 section 6.8 Structure of
+ // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
+ // the ME should set the CR(comprehension required) flag to
+ // comprehension not required.(CR=0)"
+ // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
+ // the CR flag is not set.
+ tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
buf.write(tag);
buf.write(0x02); // length
buf.write(DEV_ID_TERMINAL); // source device id
@@ -348,6 +358,8 @@
// Fill optional data for each corresponding command
if (resp != null) {
resp.format(buf);
+ } else {
+ encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
}
byte[] rawData = buf.toByteArray();
@@ -359,6 +371,52 @@
mCmdIf.sendTerminalResponse(hexString, null);
}
+ private void encodeOptionalTags(CommandDetails cmdDet,
+ ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
+ switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
+ case GET_INKEY:
+ // ETSI TS 102 384,27.22.4.2.8.4.2.
+ // If it is a response for GET_INKEY command and the response timeout
+ // occured, then add DURATION TLV for variable timeout case.
+ if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
+ (cmdInput != null) && (cmdInput.duration != null)) {
+ getInKeyResponse(buf, cmdInput);
+ }
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
+ (resultCode.value() == ResultCode.OK.value())) {
+ getPliResponse(buf);
+ }
+ break;
+ default:
+ CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+ break;
+ }
+ }
+
+ private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
+ int tag = ComprehensionTlvTag.DURATION.value();
+
+ buf.write(tag);
+ buf.write(0x02); // length
+ buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
+ buf.write(cmdInput.duration.timeInterval); // Time Duration
+ }
+
+ private void getPliResponse(ByteArrayOutputStream buf) {
+
+ // Locale Language Setting
+ String lang = SystemProperties.get("persist.sys.language");
+
+ if (lang != null) {
+ // tag
+ int tag = ComprehensionTlvTag.LANGUAGE.value();
+ buf.write(tag);
+ ResponseData.writeLength(buf, lang.length());
+ buf.write(lang.getBytes(), 0, lang.length());
+ }
+ }
private void sendMenuSelection(int menuId, boolean helpRequired) {
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index edb2dc8..12204a0 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -52,6 +52,9 @@
static final int REFRESH_NAA_INIT = 0x03;
static final int REFRESH_UICC_RESET = 0x04;
+ // Command Qualifier values for PLI command
+ static final int LANGUAGE_SETTING = 0x04;
+
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
IccFileHandler fh) {
if (sInstance != null) {
@@ -112,7 +115,10 @@
AppInterface.CommandType cmdType = AppInterface.CommandType
.fromInt(cmdDet.typeOfCommand);
if (cmdType == null) {
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ // This PROACTIVE COMMAND is presently not handled. Hence set
+ // result code as BEYOND_TERMINAL_CAPABILITY in TR.
+ mCmdParams = new CommandParams(cmdDet);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
@@ -155,10 +161,13 @@
case PLAY_TONE:
cmdPending = processPlayTone(cmdDet, ctlvs);
break;
+ case PROVIDE_LOCAL_INFORMATION:
+ cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
+ break;
default:
// unsupported proactive commands
mCmdParams = new CommandParams(cmdDet);
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
} catch (ResultException e) {
@@ -380,6 +389,12 @@
iconId = ValueParser.retrieveIconId(ctlv);
}
+ // parse duration
+ ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
+ if (ctlv != null) {
+ input.duration = ValueParser.retrieveDuration(ctlv);
+ }
+
input.minLen = 1;
input.maxLen = 1;
@@ -863,4 +878,20 @@
}
return false;
}
+
+ private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+ throws ResultException {
+ CatLog.d(this, "process ProvideLocalInfo");
+ switch (cmdDet.commandQualifier) {
+ case LANGUAGE_SETTING:
+ CatLog.d(this, "PLI [LANGUAGE_SETTING]");
+ mCmdParams = new CommandParams(cmdDet);
+ break;
+ default:
+ CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
+ mCmdParams = new CommandParams(cmdDet);
+ throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java
index 8bcaab9..13a5ad4 100644
--- a/telephony/java/com/android/internal/telephony/cat/Input.java
+++ b/telephony/java/com/android/internal/telephony/cat/Input.java
@@ -36,6 +36,7 @@
public boolean echo;
public boolean yesNo;
public boolean helpAvailable;
+ public Duration duration;
Input() {
text = "";
@@ -49,6 +50,7 @@
echo = false;
yesNo = false;
helpAvailable = false;
+ duration = null;
}
private Input(Parcel in) {
@@ -63,6 +65,7 @@
echo = in.readInt() == 1 ? true : false;
yesNo = in.readInt() == 1 ? true : false;
helpAvailable = in.readInt() == 1 ? true : false;
+ duration = in.readParcelable(null);
}
public int describeContents() {
@@ -81,6 +84,7 @@
dest.writeInt(echo ? 1 : 0);
dest.writeInt(yesNo ? 1 : 0);
dest.writeInt(helpAvailable ? 1 : 0);
+ dest.writeParcelable(duration, 0);
}
public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() {
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 84c08f8..677d66b 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -28,6 +28,16 @@
* the ByteArrayOutputStream object.
*/
public abstract void format(ByteArrayOutputStream buf);
+
+ public static void writeLength(ByteArrayOutputStream buf, int length) {
+ // As per ETSI 102.220 Sec7.1.2, if the total length is greater
+ // than 0x7F, it should be coded in two bytes and the first byte
+ // should be 0x81.
+ if (length > 0x7F) {
+ buf.write(0x81);
+ }
+ buf.write(length);
+ }
}
class SelectItemResponseData extends ResponseData {
@@ -120,7 +130,7 @@
}
// length - one more for data coding scheme.
- buf.write(data.length + 1);
+ writeLength(buf, data.length + 1);
// data coding scheme
if (mIsUcs2) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 65d87f5..e95b2f9 100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -442,7 +442,7 @@
*/
public static TextEncodingDetails calculateLength(CharSequence messageBody,
boolean use7bitOnly) {
- return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
+ return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index e9fea55..cf06dab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -402,6 +402,7 @@
/**
* Calculate the message text encoding length, fragmentation, and other details.
*
+ * @param msg message text
* @param force7BitEncoding ignore (but still count) illegal characters if true
* @return septet count, or -1 on failure
*/
@@ -424,9 +425,10 @@
ted.codeUnitCount = msg.length();
int octets = ted.codeUnitCount * 2;
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
@@ -801,7 +803,7 @@
*
* @param bData an instance of BearerData.
*
- * @return data byte array of raw encoded SMS bearer data.
+ * @return byte array of raw encoded SMS bearer data.
*/
public static byte[] encode(BearerData bData) {
bData.hasUserDataHeader = ((bData.userData != null) &&
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 876c223..2ae5a3c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -974,7 +974,9 @@
}
public void getCallWaiting(Message onComplete) {
- mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+ //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
+ //class parameter in call waiting interrogation to network
+ mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
}
public void setCallWaiting(boolean enable, Message onComplete) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 278e1ba..a77484a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -795,9 +795,10 @@
int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
ted.codeUnitCount = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ted.codeUnitsRemaining = (ted.msgCount *
+ MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
@@ -807,9 +808,10 @@
int octets = msgBody.length() * 2;
ted.codeUnitCount = msgBody.length();
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index e11bd42..b35814c 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -73,8 +73,6 @@
private static final String LOG_TAG = "SipPhone";
private static final boolean LOCAL_DEBUG = true;
- //private List<SipConnection> connections = new ArrayList<SipConnection>();
-
// A call that is ringing or (call) waiting
private SipCall ringingCall = new SipCall();
private SipCall foregroundCall = new SipCall();
@@ -111,6 +109,10 @@
return mProfile.getProfileName();
}
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
public boolean canTake(Object incomingCall) {
synchronized (SipPhone.class) {
if (!(incomingCall instanceof SipAudioCall)) return false;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
index c9e9762..611e3ea 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony.sip;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneNotifier;
import android.content.Context;
@@ -26,42 +25,25 @@
import java.text.ParseException;
/**
- * @hide
+ * {@hide}
*/
public class SipPhoneFactory {
- private static PhoneNotifier sPhoneNotifier = makeDefaultPhoneNotifier();
- private static Context sContext;
-
- public static void makeDefaultPhones(Context context) {
- makeDefaultPhone(context);
- }
-
- public static void makeDefaultPhone(Context context) {
- sContext = context;
- SipPhoneProxy.getInstance().setPhone(
- makePhone("sip:anonymous@localhost"));
- }
-
- public static Phone getDefaultPhone() {
- return SipPhoneProxy.getInstance();
- }
-
- public static SipPhone makePhone(String sipProfileUri) {
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @param context {@code Context} needed to create a Phone object
+ * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone
+ * object
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makePhone(String sipUri, Context context,
+ PhoneNotifier phoneNotifier) {
try {
- SipProfile profile = new SipProfile.Builder(sipProfileUri).build();
- return new SipPhone(sContext, sPhoneNotifier, profile);
+ SipProfile profile = new SipProfile.Builder(sipUri).build();
+ return new SipPhone(context, phoneNotifier, profile);
} catch (ParseException e) {
- Log.v("SipPhoneProxy", "setPhone", e);
+ Log.w("SipPhoneFactory", "makePhone", e);
return null;
}
}
-
- private static PhoneNotifier makeDefaultPhoneNotifier() {
- try {
- return new com.android.internal.telephony.SipPhoneNotifier();
- } catch (Error e) {
- Log.e("SipPhoneProxy", "makeDefaultPhoneNotifier", e);
- throw e;
- }
- }
}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
deleted file mode 100644
index 8fa2963..0000000
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.sip;
-
-import com.android.internal.telephony.*;
-import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-
-import android.content.Context;
-import android.net.NetworkProperties;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.CellLocation;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Temporary. Will be removed after integrating with CallManager.
- * (TODO)
- * @hide
- */
-public class SipPhoneProxy implements Phone {
- private static final String LOG_TAG = "PHONE";
-
- private static SipPhoneProxy sPhoneProxy = new SipPhoneProxy();
-
- public static SipPhoneProxy getInstance() {
- return sPhoneProxy;
- }
-
- private SipPhone mActivePhone;
- private CallProxy mRingingCall = new CallProxy();
- private CallProxy mForegroundCall = new CallProxy();
- private CallProxy mBackgroundCall = new CallProxy();
-
- private SipPhoneProxy() {
- }
-
- public void onNewCall(Object call) {
- if (mActivePhone.canTake(call)) {
- Log.v("SipPhoneProxy", "onNewCall(): call taken: " + call);
- } else {
- Log.v("SipPhoneProxy", "onNewCall(): call dropped: " + call);
- }
- }
-
- public synchronized void setPhone(SipPhone phone) {
- if (phone == null) return;
- if (mActivePhone != null) phone.migrateFrom(mActivePhone);
- mActivePhone = phone;
- mForegroundCall.setTarget(phone.getForegroundCall());
- mBackgroundCall.setTarget(phone.getBackgroundCall());
- mRingingCall.setTarget(phone.getRingingCall());
- }
-
- public synchronized Call getForegroundCall() {
- return mForegroundCall;
- }
-
- public synchronized Call getBackgroundCall() {
- return mBackgroundCall;
- }
-
- public synchronized Call getRingingCall() {
- return mRingingCall;
- }
-
-
- public ServiceState getServiceState() {
- return mActivePhone.getServiceState();
- }
-
- public CellLocation getCellLocation() {
- return mActivePhone.getCellLocation();
- }
-
- public DataState getDataConnectionState() {
- return mActivePhone.getDataConnectionState();
- }
-
- public DataState getDataConnectionState(String apnType) {
- return mActivePhone.getDataConnectionState(apnType);
- }
-
- public DataActivityState getDataActivityState() {
- return mActivePhone.getDataActivityState();
- }
-
- public Context getContext() {
- return mActivePhone.getContext();
- }
-
- public void disableDnsCheck(boolean b) {
- mActivePhone.disableDnsCheck(b);
- }
-
- public boolean isDnsCheckDisabled() {
- return mActivePhone.isDnsCheckDisabled();
- }
-
- public State getState() {
- return mActivePhone.getState();
- }
-
- public String getPhoneName() {
- return mActivePhone.getPhoneName();
- }
-
- public int getPhoneType() {
- return mActivePhone.getPhoneType();
- }
-
- public String[] getActiveApnTypes() {
- return mActivePhone.getActiveApnTypes();
- }
-
- public String getActiveApn() {
- return mActivePhone.getActiveApn();
- }
-
- public SignalStrength getSignalStrength() {
- return mActivePhone.getSignalStrength();
- }
-
- public void registerForUnknownConnection(Handler h, int what, Object obj) {
- mActivePhone.registerForUnknownConnection(h, what, obj);
- }
-
- public void unregisterForUnknownConnection(Handler h) {
- mActivePhone.unregisterForUnknownConnection(h);
- }
-
- public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
- }
-
- public void unregisterForPreciseCallStateChanged(Handler h) {
- mActivePhone.unregisterForPreciseCallStateChanged(h);
- }
-
- public void registerForNewRingingConnection(Handler h, int what, Object obj) {
- mActivePhone.registerForNewRingingConnection(h, what, obj);
- }
-
- public void unregisterForNewRingingConnection(Handler h) {
- mActivePhone.unregisterForNewRingingConnection(h);
- }
-
- public void registerForIncomingRing(Handler h, int what, Object obj) {
- mActivePhone.registerForIncomingRing(h, what, obj);
- }
-
- public void unregisterForIncomingRing(Handler h) {
- mActivePhone.unregisterForIncomingRing(h);
- }
-
- public void registerForDisconnect(Handler h, int what, Object obj) {
- mActivePhone.registerForDisconnect(h, what, obj);
- }
-
- public void unregisterForDisconnect(Handler h) {
- mActivePhone.unregisterForDisconnect(h);
- }
-
- public void registerForMmiInitiate(Handler h, int what, Object obj) {
- mActivePhone.registerForMmiInitiate(h, what, obj);
- }
-
- public void unregisterForMmiInitiate(Handler h) {
- mActivePhone.unregisterForMmiInitiate(h);
- }
-
- public void registerForMmiComplete(Handler h, int what, Object obj) {
- mActivePhone.registerForMmiComplete(h, what, obj);
- }
-
- public void unregisterForMmiComplete(Handler h) {
- mActivePhone.unregisterForMmiComplete(h);
- }
-
- public List<? extends MmiCode> getPendingMmiCodes() {
- return mActivePhone.getPendingMmiCodes();
- }
-
- public void sendUssdResponse(String ussdMessge) {
- mActivePhone.sendUssdResponse(ussdMessge);
- }
-
- public void registerForServiceStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForServiceStateChanged(h, what, obj);
- }
-
- public void unregisterForServiceStateChanged(Handler h) {
- mActivePhone.unregisterForServiceStateChanged(h);
- }
-
- public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
- mActivePhone.registerForSuppServiceNotification(h, what, obj);
- }
-
- public void unregisterForSuppServiceNotification(Handler h) {
- mActivePhone.unregisterForSuppServiceNotification(h);
- }
-
- public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
- mActivePhone.registerForSuppServiceFailed(h, what, obj);
- }
-
- public void unregisterForSuppServiceFailed(Handler h) {
- mActivePhone.unregisterForSuppServiceFailed(h);
- }
-
- public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
- mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj);
- }
-
- public void unregisterForInCallVoicePrivacyOn(Handler h){
- mActivePhone.unregisterForInCallVoicePrivacyOn(h);
- }
-
- public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
- mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj);
- }
-
- public void unregisterForInCallVoicePrivacyOff(Handler h){
- mActivePhone.unregisterForInCallVoicePrivacyOff(h);
- }
-
- public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
- mActivePhone.registerForCdmaOtaStatusChange(h,what,obj);
- }
-
- public void unregisterForCdmaOtaStatusChange(Handler h) {
- mActivePhone.unregisterForCdmaOtaStatusChange(h);
- }
-
- public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
- mActivePhone.registerForSubscriptionInfoReady(h, what, obj);
- }
-
- public void unregisterForSubscriptionInfoReady(Handler h) {
- mActivePhone.unregisterForSubscriptionInfoReady(h);
- }
-
- public void registerForEcmTimerReset(Handler h, int what, Object obj) {
- mActivePhone.registerForEcmTimerReset(h,what,obj);
- }
-
- public void unregisterForEcmTimerReset(Handler h) {
- mActivePhone.unregisterForEcmTimerReset(h);
- }
-
- public void registerForRingbackTone(Handler h, int what, Object obj) {
- mActivePhone.registerForRingbackTone(h,what,obj);
- }
-
- public void unregisterForRingbackTone(Handler h) {
- mActivePhone.unregisterForRingbackTone(h);
- }
-
- public void registerForResendIncallMute(Handler h, int what, Object obj) {
- mActivePhone.registerForResendIncallMute(h,what,obj);
- }
-
- public void unregisterForResendIncallMute(Handler h) {
- mActivePhone.unregisterForResendIncallMute(h);
- }
-
- public boolean getIccRecordsLoaded() {
- return mActivePhone.getIccRecordsLoaded();
- }
-
- public IccCard getIccCard() {
- return mActivePhone.getIccCard();
- }
-
- public void acceptCall() throws CallStateException {
- mActivePhone.acceptCall();
- }
-
- public void rejectCall() throws CallStateException {
- mActivePhone.rejectCall();
- }
-
- public void switchHoldingAndActive() throws CallStateException {
- mActivePhone.switchHoldingAndActive();
- }
-
- public boolean canConference() {
- return mActivePhone.canConference();
- }
-
- public void conference() throws CallStateException {
- mActivePhone.conference();
- }
-
- public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
- mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete);
- }
-
- public void getEnhancedVoicePrivacy(Message onComplete) {
- mActivePhone.getEnhancedVoicePrivacy(onComplete);
- }
-
- public boolean canTransfer() {
- return mActivePhone.canTransfer();
- }
-
- public void explicitCallTransfer() throws CallStateException {
- mActivePhone.explicitCallTransfer();
- }
-
- public void clearDisconnected() {
- mActivePhone.clearDisconnected();
- }
-
- public Connection dial(String dialString) throws CallStateException {
- return mActivePhone.dial(dialString);
- }
-
- public boolean handlePinMmi(String dialString) {
- return mActivePhone.handlePinMmi(dialString);
- }
-
- public boolean handleInCallMmiCommands(String command) throws CallStateException {
- return mActivePhone.handleInCallMmiCommands(command);
- }
-
- public void sendDtmf(char c) {
- mActivePhone.sendDtmf(c);
- }
-
- public void startDtmf(char c) {
- mActivePhone.startDtmf(c);
- }
-
- public void stopDtmf() {
- mActivePhone.stopDtmf();
- }
-
- public void setRadioPower(boolean power) {
- mActivePhone.setRadioPower(power);
- }
-
- public boolean getMessageWaitingIndicator() {
- return mActivePhone.getMessageWaitingIndicator();
- }
-
- public boolean getCallForwardingIndicator() {
- return mActivePhone.getCallForwardingIndicator();
- }
-
- public String getLine1Number() {
- return mActivePhone.getLine1Number();
- }
-
- public String getCdmaMin() {
- return mActivePhone.getCdmaMin();
- }
-
- public boolean isMinInfoReady() {
- return mActivePhone.isMinInfoReady();
- }
-
- public String getCdmaPrlVersion() {
- return mActivePhone.getCdmaPrlVersion();
- }
-
- public String getLine1AlphaTag() {
- return mActivePhone.getLine1AlphaTag();
- }
-
- public void setLine1Number(String alphaTag, String number, Message onComplete) {
- mActivePhone.setLine1Number(alphaTag, number, onComplete);
- }
-
- public String getVoiceMailNumber() {
- return mActivePhone.getVoiceMailNumber();
- }
-
- /** @hide */
- public int getVoiceMessageCount(){
- return mActivePhone.getVoiceMessageCount();
- }
-
- public String getVoiceMailAlphaTag() {
- return mActivePhone.getVoiceMailAlphaTag();
- }
-
- public void setVoiceMailNumber(String alphaTag,String voiceMailNumber,
- Message onComplete) {
- mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
- }
-
- public void getCallForwardingOption(int commandInterfaceCFReason,
- Message onComplete) {
- mActivePhone.getCallForwardingOption(commandInterfaceCFReason,
- onComplete);
- }
-
- public void setCallForwardingOption(int commandInterfaceCFReason,
- int commandInterfaceCFAction, String dialingNumber,
- int timerSeconds, Message onComplete) {
- mActivePhone.setCallForwardingOption(commandInterfaceCFReason,
- commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete);
- }
-
- public void getOutgoingCallerIdDisplay(Message onComplete) {
- mActivePhone.getOutgoingCallerIdDisplay(onComplete);
- }
-
- public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
- Message onComplete) {
- mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode,
- onComplete);
- }
-
- public void getCallWaiting(Message onComplete) {
- mActivePhone.getCallWaiting(onComplete);
- }
-
- public void setCallWaiting(boolean enable, Message onComplete) {
- mActivePhone.setCallWaiting(enable, onComplete);
- }
-
- public void getAvailableNetworks(Message response) {
- mActivePhone.getAvailableNetworks(response);
- }
-
- public void setNetworkSelectionModeAutomatic(Message response) {
- mActivePhone.setNetworkSelectionModeAutomatic(response);
- }
-
- public void selectNetworkManually(NetworkInfo network, Message response) {
- mActivePhone.selectNetworkManually(network, response);
- }
-
- public void setPreferredNetworkType(int networkType, Message response) {
- mActivePhone.setPreferredNetworkType(networkType, response);
- }
-
- public void getPreferredNetworkType(Message response) {
- mActivePhone.getPreferredNetworkType(response);
- }
-
- public void getNeighboringCids(Message response) {
- mActivePhone.getNeighboringCids(response);
- }
-
- public void setOnPostDialCharacter(Handler h, int what, Object obj) {
- mActivePhone.setOnPostDialCharacter(h, what, obj);
- }
-
- public void setMute(boolean muted) {
- mActivePhone.setMute(muted);
- }
-
- public boolean getMute() {
- return mActivePhone.getMute();
- }
-
- public void invokeOemRilRequestRaw(byte[] data, Message response) {
- mActivePhone.invokeOemRilRequestRaw(data, response);
- }
-
- public void invokeOemRilRequestStrings(String[] strings, Message response) {
- mActivePhone.invokeOemRilRequestStrings(strings, response);
- }
-
- public void getDataCallList(Message response) {
- mActivePhone.getDataCallList(response);
- }
-
- public List<DataConnection> getCurrentDataConnectionList() {
- return mActivePhone.getCurrentDataConnectionList();
- }
-
- public void updateServiceLocation() {
- mActivePhone.updateServiceLocation();
- }
-
- public void enableLocationUpdates() {
- mActivePhone.enableLocationUpdates();
- }
-
- public void disableLocationUpdates() {
- mActivePhone.disableLocationUpdates();
- }
-
- public void setUnitTestMode(boolean f) {
- mActivePhone.setUnitTestMode(f);
- }
-
- public boolean getUnitTestMode() {
- return mActivePhone.getUnitTestMode();
- }
-
- public void setBandMode(int bandMode, Message response) {
- mActivePhone.setBandMode(bandMode, response);
- }
-
- public void queryAvailableBandMode(Message response) {
- mActivePhone.queryAvailableBandMode(response);
- }
-
- public boolean getDataRoamingEnabled() {
- return mActivePhone.getDataRoamingEnabled();
- }
-
- public void setDataRoamingEnabled(boolean enable) {
- mActivePhone.setDataRoamingEnabled(enable);
- }
-
- public void queryCdmaRoamingPreference(Message response) {
- mActivePhone.queryCdmaRoamingPreference(response);
- }
-
- public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
- mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response);
- }
-
- public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
- mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response);
- }
-
- public SimulatedRadioControl getSimulatedRadioControl() {
- return mActivePhone.getSimulatedRadioControl();
- }
-
- public boolean enableDataConnectivity() {
- return mActivePhone.enableDataConnectivity();
- }
-
- public boolean disableDataConnectivity() {
- return mActivePhone.disableDataConnectivity();
- }
-
- public int enableApnType(String type) {
- return mActivePhone.enableApnType(type);
- }
-
- public int disableApnType(String type) {
- return mActivePhone.disableApnType(type);
- }
-
- public boolean isDataConnectivityEnabled() {
- return mActivePhone.isDataConnectivityEnabled();
- }
-
- public boolean isDataConnectivityPossible() {
- return mActivePhone.isDataConnectivityPossible();
- }
-
- public String getInterfaceName(String apnType) {
- return mActivePhone.getInterfaceName(apnType);
- }
-
- public String getIpAddress(String apnType) {
- return mActivePhone.getIpAddress(apnType);
- }
-
- public String getGateway(String apnType) {
- return mActivePhone.getGateway(apnType);
- }
-
- public String[] getDnsServers(String apnType) {
- return mActivePhone.getDnsServers(apnType);
- }
-
- public String getDeviceId() {
- return mActivePhone.getDeviceId();
- }
-
- public String getDeviceSvn() {
- return mActivePhone.getDeviceSvn();
- }
-
- public String getSubscriberId() {
- return mActivePhone.getSubscriberId();
- }
-
- public String getIccSerialNumber() {
- return mActivePhone.getIccSerialNumber();
- }
-
- public String getEsn() {
- return mActivePhone.getEsn();
- }
-
- public String getMeid() {
- return mActivePhone.getMeid();
- }
-
- public PhoneSubInfo getPhoneSubInfo(){
- return mActivePhone.getPhoneSubInfo();
- }
-
- public IccSmsInterfaceManager getIccSmsInterfaceManager(){
- return mActivePhone.getIccSmsInterfaceManager();
- }
-
- public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
- return mActivePhone.getIccPhoneBookInterfaceManager();
- }
-
- public void setTTYMode(int ttyMode, Message onComplete) {
- mActivePhone.setTTYMode(ttyMode, onComplete);
- }
-
- public void queryTTYMode(Message onComplete) {
- mActivePhone.queryTTYMode(onComplete);
- }
-
- public void activateCellBroadcastSms(int activate, Message response) {
- mActivePhone.activateCellBroadcastSms(activate, response);
- }
-
- public void getCellBroadcastSmsConfig(Message response) {
- mActivePhone.getCellBroadcastSmsConfig(response);
- }
-
- public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
- mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response);
- }
-
- public void notifyDataActivity() {
- mActivePhone.notifyDataActivity();
- }
-
- public void getSmscAddress(Message result) {
- mActivePhone.getSmscAddress(result);
- }
-
- public void setSmscAddress(String address, Message result) {
- mActivePhone.setSmscAddress(address, result);
- }
-
- public int getCdmaEriIconIndex() {
- return mActivePhone.getCdmaEriIconIndex();
- }
-
- public String getCdmaEriText() {
- return mActivePhone.getCdmaEriText();
- }
-
- public int getCdmaEriIconMode() {
- return mActivePhone.getCdmaEriIconMode();
- }
-
- public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
- mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete);
- }
-
- public void exitEmergencyCallbackMode(){
- mActivePhone.exitEmergencyCallbackMode();
- }
-
- public boolean isOtaSpNumber(String dialStr){
- return mActivePhone.isOtaSpNumber(dialStr);
- }
-
- public void registerForCallWaiting(Handler h, int what, Object obj){
- mActivePhone.registerForCallWaiting(h,what,obj);
- }
-
- public void unregisterForCallWaiting(Handler h){
- mActivePhone.unregisterForCallWaiting(h);
- }
-
- public void registerForSignalInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForSignalInfo(h,what,obj);
- }
-
- public void unregisterForSignalInfo(Handler h) {
- mActivePhone.unregisterForSignalInfo(h);
- }
-
- public void registerForDisplayInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForDisplayInfo(h,what,obj);
- }
-
- public void unregisterForDisplayInfo(Handler h) {
- mActivePhone.unregisterForDisplayInfo(h);
- }
-
- public void registerForNumberInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForNumberInfo(h, what, obj);
- }
-
- public void unregisterForNumberInfo(Handler h) {
- mActivePhone.unregisterForNumberInfo(h);
- }
-
- public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForRedirectedNumberInfo(h, what, obj);
- }
-
- public void unregisterForRedirectedNumberInfo(Handler h) {
- mActivePhone.unregisterForRedirectedNumberInfo(h);
- }
-
- public void registerForLineControlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForLineControlInfo( h, what, obj);
- }
-
- public void unregisterForLineControlInfo(Handler h) {
- mActivePhone.unregisterForLineControlInfo(h);
- }
-
- public void registerFoT53ClirlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerFoT53ClirlInfo(h, what, obj);
- }
-
- public void unregisterForT53ClirInfo(Handler h) {
- mActivePhone.unregisterForT53ClirInfo(h);
- }
-
- public void registerForT53AudioControlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForT53AudioControlInfo( h, what, obj);
- }
-
- public void unregisterForT53AudioControlInfo(Handler h) {
- mActivePhone.unregisterForT53AudioControlInfo(h);
- }
-
- public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
- mActivePhone.setOnEcbModeExitResponse(h,what,obj);
- }
-
- public void unsetOnEcbModeExitResponse(Handler h){
- mActivePhone.unsetOnEcbModeExitResponse(h);
- }
-
- public Connection dial(String dialString, UUSInfo uusInfo)
- throws CallStateException {
- return mActivePhone.dial(dialString, uusInfo);
- }
-
- public boolean needsOtaServiceProvisioning() {
- return mActivePhone.needsOtaServiceProvisioning();
- }
-
- public NetworkProperties getNetworkProperties(String apnType) {
- return mActivePhone.getNetworkProperties(apnType);
- }
-}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 5ef1c69..88aa78ef 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -524,6 +524,7 @@
assertEquals("6502910000", PhoneNumberUtils.normalizeNumber("650 2910000"));
assertEquals("1234567", PhoneNumberUtils.normalizeNumber("12,3#4*567"));
assertEquals("8004664114", PhoneNumberUtils.normalizeNumber("800-GOOG-114"));
+ assertEquals("+16502910000", PhoneNumberUtils.normalizeNumber("+1 650 2910000"));
}
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
new file mode 100644
index 0000000..b214887
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+
+public class SmsMessageBodyTest extends AndroidTestCase {
+
+ private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
+ ":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
+ private static final String sGsmBasicChars = "\u00a3\u00a5\u00e8\u00e9" +
+ "\u00f9\u00ec\u00f2\u00c7\u00d8\u00f8\u00c5\u00e5\u0394\u03a6" +
+ "\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u00c6\u00e6" +
+ "\u00df\u00c9\u00a4\u00a1\u00c4\u00d6\u00d1\u00dc\u00a7\u00bf" +
+ "\u00e4\u00f6\u00f1\u00fc\u00e0";
+ private static final String sGsmExtendedAsciiChars = "{|}\\[~]^\f";
+ private static final String sGsmExtendedEuroSymbol = "\u20ac";
+ private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
+ "\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
+ "\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
+ "\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8" +
+ "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18" +
+ "\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78" +
+ "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
+ "\u00a2\u00a9\u00ae\u2122";
+
+ private static final int sTestLengthCount = 12;
+
+ private static final int[] sSeptetTestLengths =
+ { 0, 1, 2, 80, 159, 160, 161, 240, 305, 306, 307, 320};
+
+ private static final int[] sUnicodeTestLengths =
+ { 0, 1, 2, 35, 69, 70, 71, 100, 133, 134, 135, 160};
+
+ private static final int[] sTestMsgCounts =
+ { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3};
+
+ private static final int[] sSeptetUnitsRemaining =
+ {160, 159, 158, 80, 1, 0, 145, 66, 1, 0, 152, 139};
+
+ private static final int[] sUnicodeUnitsRemaining =
+ { 70, 69, 68, 35, 1, 0, 63, 34, 1, 0, 66, 41};
+
+
+ @SmallTest
+ public void testCalcLengthAscii() throws Exception {
+ StringBuilder sb = new StringBuilder(320);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int asciiCharsLen = sAsciiChars.length();
+
+ for (int i = 0; i < sTestLengthCount; i++) {
+ int len = sSeptetTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (asciiCharsLen - startPos > addCount) ?
+ (startPos + addCount) : asciiCharsLen;
+ sb.append(sAsciiChars, startPos, endPos);
+ startPos = (endPos == asciiCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sSeptetUnitsRemaining[i];
+
+ callGsmLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, true, values);
+ }
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsm() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsmExtended() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLengthUnicode() throws Exception {
+ StringBuilder sb = new StringBuilder(160);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT};
+ int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int unicodeCharsLen = sUnicodeChars.length();
+
+ // start with length 1: empty string uses ENCODING_7BIT
+ for (int i = 1; i < sTestLengthCount; i++) {
+ int len = sUnicodeTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (unicodeCharsLen - startPos > addCount) ?
+ (startPos + addCount) : unicodeCharsLen;
+ sb.append(sUnicodeChars, startPos, endPos);
+ startPos = (endPos == unicodeCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sUnicodeUnitsRemaining[i];
+ values7bit[1] = len;
+ values7bit[2] = MAX_USER_DATA_SEPTETS - len;
+
+ callGsmLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values7bit);
+ callCdmaLengthMethods(testStr, true, values7bit);
+ }
+ }
+
+ private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ // deprecated GSM-specific method
+ int[] values = android.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_GSM == activePhone) {
+ values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+
+ private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
+ int[] values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+
+ ted = com.android.internal.telephony.cdma.sms.BearerData.calcTextEncodingDetails(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
new file mode 100644
index 0000000..d31b294
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class Wap230WspContentTypeTest extends TestCase {
+
+ public static final Map<Integer, String> WELL_KNOWN_SHORT_MIME_TYPES
+ = new HashMap<Integer, String>();
+ public static final Map<Integer, String> WELL_KNOWN_LONG_MIME_TYPES
+ = new HashMap<Integer, String>();
+ public static final Map<Integer, String> WELL_KNOWN_PARAMETERS
+ = new HashMap<Integer, String>();
+
+ static {
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x00, "*/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x01, "text/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x02, "text/html");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x03, "text/plain");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x04, "text/x-hdml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x05, "text/x-ttml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x06, "text/x-vCalendar");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x07, "text/x-vCard");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x08, "text/vnd.wap.wml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0B, "multipart/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0C, "multipart/mixed");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0D, "multipart/form-data");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0E, "multipart/byterantes");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x0F, "multipart/alternative");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x10, "application/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x11, "application/java-vm");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x13, "application/x-hdmlc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1B, "application/x-x509-user-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1C, "image/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1D, "image/gif");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1E, "image/jpeg");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x1F, "image/tiff");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x20, "image/png");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x27, "application/xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x28, "text/xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2C, "application/x-x968-user-cert");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2D, "text/vnd.wap.si");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2E, "application/vnd.wap.sic");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x2F, "text/vnd.wap.sl");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x30, "application/vnd.wap.slc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x31, "text/vnd.wap.co");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x32, "application/vnd.wap.coc");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x34, "application/vnd.wap.sia");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x37, "application/pkcs7-mime");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3B, "application/xhtml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3C, "application/wml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3D, "text/css");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x44, "application/vnd.syncml.notification");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x4F, "audio/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x50, "video/*");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x52, "application/mikey");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x53, "application/vnd.oma.dcd");
+ WELL_KNOWN_SHORT_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc");
+
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x020C, "image/x-up-wpng");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0301, "application/iota.mmc-xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0305, "text/calendar");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0309, "text/directory;profile=vCard");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0314, "application/oma-directory+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid");
+ WELL_KNOWN_LONG_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger");
+
+ WELL_KNOWN_PARAMETERS.put(0x00, "Q");
+ WELL_KNOWN_PARAMETERS.put(0x01, "Charset");
+ WELL_KNOWN_PARAMETERS.put(0x02, "Level");
+ WELL_KNOWN_PARAMETERS.put(0x03, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x07, "Differences");
+ WELL_KNOWN_PARAMETERS.put(0x08, "Padding");
+ WELL_KNOWN_PARAMETERS.put(0x09, "Type");
+ WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age");
+ WELL_KNOWN_PARAMETERS.put(0x10, "Secure");
+ WELL_KNOWN_PARAMETERS.put(0x11, "SEC");
+ WELL_KNOWN_PARAMETERS.put(0x12, "MAC");
+ WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date");
+ WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date");
+ WELL_KNOWN_PARAMETERS.put(0x15, "Read-date");
+ WELL_KNOWN_PARAMETERS.put(0x16, "Size");
+ WELL_KNOWN_PARAMETERS.put(0x17, "Name");
+ WELL_KNOWN_PARAMETERS.put(0x18, "Filename");
+ WELL_KNOWN_PARAMETERS.put(0x19, "Start");
+ WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info");
+ WELL_KNOWN_PARAMETERS.put(0x1B, "Comment");
+ WELL_KNOWN_PARAMETERS.put(0x1C, "Domain");
+ WELL_KNOWN_PARAMETERS.put(0x1D, "Path");
+
+ }
+
+ final int WSP_DEFINED_SHORT_MIME_TYPE_COUNT = 85;
+ final int WSP_DEFINED_LONG_MIME_TYPE_COUNT = 85;
+
+ private static final byte WSP_STRING_TERMINATOR = 0x00;
+ private static final byte WSP_SHORT_INTEGER_MASK = (byte) 0x80;
+ private static final byte WSP_LENGTH_QUOTE = 0x1F;
+ private static final byte WSP_QUOTE = 0x22;
+
+ private static final short LONG_MIME_TYPE_OMA_DIRECTORY_XML = 0x0314;
+ private static final short LONG_MIME_TYPE_UNASSIGNED = 0x052C;
+
+ private static final byte SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE = 0x3F;
+ private static final byte SHORT_MIME_TYPE_UNASSIGNED = 0x60;
+
+ private static final String STRING_MIME_TYPE_ROLLOVER_CERTIFICATE
+ = "application/vnd.wap.rollover-certificate";
+
+ private static final byte TYPED_PARAM_Q = 0x00;
+ private static final byte TYPED_PARAM_DOMAIN = 0x1C;
+ private static final byte PARAM_UNASSIGNED = 0x42;
+ private static final byte PARAM_NO_VALUE = 0x00;
+ private static final byte TYPED_PARAM_SEC = 0x11;
+ private static final byte TYPED_PARAM_MAC = 0x12;
+
+ public void testHasExpectedNumberOfShortMimeTypes() {
+ assertEquals(WSP_DEFINED_SHORT_MIME_TYPE_COUNT, WELL_KNOWN_SHORT_MIME_TYPES.size());
+ }
+
+ public void testHasExpectedNumberOfLongMimeTypes() {
+ assertEquals(WSP_DEFINED_LONG_MIME_TYPE_COUNT, WELL_KNOWN_LONG_MIME_TYPES.size());
+ }
+
+ public void testWellKnownShortIntegerMimeTypeValues() {
+
+ for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+ WspTypeDecoder unit = new WspTypeDecoder(
+ HexDump.toByteArray((byte) (value | WSP_SHORT_INTEGER_MASK)));
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ int wellKnownValue = (int) unit.getValue32();
+ assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+ assertEquals(value, wellKnownValue);
+ assertEquals(1, unit.getDecodedDataLength());
+ }
+ }
+
+ public void testWellKnownLongIntegerMimeTypeValues() {
+ byte headerLength = 3;
+ byte typeLength = 2;
+ for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) {
+ byte[] data = new byte[10];
+ data[0] = headerLength;
+ data[1] = typeLength;
+ data[2] = (byte) (value >> 8);
+ data[3] = (byte) (value & 0xFF);
+ WspTypeDecoder unit = new WspTypeDecoder(data);
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ int wellKnownValue = (int) unit.getValue32();
+ assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType);
+ assertEquals(value, wellKnownValue);
+ assertEquals(4, unit.getDecodedDataLength());
+ }
+ }
+
+ public void testDecodeReturnsFalse_WhenOnlyAZeroBytePresent() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x00);
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+
+ public void testConstrainedMediaExtensionMedia() throws Exception {
+
+ String testType = "application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(19, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthExtensionMedia() throws Exception {
+
+ String testType = "12345678901234567890123456789";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.length() + 1);
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(31, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthWellKnownShortInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x01);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(2, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormShortLengthWellKnownShortIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x01);
+ out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertNull(mimeType);
+ assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(2, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormShortLengthWellKnownLongInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(0x03); // header length
+ out.write(0x02); // type length (2 octets)
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals("application/oma-directory+xml", mimeType);
+ assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormShortLengthWellKnownLongIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(0x03); // Value-length, short-length
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+ out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertNull(mimeType);
+ assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownShortInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x01); // Length as UINTVAR
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(3, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownShortIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x01); // Length as UINTVAR
+ out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+ assertNull(mimeType);
+ assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(3, unit.getDecodedDataLength());
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownLongInteger() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x03); // Length as UINTVAR
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8);
+ out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals("application/oma-directory+xml", mimeType);
+ assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteWellKnownLongIntegerWithUnknownValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x03); // Length as UINTVAR
+ out.write(0x02); // long-integer length (2 octets)
+ out.write(LONG_MIME_TYPE_UNASSIGNED >> 8);
+ out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertNull(mimeType);
+ assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteExtensionMedia() throws Exception {
+
+ String testType = "application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(testType.length() + 1); // Length as UINTVAR
+
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(21, unit.getDecodedDataLength());
+
+ }
+
+ public void testGeneralFormLengthQuoteExtensionMediaWithNiceLongMimeType() throws Exception {
+
+ String testType =
+ "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ +"01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write(WSP_LENGTH_QUOTE);
+ out.write(0x81); // Length as UINTVAR (161 decimal, 0xA1), 2 bytes
+ out.write(0x21);
+
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(164, unit.getDecodedDataLength());
+
+ }
+
+ public void testConstrainedMediaExtensionMediaWithSpace() throws Exception {
+
+ String testType = " application/wibble";
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(testType.getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(testType, mimeType);
+ assertEquals(-1, unit.getValue32());
+ assertEquals(20, unit.getDecodedDataLength());
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerNoValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x03); // Value-length, short-length
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write(PARAM_NO_VALUE);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(4, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals(null, params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x14); // Value-length, short-length
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(out.toByteArray().length, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownLongIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01);
+ out.write(TYPED_PARAM_DOMAIN);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerQuotedText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK);
+ out.write(WSP_QUOTE);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("Domain"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerCompactIntegerValue() {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x3);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(4, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("1", params.get("SEC"));
+
+ }
+
+ public void testTypedParamWellKnownShortIntegerMultipleParameters() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0B);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01 | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_MAC | WSP_SHORT_INTEGER_MASK);
+ out.write(WSP_QUOTE);
+ out.write("imapc".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(12, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("1", params.get("SEC"));
+ assertEquals("imapc", params.get("MAC"));
+ }
+
+ public void testUntypedParamIntegerValueShortInteger() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0A);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR); // EOS
+ out.write(0x45 | WSP_SHORT_INTEGER_MASK);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(11, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("69", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamIntegerValueLongInteger() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0C);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(0x02); // Short Length
+ out.write(0x42); // Long Integer byte 1
+ out.write(0x69); // Long Integer byte 2
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(13, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("17001", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamTextNoValue() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x0A);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(PARAM_NO_VALUE);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(11, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals(null, params.get("MYPARAM"));
+
+ }
+
+ public void testUntypedParamTextTokenText() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x11);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write("myvalue".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(18, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("myvalue", params.get("MYPARAM"));
+ }
+
+ public void testUntypedParamTextQuotedString() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x11);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+ out.write(WSP_QUOTE);
+ out.write("myvalue".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+ assertEquals(19, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("myvalue", params.get("MYPARAM"));
+
+ }
+
+ public void testDecodesReturnsFalse_ForParamWithMissingValue() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x09);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write("MYPARAM".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+
+ public void testTypedParamTextQValue() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x04);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(TYPED_PARAM_Q);
+ out.write(0x83); // Q value byte 1
+ out.write(0x31); // Q value byte 2
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(0x3F, unit.getValue32());
+ assertEquals(5, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("433", params.get("Q"));
+
+ }
+
+ public void testTypedParamUnassignedWellKnownShortIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x14);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(PARAM_UNASSIGNED | WSP_SHORT_INTEGER_MASK);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(21, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+
+ }
+
+ public void testTypedParamUnassignedWellKnownLongIntegerTokenText() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01); // Short-length of well-known parameter token
+ out.write(PARAM_UNASSIGNED);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+ out.write(WSP_STRING_TERMINATOR);
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertTrue(unit.decodeContentType(0));
+
+ String mimeType = unit.getValueString();
+
+ assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType);
+ assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32());
+
+ assertEquals(22, unit.getDecodedDataLength());
+
+ Map<String, String> params = unit.getContentParameters();
+ assertEquals("wdstechnology.com", params.get("unassigned/0x42"));
+ }
+
+ public void testDecodesReturnsFalse_WhenParamValueNotTerminated() throws Exception {
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(0x15);
+ out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK);
+ out.write(0x01);
+ out.write(PARAM_UNASSIGNED);
+ out.write("wdstechnology.com".getBytes("US-ASCII"));
+
+ WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray());
+ assertFalse(unit.decodeContentType(0));
+ }
+}
\ No newline at end of file
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index e1cb6bf..57a5b55 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -25,11 +25,23 @@
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="25dp"
+ android:saveEnabled="false"
android:textSize="18sp"
android:textColor="#ffffffff"
android:text="@string/waste_away"
/>
+ <CheckBox android:id="@+id/checkbox_wake"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="25dp"
+ android:layout_marginTop="25dp"
+ android:saveEnabled="false"
+ android:textSize="18sp"
+ android:textColor="#ffffffff"
+ android:text="@string/wake_away"
+ />
+
<ScrollView android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="0px"
diff --git a/tests/BatteryWaster/res/values/strings.xml b/tests/BatteryWaster/res/values/strings.xml
index 46c5fa1..a3b849a 100644
--- a/tests/BatteryWaster/res/values/strings.xml
+++ b/tests/BatteryWaster/res/values/strings.xml
@@ -18,5 +18,7 @@
<string name="waste_away">Discharge my battery!</string>
+ <string name="wake_away">Keep my device awake!</string>
+
</resources>
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 8ea7e00..48c4520 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -39,8 +39,11 @@
DateFormat mDateFormat;
IntentFilter mFilter;
PowerManager.WakeLock mWakeLock;
+ PowerManager.WakeLock mPartialWakeLock;
SpinThread mThread;
+ boolean mWasting, mWaking;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -50,6 +53,7 @@
setContentView(R.layout.battery_waster);
findViewById(R.id.checkbox).setOnClickListener(mClickListener);
+ findViewById(R.id.checkbox_wake).setOnClickListener(mWakeClickListener);
mLog = (TextView)findViewById(R.id.log);
mDateFormat = DateFormat.getInstance();
@@ -63,13 +67,27 @@
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
mWakeLock.setReferenceCounted(false);
+ mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
+ mPartialWakeLock.setReferenceCounted(false);
}
@Override
public void onPause() {
+ super.onPause();
stopRunning();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
+ }
+
View.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
CheckBox checkbox = (CheckBox)v;
@@ -81,23 +99,63 @@
}
};
+ View.OnClickListener mWakeClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkbox = (CheckBox)v;
+ if (checkbox.isChecked()) {
+ mWaking = true;
+ updateWakeLock();
+ } else {
+ mWaking = false;
+ updateWakeLock();
+ }
+ }
+ };
+
void startRunning() {
- log("Start");
- registerReceiver(mReceiver, mFilter);
- mWakeLock.acquire();
- if (mThread == null) {
- mThread = new SpinThread();
- mThread.start();
+ if (!mWasting) {
+ log("Start");
+ registerReceiver(mReceiver, mFilter);
+ mWasting = true;
+ updateWakeLock();
+ if (mThread == null) {
+ mThread = new SpinThread();
+ mThread.start();
+ }
}
}
void stopRunning() {
- log("Stop");
- unregisterReceiver(mReceiver);
- mWakeLock.release();
- if (mThread != null) {
- mThread.quit();
- mThread = null;
+ if (mWasting) {
+ log("Stop");
+ unregisterReceiver(mReceiver);
+ mWasting = false;
+ updateWakeLock();
+ if (mThread != null) {
+ mThread.quit();
+ mThread = null;
+ }
+ }
+ }
+
+ void updateWakeLock() {
+ if (mWasting) {
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ } else {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ if (mWaking) {
+ if (!mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.acquire();
+ }
+ } else {
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
}
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index b62db51..740f544 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -510,6 +510,14 @@
obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via the TestShellActivity
+ // as we need access to the Webview.
+ mLayoutTestController.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
Message message = obtainMessage(OVERRIDE_PREFERENCE);
message.getData().putString("key", key);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index acc0ff2..784a6d5 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -73,7 +73,6 @@
static final String[] ignoreTestList = {
"editing/selection/move-left-right.html", // Causes DumpRenderTree to hang
- "fast/dom/DeviceOrientation/basic-operation.html", // Will cause crash until mock DeviceOrientationClient is hooked up.
"fast/js/excessive-comma-usage.html", // Tests huge initializer list, causes OOM.
"fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM
"fast/regex/test1.html", // Causes DumpRenderTree to hang with V8
@@ -90,6 +89,8 @@
// This first block of tests are for 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("fast/dom/Geolocation/delayed-permission-allowed.html"); // requires layoutTestController.permissionSet
+ ignoreResultList.add("fast/dom/Geolocation/delayed-permission-denied.html"); // requires layoutTestController.permissionSet
ignoreResultList.add("fast/events/touch/basic-multi-touch-events.html"); // Requires multi-touch
ignoreResultList.add("fast/events/touch/touch-target.html"); // Requires multi-touch
ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 15288b5..9be2f1c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -71,4 +71,8 @@
// For XSSAuditor tests
public void setXSSAuditorEnabled(boolean flag);
+
+ // For DeviceOrientation tests
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 8c7254c..db076da 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -150,6 +150,9 @@
if (intent != null) {
executeIntent(intent);
}
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mWebView.useMockDeviceOrientation();
}
@Override
@@ -494,6 +497,12 @@
mGeolocationPermission = allow;
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
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
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
index 948ad72..81fc633 100644
--- a/tests/DumpRenderTree2/Android.mk
+++ b/tests/DumpRenderTree2/Android.mk
@@ -1,3 +1,18 @@
+#
+# 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)
@@ -5,6 +20,8 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch
LOCAL_PACKAGE_NAME := DumpRenderTree2
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index 9f6097a..dd0c4e9f 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -16,6 +16,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2">
<application>
+ <uses-library android:name="android.test.runner" />
+
<activity android:name=".ui.DirListActivity"
android:label="Dump Render Tree 2"
android:configChanges="orientation">
@@ -25,8 +27,15 @@
</intent-filter>
</activity>
+ <!-- android:launchMode="singleTask" is there so we only have a one instance
+ of this activity. However, it doesn't seem to work exactly like described in the
+ documentation, because the behaviour of the application suggest
+ there is only a single task for all 3 activities. We don't understand
+ how exactly it all works, but at the moment it works just fine.
+ It can lead to some weird behaviour in the future. -->
<activity android:name=".TestsListActivity"
- android:label="Tests' list activity">
+ android:label="Tests' list activity"
+ android:launchMode="singleTask">
</activity>
<activity android:name=".LayoutTestsExecutor"
@@ -38,6 +47,10 @@
</service>
</application>
+ <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+ android:targetPackage="com.android.dumprendertree2"
+ android:label="Layout tests script runner" />
+
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SDCARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/tests/DumpRenderTree2/assets/run-apache2.py b/tests/DumpRenderTree2/assets/run-apache2.py
new file mode 100644
index 0000000..cffbe37
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run-apache2.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+#
+# 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.
+#
+"""Start, stop, or restart apache2 server.
+
+ Apache2 must be installed with mod_php!
+
+ Usage:
+ run-apache2.py start|stop|restart
+"""
+
+import sys
+import os
+import subprocess
+import logging
+
+def main():
+ if len(sys.argv) < 2:
+ run_cmd = ""
+ else:
+ run_cmd = sys.argv[1]
+
+ #Setup logging class
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ if not run_cmd in ("start", "stop", "restart"):
+ logging.info("illegal argument: " + run_cmd)
+ logging.info("Usage: python run-apache2.py start|stop|restart")
+ return
+
+ #Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
+ tmp_WebKit = os.path.join("/tmp", "WebKit")
+ if not os.path.exists(tmp_WebKit):
+ os.mkdir(tmp_WebKit)
+
+ #Get the path to android tree root based on the script location.
+ #Basically we go 5 levels up
+ parent = os.pardir
+ script_location = os.path.abspath(os.path.dirname(sys.argv[0]))
+ android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
+ android_tree_root = os.path.normpath(android_tree_root)
+
+ #Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
+ envvars_path = os.path.join("/etc", "apache2", "envvars")
+ export_envvars_cmd = "source " + envvars_path
+
+ error_log_path = os.path.join(tmp_WebKit, "apache2-error.log")
+
+ #Prepare the command to (re)start/stop the server with specified settings
+ apache2_restart_cmd = "apache2 -k " + run_cmd
+ directives = " -c \"ServerRoot " + android_tree_root + "\""
+ directives += " -c \"DocumentRoot " + os.path.join("external", "webkit") + "\""
+
+ #This directive is commented out in apache2-debian-httpd.conf for some reason
+ #However, it is useful to browse through tests in the browser, so it's added here.
+ #One thing to note is that because of problems with mod_dir and port numbers, mod_dir
+ #is turned off. That means that there _must_ be a trailing slash at the end of URL
+ #for auto indexes to work correctly.
+ directives += " -c \"LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so\""
+
+ directives += " -c \"ErrorLog " + error_log_path +"\""
+ directives += " -c \"SSLCertificateFile " + os.path.join ("external", "webkit", "LayoutTests",
+ "http", "conf", "webkit-httpd.pem") + "\""
+ directives += " -c \"User ${APACHE_RUN_USER}\""
+ directives += " -c \"Group ${APACHE_RUN_GROUP}\""
+ directives += " -C \"TypesConfig " + os.path.join("/etc", "mime.types") + "\""
+ conf_file_cmd = " -f " + os.path.join(android_tree_root, "external", "webkit", "LayoutTests",
+ "http", "conf", "apache2-debian-httpd.conf")
+
+ #Try to execute the commands
+ logging.info("Will " + run_cmd + " apache2 server.")
+ cmd = export_envvars_cmd + " && " + apache2_restart_cmd + directives + conf_file_cmd
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (out, err) = p.communicate()
+
+ #Output the stdout from the command to console
+ logging.info(out)
+
+ #Report any errors
+ if p.returncode != 0:
+ logging.info("!! ERRORS:")
+
+ if err.find(envvars_path) != -1:
+ logging.info(err)
+ elif err.find('command not found') != -1:
+ logging.info("apache2 is probably not installed")
+ else:
+ logging.info(err)
+ logging.info("Try looking in " + error_log_path + " for details")
+ else:
+ logging.info("OK")
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
new file mode 100644
index 0000000..b13d8c9
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+"""Run layout tests on the device.
+
+ It runs the specified tests on the device, downloads the summaries to the temporary directory
+ and opens html details in the default browser.
+
+ Usage:
+ run_layout_tests.py PATH
+"""
+
+import sys
+import os
+import subprocess
+import logging
+import webbrowser
+import tempfile
+
+#TODO: These should not be hardcoded
+RESULTS_ABSOLUTE_PATH = "/sdcard/android/LayoutTests-results/"
+DETAILS_HTML = "details.html"
+SUMMARY_TXT = "summary.txt"
+
+def main():
+ if len(sys.argv) > 1:
+ path = sys.argv[1]
+ else:
+ path = ""
+
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ tmpdir = tempfile.gettempdir()
+
+ # Run the tests in path
+ cmd = "adb shell am instrument "
+ cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
+ cmd += "-e path \"" + path + "\" "
+ cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+
+ logging.info("Running the tests...")
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ logging.info("Downloading the summaries...")
+
+ # Download the txt summary to tmp folder
+ summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Download the html summary to tmp folder
+ details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Print summary to console
+ logging.info("All done.\n")
+ cmd = "cat " + summary_txt_tmp_path
+ os.system(cmd)
+ logging.info("")
+
+ # Open the browser with summary
+ webbrowser.open(details_html_tmp_path)
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
new file mode 100644
index 0000000..5b7cbc4
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
@@ -0,0 +1,110 @@
+/*
+ * 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.dumprendertree2;
+
+import android.webkit.WebView;
+
+/**
+ * A class that acts as a JS interface for webview to mock various touch events,
+ * mouse actions and key presses.
+ *
+ * The methods here just call corresponding methods on EventSenderImpl
+ * that contains the logic of how to execute the methods.
+ */
+public class EventSender {
+ EventSenderImpl mEventSenderImpl = new EventSenderImpl();
+
+ public void reset(WebView webView) {
+ mEventSenderImpl.reset(webView);
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ mEventSenderImpl.enableDOMUIEventLogging(domNode);
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ mEventSenderImpl.fireKeyboardEventsToElement(domNode);
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ mEventSenderImpl.keyDown(character, withModifiers);
+ }
+
+ public void keyDown(String character) {
+ keyDown(character, null);
+ }
+
+ public void leapForward(int milliseconds) {
+ mEventSenderImpl.leapForward(milliseconds);
+ }
+
+ public void mouseClick() {
+ mEventSenderImpl.mouseClick();
+ }
+
+ public void mouseDown() {
+ mEventSenderImpl.mouseDown();
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderImpl.mouseMoveTo(x, y);
+ }
+
+ public void mouseUp() {
+ mEventSenderImpl.mouseUp();
+ }
+
+ public void touchStart() {
+ mEventSenderImpl.touchStart();
+ }
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderImpl.addTouchPoint(x, y);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ mEventSenderImpl.updateTouchPoint(id, x, y);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ mEventSenderImpl.setTouchModifier(modifier, enabled);
+ }
+
+ public void touchMove() {
+ mEventSenderImpl.touchMove();
+ }
+
+ public void releaseTouchPoint(int id) {
+ mEventSenderImpl.releaseTouchPoint(id);
+ }
+
+ public void touchEnd() {
+ mEventSenderImpl.touchEnd();
+ }
+
+ public void touchCancel() {
+ mEventSenderImpl.touchCancel();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderImpl.clearTouchPoints();
+ }
+
+ public void cancelTouchPoint(int id) {
+ mEventSenderImpl.cancelTouchPoint(id);
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
new file mode 100644
index 0000000..93e6137
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -0,0 +1,563 @@
+/*
+ * 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An implementation of EventSender
+ */
+public class EventSenderImpl {
+ private static final String LOG_TAG = "EventSenderImpl";
+
+ private static final int MSG_ENABLE_DOM_UI_EVENT_LOGGING = 0;
+ private static final int MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT = 1;
+ private static final int MSG_LEAP_FORWARD = 2;
+
+ private static final int MSG_KEY_DOWN = 3;
+
+ private static final int MSG_MOUSE_DOWN = 4;
+ private static final int MSG_MOUSE_UP = 5;
+ private static final int MSG_MOUSE_CLICK = 6;
+ private static final int MSG_MOUSE_MOVE_TO = 7;
+
+ private static final int MSG_ADD_TOUCH_POINT = 8;
+ private static final int MSG_TOUCH_START = 9;
+ private static final int MSG_UPDATE_TOUCH_POINT = 10;
+ private static final int MSG_TOUCH_MOVE = 11;
+ private static final int MSG_CLEAR_TOUCH_POINTS = 12;
+ private static final int MSG_TOUCH_CANCEL = 13;
+ private static final int MSG_RELEASE_TOUCH_POINT = 14;
+ private static final int MSG_TOUCH_END = 15;
+ private static final int MSG_SET_TOUCH_MODIFIER = 16;
+ private static final int MSG_CANCEL_TOUCH_POINT = 17;
+
+ public static class TouchPoint {
+ WebView mWebView;
+ private int mX;
+ private int mY;
+ private long mDownTime;
+ private boolean mReleased = false;
+ private boolean mMoved = false;
+ private boolean mCancelled = false;
+
+ public TouchPoint(WebView webView, int x, int y) {
+ mWebView = webView;
+ mX = scaleX(x);
+ mY = scaleY(y);
+ }
+
+ public int getX() {
+ return mX;
+ }
+
+ public int getY() {
+ return mY;
+ }
+
+ public boolean hasMoved() {
+ return mMoved;
+ }
+
+ public void move(int newX, int newY) {
+ mX = scaleX(newX);
+ mY = scaleY(newY);
+ mMoved = true;
+ }
+
+ public void resetHasMoved() {
+ mMoved = false;
+ }
+
+ public long getDownTime() {
+ return mDownTime;
+ }
+
+ public void setDownTime(long downTime) {
+ mDownTime = downTime;
+ }
+
+ public boolean isReleased() {
+ return mReleased;
+ }
+
+ public void release() {
+ mReleased = true;
+ }
+
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ }
+
+ private int scaleX(int x) {
+ return (int)(x * mWebView.getScale()) - mWebView.getScrollX();
+ }
+
+ private int scaleY(int y) {
+ return (int)(y * mWebView.getScale()) - mWebView.getScrollY();
+ }
+ }
+
+ private List<TouchPoint> mTouchPoints;
+ private int mTouchMetaState;
+ private int mMouseX;
+ private int mMouseY;
+
+ private WebView mWebView;
+
+ private Handler mEventSenderHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ TouchPoint touchPoint;
+ Bundle bundle;
+ KeyEvent event;
+
+ switch (msg.what) {
+ case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
+ /** TODO: implement */
+ break;
+
+ case MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT:
+ /** TODO: implement */
+ break;
+
+ case MSG_LEAP_FORWARD:
+ /** TODO: implement */
+ break;
+
+ case MSG_KEY_DOWN:
+ bundle = (Bundle)msg.obj;
+ String character = bundle.getString("character");
+ String[] withModifiers = bundle.getStringArray("withModifiers");
+
+ if (withModifiers != null && withModifiers.length > 0) {
+ for (int i = 0; i < withModifiers.length; i++) {
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ modifierToKeyCode(withModifiers[i]));
+ }
+ }
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ charToKeyCode(character.toLowerCase().toCharArray()[0]));
+ break;
+
+ /** MOUSE */
+
+ case MSG_MOUSE_DOWN:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_UP:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_CLICK:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_MOVE_TO:
+ int x = msg.arg1;
+ int y = msg.arg2;
+
+ event = null;
+ if (x > mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
+ } else if (x < mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ event = null;
+ if (y > mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
+ } else if (y < mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ mMouseX = x;
+ mMouseY = y;
+ break;
+
+ /** TOUCH */
+
+ case MSG_ADD_TOUCH_POINT:
+ getTouchPoints().add(new TouchPoint(mWebView,
+ msg.arg1, msg.arg2));
+ if (getTouchPoints().size() > 1) {
+ Log.w(LOG_TAG + "::MSG_ADD_TOUCH_POINT", "Added more than one touch point");
+ }
+ break;
+
+ case MSG_TOUCH_START:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ touchPoint.setDownTime(SystemClock.uptimeMillis());
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_DOWN);
+ break;
+
+ case MSG_UPDATE_TOUCH_POINT:
+ bundle = (Bundle)msg.obj;
+
+ int id = bundle.getInt("id");
+ if (id >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + id);
+ break;
+ }
+
+ getTouchPoints().get(id).move(bundle.getInt("x"), bundle.getInt("y"));
+ break;
+
+ case MSG_TOUCH_MOVE:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (!touchPoint.hasMoved()) {
+ return;
+ }
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_MOVE);
+ touchPoint.resetHasMoved();
+ break;
+
+ case MSG_CANCEL_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).cancel();
+ break;
+
+ case MSG_TOUCH_CANCEL:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (touchPoint.isCancelled()) {
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_CANCEL);
+ }
+ break;
+
+ case MSG_RELEASE_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).release();
+ break;
+
+ case MSG_TOUCH_END:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_UP);
+ if (touchPoint.isReleased()) {
+ getTouchPoints().remove(0);
+ touchPoint = null;
+ }
+ break;
+
+ case MSG_SET_TOUCH_MODIFIER:
+ bundle = (Bundle)msg.obj;
+ String modifier = bundle.getString("modifier");
+ boolean enabled = bundle.getBoolean("enabled");
+
+ int mask = 0;
+ if ("alt".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_ALT_ON;
+ } else if ("shift".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SHIFT_ON;
+ } else if ("ctrl".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SYM_ON;
+ }
+
+ if (enabled) {
+ mTouchMetaState |= mask;
+ } else {
+ mTouchMetaState &= ~mask;
+ }
+
+ break;
+
+ case MSG_CLEAR_TOUCH_POINTS:
+ getTouchPoints().clear();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ public void reset(WebView webView) {
+ mWebView = webView;
+ mTouchPoints = null;
+ mTouchMetaState = 0;
+ mMouseX = 0;
+ mMouseY = 0;
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_ENABLE_DOM_UI_EVENT_LOGGING);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void leapForward(int milliseconds) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_LEAP_FORWARD);
+ msg.arg1 = milliseconds;
+ msg.sendToTarget();
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ Bundle bundle = new Bundle();
+ bundle.putString("character", character);
+ bundle.putStringArray("withModifiers", withModifiers);
+ mEventSenderHandler.obtainMessage(MSG_KEY_DOWN, bundle).sendToTarget();
+ }
+
+ /** MOUSE */
+
+ public void mouseDown() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_DOWN);
+ }
+
+ public void mouseUp() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_UP);
+ }
+
+ public void mouseClick() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_CLICK);
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_MOUSE_MOVE_TO, x, y).sendToTarget();
+ }
+
+ /** TOUCH */
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_ADD_TOUCH_POINT, x, y).sendToTarget();
+ }
+
+ public void touchStart() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_START);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ Bundle bundle = new Bundle();
+ bundle.putInt("id", id);
+ bundle.putInt("x", x);
+ bundle.putInt("y", y);
+ mEventSenderHandler.obtainMessage(MSG_UPDATE_TOUCH_POINT, bundle).sendToTarget();
+ }
+
+ public void touchMove() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_MOVE);
+ }
+
+ public void cancelTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_CANCEL_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchCancel() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_CANCEL);
+ }
+
+ public void releaseTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_RELEASE_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchEnd() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_END);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ Bundle bundle = new Bundle();
+ bundle.putString("modifier", modifier);
+ bundle.putBoolean("enabled", enabled);
+ mEventSenderHandler.obtainMessage(MSG_SET_TOUCH_MODIFIER, bundle).sendToTarget();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderHandler.sendEmptyMessage(MSG_CLEAR_TOUCH_POINTS);
+ }
+
+ private List<TouchPoint> getTouchPoints() {
+ if (mTouchPoints == null) {
+ mTouchPoints = new LinkedList<TouchPoint>();
+ }
+
+ return mTouchPoints;
+ }
+
+ private void executeTouchEvent(TouchPoint touchPoint, int action) {
+ MotionEvent event =
+ MotionEvent.obtain(touchPoint.getDownTime(), SystemClock.uptimeMillis(),
+ action, touchPoint.getX(), touchPoint.getY(), mTouchMetaState);
+ mWebView.onTouchEvent(event);
+ }
+
+ private void executeKeyEvent(int action, int keyCode) {
+ KeyEvent event = new KeyEvent(action, keyCode);
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ }
+
+ /**
+ * Assumes lowercase chars, case needs to be handled by calling function.
+ */
+ private static int charToKeyCode(char c) {
+ // handle numbers
+ if (c >= '0' && c <= '9') {
+ int offset = c - '0';
+ return KeyEvent.KEYCODE_0 + offset;
+ }
+
+ // handle characters
+ if (c >= 'a' && c <= 'z') {
+ int offset = c - 'a';
+ return KeyEvent.KEYCODE_A + offset;
+ }
+
+ // handle all others
+ switch (c) {
+ case '*':
+ return KeyEvent.KEYCODE_STAR;
+
+ case '#':
+ return KeyEvent.KEYCODE_POUND;
+
+ case ',':
+ return KeyEvent.KEYCODE_COMMA;
+
+ case '.':
+ return KeyEvent.KEYCODE_PERIOD;
+
+ case '\t':
+ return KeyEvent.KEYCODE_TAB;
+
+ case ' ':
+ return KeyEvent.KEYCODE_SPACE;
+
+ case '\n':
+ return KeyEvent.KEYCODE_ENTER;
+
+ case '\b':
+ case 0x7F:
+ return KeyEvent.KEYCODE_DEL;
+
+ case '~':
+ return KeyEvent.KEYCODE_GRAVE;
+
+ case '-':
+ return KeyEvent.KEYCODE_MINUS;
+
+ case '=':
+ return KeyEvent.KEYCODE_EQUALS;
+
+ case '(':
+ return KeyEvent.KEYCODE_LEFT_BRACKET;
+
+ case ')':
+ return KeyEvent.KEYCODE_RIGHT_BRACKET;
+
+ case '\\':
+ return KeyEvent.KEYCODE_BACKSLASH;
+
+ case ';':
+ return KeyEvent.KEYCODE_SEMICOLON;
+
+ case '\'':
+ return KeyEvent.KEYCODE_APOSTROPHE;
+
+ case '/':
+ return KeyEvent.KEYCODE_SLASH;
+
+ default:
+ return c;
+ }
+ }
+
+ private static int modifierToKeyCode(String modifier) {
+ if (modifier.equals("ctrlKey")) {
+ return KeyEvent.KEYCODE_ALT_LEFT;
+ } else if (modifier.equals("shiftKey")) {
+ return KeyEvent.KEYCODE_SHIFT_LEFT;
+ } else if (modifier.equals("altKey")) {
+ return KeyEvent.KEYCODE_SYM;
+ }
+
+ return KeyEvent.KEYCODE_UNKNOWN;
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index 8ff5e63..6db9571 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -89,4 +89,12 @@
Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
MockGeolocation.getInstance().setError(code, message);
}
-}
\ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
+ // as we need access to the Webview.
+ mLayoutTestsExecutor.setMockDeviceOrientation(
+ canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index dc82c34..8cc4921 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -110,6 +110,8 @@
private boolean mSetGeolocationPermissionCalled;
private boolean mGeolocationPermission;
+ private EventSender mEventSender = new EventSender();
+
private WakeLock mScreenDimLock;
/** COMMUNICATION WITH ManagerService */
@@ -248,7 +250,7 @@
mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
mTotalTestCount = mCurrentTestIndex + mTestsList.size();
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
mScreenDimLock.acquire();
@@ -269,6 +271,8 @@
mCurrentWebView = new WebView(this);
setupWebView(mCurrentWebView);
+ mEventSender.reset(mCurrentWebView);
+
setContentView(mCurrentWebView);
if (previousWebView != null) {
Log.d(LOG_TAG + "::reset", "previousWebView != null");
@@ -280,6 +284,7 @@
webView.setWebViewClient(mWebViewClient);
webView.setWebChromeClient(mWebChromeClient);
webView.addJavascriptInterface(mLayoutTestController, "layoutTestController");
+ webView.addJavascriptInterface(mEventSender, "eventSender");
/**
* Setting a touch interval of -1 effectively disables the optimisation in WebView
@@ -303,6 +308,9 @@
webViewSettings.setDomStorageEnabled(true);
webViewSettings.setWorkersEnabled(false);
webViewSettings.setXSSAuditorEnabled(false);
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mCurrentWebView.useMockDeviceOrientation();
}
private void startTests() {
@@ -462,8 +470,7 @@
Handler mLayoutTestControllerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- assert mCurrentState.isRunningState()
- : "mCurrentState = " + mCurrentState.name();
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
switch (msg.what) {
case MSG_WAIT_UNTIL_DONE:
@@ -565,4 +572,10 @@
msg.arg1 = allow ? 1 : 0;
msg.sendToTarget();
}
-}
\ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 3bbfb89..31026d6 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -82,6 +82,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_FIRST_TEST:
+ mSummarizer.reset();
Bundle bundle = msg.getData();
ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
break;
@@ -92,7 +93,13 @@
break;
case MSG_ALL_TESTS_FINISHED:
+ mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
mSummarizer.summarize();
+ Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_SHUTDOWN);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
break;
}
}
@@ -115,6 +122,8 @@
private String mCurrentlyRunningTest;
private int mCurrentlyRunningTestIndex;
+ private String mAllTestsRelativePath;
+
@Override
public void onCreate() {
super.onCreate();
@@ -125,6 +134,8 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ mAllTestsRelativePath = intent.getStringExtra("path");
+ assert mAllTestsRelativePath != null;
return START_STICKY;
}
@@ -166,7 +177,8 @@
Intent intent = new Intent(this, TestsListActivity.class);
intent.setAction(Intent.ACTION_REBOOT);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
startActivity(intent);
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 1b73f97..43bc0b1 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -16,12 +16,20 @@
package com.android.dumprendertree2;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.util.DisplayMetrics;
+
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* A class that collects information about tests that ran and can create HTML
@@ -166,7 +174,8 @@
"</script>";
/** TODO: Make it a setting */
- private static final String HTML_SUMMARY_RELATIVE_PATH = "summary.html";
+ private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
+ private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
private int mCrashedTestsCount = 0;
private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>();
@@ -176,6 +185,10 @@
private FileFilter mFileFilter;
private String mResultsRootDirPath;
+ private String mTestsRelativePath;
+
+ private Date mDate;
+
public Summarizer(FileFilter fileFilter, String resultsRootDirPath) {
mFileFilter = fileFilter;
mResultsRootDirPath = resultsRootDirPath;
@@ -197,7 +210,46 @@
}
}
+ public void setTestsRelativePath(String testsRelativePath) {
+ mTestsRelativePath = testsRelativePath;
+ }
+
public void summarize() {
+ createHtmlDetails();
+ createTxtSummary();
+ }
+
+ public void reset() {
+ mCrashedTestsCount = 0;
+ mFailedNotIgnoredTests.clear();
+ mIgnoredTests.clear();
+ mPassedNotIgnoredTests.clear();
+ mDate = new Date();
+ }
+
+ private void createTxtSummary() {
+ StringBuilder txt = new StringBuilder();
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ txt.append(mTestsRelativePath + "\n");
+ txt.append("Date: " + dateFormat.format(mDate) + "\n");
+ txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
+ txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
+
+ txt.append("TOTAL: " + getTotalTestCount() + "\n");
+ if (mCrashedTestsCount > 0) {
+ txt.append("CRASHED (total among all tests): " + mCrashedTestsCount + "\n");
+ txt.append("-------------");
+ }
+ txt.append("FAILED: " + mFailedNotIgnoredTests.size() + "\n");
+ txt.append("IGNORED: " + mIgnoredTests.size() + "\n");
+ txt.append("PASSED: " + mPassedNotIgnoredTests.size() + "\n");
+
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
+ txt.toString().getBytes(), false);
+ }
+
+ private void createHtmlDetails() {
StringBuilder html = new StringBuilder();
html.append("<html><head>");
@@ -215,19 +267,38 @@
html.append("</body></html>");
- FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_SUMMARY_RELATIVE_PATH),
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
html.toString().getBytes(), false);
}
- private void createTopSummaryTable(StringBuilder html) {
- int total = mFailedNotIgnoredTests.size() +
+ private int getTotalTestCount() {
+ return mFailedNotIgnoredTests.size() +
mPassedNotIgnoredTests.size() +
mIgnoredTests.size();
+ }
+
+ private String getWebKitVersionFromUserAgentString() {
+ Resources resources = new Resources(new AssetManager(), new DisplayMetrics(),
+ new Configuration());
+ String userAgent =
+ resources.getString(com.android.internal.R.string.web_user_agent);
+
+ Matcher matcher = Pattern.compile("AppleWebKit/([0-9]+?\\.[0-9])").matcher(userAgent);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return "unknown";
+ }
+
+ private void createTopSummaryTable(StringBuilder html) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
- html.append("<h1> - total of " + total + " tests - ");
- html.append(dateFormat.format(new Date()) + "</h1>");
+ html.append("<h1>" + mTestsRelativePath + "</h1>");
+ html.append("<h3>" + "Date: " + dateFormat.format(new Date()) + "</h3>");
+ html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
+ html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
html.append("<table class=\"summary\">");
+ createSummaryTableRow(html, "TOTAL", getTotalTestCount());
createSummaryTableRow(html, "CRASHED", mCrashedTestsCount);
createSummaryTableRow(html, "FAILED", mFailedNotIgnoredTests.size());
createSummaryTableRow(html, "IGNORED", mIgnoredTests.size());
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
index c21463b..4965fd9 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -24,6 +24,8 @@
import android.os.Message;
import android.view.Window;
+import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
+
import java.util.ArrayList;
/**
@@ -57,6 +59,9 @@
private ArrayList<String> mTestsList;
private int mTotalTestCount;
+ private OnEverythingFinishedCallback mOnEverythingFinishedCallback;
+ private boolean mEverythingFinished;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -80,11 +85,21 @@
Message doneMsg = Message.obtain(mHandler, MSG_TEST_LIST_PRELOADER_DONE);
Intent serviceIntent = new Intent(this, ManagerService.class);
+ serviceIntent.putExtra("path", path);
startService(serviceIntent);
new TestsListPreloaderThread(path, doneMsg).start();
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
+ onCrashIntent(intent);
+ } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+ onEverythingFinishedIntent(intent);
+ }
+ }
+
/**
* This method handles an intent that comes from ManageService when crash is detected.
* The intent contains an index in mTestsList of the test that crashed. TestsListActivity
@@ -94,18 +109,28 @@
* LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
* detects the crash it sends a new intent and the flow repeats.
*/
- @Override
- protected void onNewIntent(Intent intent) {
- if (!intent.getAction().equals(Intent.ACTION_REBOOT)) {
- return;
- }
-
+ private void onCrashIntent(Intent intent) {
int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
restartExecutor(nextTestToRun);
}
}
+ public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) {
+ mOnEverythingFinishedCallback = callback;
+ if (mEverythingFinished) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ private void onEverythingFinishedIntent(Intent intent) {
+ /** TODO: Show some kind of summary to the user */
+ mEverythingFinished = true;
+ if (mOnEverythingFinishedCallback != null) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putStringArrayList("testsList", mTestsList);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
index f76105d..2145af7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -68,15 +68,15 @@
File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath);
if (!file.exists()) {
Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath);
- return;
+ } else {
+ /** Populate the tests' list accordingly */
+ if (file.isDirectory()) {
+ preloadTests(mRelativePath);
+ } else {
+ mTestsList.add(mRelativePath);
+ }
}
- /** Populate the tests' list accordingly */
- if (file.isDirectory()) {
- preloadTests(mRelativePath);
- } else {
- mTestsList.add(mRelativePath);
- }
mDoneMsg.obj = mTestsList;
mDoneMsg.sendToTarget();
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
new file mode 100644
index 0000000..e1d4364
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+/**
+ * Callback used to inform scriptsupport.Starter that everything is finished and
+ * we can exit
+ */
+public interface OnEverythingFinishedCallback {
+ public void onFinished();
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
new file mode 100644
index 0000000..78f58d5
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * Extends InstrumentationTestRunner to allow the script to pass arguments to the application
+ */
+public class ScriptTestRunner extends InstrumentationTestRunner {
+ String mTestsRelativePath;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ mTestsRelativePath = arguments.getString("path");
+ super.onCreate(arguments);
+ }
+
+ public String getTestsRelativePath() {
+ return mTestsRelativePath;
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
new file mode 100644
index 0000000..ddfae69
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.android.dumprendertree2.TestsListActivity;
+
+/**
+ * A class which provides methods that can be invoked by a script running on the host machine to
+ * run the tests.
+ *
+ * It starts a TestsListActivity and does not return until all the tests finish executing.
+ */
+public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> {
+ private static final String LOG_TAG = "Starter";
+ private boolean mEverythingFinished;
+
+ public Starter() {
+ super(TestsListActivity.class);
+ }
+
+ /**
+ * This method is called from adb to start executing the tests. It doesn't return
+ * until everything is finished so that the script can wait for the end if it needs
+ * to.
+ */
+ public void startLayoutTests() {
+ ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation();
+ String relativePath = runner.getTestsRelativePath();
+
+ Intent intent = new Intent();
+ intent.setClassName("com.android.dumprendertree2", "TestsListActivity");
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+ setActivityIntent(intent);
+ getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() {
+ /** This method is safe to call on any thread */
+ @Override
+ public void onFinished() {
+ synchronized (Starter.this) {
+ mEverythingFinished = true;
+ Starter.this.notifyAll();
+ }
+ }
+ });
+
+ synchronized (this) {
+ while (!mEverythingFinished) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
index 661a8ec..af0d7d1 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -378,6 +378,10 @@
private ListItem[] getDirList(String dirPath) {
File dir = new File(mRootDirPath, dirPath);
+ if (!dir.exists()) {
+ return new ListItem[0];
+ }
+
List<ListItem> subDirs = new ArrayList<ListItem>();
List<ListItem> subFiles = new ArrayList<ListItem>();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 73994f7..775dc249 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -171,5 +171,32 @@
</intent-filter>
</activity>
+ <activity
+ android:name="SimplePathsActivity"
+ android:label="_SimplePaths">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="AdvancedBlendActivity"
+ android:label="_AdvancedBlend">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="StackActivity"
+ android:label="_Stacks">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml
index f548f53..6bba370 100644
--- a/tests/HwAccelerationTest/res/layout/list_activity.xml
+++ b/tests/HwAccelerationTest/res/layout/list_activity.xml
@@ -19,6 +19,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="3dip"
+
+ android:text="Add" />
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginRight="10dip"
+
+ android:text="Remove" />
+
+ </LinearLayout>
+
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
diff --git a/tests/HwAccelerationTest/res/layout/stack.xml b/tests/HwAccelerationTest/res/layout/stack.xml
new file mode 100644
index 0000000..b4d2d73a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:focusable="true">
+ <StackView
+ android:id="@+id/stack_view"
+ android:layout_width="348px"
+ android:layout_height="374px"
+ android:layout_gravity="center"
+ android:background="#00000000"
+ android:cacheColorHint="#00000000"
+ android:autoStart="true" />
+</FrameLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack_item.xml b/tests/HwAccelerationTest/res/layout/stack_item.xml
new file mode 100644
index 0000000..3504018
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack_item.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/stack_item"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView android:id="@+id/textview_icon"
+ android:layout_height="250dip"
+ android:layout_width="250dip"
+ android:layout_gravity="center" />
+ <TextView android:id="@+id/mini_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+ </FrameLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 0000000..6c80a6d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private ComposeShader mComposeShader;
+ private ComposeShader mCompose2Shader;
+ private ComposeShader mCompose3Shader;
+ private ComposeShader mCompose4Shader;
+ private ComposeShader mCompose5Shader;
+ private ComposeShader mCompose6Shader;
+ private BitmapShader mScaled2Shader;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m3 = new Matrix();
+ m3.setScale(0.1f, 0.1f);
+ mScaled2Shader.setLocalMatrix(m3);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+
+ mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.DARKEN);
+ mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.LIGHTEN);
+ mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.MULTIPLY);
+ mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.SCREEN);
+ mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.ADD);
+ mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+ PorterDuff.Mode.OVERLAY);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mComposeShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose2Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose3Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mCompose4Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose5Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose6Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
index fd7a1e6..2ba249a 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
@@ -23,6 +23,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
@SuppressWarnings({"UnusedDeclaration"})
@@ -51,6 +52,32 @@
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ int count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count=" + count);
+ count = canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after save=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.restoreToCount(count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restoreToCount=" + count);
+ count = canvas.saveLayer(0, 0, 10, 10, mBitmapPaint, Canvas.ALL_SAVE_FLAG);
+ Log.d("OpenGLRenderer", "count after saveLayer=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after saveLayer=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+
canvas.save();
canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
new file mode 100644
index 0000000..071a118
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class SimplePathsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ EditText text = new EditText(this);
+ layout.addView(text, new FrameLayout.LayoutParams(600, 350, Gravity.CENTER));
+ text.setText("This is an example of an EditText widget \n" +
+ "using simple paths to create the selection.");
+ //text.setSelection(0, text.getText().length());
+
+ setContentView(layout);
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java
new file mode 100644
index 0000000..5c8db6e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.StackView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class StackActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.stack);
+
+ StackView stack = (StackView) findViewById(R.id.stack_view);
+ stack.setAdapter(new ArrayAdapter<Drawable>(this, android.R.layout.simple_list_item_1,
+ android.R.id.text1, new Drawable[] {
+ getResources().getDrawable(R.drawable.sunset1),
+ getResources().getDrawable(R.drawable.sunset2),
+ }) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View item = convertView;
+ if (item == null) {
+ item = LayoutInflater.from(getContext()).inflate(
+ R.layout.stack_item, null, false);
+ }
+ ((ImageView) item.findViewById(R.id.textview_icon)).setImageDrawable(
+ getItem(position % getCount()));
+ ((TextView) item.findViewById(R.id.mini_text)).setText("" + position);
+ return item;
+ }
+ });
+ stack.setDisplayedChild(0);
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
index 3b5cf43..8af1b7b 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -35,6 +35,7 @@
static class CustomTextView extends View {
private final Paint mMediumPaint;
private final Paint mLargePaint;
+ private final Paint mStrikePaint;
CustomTextView(Context c) {
super(c);
@@ -45,6 +46,11 @@
mLargePaint = new Paint();
mLargePaint.setAntiAlias(true);
mLargePaint.setTextSize(36.0f);
+ mStrikePaint = new Paint();
+ mStrikePaint.setAntiAlias(true);
+ mStrikePaint.setTextSize(16.0f);
+ mStrikePaint.setUnderlineText(true);
+
}
@Override
@@ -59,7 +65,24 @@
canvas.drawText("Hello OpenGL renderer!", 100, 60, mMediumPaint);
mMediumPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
+ mMediumPaint.setShadowLayer(2.5f, 0.0f, 0.0f, 0xff000000);
+ canvas.drawText("Hello OpenGL renderer!", 100, 150, mMediumPaint);
+ mMediumPaint.clearShadowLayer();
canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+
+ mLargePaint.setShadowLayer(2.5f, 3.0f, 3.0f, 0xff000000);
+ canvas.drawText("Hello OpenGL renderer!", 100, 400, mLargePaint);
+ mLargePaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff00ff00);
+ canvas.drawText("Hello OpenGL renderer!", 100, 500, mLargePaint);
+ mLargePaint.clearShadowLayer();
+
+ canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint);
+ mStrikePaint.setStrikeThruText(true);
+ canvas.drawText("Hello OpenGL renderer!", 500, 70, mStrikePaint);
+ mStrikePaint.setUnderlineText(false);
+ canvas.drawText("Hello OpenGL renderer!", 500, 100, mStrikePaint);
+ mStrikePaint.setStrikeThruText(false);
+ mStrikePaint.setUnderlineText(true);
canvas.save();
canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index d6cfb12..e4f447e 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1830,6 +1830,16 @@
continue;
}
+ if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
+ int maxResInt = atoi(bundle->getMaxResVersion());
+ const char *verString = group.version.string();
+ int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
+ if (dirVersionInt > maxResInt) {
+ fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
+ continue;
+ }
+ }
+
FileType type = getFileType(subdirName.string());
if (type == kFileTypeDirectory) {
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index b339a2c..094b7db 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -41,7 +41,7 @@
libpng
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
endif
# Statically link libz for MinGW (Win SDK under Linux),
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 3308a35..6a1f2d5 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -45,6 +45,7 @@
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
+ mMaxResVersion(NULL),
mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -134,6 +135,8 @@
void setVersionName(const char* val) { mVersionName = val; }
const char* getCustomPackage() const { return mCustomPackage; }
void setCustomPackage(const char* val) { mCustomPackage = val; }
+ const char* getMaxResVersion() const { return mMaxResVersion; }
+ void setMaxResVersion(const char * val) { mMaxResVersion = val; }
/*
* Set and get the file specification.
@@ -230,6 +233,7 @@
const char* mVersionCode;
const char* mVersionName;
const char* mCustomPackage;
+ const char* mMaxResVersion;
/* file specification */
int mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index b0c6e39..f457cc8 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -62,6 +62,7 @@
" [--rename-manifest-package PACKAGE] \\\n"
" [--rename-instrumentation-target-package PACKAGE] \\\n"
" [--utf16] [--auto-add-overlay] \\\n"
+ " [--max-res-version VAL] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] "
@@ -128,6 +129,8 @@
" higher, the default encoding for resources will be in UTF-8.\n"
" --target-sdk-version\n"
" inserts android:targetSdkVersion in to manifest.\n"
+ " --max-res-version\n"
+ " ignores versioned resource directories above the given value.\n"
" --values\n"
" when used with \"dump resources\" also includes resource values.\n"
" --version-code\n"
@@ -416,6 +419,15 @@
goto bail;
}
bundle.setMaxSdkVersion(argv[0]);
+ } else if (strcmp(cp, "-max-res-version") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--max-res-version' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setMaxResVersion(argv[0]);
} else if (strcmp(cp, "-version-code") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 5855b56..31c1722 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1880,7 +1880,7 @@
className.append(inClassName);
}
}
-
+
String8 rule("-keep class ");
rule += className;
rule += " { <init>(...); }";
@@ -1955,7 +1955,7 @@
if (tag == "application") {
inApplication = true;
keepTag = true;
-
+
String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
"backupAgent", &error);
if (agent.length() > 0) {
@@ -1988,9 +1988,17 @@
return NO_ERROR;
}
+struct NamespaceAttributePair {
+ const char* ns;
+ const char* attr;
+
+ NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
+ NamespaceAttributePair() : ns(NULL), attr(NULL) {}
+};
+
status_t
writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
- const char* startTag, const char* altTag)
+ const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
{
status_t err;
ResXMLTree tree;
@@ -2020,7 +2028,7 @@
return NO_ERROR;
}
}
-
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
continue;
@@ -2031,16 +2039,21 @@
if (strchr(tag.string(), '.')) {
addProguardKeepRule(keep, tag, NULL,
layoutFile->getPrintableSource(), tree.getLineNumber());
- } else if (altTag != NULL && tag == altTag) {
- ssize_t classIndex = tree.indexOfAttribute(NULL, "class");
- if (classIndex < 0) {
- fprintf(stderr, "%s:%d: <view> does not have class attribute.\n",
- layoutFile->getPrintableSource().string(), tree.getLineNumber());
- } else {
- size_t len;
- addProguardKeepRule(keep,
- String8(tree.getAttributeStringValue(classIndex, &len)), NULL,
- layoutFile->getPrintableSource(), tree.getLineNumber());
+ } else if (tagAttrPairs != NULL) {
+ ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
+ if (tagIndex >= 0) {
+ const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
+ ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
+ if (attrIndex < 0) {
+ // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
+ // layoutFile->getPrintableSource().string(), tree.getLineNumber(),
+ // tag.string(), nsAttr.ns, nsAttr.attr);
+ } else {
+ size_t len;
+ addProguardKeepRule(keep,
+ String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+ layoutFile->getPrintableSource(), tree.getLineNumber());
+ }
}
}
}
@@ -2048,25 +2061,42 @@
return NO_ERROR;
}
+static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
+ const char* tag, const char* ns, const char* attr) {
+ dest->add(String8(tag), NamespaceAttributePair(ns, attr));
+}
+
status_t
writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
{
status_t err;
+
+ // tag:attribute pairs that should be checked in layout files.
+ KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
+ addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+ addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
+
+ // tag:attribute pairs that should be checked in xml files.
+ KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
+ addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
+ addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
const char* startTag = NULL;
- const char* altTag = NULL;
+ const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
- altTag = "view";
+ tagAttrPairs = &kLayoutTagAttrPairs;
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
startTag = "PreferenceScreen";
+ tagAttrPairs = &kXmlTagAttrPairs;
} else {
continue;
}
-
+
const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
const size_t N = groups.size();
for (size_t i=0; i<N; i++) {
@@ -2074,7 +2104,7 @@
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
- err = writeProguardForXml(keep, files.valueAt(j), startTag, altTag);
+ err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
if (err < 0) {
return err;
}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 452549b..cee8546 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -166,7 +166,7 @@
if (argCount > 1 && nonpositional) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"Multiple substitutions specified in non-positional format; "
- "did you mean to add the formatted=\"true\" attribute?\n");
+ "did you mean to add the formatted=\"false\" attribute?\n");
return NOT_ENOUGH_DATA;
}
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
index ab79f8d..f284e86 100644
--- a/tools/localize/Android.mk
+++ b/tools/localize/Android.mk
@@ -34,7 +34,7 @@
libcutils
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
endif
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
new file mode 100644
index 0000000..b02c1cb
--- /dev/null
+++ b/tools/obbtool/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2010 The Android Open Source Project
+#
+# Opaque Binary Blob (OBB) Tool
+#
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ Main.cpp
+
+#LOCAL_C_INCLUDES +=
+
+LOCAL_STATIC_LIBRARIES := \
+ libutils \
+ libcutils
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_MODULE := obbtool
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_BUILD_APPS
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
new file mode 100644
index 0000000..2a9bf04
--- /dev/null
+++ b/tools/obbtool/Main.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/ObbFile.h>
+#include <utils/String8.h>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+static const char* gProgName = "obbtool";
+static const char* gProgVersion = "1.0";
+
+static int wantUsage = 0;
+static int wantVersion = 0;
+
+#define ADD_OPTS "n:v:f:c:"
+static const struct option longopts[] = {
+ {"help", no_argument, &wantUsage, 1},
+ {"version", no_argument, &wantVersion, 1},
+
+ /* Args for "add" */
+ {"name", required_argument, NULL, 'n'},
+ {"version", required_argument, NULL, 'v'},
+ {"filesystem", required_argument, NULL, 'f'},
+ {"crypto", required_argument, NULL, 'c'},
+
+ {NULL, 0, NULL, '\0'}
+};
+
+struct package_info_t {
+ char* packageName;
+ int packageVersion;
+ char* filesystem;
+ char* crypto;
+};
+
+/*
+ * Print usage info.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s a[dd] [ OPTIONS ] FILENAME\n"
+ " Adds an OBB signature to the file.\n\n", gProgName);
+ fprintf(stderr,
+ " %s r[emove] FILENAME\n"
+ " Removes the OBB signature from the file.\n\n", gProgName);
+ fprintf(stderr,
+ " %s i[nfo] FILENAME\n"
+ " Prints the OBB signature information of a file.\n\n", gProgName);
+}
+
+void doAdd(const char* filename, struct package_info_t* info) {
+ ObbFile *obb = new ObbFile();
+ if (obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
+ return;
+ }
+
+ obb->setPackageName(String8(info->packageName));
+ obb->setVersion(info->packageVersion);
+
+ if (!obb->writeTo(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+
+ fprintf(stderr, "OBB signature successfully written\n");
+}
+
+void doRemove(const char* filename) {
+ ObbFile *obb = new ObbFile();
+ if (!obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename);
+ return;
+ }
+
+ if (!obb->removeFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename);
+ return;
+ }
+
+ fprintf(stderr, "OBB signature successfully removed\n");
+}
+
+void doInfo(const char* filename) {
+ ObbFile *obb = new ObbFile();
+ if (!obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename);
+ return;
+ }
+
+ printf("OBB info for '%s':\n", filename);
+ printf("Package name: %s\n", obb->getPackageName().string());
+ printf(" Version: %d\n", obb->getVersion());
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+ const char *prog = argv[0];
+ struct options *options;
+ int opt;
+ int option_index = 0;
+ struct package_info_t package_info;
+
+ int result = 1; // pessimistically assume an error.
+
+ if (argc < 2) {
+ wantUsage = 1;
+ goto bail;
+ }
+
+ while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) {
+ switch (opt) {
+ case 0:
+ if (longopts[option_index].flag)
+ break;
+ fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name);
+ wantUsage = 1;
+ goto bail;
+ case 'n':
+ package_info.packageName = optarg;
+ break;
+ case 'v':
+ char *end;
+ package_info.packageVersion = strtol(optarg, &end, 10);
+ if (*optarg == '\0' || *end != '\0') {
+ fprintf(stderr, "ERROR: invalid version; should be integer!\n\n");
+ wantUsage = 1;
+ goto bail;
+ }
+ break;
+ case 'f':
+ package_info.filesystem = optarg;
+ break;
+ case 'c':
+ package_info.crypto = optarg;
+ break;
+ case '?':
+ wantUsage = 1;
+ goto bail;
+ }
+ }
+
+ if (wantVersion) {
+ fprintf(stderr, "%s %s\n", gProgName, gProgVersion);
+ }
+
+ if (wantUsage) {
+ goto bail;
+ }
+
+#define CHECK_OP(name) \
+ if (strncmp(op, name, opsize)) { \
+ fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \
+ wantUsage = 1; \
+ goto bail; \
+ }
+
+ if (optind < argc) {
+ const char* op = argv[optind++];
+ const int opsize = strlen(op);
+
+ if (optind >= argc) {
+ fprintf(stderr, "ERROR: filename required!\n\n");
+ wantUsage = 1;
+ goto bail;
+ }
+
+ const char* filename = argv[optind++];
+
+ switch (op[0]) {
+ case 'a':
+ CHECK_OP("add");
+ if (package_info.packageName == NULL) {
+ fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n");
+ goto bail;
+ }
+ doAdd(filename, &package_info);
+ break;
+ case 'r':
+ CHECK_OP("remove");
+ doRemove(filename);
+ break;
+ case 'i':
+ CHECK_OP("info");
+ doInfo(filename);
+ break;
+ default:
+ fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op);
+ wantUsage = 1;
+ goto bail;
+ }
+ }
+
+bail:
+ if (wantUsage) {
+ usage();
+ result = 2;
+ }
+
+ return result;
+}
diff --git a/voip/java/android/net/sip/BinderHelper.java b/voip/java/android/net/sip/BinderHelper.java
deleted file mode 100644
index bd3da32..0000000
--- a/voip/java/android/net/sip/BinderHelper.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Looper;
-import android.util.Log;
-
-// TODO: throw away this class after moving SIP classes to framework
-// This class helps to get IBinder instance of a service in a blocking call.
-// The method cannot be called in app's main thread as the ServiceConnection
-// callback will.
-class BinderHelper<T extends IInterface> {
- private Context mContext;
- private IBinder mBinder;
- private Class<T> mClass;
-
- BinderHelper(Context context, Class<T> klass) {
- mContext = context;
- mClass = klass;
- }
-
- void startService() {
- mContext.startService(new Intent(mClass.getName()));
- }
-
- void stopService() {
- mContext.stopService(new Intent(mClass.getName()));
- }
-
- IBinder getBinder() {
- // cannot call this method in app's main thread
- if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
- throw new RuntimeException(
- "This method cannot be called in app's main thread");
- }
-
- final ConditionVariable cv = new ConditionVariable();
- cv.close();
- ServiceConnection c = new ServiceConnection() {
- public synchronized void onServiceConnected(
- ComponentName className, IBinder binder) {
- Log.v("BinderHelper", "service connected!");
- mBinder = binder;
- cv.open();
- mContext.unbindService(this);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- cv.open();
- mContext.unbindService(this);
- }
- };
- if (mContext.bindService(new Intent(mClass.getName()), c, 0)) {
- cv.block(4500);
- }
- return mBinder;
- }
-}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index abdc9d7..f4be839 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -191,11 +191,8 @@
*/
void continueCall() throws SipException;
- /** Puts the device to in-call mode. */
- void setInCallMode();
-
/** Puts the device to speaker mode. */
- void setSpeakerMode();
+ void setSpeakerMode(boolean speakerMode);
/** Toggles mute. */
void toggleMute();
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 57e0bd2..7161309 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -343,8 +343,11 @@
public synchronized void endCall() throws SipException {
try {
stopRinging();
- if (mSipSession != null) mSipSession.endCall();
stopCall(true);
+ mInCall = false;
+
+ // perform the above local ops first and then network op
+ if (mSipSession != null) mSipSession.endCall();
} catch (Throwable e) {
throwSipException(e);
}
@@ -482,16 +485,9 @@
return mMuted;
}
- public synchronized void setInCallMode() {
+ public synchronized void setSpeakerMode(boolean speakerMode) {
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(false);
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setMode(AudioManager.MODE_NORMAL);
- }
-
- public synchronized void setSpeakerMode() {
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(true);
+ .setSpeakerphoneOn(speakerMode);
}
public void sendDtmf(int code) {
@@ -584,8 +580,15 @@
Log.d(TAG, " not sending");
audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
}
+ } else {
+ /* The recorder volume will be very low if the device is in
+ * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+ * in order to have the normal microphone level.
+ */
+ ((AudioManager) mContext.getSystemService
+ (Context.AUDIO_SERVICE))
+ .setMode(AudioManager.MODE_NORMAL);
}
- setInCallMode();
AudioGroup audioGroup = new AudioGroup();
audioStream.join(audioGroup);
@@ -611,7 +614,6 @@
mRtpSession = null;
}
}
- setInCallMode();
}
private int getLocalMediaPort() {
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index f28b41c..287a13a 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -68,9 +68,6 @@
private ISipService mSipService;
- // Will be removed once the SIP service is integrated into framework
- private BinderHelper<ISipService> mBinderHelper;
-
/**
* Creates a manager instance and initializes the background SIP service.
* Will be removed once the SIP service is integrated into framework.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 6e0bc9d..cd07f0e 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -85,5 +85,21 @@
WifiConfiguration getWifiApConfiguration();
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+ void startWifi();
+
+ void stopWifi();
+
+ void addToBlacklist(String bssid);
+
+ void clearBlacklist();
+
+ void connectNetworkWithConfig(in WifiConfiguration wifiConfig);
+
+ void connectNetworkWithId(int networkId);
+
+ void saveNetwork(in WifiConfiguration wifiConfig);
+
+ void forgetNetwork(int networkId);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6fac902..a774c12 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -274,7 +274,14 @@
* @see #ERROR_AUTHENTICATING
*/
public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-
+ /**
+ * Broadcast intent action indicating that the supplicant configuration changed.
+ * This can be as a result of adding/updating/deleting a network
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SUPPLICANT_CONFIG_CHANGED_ACTION =
+ "android.net.wifi.supplicant.CONFIG_CHANGE";
/**
* An access point scan has completed, and results are available from the supplicant.
* Call {@link #getScanResults()} to obtain the results.
@@ -293,6 +300,21 @@
public static final String EXTRA_NEW_RSSI = "newRssi";
/**
+ * Broadcast intent action indicating that the IP configuration
+ * changed on wifi.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+ /**
+ * The lookup key for a {@link android.net.NetworkProperties} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_NETWORK_PROPERTIES = "networkProperties";
+
+ /**
* The network IDs of the configured networks could have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -838,6 +860,166 @@
}
}
+ /**
+ * Start the driver and connect to network.
+ *
+ * This function will over-ride WifiLock and device idle status. For example,
+ * even if the device is idle or there is only a scan-only lock held,
+ * a start wifi would mean that wifi connection is kept active until
+ * a stopWifi() is sent.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean startWifi() {
+ try {
+ mService.startWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disconnect from a network (if any) and stop the driver.
+ *
+ * This function will over-ride WifiLock and device idle status. Wi-Fi
+ * stays inactive until a startWifi() is issued.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean stopWifi() {
+ try {
+ mService.stopWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a bssid to the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean addToBlacklist(String bssid) {
+ try {
+ mService.addToBlacklist(bssid);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean clearBlacklist() {
+ try {
+ mService.clearBlacklist();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /* TODO: deprecate synchronous API and open up the following API */
+ /**
+ * Connect to a network with the given configuration. The network also
+ * gets added to the supplicant configuration.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void connectNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+ try {
+ mService.connectNetworkWithConfig(config);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Connect to a network with the given networkId.
+ *
+ * This function is used instead of a enableNetwork(), saveConfiguration() and
+ * reconnect()
+ *
+ * @param networkId the network id identifiying the network in the
+ * supplicant configuration list
+ * @hide
+ */
+ public void connectNetwork(int networkId) {
+ if (networkId < 0) {
+ return;
+ }
+ try {
+ mService.connectNetworkWithId(networkId);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Save the given network in the supplicant config. If the network already
+ * exists, the configuration is updated. A new network is enabled
+ * by default.
+ *
+ * For a new network, this function is used instead of a
+ * sequence of addNetwork(), enableNetwork() and saveConfiguration().
+ *
+ * For an existing network, it accomplishes the task of updateNetwork()
+ * and saveConfiguration()
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void saveNetwork(WifiConfiguration config) {
+ if (config == null) {
+ return;
+ }
+ try {
+ mService.saveNetwork(config);
+ } catch (RemoteException e) { }
+ }
+
+ /**
+ * Delete the network in the supplicant config.
+ *
+ * This function is used instead of a sequence of removeNetwork()
+ * and saveConfiguration().
+ *
+ * @param config the set of variables that describe the configuration,
+ * contained in a {@link WifiConfiguration} object.
+ * @hide
+ */
+ public void forgetNetwork(int netId) {
+ if (netId < 0) {
+ return;
+ }
+ try {
+ mService.forgetNetwork(netId);
+ } catch (RemoteException e) { }
+ }
+
/**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index f2f8343..af3132f 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@
import android.util.Log;
import android.util.Config;
import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -117,7 +116,7 @@
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
/**
* This indicates the supplicant connection for the monitor is closed
@@ -139,18 +138,14 @@
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateTracker tracker) {
- mWifiStateTracker = tracker;
+ public WifiMonitor(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
}
public void startMonitoring() {
new MonitorThread().start();
}
- public NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
@@ -161,9 +156,9 @@
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateTracker.notifySupplicantConnection();
+ mWifiStateMachine.notifySupplicantConnection();
} else {
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
return;
}
@@ -259,7 +254,7 @@
}
// notify and exit
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
break;
} else {
handleEvent(event, eventData);
@@ -285,7 +280,7 @@
}
private void handlePasswordKeyMayBeIncorrect() {
- mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+ mWifiStateMachine.notifyPasswordKeyMayBeIncorrect();
}
private void handleDriverEvent(String state) {
@@ -293,11 +288,11 @@
return;
}
if (state.equals("STOPPED")) {
- mWifiStateTracker.notifyDriverStopped();
+ mWifiStateMachine.notifyDriverStopped();
} else if (state.equals("STARTED")) {
- mWifiStateTracker.notifyDriverStarted();
+ mWifiStateMachine.notifyDriverStarted();
} else if (state.equals("HANGED")) {
- mWifiStateTracker.notifyDriverHung();
+ mWifiStateMachine.notifyDriverHung();
}
}
@@ -318,7 +313,7 @@
break;
case SCAN_RESULTS:
- mWifiStateTracker.notifyScanResultsAvailable();
+ mWifiStateMachine.notifyScanResultsAvailable();
break;
case UNKNOWN:
@@ -375,7 +370,7 @@
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateTracker.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+ mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -395,7 +390,7 @@
}
}
}
- mWifiStateTracker.notifyNetworkStateChange(newState, BSSID, networkId);
+ mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 0000000..360b642
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3697 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+/**
+ * TODO: Add soft AP states as part of WIFI_STATE_XXX
+ * Retain WIFI_STATE_ENABLING that indicates driver is loading
+ * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
+ * and WIFI_STATE_FAILED for failure
+ * Deprecate WIFI_STATE_UNKNOWN
+ *
+ * Doing this will simplify the logic for sending broadcasts
+ */
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import android.app.ActivityManagerNative;
+import android.net.NetworkInfo;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProperties;
+import android.net.wifi.WifiConfiguration.Status;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Process;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.database.ContentObserver;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * @hide
+ */
+//TODO: we still need frequent scanning for the case when
+// we issue disconnect but need scan results for open network notification
+public class WifiStateMachine extends HierarchicalStateMachine {
+
+ private static final String TAG = "WifiStateMachine";
+ private static final String NETWORKTYPE = "WIFI";
+ private static final boolean DBG = false;
+
+ /* TODO: fetch a configurable interface */
+ private static final String SOFTAP_IFACE = "wl0.1";
+
+ private WifiMonitor mWifiMonitor;
+ private INetworkManagementService nwService;
+ private ConnectivityManager mCm;
+
+ /* Scan results handling */
+ private List<ScanResult> mScanResults;
+ private static final Pattern scanResultPattern = Pattern.compile("\t+");
+ private static final int SCAN_RESULT_CACHE_SIZE = 80;
+ private final LinkedHashMap<String, ScanResult> mScanResultCache;
+
+ private String mInterfaceName;
+
+ private int mNumAllowedChannels = 0;
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private int mLastNetworkId;
+ private boolean mEnableRssiPolling = false;
+ private boolean mPasswordKeyMayBeIncorrect = false;
+ private boolean mUseStaticIp = false;
+ private int mReconnectCount = 0;
+ private boolean mIsScanMode = false;
+ private boolean mConfigChanged = false;
+
+ /**
+ * Instance of the bluetooth headset helper. This needs to be created
+ * early because there is a delay before it actually 'connects', as
+ * noted by its javadoc. If we check before it is connected, it will be
+ * in an error state and we will not disable coexistence.
+ */
+ private BluetoothHeadset mBluetoothHeadset;
+
+ private BluetoothA2dp mBluetoothA2dp;
+
+ /**
+ * Observes the static IP address settings.
+ */
+ private SettingsObserver mSettingsObserver;
+ private NetworkProperties mNetworkProperties;
+
+ // Held during driver load and unload
+ private static PowerManager.WakeLock sWakeLock;
+
+ private Context mContext;
+
+ private DhcpInfo mDhcpInfo;
+ private WifiInfo mWifiInfo;
+ private NetworkInfo mNetworkInfo;
+ private SupplicantStateTracker mSupplicantStateTracker;
+ /* Tracks the highest priority of configured networks */
+ private int mLastPriority = -1;
+ /* Tracks if all networks need to be enabled */
+ private boolean mEnableAllNetworks = false;
+
+ // Event log tags (must be in sync with event-log-tags)
+ private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
+ private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
+ private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
+
+ /* Load the driver */
+ private static final int CMD_LOAD_DRIVER = 1;
+ /* Unload the driver */
+ private static final int CMD_UNLOAD_DRIVER = 2;
+ /* Indicates driver load succeeded */
+ private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ /* Indicates driver load failed */
+ private static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ /* Indicates driver unload succeeded */
+ private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ /* Indicates driver unload failed */
+ private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+
+ /* Start the supplicant */
+ private static final int CMD_START_SUPPLICANT = 11;
+ /* Stop the supplicant */
+ private static final int CMD_STOP_SUPPLICANT = 12;
+ /* Start the driver */
+ private static final int CMD_START_DRIVER = 13;
+ /* Start the driver */
+ private static final int CMD_STOP_DRIVER = 14;
+ /* Indicates DHCP succeded */
+ private static final int CMD_IP_CONFIG_SUCCESS = 15;
+ /* Indicates DHCP failed */
+ private static final int CMD_IP_CONFIG_FAILURE = 16;
+ /* Re-configure interface */
+ private static final int CMD_RECONFIGURE_IP = 17;
+
+
+ /* Start the soft access point */
+ private static final int CMD_START_AP = 21;
+ /* Stop the soft access point */
+ private static final int CMD_STOP_AP = 22;
+
+
+ /* Supplicant events */
+ /* Connection to supplicant established */
+ private static final int SUP_CONNECTION_EVENT = 31;
+ /* Connection to supplicant lost */
+ private static final int SUP_DISCONNECTION_EVENT = 32;
+ /* Driver start completed */
+ private static final int DRIVER_START_EVENT = 33;
+ /* Driver stop completed */
+ private static final int DRIVER_STOP_EVENT = 34;
+ /* Network connection completed */
+ private static final int NETWORK_CONNECTION_EVENT = 36;
+ /* Network disconnection completed */
+ private static final int NETWORK_DISCONNECTION_EVENT = 37;
+ /* Scan results are available */
+ private static final int SCAN_RESULTS_EVENT = 38;
+ /* Supplicate state changed */
+ private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ /* Password may be incorrect */
+ private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
+
+ /* Supplicant commands */
+ /* Is supplicant alive ? */
+ private static final int CMD_PING_SUPPLICANT = 51;
+ /* Add/update a network configuration */
+ private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ /* Delete a network */
+ private static final int CMD_REMOVE_NETWORK = 53;
+ /* Enable a network. The device will attempt a connection to the given network. */
+ private static final int CMD_ENABLE_NETWORK = 54;
+ /* Disable a network. The device does not attempt a connection to the given network. */
+ private static final int CMD_DISABLE_NETWORK = 55;
+ /* Blacklist network. De-prioritizes the given BSSID for connection. */
+ private static final int CMD_BLACKLIST_NETWORK = 56;
+ /* Clear the blacklist network list */
+ private static final int CMD_CLEAR_BLACKLIST = 57;
+ /* Get the configured networks */
+ private static final int CMD_GET_NETWORK_CONFIG = 58;
+ /* Save configuration */
+ private static final int CMD_SAVE_CONFIG = 59;
+ /* Connection status */
+ private static final int CMD_CONNECTION_STATUS = 60;
+
+ /* Supplicant commands after driver start*/
+ /* Initiate a scan */
+ private static final int CMD_START_SCAN = 71;
+ /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+ private static final int CMD_SET_SCAN_MODE = 72;
+ /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+ private static final int CMD_SET_SCAN_TYPE = 73;
+ /* Disconnect from a network */
+ private static final int CMD_DISCONNECT = 74;
+ /* Reconnect to a network */
+ private static final int CMD_RECONNECT = 75;
+ /* Reassociate to a network */
+ private static final int CMD_REASSOCIATE = 76;
+ /* Set power mode
+ * POWER_MODE_ACTIVE
+ * POWER_MODE_AUTO
+ */
+ private static final int CMD_SET_POWER_MODE = 77;
+ /* Set bluetooth co-existence
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
+ /* Enable/disable bluetooth scan mode
+ * true(1)
+ * false(0)
+ */
+ private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
+ /* Set number of allowed channels */
+ private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
+ /* Request connectivity manager wake lock before driver stop */
+ private static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ /* Enables RSSI poll */
+ private static final int CMD_ENABLE_RSSI_POLL = 82;
+ /* RSSI poll */
+ private static final int CMD_RSSI_POLL = 83;
+ /* Get current RSSI */
+ private static final int CMD_GET_RSSI = 84;
+ /* Get approx current RSSI */
+ private static final int CMD_GET_RSSI_APPROX = 85;
+ /* Get link speed on connection */
+ private static final int CMD_GET_LINK_SPEED = 86;
+ /* Radio mac address */
+ private static final int CMD_GET_MAC_ADDR = 87;
+ /* Set up packet filtering */
+ private static final int CMD_START_PACKET_FILTERING = 88;
+ /* Clear packet filter */
+ private static final int CMD_STOP_PACKET_FILTERING = 89;
+ /* Connect to a specified network (network id
+ * or WifiConfiguration) This involves increasing
+ * the priority of the network, enabling the network
+ * (while disabling others) and issuing a reconnect.
+ * Note that CMD_RECONNECT just does a reconnect to
+ * an existing network. All the networks get enabled
+ * upon a successful connection or a failure.
+ */
+ private static final int CMD_CONNECT_NETWORK = 90;
+ /* Save the specified network. This involves adding
+ * an enabled network (if new) and updating the
+ * config and issuing a save on supplicant config.
+ */
+ private static final int CMD_SAVE_NETWORK = 91;
+ /* Delete the specified network. This involves
+ * removing the network and issuing a save on
+ * supplicant config.
+ */
+ private static final int CMD_FORGET_NETWORK = 92;
+
+
+
+ /**
+ * Interval in milliseconds between polling for connection
+ * status items that are not sent via asynchronous events.
+ * An example is RSSI (signal strength).
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ private static final int CONNECT_MODE = 1;
+ private static final int SCAN_ONLY_MODE = 2;
+
+ private static final int SCAN_ACTIVE = 1;
+ private static final int SCAN_PASSIVE = 2;
+
+ /**
+ * The maximum number of times we will retry a connection to an access point
+ * for which we have failed in acquiring an IP address from DHCP. A value of
+ * N means that we will make N+1 connection attempts in all.
+ * <p>
+ * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+ * value if a Settings value is not present.
+ */
+ private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+ private static final int DRIVER_POWER_MODE_ACTIVE = 1;
+ private static final int DRIVER_POWER_MODE_AUTO = 0;
+
+ /* Default parent state */
+ private HierarchicalState mDefaultState = new DefaultState();
+ /* Temporary initial state */
+ private HierarchicalState mInitialState = new InitialState();
+ /* Unloading the driver */
+ private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+ /* Loading the driver */
+ private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+ /* Driver load/unload failed */
+ private HierarchicalState mDriverFailedState = new DriverFailedState();
+ /* Driver loading */
+ private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+ /* Driver loaded */
+ private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+ /* Driver loaded, waiting for supplicant to start */
+ private HierarchicalState mWaitForSupState = new WaitForSupState();
+
+ /* Driver loaded and supplicant ready */
+ private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+ /* Driver start issued, waiting for completed event */
+ private HierarchicalState mDriverStartingState = new DriverStartingState();
+ /* Driver started */
+ private HierarchicalState mDriverStartedState = new DriverStartedState();
+ /* Driver stopping */
+ private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+ /* Driver stopped */
+ private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+ /* Scan for networks, no connection will be established */
+ private HierarchicalState mScanModeState = new ScanModeState();
+ /* Connecting to an access point */
+ private HierarchicalState mConnectModeState = new ConnectModeState();
+ /* Fetching IP after network connection (assoc+auth complete) */
+ private HierarchicalState mConnectingState = new ConnectingState();
+ /* Connected with IP addr */
+ private HierarchicalState mConnectedState = new ConnectedState();
+ /* disconnect issued, waiting for network disconnect confirmation */
+ private HierarchicalState mDisconnectingState = new DisconnectingState();
+ /* Network is not connected, supplicant assoc+auth is not complete */
+ private HierarchicalState mDisconnectedState = new DisconnectedState();
+
+ /* Soft Ap is running */
+ private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+ /* Argument for Message object to indicate a synchronous call */
+ private static final int SYNCHRONOUS_CALL = 1;
+ private static final int ASYNCHRONOUS_CALL = 0;
+
+
+ /**
+ * One of {@link WifiManager#WIFI_STATE_DISABLED},
+ * {@link WifiManager#WIFI_STATE_DISABLING},
+ * {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING},
+ * {@link WifiManager#WIFI_STATE_UNKNOWN}
+ *
+ */
+ private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
+
+ /**
+ * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ *
+ */
+ private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
+
+ private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
+ private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
+
+ private final IBatteryStats mBatteryStats;
+
+ public WifiStateMachine(Context context) {
+ super(TAG);
+
+ mContext = context;
+
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ nwService = INetworkManagementService.Stub.asInterface(b);
+
+ mWifiMonitor = new WifiMonitor(this);
+ mDhcpInfo = new DhcpInfo();
+ mWifiInfo = new WifiInfo();
+ mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+ mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+
+ mBluetoothHeadset = new BluetoothHeadset(mContext, null);
+ mNetworkProperties = new NetworkProperties();
+
+ mNetworkInfo.setIsAvailable(false);
+ mNetworkProperties.clear();
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mScanResultCache = new LinkedHashMap<String, ScanResult>(
+ SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
+ /*
+ * Limit the cache size by SCAN_RESULT_CACHE_SIZE
+ * elements
+ */
+ @Override
+ public boolean removeEldestEntry(Map.Entry eldest) {
+ return SCAN_RESULT_CACHE_SIZE < this.size();
+ }
+ };
+
+ mSettingsObserver = new SettingsObserver(new Handler());
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ addState(mDefaultState);
+ addState(mInitialState, mDefaultState);
+ addState(mDriverUnloadingState, mDefaultState);
+ addState(mDriverUnloadedState, mDefaultState);
+ addState(mDriverFailedState, mDriverUnloadedState);
+ addState(mDriverLoadingState, mDefaultState);
+ addState(mDriverLoadedState, mDefaultState);
+ addState(mWaitForSupState, mDriverLoadedState);
+ addState(mDriverSupReadyState, mDefaultState);
+ addState(mDriverStartingState, mDriverSupReadyState);
+ addState(mDriverStartedState, mDriverSupReadyState);
+ addState(mScanModeState, mDriverStartedState);
+ addState(mConnectModeState, mDriverStartedState);
+ addState(mConnectingState, mConnectModeState);
+ addState(mConnectedState, mConnectModeState);
+ addState(mDisconnectingState, mConnectModeState);
+ addState(mDisconnectedState, mConnectModeState);
+ addState(mDriverStoppingState, mDriverSupReadyState);
+ addState(mDriverStoppedState, mDriverSupReadyState);
+ addState(mSoftApStartedState, mDefaultState);
+
+ setInitialState(mInitialState);
+
+ if (DBG) setDbg(true);
+
+ //start the state machine
+ start();
+ }
+
+ /*********************************************************
+ * Methods exposed for public use
+ ********************************************************/
+
+ /**
+ * TODO: doc
+ */
+ public boolean pingSupplicant() {
+ return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public boolean startScan(boolean forceActive) {
+ return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiEnabled(boolean enable) {
+ mLastEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
+ sendMessage(CMD_START_SUPPLICANT);
+ } else {
+ sendMessage(CMD_STOP_SUPPLICANT);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
+ mLastApEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
+ sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+ } else {
+ sendMessage(CMD_STOP_AP);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiState() {
+ return mWifiState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiStateByName() {
+ switch (mWifiState.get()) {
+ case WIFI_STATE_DISABLING:
+ return "disabling";
+ case WIFI_STATE_DISABLED:
+ return "disabled";
+ case WIFI_STATE_ENABLING:
+ return "enabling";
+ case WIFI_STATE_ENABLED:
+ return "enabled";
+ case WIFI_STATE_UNKNOWN:
+ return "unknown state";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiApState() {
+ return mWifiApState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiApStateByName() {
+ switch (mWifiApState.get()) {
+ case WIFI_AP_STATE_DISABLING:
+ return "disabling";
+ case WIFI_AP_STATE_DISABLED:
+ return "disabled";
+ case WIFI_AP_STATE_ENABLING:
+ return "enabling";
+ case WIFI_AP_STATE_ENABLED:
+ return "enabled";
+ case WIFI_AP_STATE_FAILED:
+ return "failed";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * Get status information for the current connection, if any.
+ * @return a {@link WifiInfo} object containing information about the current connection
+ *
+ */
+ public WifiInfo requestConnectionInfo() {
+ return mWifiInfo;
+ }
+
+ public DhcpInfo getDhcpInfo() {
+ return mDhcpInfo;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setDriverStart(boolean enable) {
+ if (enable) {
+ sendMessage(CMD_START_DRIVER);
+ } else {
+ sendMessage(CMD_STOP_DRIVER);
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanOnlyMode(boolean enable) {
+ if (enable) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanType(boolean active) {
+ if (active) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public List<ScanResult> getScanResultsList() {
+ return mScanResults;
+ }
+
+ /**
+ * Disconnect from Access Point
+ */
+ public boolean disconnectCommand() {
+ return sendSyncMessage(CMD_DISCONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a reconnection to AP
+ */
+ public boolean reconnectCommand() {
+ return sendSyncMessage(CMD_RECONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a re-association to AP
+ */
+ public boolean reassociateCommand() {
+ return sendSyncMessage(CMD_REASSOCIATE).boolValue;
+ }
+
+ /**
+ * Add a network synchronously
+ *
+ * @return network id of the new network
+ */
+ public int addOrUpdateNetwork(WifiConfiguration config) {
+ return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
+ }
+
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
+ }
+
+ /**
+ * Delete a network
+ *
+ * @param networkId id of the network to be removed
+ */
+ public boolean removeNetwork(int networkId) {
+ return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
+ }
+
+ private class EnableNetParams {
+ private int netId;
+ private boolean disableOthers;
+ EnableNetParams(int n, boolean b) {
+ netId = n;
+ disableOthers = b;
+ }
+ }
+ /**
+ * Enable a network
+ *
+ * @param netId network id of the network
+ * @param disableOthers true, if all other networks have to be disabled
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean enableNetwork(int netId, boolean disableOthers) {
+ return sendSyncMessage(CMD_ENABLE_NETWORK,
+ new EnableNetParams(netId, disableOthers)).boolValue;
+ }
+
+ /**
+ * Disable a network
+ *
+ * @param netId network id of the network
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean disableNetwork(int netId) {
+ return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
+ }
+
+ /**
+ * Blacklist a BSSID. This will avoid the AP if there are
+ * alternate APs to connect
+ *
+ * @param bssid BSSID of the network
+ */
+ public void addToBlacklist(String bssid) {
+ sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+ }
+
+ /**
+ * Clear the blacklist list
+ *
+ */
+ public void clearBlacklist() {
+ sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+ }
+
+ public void connectNetwork(int netId) {
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
+ }
+
+ public void connectNetwork(WifiConfiguration wifiConfig) {
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, wifiConfig));
+ }
+
+ public void saveNetwork(WifiConfiguration wifiConfig) {
+ sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
+ }
+
+ public void forgetNetwork(int netId) {
+ sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
+ }
+
+ /**
+ * Get detailed status of the connection
+ *
+ * @return Example status result
+ * bssid=aa:bb:cc:dd:ee:ff
+ * ssid=TestNet
+ * id=3
+ * pairwise_cipher=NONE
+ * group_cipher=NONE
+ * key_mgmt=NONE
+ * wpa_state=COMPLETED
+ * ip_address=X.X.X.X
+ */
+ public String status() {
+ return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
+ }
+
+ public void enableRssiPolling(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+ }
+ /**
+ * Get RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssi() {
+ return sendSyncMessage(CMD_GET_RSSI).intValue;
+ }
+
+ /**
+ * Get approx RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssiApprox() {
+ return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
+ }
+
+ /**
+ * Get link speed to currently connected network
+ *
+ * @return link speed, -1 on failure
+ */
+ public int getLinkSpeed() {
+ return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
+ }
+
+ /**
+ * Get MAC address of radio
+ *
+ * @return MAC address, null on failure
+ */
+ public String getMacAddress() {
+ return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
+ }
+
+ /**
+ * Start packet filtering
+ */
+ public void startPacketFiltering() {
+ sendMessage(CMD_START_PACKET_FILTERING);
+ }
+
+ /**
+ * Stop packet filtering
+ */
+ public void stopPacketFiltering() {
+ sendMessage(CMD_STOP_PACKET_FILTERING);
+ }
+
+ /**
+ * Set power mode
+ * @param mode
+ * DRIVER_POWER_MODE_AUTO
+ * DRIVER_POWER_MODE_ACTIVE
+ */
+ public void setPowerMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
+ }
+
+ /**
+ * Set the number of allowed radio frequency channels from the system
+ * setting value, if any.
+ */
+ public void setNumAllowedChannels() {
+ try {
+ setNumAllowedChannels(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+ } catch (Settings.SettingNotFoundException e) {
+ if (mNumAllowedChannels != 0) {
+ setNumAllowedChannels(mNumAllowedChannels);
+ }
+ // otherwise, use the driver default
+ }
+ }
+
+ /**
+ * Set the number of radio frequency channels that are allowed to be used
+ * in the current regulatory domain.
+ * @param numChannels the number of allowed channels. Must be greater than 0
+ * and less than or equal to 16.
+ */
+ public void setNumAllowedChannels(int numChannels) {
+ sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
+ }
+
+ /**
+ * Get number of allowed channels
+ *
+ * @return channel count, -1 on failure
+ *
+ * TODO: this is not a public API and needs to be removed in favor
+ * of asynchronous reporting. unused for now.
+ */
+ public int getNumAllowedChannels() {
+ return -1;
+ }
+
+ /**
+ * Set bluetooth coex mode:
+ *
+ * @param mode
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ public void setBluetoothCoexistenceMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
+ }
+
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isBluetoothPlaying whether to enable or disable this mode
+ */
+ public void setBluetoothScanMode(boolean isBluetoothPlaying) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
+ }
+
+ /**
+ * Save configuration on supplicant
+ *
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * TODO: deprecate this
+ */
+ public boolean saveConfig() {
+ return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void requestCmWakeLock() {
+ sendMessage(CMD_REQUEST_CM_WAKELOCK);
+ }
+
+ /*********************************************************
+ * Internal private functions
+ ********************************************************/
+
+ class SyncReturn {
+ boolean boolValue;
+ int intValue;
+ String stringValue;
+ Object objValue;
+ List<WifiConfiguration> configList;
+ }
+
+ class SyncParams {
+ Object mParameter;
+ SyncReturn mSyncReturn;
+ SyncParams() {
+ mSyncReturn = new SyncReturn();
+ }
+ SyncParams(Object p) {
+ mParameter = p;
+ mSyncReturn = new SyncReturn();
+ }
+ }
+
+ /**
+ * message.arg2 is reserved to indicate synchronized
+ * message.obj is used to store SyncParams
+ */
+ private SyncReturn syncedSend(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ msg.arg2 = SYNCHRONOUS_CALL;
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "syncedSend " + msg);
+ sendMessage(msg);
+ try {
+ syncParams.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
+ return null;
+ }
+ }
+ return syncParams.mSyncReturn;
+ }
+
+ private SyncReturn sendSyncMessage(Message msg) {
+ SyncParams syncParams = new SyncParams();
+ msg.obj = syncParams;
+ return syncedSend(msg);
+ }
+
+ private SyncReturn sendSyncMessage(int what, Object param) {
+ SyncParams syncParams = new SyncParams(param);
+ Message msg = obtainMessage(what, syncParams);
+ return syncedSend(msg);
+ }
+
+
+ private SyncReturn sendSyncMessage(int what) {
+ return sendSyncMessage(obtainMessage(what));
+ }
+
+ private void notifyOnMsgObject(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ if (syncParams != null) {
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
+ syncParams.notify();
+ }
+ }
+ else {
+ Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
+ }
+ }
+
+ private void setWifiState(int wifiState) {
+ final int previousWifiState = mWifiState.get();
+
+ try {
+ if (wifiState == WIFI_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastEnableUid.get());
+ } else if (wifiState == WIFI_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to note battery stats in wifi");
+ }
+
+ mWifiState.set(wifiState);
+
+ if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void setWifiApState(int wifiApState) {
+ final int previousWifiApState = mWifiApState.get();
+
+ try {
+ if (wifiApState == WIFI_AP_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastApEnableUid.get());
+ } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastApEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to note battery stats in wifi");
+ }
+
+ // Update state
+ mWifiApState.set(wifiApState);
+
+ if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /**
+ * Parse the scan result line passed to us by wpa_supplicant (helper).
+ * @param line the line to parse
+ * @return the {@link ScanResult} object
+ */
+ private ScanResult parseScanResult(String line) {
+ ScanResult scanResult = null;
+ if (line != null) {
+ /*
+ * Cache implementation (LinkedHashMap) is not synchronized, thus,
+ * must synchronized here!
+ */
+ synchronized (mScanResultCache) {
+ String[] result = scanResultPattern.split(line);
+ if (3 <= result.length && result.length <= 5) {
+ String bssid = result[0];
+ // bssid | frequency | level | flags | ssid
+ int frequency;
+ int level;
+ try {
+ frequency = Integer.parseInt(result[1]);
+ level = Integer.parseInt(result[2]);
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (level > 0) level -= 256;
+ } catch (NumberFormatException e) {
+ frequency = 0;
+ level = 0;
+ }
+
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
+ flags = result[3];
+ ssid = "";
+ } else {
+ flags = "";
+ ssid = result[3];
+ }
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
+ // Do not add scan results that have no SSID set
+ if (0 < ssid.trim().length()) {
+ scanResult =
+ new ScanResult(
+ ssid, bssid, flags, level, frequency);
+ mScanResultCache.put(key, scanResult);
+ }
+ }
+ } else {
+ Log.w(TAG, "Misformatted scan result text with " +
+ result.length + " fields: " + line);
+ }
+ }
+ }
+
+ return scanResult;
+ }
+
+ /**
+ * scanResults input format
+ * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
+ * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
+ */
+ private void setScanResults(String scanResults) {
+ if (scanResults == null) {
+ return;
+ }
+
+ List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+ int lineCount = 0;
+
+ int scanResultsLen = scanResults.length();
+ // Parse the result string, keeping in mind that the last line does
+ // not end with a newline.
+ for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+ if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+ ++lineCount;
+
+ if (lineCount == 1) {
+ lineBeg = lineEnd + 1;
+ continue;
+ }
+ if (lineEnd > lineBeg) {
+ String line = scanResults.substring(lineBeg, lineEnd);
+ ScanResult scanResult = parseScanResult(line);
+ if (scanResult != null) {
+ scanList.add(scanResult);
+ } else {
+ Log.w(TAG, "misformatted scan result for: " + line);
+ }
+ }
+ lineBeg = lineEnd + 1;
+ }
+ }
+
+ mScanResults = scanList;
+ }
+
+ private void configureNetworkProperties() {
+ try {
+ mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
+ } catch (SocketException e) {
+ Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
+ ". e=" + e);
+ return;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
+ return;
+ }
+ // TODO - fix this for v6
+ try {
+ mNetworkProperties.addAddress(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
+ mDhcpInfo.gateway)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
+ }
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
+
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
+ }
+ // TODO - add proxy info
+ }
+
+
+ private void checkUseStaticIp() {
+ mUseStaticIp = false;
+ final ContentResolver cr = mContext.getContentResolver();
+ try {
+ if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
+ return;
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ return;
+ }
+
+ try {
+ String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
+ if (addr != null) {
+ mDhcpInfo.ipAddress = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
+ if (addr != null) {
+ mDhcpInfo.gateway = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
+ if (addr != null) {
+ mDhcpInfo.netmask = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
+ if (addr != null) {
+ mDhcpInfo.dns1 = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
+ if (addr != null) {
+ mDhcpInfo.dns2 = stringToIpAddr(addr);
+ } else {
+ mDhcpInfo.dns2 = 0;
+ }
+ } catch (UnknownHostException e) {
+ return;
+ }
+ mUseStaticIp = true;
+ }
+
+ private static int stringToIpAddr(String addrString) throws UnknownHostException {
+ try {
+ String[] parts = addrString.split("\\.");
+ if (parts.length != 4) {
+ throw new UnknownHostException(addrString);
+ }
+
+ int a = Integer.parseInt(parts[0]) ;
+ int b = Integer.parseInt(parts[1]) << 8;
+ int c = Integer.parseInt(parts[2]) << 16;
+ int d = Integer.parseInt(parts[3]) << 24;
+
+ return a | b | c | d;
+ } catch (NumberFormatException ex) {
+ throw new UnknownHostException(addrString);
+ }
+ }
+
+ private int getMaxDhcpRetries() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+ DEFAULT_MAX_DHCP_RETRIES);
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_USE_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_GATEWAY), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_NETMASK), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS1), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS2), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ boolean wasStaticIp = mUseStaticIp;
+ int oIp, oGw, oMsk, oDns1, oDns2;
+ oIp = oGw = oMsk = oDns1 = oDns2 = 0;
+ if (wasStaticIp) {
+ oIp = mDhcpInfo.ipAddress;
+ oGw = mDhcpInfo.gateway;
+ oMsk = mDhcpInfo.netmask;
+ oDns1 = mDhcpInfo.dns1;
+ oDns2 = mDhcpInfo.dns2;
+ }
+ checkUseStaticIp();
+
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
+ return;
+ }
+
+ boolean changed =
+ (wasStaticIp != mUseStaticIp) ||
+ (wasStaticIp && (
+ oIp != mDhcpInfo.ipAddress ||
+ oGw != mDhcpInfo.gateway ||
+ oMsk != mDhcpInfo.netmask ||
+ oDns1 != mDhcpInfo.dns1 ||
+ oDns2 != mDhcpInfo.dns2));
+
+ if (changed) {
+ sendMessage(CMD_RECONFIGURE_IP);
+ mConfigChanged = true;
+ }
+ }
+ }
+
+ /**
+ * Whether to disable coexistence mode while obtaining IP address. This
+ * logic will return true only if the current bluetooth
+ * headset/handsfree state is disconnected. This means if it is in an
+ * error state, we will NOT disable coexistence mode to err on the side
+ * of safety.
+ *
+ * @return Whether to disable coexistence mode.
+ */
+ private boolean shouldDisableCoexistenceMode() {
+ int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
+ return state == BluetoothHeadset.STATE_DISCONNECTED;
+ }
+
+ private void checkIsBluetoothPlaying() {
+ boolean isBluetoothPlaying = false;
+ Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
+
+ for (BluetoothDevice device : connected) {
+ if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
+ isBluetoothPlaying = true;
+ break;
+ }
+ }
+ setBluetoothScanMode(isBluetoothPlaying);
+ }
+
+ private void sendScanResultsAvailableBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendNetworkStateChangeBroadcast(String bssid) {
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ if (bssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendConfigChangeBroadcast() {
+ Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendSupplicantConfigChangedBroadcast() {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Record the detailed state of a network.
+ * @param state the new @{code DetailedState}
+ */
+ private void setDetailedState(NetworkInfo.DetailedState state) {
+ Log.d(TAG, "setDetailed state, old ="
+ + mNetworkInfo.getDetailedState() + " and new state=" + state);
+ if (state != mNetworkInfo.getDetailedState()) {
+ mNetworkInfo.setDetailedState(state, null, null);
+ }
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string.length() <= 2) return "";
+ return string.substring(1, string.length() - 1);
+ }
+
+ private static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ private static String makeString(BitSet set, String[] strings) {
+ StringBuffer buf = new StringBuffer();
+ int nextSetBit = -1;
+
+ /* Make sure all set bits are in [0, strings.length) to avoid
+ * going out of bounds on strings. (Shouldn't happen, but...) */
+ set = set.get(0, strings.length);
+
+ while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
+ buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+ }
+
+ // remove trailing space
+ if (set.cardinality() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+
+ return buf.toString();
+ }
+
+ private static int lookupString(String string, String[] strings) {
+ int size = strings.length;
+
+ string = string.replace('-', '_');
+
+ for (int i = 0; i < size; i++)
+ if (string.equals(strings[i]))
+ return i;
+
+ // if we ever get here, we should probably add the
+ // value to WifiConfiguration to reflect that it's
+ // supported by the WPA supplicant
+ Log.w(TAG, "Failed to look-up a string: " + string);
+
+ return -1;
+ }
+
+ private void enableAllNetworks() {
+ if (mEnableAllNetworks) {
+ mEnableAllNetworks = false;
+ List<WifiConfiguration> configList = getConfiguredNetworksNative();
+ for (WifiConfiguration config : configList) {
+ if(config != null && config.status == Status.DISABLED) {
+ WifiNative.enableNetworkCommand(config.networkId, false);
+ }
+ }
+ WifiNative.saveConfigCommand();
+ sendSupplicantConfigChangedBroadcast();
+ }
+ }
+
+ private int addOrUpdateNetworkNative(WifiConfiguration config) {
+ /*
+ * If the supplied networkId is -1, we create a new empty
+ * network configuration. Otherwise, the networkId should
+ * refer to an existing configuration.
+ */
+ int netId = config.networkId;
+ boolean newNetwork = netId == -1;
+ // networkId of -1 means we want to create a new network
+
+ if (newNetwork) {
+ netId = WifiNative.addNetworkCommand();
+ if (netId < 0) {
+ Log.e(TAG, "Failed to add a network!");
+ return -1;
+ }
+ }
+
+ setVariables: {
+
+ if (config.SSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.ssidVarName,
+ config.SSID)) {
+ Log.d(TAG, "failed to set SSID: "+config.SSID);
+ break setVariables;
+ }
+
+ if (config.BSSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.bssidVarName,
+ config.BSSID)) {
+ Log.d(TAG, "failed to set BSSID: "+config.BSSID);
+ break setVariables;
+ }
+
+ String allowedKeyManagementString =
+ makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
+ if (config.allowedKeyManagement.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.KeyMgmt.varName,
+ allowedKeyManagementString)) {
+ Log.d(TAG, "failed to set key_mgmt: "+
+ allowedKeyManagementString);
+ break setVariables;
+ }
+
+ String allowedProtocolsString =
+ makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
+ if (config.allowedProtocols.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.Protocol.varName,
+ allowedProtocolsString)) {
+ Log.d(TAG, "failed to set proto: "+
+ allowedProtocolsString);
+ break setVariables;
+ }
+
+ String allowedAuthAlgorithmsString =
+ makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
+ if (config.allowedAuthAlgorithms.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.AuthAlgorithm.varName,
+ allowedAuthAlgorithmsString)) {
+ Log.d(TAG, "failed to set auth_alg: "+
+ allowedAuthAlgorithmsString);
+ break setVariables;
+ }
+
+ String allowedPairwiseCiphersString =
+ makeString(config.allowedPairwiseCiphers,
+ WifiConfiguration.PairwiseCipher.strings);
+ if (config.allowedPairwiseCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.PairwiseCipher.varName,
+ allowedPairwiseCiphersString)) {
+ Log.d(TAG, "failed to set pairwise: "+
+ allowedPairwiseCiphersString);
+ break setVariables;
+ }
+
+ String allowedGroupCiphersString =
+ makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
+ if (config.allowedGroupCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.GroupCipher.varName,
+ allowedGroupCiphersString)) {
+ Log.d(TAG, "failed to set group: "+
+ allowedGroupCiphersString);
+ break setVariables;
+ }
+
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.pskVarName,
+ config.preSharedKey)) {
+ Log.d(TAG, "failed to set psk: "+config.preSharedKey);
+ break setVariables;
+ }
+
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepKeyVarNames[i],
+ config.wepKeys[i])) {
+ Log.d(TAG,
+ "failed to set wep_key"+i+": " +
+ config.wepKeys[i]);
+ break setVariables;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+
+ if (hasSetKey) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepTxKeyIdxVarName,
+ Integer.toString(config.wepTxKeyIndex))) {
+ Log.d(TAG,
+ "failed to set wep_tx_keyidx: "+
+ config.wepTxKeyIndex);
+ break setVariables;
+ }
+ }
+
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.priorityVarName,
+ Integer.toString(config.priority))) {
+ Log.d(TAG, config.SSID + ": failed to set priority: "
+ +config.priority);
+ break setVariables;
+ }
+
+ if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.hiddenSSIDVarName,
+ Integer.toString(config.hiddenSSID ? 1 : 0))) {
+ Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
+ config.hiddenSSID);
+ break setVariables;
+ }
+
+ for (WifiConfiguration.EnterpriseField field
+ : config.enterpriseFields) {
+ String varName = field.varName();
+ String value = field.value();
+ if (value != null) {
+ if (field != config.eap) {
+ value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
+ }
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ varName,
+ value)) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ break setVariables;
+ }
+ }
+ }
+ return netId;
+ }
+
+ if (newNetwork) {
+ WifiNative.removeNetworkCommand(netId);
+ Log.d(TAG,
+ "Failed to set a network variable, removed network: "
+ + netId);
+ }
+
+ return -1;
+ }
+
+ private List<WifiConfiguration> getConfiguredNetworksNative() {
+ String listStr = WifiNative.listNetworksCommand();
+ mLastPriority = 0;
+ List<WifiConfiguration> networks =
+ new ArrayList<WifiConfiguration>();
+ if (listStr == null)
+ return networks;
+
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ // network-id | ssid | bssid | flags
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ config.networkId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ continue;
+ }
+ if (result.length > 3) {
+ if (result[3].indexOf("[CURRENT]") != -1)
+ config.status = WifiConfiguration.Status.CURRENT;
+ else if (result[3].indexOf("[DISABLED]") != -1)
+ config.status = WifiConfiguration.Status.DISABLED;
+ else
+ config.status = WifiConfiguration.Status.ENABLED;
+ } else {
+ config.status = WifiConfiguration.Status.ENABLED;
+ }
+ readNetworkVariables(config);
+ if (config.priority > mLastPriority) {
+ mLastPriority = config.priority;
+ }
+ networks.add(config);
+ }
+ return networks;
+ }
+
+ /**
+ * Read the variables from the supplicant daemon that are needed to
+ * fill in the WifiConfiguration object.
+ *
+ * @param config the {@link WifiConfiguration} object to be filled in.
+ */
+ private void readNetworkVariables(WifiConfiguration config) {
+
+ int netId = config.networkId;
+ if (netId < 0)
+ return;
+
+ /*
+ * TODO: maybe should have a native method that takes an array of
+ * variable names and returns an array of values. But we'd still
+ * be doing a round trip to the supplicant daemon for each variable.
+ */
+ String value;
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.SSID = removeDoubleQuotes(value);
+ } else {
+ config.SSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.BSSID = value;
+ } else {
+ config.BSSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+ config.priority = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.priority = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+ config.hiddenSSID = false;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.hiddenSSID = Integer.parseInt(value) != 0;
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+ config.wepTxKeyIndex = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.wepTxKeyIndex = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ WifiConfiguration.wepKeyVarNames[i]);
+ if (!TextUtils.isEmpty(value)) {
+ config.wepKeys[i] = value;
+ } else {
+ config.wepKeys[i] = null;
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.preSharedKey = value;
+ } else {
+ config.preSharedKey = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.Protocol.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.Protocol.strings);
+ if (0 <= index) {
+ config.allowedProtocols.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.KeyMgmt.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.KeyMgmt.strings);
+ if (0 <= index) {
+ config.allowedKeyManagement.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.AuthAlgorithm.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
+ if (0 <= index) {
+ config.allowedAuthAlgorithms.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.PairwiseCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.PairwiseCipher.strings);
+ if (0 <= index) {
+ config.allowedPairwiseCiphers.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.GroupCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.GroupCipher.strings);
+ if (0 <= index) {
+ config.allowedGroupCiphers.set(index);
+ }
+ }
+ }
+
+ for (WifiConfiguration.EnterpriseField field :
+ config.enterpriseFields) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ field.varName());
+ if (!TextUtils.isEmpty(value)) {
+ if (field != config.eap) value = removeDoubleQuotes(value);
+ field.setValue(value);
+ }
+ }
+
+ }
+
+ /**
+ * Poll for info not reported via events
+ * RSSI & Linkspeed
+ */
+ private void requestPolledInfo() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
+ mWifiInfo.setRssi(newRssi);
+ /*
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not mWifiInforming others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The second arg to the call below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ mWifiInfo.setRssi(-200);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ /**
+ * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
+ * using the interface, stopping DHCP & disabling interface
+ */
+ private void handleNetworkDisconnect() {
+ Log.d(TAG, "Reset connections and stopping DHCP");
+
+ /*
+ * Reset connections & stop DHCP
+ */
+ NetworkUtils.resetConnections(mInterfaceName);
+
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Could not stop DHCP");
+ }
+
+ /* Disable interface */
+ NetworkUtils.disableInterface(mInterfaceName);
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ /* Reset data structures */
+ mWifiInfo.setIpAddress(0);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
+ mWifiInfo.setNetworkId(-1);
+
+ /* Clear network properties */
+ mNetworkProperties.clear();
+
+ mLastBssid= null;
+ mLastNetworkId = -1;
+
+ }
+
+
+ /*********************************************************
+ * Notifications from WifiMonitor
+ ********************************************************/
+
+ /**
+ * A structure for supplying information about a supplicant state
+ * change in the STATE_CHANGE event message that comes from the
+ * WifiMonitor
+ * thread.
+ */
+ private static class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, Object state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ Object state;
+ }
+
+ /**
+ * Send the tracker a notification that a user-entered password key
+ * may be incorrect (i.e., caused authentication to fail).
+ */
+ void notifyPasswordKeyMayBeIncorrect() {
+ sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantConnection() {
+ sendMessage(SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantLost() {
+ sendMessage(SUP_DISCONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that the state of Wifi connectivity
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ } else {
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+ }
+
+ /**
+ * Send the tracker a notification that the state of the supplicant
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+
+ /**
+ * Send the tracker a notification that a scan has completed, and results
+ * are available.
+ */
+ void notifyScanResultsAvailable() {
+ /**
+ * Switch scan mode over to passive.
+ * Turning off scan-only mode happens only in "Connect" mode
+ */
+ setScanType(false);
+ sendMessage(SCAN_RESULTS_EVENT);
+ }
+
+ void notifyDriverStarted() {
+ sendMessage(DRIVER_START_EVENT);
+ }
+
+ void notifyDriverStopped() {
+ sendMessage(DRIVER_STOP_EVENT);
+ }
+
+ void notifyDriverHung() {
+ setWifiEnabled(false);
+ setWifiEnabled(true);
+ }
+
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState extends HierarchicalState {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch (message.what) {
+ /* Synchronous call returns */
+ case CMD_PING_SUPPLICANT:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_REMOVE_NETWORK:
+ case CMD_ENABLE_NETWORK:
+ case CMD_DISABLE_NETWORK:
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ case CMD_GET_RSSI:
+ case CMD_GET_RSSI_APPROX:
+ case CMD_GET_LINK_SPEED:
+ case CMD_GET_MAC_ADDR:
+ case CMD_SAVE_CONFIG:
+ case CMD_CONNECTION_STATUS:
+ case CMD_GET_NETWORK_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = false;
+ syncParams.mSyncReturn.intValue = -1;
+ syncParams.mSyncReturn.stringValue = null;
+ syncParams.mSyncReturn.configList = null;
+ notifyOnMsgObject(message);
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+ break;
+ /* Discard */
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_RECONFIGURE_IP:
+ case SUP_CONNECTION_EVENT:
+ case SUP_DISCONNECTION_EVENT:
+ case DRIVER_START_EVENT:
+ case DRIVER_STOP_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case SCAN_RESULTS_EVENT:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_BLACKLIST_NETWORK:
+ case CMD_CLEAR_BLACKLIST:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_REQUEST_CM_WAKELOCK:
+ case CMD_CONNECT_NETWORK:
+ case CMD_SAVE_NETWORK:
+ case CMD_FORGET_NETWORK:
+ break;
+ default:
+ Log.e(TAG, "Error! unhandled message" + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class InitialState extends HierarchicalState {
+ @Override
+ //TODO: could move logging into a common class
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ // [31-8] Reserved for future use
+ // [7 - 0] HSM state change
+ // 50021 wifi_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (WifiNative.isDriverLoaded()) {
+ transitionTo(mDriverLoadedState);
+ }
+ else {
+ transitionTo(mDriverUnloadedState);
+ }
+ }
+ }
+
+ class DriverLoadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ /* TODO: add a timeout to fail when driver load is hung.
+ * Similarly for driver unload.
+ */
+ new Thread(new Runnable() {
+ public void run() {
+ sWakeLock.acquire();
+ //enabling state
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_ENABLING);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_ENABLING);
+ break;
+ }
+
+ if(WifiNative.loadDriver()) {
+ Log.d(TAG, "Driver load successful");
+ sendMessage(CMD_LOAD_DRIVER_SUCCESS);
+ } else {
+ Log.e(TAG, "Failed to load driver!");
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ sendMessage(CMD_LOAD_DRIVER_FAILURE);
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_LOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverLoadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_UNLOAD_DRIVER:
+ transitionTo(mDriverUnloadingState);
+ break;
+ case CMD_START_SUPPLICANT:
+ if(WifiNative.startSupplicant()) {
+ Log.d(TAG, "Supplicant start successful");
+ mWifiMonitor.startMonitoring();
+ setWifiState(WIFI_STATE_ENABLED);
+ transitionTo(mWaitForSupState);
+ } else {
+ Log.e(TAG, "Failed to start supplicant!");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_AP:
+ try {
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in startAccessPoint()");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ break;
+ }
+ Log.d(TAG, "Soft AP start successful");
+ setWifiApState(WIFI_AP_STATE_ENABLED);
+ transitionTo(mSoftApStartedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ new Thread(new Runnable() {
+ public void run() {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ sWakeLock.acquire();
+ if(WifiNative.unloadDriver()) {
+ Log.d(TAG, "Driver unload successful");
+ sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(message.arg1);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(message.arg1);
+ break;
+ }
+ } else {
+ Log.e(TAG, "Failed to unload driver!");
+ sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_UNLOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverUnloadedState);
+ break;
+ case CMD_UNLOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER:
+ transitionTo(mDriverLoadingState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverFailedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ Log.e(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+
+ class WaitForSupState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case SUP_CONNECTION_EVENT:
+ Log.d(TAG, "Supplicant connection established");
+ mSupplicantStateTracker.resetSupplicantState();
+ /* Initialize data structures */
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+ //TODO: initialize and fix multicast filtering
+ //mWM.initializeMulticastFiltering();
+
+ if (mBluetoothA2dp == null) {
+ mBluetoothA2dp = new BluetoothA2dp(mContext);
+ }
+ checkIsBluetoothPlaying();
+
+ checkUseStaticIp();
+ sendSupplicantConnectionChangedBroadcast(true);
+ transitionTo(mDriverSupReadyState);
+ break;
+ case CMD_STOP_SUPPLICANT:
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ transitionTo(mDriverLoadedState);
+ break;
+ /* Fail soft ap when waiting for supplicant start */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ case CMD_STOP_AP:
+ case CMD_START_SUPPLICANT:
+ case CMD_UNLOAD_DRIVER:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverSupReadyState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ /* Initialize for connect mode operation at start */
+ mIsScanMode = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ WifiConfiguration config;
+ switch(message.what) {
+ case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ //$FALL-THROUGH$
+ case SUP_DISCONNECTION_EVENT: /* Supplicant died */
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.closeSupplicantConnection();
+ handleNetworkDisconnect();
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.resetSupplicantState();
+ transitionTo(mDriverLoadedState);
+
+ /* When supplicant dies, unload driver and enter failed state */
+ //TODO: consider bringing up supplicant again
+ if (message.what == SUP_DISCONNECTION_EVENT) {
+ Log.d(TAG, "Supplicant died, unloading driver");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_DRIVER:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.startDriverCommand();
+ transitionTo(mDriverStartingState);
+ break;
+ case SCAN_RESULTS_EVENT:
+ setScanResults(WifiNative.scanResultsCommand());
+ sendScanResultsAvailableBroadcast();
+ break;
+ case CMD_PING_SUPPLICANT:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ config = (WifiConfiguration) syncParams.mParameter;
+ syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
+ notifyOnMsgObject(message);
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ case CMD_REMOVE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.removeNetworkCommand(message.arg1);
+ }
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ case CMD_ENABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
+ syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
+ enableNetParams.netId, enableNetParams.disableOthers);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
+ }
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ case CMD_DISABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disableNetworkCommand(message.arg1);
+ }
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ case CMD_BLACKLIST_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.addToBlacklistCommand((String)message.obj);
+ break;
+ case CMD_CLEAR_BLACKLIST:
+ WifiNative.clearBlacklistCommand();
+ break;
+ case CMD_GET_NETWORK_CONFIG:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_SAVE_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.saveConfigCommand();
+ }
+ // Inform the backup manager about a data change
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (ibm != null) {
+ try {
+ ibm.dataChanged("com.android.providers.settings");
+ } catch (Exception e) {
+ // Try again later
+ }
+ }
+ break;
+ case CMD_CONNECTION_STATUS:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_MAC_ADDR:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
+ notifyOnMsgObject(message);
+ break;
+ /* Cannot start soft AP while in client mode */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_SET_SCAN_MODE:
+ mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
+ break;
+ case CMD_SAVE_NETWORK:
+ config = (WifiConfiguration) message.obj;
+ int netId = addOrUpdateNetworkNative(config);
+ /* enable a new network */
+ if (config.networkId < 0) {
+ WifiNative.enableNetworkCommand(netId, false);
+ }
+ WifiNative.saveConfigCommand();
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ case CMD_FORGET_NETWORK:
+ WifiNative.removeNetworkCommand(message.arg1);
+ WifiNative.saveConfigCommand();
+ sendSupplicantConfigChangedBroadcast();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class DriverStartingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_START_EVENT:
+ transitionTo(mDriverStartedState);
+ break;
+ /* Queue driver commands & connection events */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ try {
+ mBatteryStats.noteWifiRunning();
+ } catch (RemoteException ignore) {}
+
+ /* Initialize channel count */
+ setNumAllowedChannels();
+
+ if (mIsScanMode) {
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.disconnectCommand();
+ transitionTo(mScanModeState);
+ } else {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* If supplicant has already connected, before we could finish establishing
+ * the control channel connection, we miss all the supplicant events.
+ * Disconnect and reconnect when driver has started to ensure we receive
+ * all supplicant events.
+ *
+ * TODO: This is a bit unclean, ideally the supplicant should never
+ * connect until told to do so by the framework
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mConnectModeState);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_TYPE:
+ if (message.arg1 == SCAN_ACTIVE) {
+ WifiNative.setScanModeCommand(true);
+ } else {
+ WifiNative.setScanModeCommand(false);
+ }
+ break;
+ case CMD_SET_POWER_MODE:
+ WifiNative.setPowerModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
+ break;
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ mNumAllowedChannels = message.arg1;
+ WifiNative.setNumAllowedChannelsCommand(message.arg1);
+ break;
+ case CMD_START_DRIVER:
+ /* Ignore another driver start */
+ break;
+ case CMD_STOP_DRIVER:
+ WifiNative.stopDriverCommand();
+ transitionTo(mDriverStoppingState);
+ break;
+ case CMD_REQUEST_CM_WAKELOCK:
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ mCm.requestNetworkTransitionWakelock(TAG);
+ break;
+ case CMD_START_PACKET_FILTERING:
+ WifiNative.startPacketFiltering();
+ break;
+ case CMD_STOP_PACKET_FILTERING:
+ WifiNative.stopPacketFiltering();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ try {
+ mBatteryStats.noteWifiStopped();
+ } catch (RemoteException ignore) { }
+ }
+ }
+
+ class DriverStoppingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_STOP_EVENT:
+ transitionTo(mDriverStoppedState);
+ break;
+ /* Queue driver commands */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStoppedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+ class ScanModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ /* Ignore */
+ return HANDLED;
+ } else {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ WifiNative.reconnectCommand();
+ mIsScanMode = false;
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
+ message.arg1 == SCAN_ACTIVE);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ }
+ break;
+ /* Ignore */
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ StateChangeResult stateChangeResult;
+ switch(message.what) {
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ mPasswordKeyMayBeIncorrect = true;
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ stateChangeResult = (StateChangeResult) message.obj;
+ mSupplicantStateTracker.handleEvent(stateChangeResult);
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = true;
+ notifyOnMsgObject(message);
+ }
+ /* We need to set scan type in completed state */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(message);
+ mSupplicantStateTracker.sendMessage(newMsg);
+ break;
+ /* Do a redundant disconnect without transition */
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ break;
+ case CMD_RECONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reconnectCommand();
+ }
+ break;
+ case CMD_REASSOCIATE:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reassociateCommand();
+ }
+ break;
+ case CMD_CONNECT_NETWORK:
+ int netId = message.arg1;
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+ if (config != null) {
+ netId = addOrUpdateNetworkNative(config);
+ }
+ // Reset the priority of each network at start or if it goes too high.
+ if (mLastPriority == -1 || mLastPriority > 1000000) {
+ List<WifiConfiguration> configList = getConfiguredNetworksNative();
+ for (WifiConfiguration conf : configList) {
+ if (conf.networkId != -1) {
+ conf.priority = 0;
+ addOrUpdateNetworkNative(conf);
+ }
+ }
+ mLastPriority = 0;
+ }
+
+ // Set to the highest priority and save the configuration.
+ config = new WifiConfiguration();
+ config.networkId = netId;
+ config.priority = ++mLastPriority;
+
+ addOrUpdateNetworkNative(config);
+ WifiNative.saveConfigCommand();
+
+ // Connect to network by disabling others.
+ WifiNative.enableNetworkCommand(netId, true);
+ WifiNative.reconnectCommand();
+ mEnableAllNetworks = true;
+ /* Dont send a supplicant config change broadcast here
+ * as it is better to not expose the temporary disabling
+ * of all networks
+ */
+ break;
+ case SCAN_RESULTS_EVENT:
+ /* Set the scan setting back to "connect" mode */
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* Handle scan results */
+ return NOT_HANDLED;
+ case NETWORK_CONNECTION_EVENT:
+ Log.d(TAG,"Network connection established");
+ stateChangeResult = (StateChangeResult) message.obj;
+
+ mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ mLastNetworkId = stateChangeResult.networkId;
+ enableAllNetworks();
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ transitionTo(mConnectingState);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ enableAllNetworks();
+ handleNetworkDisconnect();
+ transitionTo(mDisconnectedState);
+ break;
+ case CMD_GET_RSSI:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_RSSI_APPROX:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_LINK_SPEED:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
+ notifyOnMsgObject(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectingState extends HierarchicalState {
+ boolean modifiedBluetoothCoexistenceMode;
+ int powerMode;
+ Thread mDhcpThread;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (!mUseStaticIp) {
+
+ mDhcpThread = null;
+ modifiedBluetoothCoexistenceMode = false;
+ powerMode = DRIVER_POWER_MODE_AUTO;
+
+ if (shouldDisableCoexistenceMode()) {
+ /*
+ * There are problems setting the Wi-Fi driver's power
+ * mode to active when bluetooth coexistence mode is
+ * enabled or sense.
+ * <p>
+ * We set Wi-Fi to active mode when
+ * obtaining an IP address because we've found
+ * compatibility issues with some routers with low power
+ * mode.
+ * <p>
+ * In order for this active power mode to properly be set,
+ * we disable coexistence mode until we're done with
+ * obtaining an IP address. One exception is if we
+ * are currently connected to a headset, since disabling
+ * coexistence would interrupt that connection.
+ */
+ modifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+
+ powerMode = WifiNative.getPowerModeCommand();
+ if (powerMode < 0) {
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
+ powerMode = DRIVER_POWER_MODE_AUTO;
+ }
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
+ }
+
+ Log.d(TAG, "DHCP request started");
+ mDhcpThread = new Thread(new Runnable() {
+ public void run() {
+ if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
+ Log.d(TAG, "DHCP request succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.d(TAG, "DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ });
+ mDhcpThread.start();
+ } else {
+ if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
+ Log.v(TAG, "Static IP configuration succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.v(TAG, "Static IP configuration failed");
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+
+ switch(message.what) {
+ case CMD_IP_CONFIG_SUCCESS:
+ mReconnectCount = 0;
+ mLastSignalLevel = -1; // force update of signal strength
+ mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+ Log.d(TAG, "IP configuration: " + mDhcpInfo);
+ configureNetworkProperties();
+ setDetailedState(DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ //TODO: we could also detect an IP config change
+ // from a DHCP renewal and send out a config change
+ // broadcast
+ if (mConfigChanged) {
+ sendConfigChangeBroadcast();
+ mConfigChanged = false;
+ }
+ transitionTo(mConnectedState);
+ break;
+ case CMD_IP_CONFIG_FAILURE:
+ mWifiInfo.setIpAddress(0);
+
+ Log.e(TAG, "IP configuration failed");
+ /**
+ * If we've exceeded the maximum number of retries for DHCP
+ * to a given network, disable the network
+ */
+ if (++mReconnectCount > getMaxDhcpRetries()) {
+ Log.e(TAG, "Failed " +
+ mReconnectCount + " times, Disabling " + mLastNetworkId);
+ WifiNative.disableNetworkCommand(mLastNetworkId);
+ sendSupplicantConfigChangedBroadcast();
+ }
+
+ /* DHCP times out after about 30 seconds, we do a
+ * disconnect and an immediate reconnect to try again
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ case CMD_RECONFIGURE_IP:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ /* reset power state & bluetooth coexistence if on DHCP */
+ if (!mUseStaticIp) {
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(powerMode);
+ }
+
+ if (modifiedBluetoothCoexistenceMode) {
+ // Set the coexistence mode back to its default value
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ }
+ }
+
+ }
+ }
+
+ class ConnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_RECONFIGURE_IP:
+ Log.d(TAG,"Reconfiguring IP on connection");
+ NetworkUtils.resetConnections(mInterfaceName);
+ transitionTo(mConnectingState);
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ //Supplicant disconnect to prevent further connects
+ WifiNative.disconnectCommand();
+ mIsScanMode = true;
+ transitionTo(mScanModeState);
+ }
+ break;
+ /* Ignore network disconnect */
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class SoftApStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STOP_AP:
+ Log.d(TAG,"Stopping Soft AP");
+ setWifiApState(WIFI_AP_STATE_DISABLING);
+ try {
+ nwService.stopAccessPoint();
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in stopAccessPoint()");
+ }
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_START_AP:
+ Log.d(TAG,"SoftAP set on a running access point");
+ try {
+ nwService.setAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in nwService during soft AP set");
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ }
+ break;
+ /* Fail client mode operation when soft AP is enabled */
+ case CMD_START_SUPPLICANT:
+ Log.e(TAG,"Cannot start supplicant with a running soft AP");
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+
+ class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private int mRssiPollToken = 0;
+
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+ private int mLoopDetectIndex = 0;
+ private int mLoopDetectCount = 0;
+
+ /**
+ * Supplicant state change commands follow
+ * the ordinal values defined in SupplicantState.java
+ */
+ private static final int DISCONNECTED = 0;
+ private static final int INACTIVE = 1;
+ private static final int SCANNING = 2;
+ private static final int ASSOCIATING = 3;
+ private static final int ASSOCIATED = 4;
+ private static final int FOUR_WAY_HANDSHAKE = 5;
+ private static final int GROUP_HANDSHAKE = 6;
+ private static final int COMPLETED = 7;
+ private static final int DORMANT = 8;
+ private static final int UNINITIALIZED = 9;
+ private static final int INVALID = 10;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mInitializedState = new InitializedState();;
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mConnectState = new ConnectState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, Handler target) {
+ super(TAG, target.getLooper());
+
+ addState(mUninitializedState);
+ addState(mInitializedState);
+ addState(mInactiveState, mInitializedState);
+ addState(mDisconnectState, mInitializedState);
+ addState(mScanState, mInitializedState);
+ addState(mConnectState, mInitializedState);
+ addState(mHandshakeState, mConnectState);
+ addState(mCompletedState, mConnectState);
+ addState(mDormantState, mInitializedState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ public void handleEvent(StateChangeResult stateChangeResult) {
+ SupplicantState newState = (SupplicantState) stateChangeResult.state;
+
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
+
+ sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
+ }
+
+ public void resetSupplicantState() {
+ transitionTo(mUninitializedState);
+ }
+
+ private void resetLoopDetection() {
+ mLoopDetectCount = 0;
+ mLoopDetectIndex = 0;
+ }
+
+ private boolean handleTransition(Message msg) {
+ if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
+ switch (msg.what) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ //$FALL-THROUGH$
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ transitionTo(mHandshakeState);
+ break;
+ case COMPLETED:
+ transitionTo(mCompletedState);
+ break;
+ case DORMANT:
+ transitionTo(mDormantState);
+ break;
+ case INACTIVE:
+ transitionTo(mInactiveState);
+ break;
+ case UNINITIALIZED:
+ case INVALID:
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+ setDetailedState(WifiInfo.getDetailedStateOf(supState));
+ mWifiInfo.setSupplicantState(supState);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ //TODO: Modify WifiMonitor to report SSID on events
+ //mWifiInfo.setSSID()
+ return HANDLED;
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class InitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ default:
+ if (!handleTransition(message)) {
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ }
+ break;
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+
+ /* If a disconnect event happens after a password key failure
+ * event, disable the network
+ */
+ if (mPasswordKeyMayBeIncorrect) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ mWifiInfo.getNetworkId());
+ WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, true);
+ sendSupplicantConfigChangedBroadcast();
+ }
+ else {
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mPasswordKeyMayBeIncorrect = false;
+ resetLoopDetection();
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ConnectState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ final Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mLoopDetectIndex > message.what) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ WifiNative.disableNetworkCommand(stateChangeResult.networkId);
+ sendSupplicantConfigChangedBroadcast();
+ mLoopDetectCount = 0;
+ }
+
+ mLoopDetectIndex = message.what;
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+
+ resetLoopDetection();
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ break;
+ case CMD_RSSI_POLL:
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
+ default:
+ return handleTransition(message);
+ }
+ return HANDLED;
+ }
+ }
+
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+
+ /* TODO: reconnect is now being handled at DHCP failure handling
+ * If we run into issues with staying in Dormant state, might
+ * need a reconnect here
+ */
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 388beea..a5f62c1 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,524 +16,56 @@
package android.net.wifi;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-/**
- * TODO: Add soft AP states as part of WIFI_STATE_XXX
- * Retain WIFI_STATE_ENABLING that indicates driver is loading
- * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
- * and WIFI_STATE_FAILED for failure
- * Deprecate WIFI_STATE_UNKNOWN
- *
- * Doing this will simplify the logic for sending broadcasts
- */
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
-import android.app.ActivityManagerNative;
-import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
-import android.net.NetworkProperties;
-import android.os.Binder;
-import android.os.Message;
-import android.os.Parcelable;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Process;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.backup.IBackupManager;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
-import android.content.ContentResolver;
-import android.content.Intent;
+import android.content.BroadcastReceiver;
import android.content.Context;
-import android.database.ContentObserver;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkProperties;
+import android.net.NetworkStateTracker;
+import android.os.Handler;
+import android.os.Message;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
/**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
*
* @hide
*/
-//TODO: we still need frequent scanning for the case when
-// we issue disconnect but need scan results for open network notification
-public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
- private static final String TAG = "WifiStateTracker";
private static final String NETWORKTYPE = "WIFI";
- private static final boolean DBG = false;
+ private static final String TAG = "WifiStateTracker";
- /* TODO: fetch a configurable interface */
- private static final String SOFTAP_IFACE = "wl0.1";
-
- private WifiMonitor mWifiMonitor;
- private INetworkManagementService nwService;
- private ConnectivityManager mCm;
-
- /* Scan results handling */
- private List<ScanResult> mScanResults;
- private static final Pattern scanResultPattern = Pattern.compile("\t+");
- private static final int SCAN_RESULT_CACHE_SIZE = 80;
- private final LinkedHashMap<String, ScanResult> mScanResultCache;
-
- private String mInterfaceName;
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
- private int mNumAllowedChannels = 0;
- private int mLastSignalLevel = -1;
- private String mLastBssid;
- private int mLastNetworkId;
- private boolean mEnableRssiPolling = false;
- private boolean mPasswordKeyMayBeIncorrect = false;
- private boolean mUseStaticIp = false;
- private int mReconnectCount = 0;
- private boolean mIsScanMode = false;
-
- /**
- * Instance of the bluetooth headset helper. This needs to be created
- * early because there is a delay before it actually 'connects', as
- * noted by its javadoc. If we check before it is connected, it will be
- * in an error state and we will not disable coexistence.
- */
- private BluetoothHeadset mBluetoothHeadset;
-
- private BluetoothA2dp mBluetoothA2dp;
-
- /**
- * Observes the static IP address settings.
- */
- private SettingsObserver mSettingsObserver;
private NetworkProperties mNetworkProperties;
-
-
- // Variables relating to the 'available networks' notification
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
-
- // Held during driver load and unload
- private static PowerManager.WakeLock sWakeLock;
+ private NetworkInfo mNetworkInfo;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
+ private BroadcastReceiver mWifiStateReceiver;
+ private WifiManager mWifiManager;
- private DhcpInfo mDhcpInfo;
- private WifiInfo mWifiInfo;
- private NetworkInfo mNetworkInfo;
- private SupplicantStateTracker mSupplicantStateTracker;
-
- // Event log tags (must be in sync with event-log-tags)
- private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
- private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
- private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
-
- /* Load the driver */
- private static final int CMD_LOAD_DRIVER = 1;
- /* Unload the driver */
- private static final int CMD_UNLOAD_DRIVER = 2;
- /* Indicates driver load succeeded */
- private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
- /* Indicates driver load failed */
- private static final int CMD_LOAD_DRIVER_FAILURE = 4;
- /* Indicates driver unload succeeded */
- private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
- /* Indicates driver unload failed */
- private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
-
- /* Start the supplicant */
- private static final int CMD_START_SUPPLICANT = 11;
- /* Stop the supplicant */
- private static final int CMD_STOP_SUPPLICANT = 12;
- /* Start the driver */
- private static final int CMD_START_DRIVER = 13;
- /* Start the driver */
- private static final int CMD_STOP_DRIVER = 14;
- /* Indicates DHCP succeded */
- private static final int CMD_IP_CONFIG_SUCCESS = 15;
- /* Indicates DHCP failed */
- private static final int CMD_IP_CONFIG_FAILURE = 16;
- /* Re-configure interface */
- private static final int CMD_RECONFIGURE_IP = 17;
-
-
- /* Start the soft access point */
- private static final int CMD_START_AP = 21;
- /* Stop the soft access point */
- private static final int CMD_STOP_AP = 22;
-
-
- /* Supplicant events */
- /* Connection to supplicant established */
- private static final int SUP_CONNECTION_EVENT = 31;
- /* Connection to supplicant lost */
- private static final int SUP_DISCONNECTION_EVENT = 32;
- /* Driver start completed */
- private static final int DRIVER_START_EVENT = 33;
- /* Driver stop completed */
- private static final int DRIVER_STOP_EVENT = 34;
- /* Network connection completed */
- private static final int NETWORK_CONNECTION_EVENT = 36;
- /* Network disconnection completed */
- private static final int NETWORK_DISCONNECTION_EVENT = 37;
- /* Scan results are available */
- private static final int SCAN_RESULTS_EVENT = 38;
- /* Supplicate state changed */
- private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
- /* Password may be incorrect */
- private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
-
- /* Supplicant commands */
- /* Is supplicant alive ? */
- private static final int CMD_PING_SUPPLICANT = 51;
- /* Add/update a network configuration */
- private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
- /* Delete a network */
- private static final int CMD_REMOVE_NETWORK = 53;
- /* Enable a network. The device will attempt a connection to the given network. */
- private static final int CMD_ENABLE_NETWORK = 54;
- /* Disable a network. The device does not attempt a connection to the given network. */
- private static final int CMD_DISABLE_NETWORK = 55;
- /* Blacklist network. De-prioritizes the given BSSID for connection. */
- private static final int CMD_BLACKLIST_NETWORK = 56;
- /* Clear the blacklist network list */
- private static final int CMD_CLEAR_BLACKLIST = 57;
- /* Get the configured networks */
- private static final int CMD_GET_NETWORK_CONFIG = 58;
- /* Save configuration */
- private static final int CMD_SAVE_CONFIG = 59;
- /* Connection status */
- private static final int CMD_CONNECTION_STATUS = 60;
-
- /* Supplicant commands after driver start*/
- /* Initiate a scan */
- private static final int CMD_START_SCAN = 71;
- /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
- private static final int CMD_SET_SCAN_MODE = 72;
- /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
- private static final int CMD_SET_SCAN_TYPE = 73;
- /* Disconnect from a network */
- private static final int CMD_DISCONNECT = 74;
- /* Reconnect to a network */
- private static final int CMD_RECONNECT = 75;
- /* Reassociate to a network */
- private static final int CMD_REASSOCIATE = 76;
- /* Set power mode
- * POWER_MODE_ACTIVE
- * POWER_MODE_AUTO
- */
- private static final int CMD_SET_POWER_MODE = 77;
- /* Set bluetooth co-existence
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
- /* Enable/disable bluetooth scan mode
- * true(1)
- * false(0)
- */
- private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
- /* Set number of allowed channels */
- private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
- /* Request connectivity manager wake lock before driver stop */
- private static final int CMD_REQUEST_CM_WAKELOCK = 81;
- /* Enables RSSI poll */
- private static final int CMD_ENABLE_RSSI_POLL = 82;
- /* RSSI poll */
- private static final int CMD_RSSI_POLL = 83;
- /* Get current RSSI */
- private static final int CMD_GET_RSSI = 84;
- /* Get approx current RSSI */
- private static final int CMD_GET_RSSI_APPROX = 85;
- /* Get link speed on connection */
- private static final int CMD_GET_LINK_SPEED = 86;
- /* Radio mac address */
- private static final int CMD_GET_MAC_ADDR = 87;
- /* Set up packet filtering */
- private static final int CMD_START_PACKET_FILTERING = 88;
- /* Clear packet filter */
- private static final int CMD_STOP_PACKET_FILTERING = 89;
-
- /* Connectivity service commands */
- /* Bring down wifi connection */
- private static final int CM_CMD_TEARDOWN = 110;
- /* Reconnect to wifi */
- private static final int CM_CMD_RECONNECT = 111;
-
- /**
- * Interval in milliseconds between polling for connection
- * status items that are not sent via asynchronous events.
- * An example is RSSI (signal strength).
- */
- private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
-
- private static final int CONNECT_MODE = 1;
- private static final int SCAN_ONLY_MODE = 2;
-
- private static final int SCAN_ACTIVE = 1;
- private static final int SCAN_PASSIVE = 2;
-
- /**
- * The maximum number of times we will retry a connection to an access point
- * for which we have failed in acquiring an IP address from DHCP. A value of
- * N means that we will make N+1 connection attempts in all.
- * <p>
- * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
- * value if a Settings value is not present.
- */
- private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
- private static final int DRIVER_POWER_MODE_ACTIVE = 1;
- private static final int DRIVER_POWER_MODE_AUTO = 0;
-
- /* Default parent state */
- private HierarchicalState mDefaultState = new DefaultState();
- /* Temporary initial state */
- private HierarchicalState mInitialState = new InitialState();
- /* Unloading the driver */
- private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
- /* Loading the driver */
- private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
- /* Driver load/unload failed */
- private HierarchicalState mDriverFailedState = new DriverFailedState();
- /* Driver loading */
- private HierarchicalState mDriverLoadingState = new DriverLoadingState();
- /* Driver loaded */
- private HierarchicalState mDriverLoadedState = new DriverLoadedState();
- /* Driver loaded, waiting for supplicant to start */
- private HierarchicalState mWaitForSupState = new WaitForSupState();
-
- /* Driver loaded and supplicant ready */
- private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
- /* Driver start issued, waiting for completed event */
- private HierarchicalState mDriverStartingState = new DriverStartingState();
- /* Driver started */
- private HierarchicalState mDriverStartedState = new DriverStartedState();
- /* Driver stopping */
- private HierarchicalState mDriverStoppingState = new DriverStoppingState();
- /* Driver stopped */
- private HierarchicalState mDriverStoppedState = new DriverStoppedState();
- /* Scan for networks, no connection will be established */
- private HierarchicalState mScanModeState = new ScanModeState();
- /* Connecting to an access point */
- private HierarchicalState mConnectModeState = new ConnectModeState();
- /* Fetching IP after network connection (assoc+auth complete) */
- private HierarchicalState mConnectingState = new ConnectingState();
- /* Connected with IP addr */
- private HierarchicalState mConnectedState = new ConnectedState();
- /* disconnect issued, waiting for network disconnect confirmation */
- private HierarchicalState mDisconnectingState = new DisconnectingState();
- /* Network is not connected, supplicant assoc+auth is not complete */
- private HierarchicalState mDisconnectedState = new DisconnectedState();
-
- /* Soft Ap is running */
- private HierarchicalState mSoftApStartedState = new SoftApStartedState();
-
- /* Argument for Message object to indicate a synchronous call */
- private static final int SYNCHRONOUS_CALL = 1;
- private static final int ASYNCHRONOUS_CALL = 0;
-
-
- /**
- * One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- *
- */
- private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
-
- /**
- * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
- *
- */
- private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
-
- private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
- private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
-
- private final IBatteryStats mBatteryStats;
-
- public WifiStateTracker(Context context, Handler target) {
- super(NETWORKTYPE);
-
- mCsHandler = target;
- mContext = context;
-
+ public WifiStateTracker() {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- nwService = INetworkManagementService.Stub.asInterface(b);
-
- mWifiMonitor = new WifiMonitor(this);
- mDhcpInfo = new DhcpInfo();
- mWifiInfo = new WifiInfo();
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
-
- mBluetoothHeadset = new BluetoothHeadset(mContext, null);
mNetworkProperties = new NetworkProperties();
mNetworkInfo.setIsAvailable(false);
mNetworkProperties.clear();
- resetNotificationTimer();
setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mScanResultCache = new LinkedHashMap<String, ScanResult>(
- SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
- /*
- * Limit the cache size by SCAN_RESULT_CACHE_SIZE
- * elements
- */
- @Override
- public boolean removeEldestEntry(Map.Entry eldest) {
- return SCAN_RESULT_CACHE_SIZE < this.size();
- }
- };
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target);
- mNotificationEnabledSettingObserver.register();
-
- mSettingsObserver = new SettingsObserver(new Handler());
-
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- addState(mDefaultState);
- addState(mInitialState, mDefaultState);
- addState(mDriverUnloadingState, mDefaultState);
- addState(mDriverUnloadedState, mDefaultState);
- addState(mDriverFailedState, mDriverUnloadedState);
- addState(mDriverLoadingState, mDefaultState);
- addState(mDriverLoadedState, mDefaultState);
- addState(mWaitForSupState, mDriverLoadedState);
- addState(mDriverSupReadyState, mDefaultState);
- addState(mDriverStartingState, mDriverSupReadyState);
- addState(mDriverStartedState, mDriverSupReadyState);
- addState(mScanModeState, mDriverStartedState);
- addState(mConnectModeState, mDriverStartedState);
- addState(mConnectingState, mConnectModeState);
- addState(mConnectedState, mConnectModeState);
- addState(mDisconnectingState, mConnectModeState);
- addState(mDisconnectedState, mConnectModeState);
- addState(mDriverStoppingState, mDriverSupReadyState);
- addState(mDriverStoppedState, mDriverSupReadyState);
- addState(mSoftApStartedState, mDefaultState);
-
- setInitialState(mInitialState);
-
- if (DBG) setDbg(true);
-
- //start the state machine
- start();
}
- /*********************************************************
- * NetworkStateTracker interface implementation
- ********************************************************/
-
public void setTeardownRequested(boolean isRequested) {
mTeardownRequested.set(isRequested);
}
@@ -544,11 +76,20 @@
/**
* Begin monitoring wifi connectivity
- * @deprecated
- *
- * TODO: remove this from callers
*/
- public void startMonitoring() {
+ public void startMonitoring(Context context, Handler target) {
+ mCsHandler = target;
+ mContext = context;
+
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.CONFIG_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mWifiStateReceiver = new WifiStateReceiver();
+ mContext.registerReceiver(mWifiStateReceiver, filter);
}
/**
@@ -556,7 +97,8 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean teardown() {
- sendMessage(CM_CMD_TEARDOWN);
+ mTeardownRequested.set(true);
+ mWifiManager.stopWifi();
return true;
}
@@ -565,7 +107,8 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean reconnect() {
- sendMessage(CM_CMD_RECONNECT);
+ mTeardownRequested.set(false);
+ mWifiManager.startWifi();
return true;
}
@@ -575,7 +118,7 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean setRadio(boolean turnOn) {
- setWifiEnabled(turnOn);
+ mWifiManager.setWifiEnabled(turnOn);
return true;
}
@@ -626,21 +169,6 @@
return -1;
}
- /* TODO: will go away.
- * Notifications are directly handled in WifiStateTracker at checkAndSetNotification()
- */
- public void interpretScanResultsAvailable() {
-
- }
-
- /**
- * Return the name of our WLAN network interface.
- * @return the name of our interface.
- */
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
/**
* Check if private DNS route is set for the network
*/
@@ -698,3279 +226,23 @@
return "net.tcp.buffersize.wifi";
}
-
- /*********************************************************
- * Methods exposed for public use
- ********************************************************/
-
- /**
- * TODO: doc
- */
- public boolean pingSupplicant() {
- return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public boolean startScan(boolean forceActive) {
- return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
- SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void setWifiEnabled(boolean enable) {
- mLastEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
- sendMessage(CMD_START_SUPPLICANT);
- } else {
- sendMessage(CMD_STOP_SUPPLICANT);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
- mLastApEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
- sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
- } else {
- sendMessage(CMD_STOP_AP);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiState() {
- return mWifiState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiStateByName() {
- switch (mWifiState.get()) {
- case WIFI_STATE_DISABLING:
- return "disabling";
- case WIFI_STATE_DISABLED:
- return "disabled";
- case WIFI_STATE_ENABLING:
- return "enabling";
- case WIFI_STATE_ENABLED:
- return "enabled";
- case WIFI_STATE_UNKNOWN:
- return "unknown state";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiApState() {
- return mWifiApState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiApStateByName() {
- switch (mWifiApState.get()) {
- case WIFI_AP_STATE_DISABLING:
- return "disabling";
- case WIFI_AP_STATE_DISABLED:
- return "disabled";
- case WIFI_AP_STATE_ENABLING:
- return "enabling";
- case WIFI_AP_STATE_ENABLED:
- return "enabled";
- case WIFI_AP_STATE_FAILED:
- return "failed";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
- *
- */
- public WifiInfo requestConnectionInfo() {
- return mWifiInfo;
- }
-
- public DhcpInfo getDhcpInfo() {
- return mDhcpInfo;
- }
-
- /**
- * TODO: doc
- */
- public void startWifi(boolean enable) {
- if (enable) {
- sendMessage(CMD_START_DRIVER);
- } else {
- sendMessage(CMD_STOP_DRIVER);
- }
- }
-
- /**
- * TODO: doc
- */
- public void disconnectAndStop() {
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- }
-
- /**
- * TODO: doc
- */
- public void setScanOnlyMode(boolean enable) {
- if (enable) {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setScanType(boolean active) {
- if (active) {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public List<ScanResult> getScanResultsList() {
- return mScanResults;
- }
-
- /**
- * Disconnect from Access Point
- */
- public boolean disconnectCommand() {
- return sendSyncMessage(CMD_DISCONNECT).boolValue;
- }
-
- /**
- * Initiate a reconnection to AP
- */
- public boolean reconnectCommand() {
- return sendSyncMessage(CMD_RECONNECT).boolValue;
- }
-
- /**
- * Initiate a re-association to AP
- */
- public boolean reassociateCommand() {
- return sendSyncMessage(CMD_REASSOCIATE).boolValue;
- }
-
- /**
- * Add a network synchronously
- *
- * @return network id of the new network
- */
- public int addOrUpdateNetwork(WifiConfiguration config) {
- return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
- }
-
- public List<WifiConfiguration> getConfiguredNetworks() {
- return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
- }
-
- /**
- * Delete a network
- *
- * @param networkId id of the network to be removed
- */
- public boolean removeNetwork(int networkId) {
- return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
- }
-
- private class EnableNetParams {
- private int netId;
- private boolean disableOthers;
- EnableNetParams(int n, boolean b) {
- netId = n;
- disableOthers = b;
- }
- }
- /**
- * Enable a network
- *
- * @param netId network id of the network
- * @param disableOthers true, if all other networks have to be disabled
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean enableNetwork(int netId, boolean disableOthers) {
- return sendSyncMessage(CMD_ENABLE_NETWORK,
- new EnableNetParams(netId, disableOthers)).boolValue;
- }
-
- /**
- * Disable a network
- *
- * @param netId network id of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean disableNetwork(int netId) {
- return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- */
- public void addToBlacklist(String bssid) {
- sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
- }
-
- /**
- * Clear the blacklist list
- *
- */
- public void clearBlacklist() {
- sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
- }
-
- /**
- * Get detailed status of the connection
- *
- * @return Example status result
- * bssid=aa:bb:cc:dd:ee:ff
- * ssid=TestNet
- * id=3
- * pairwise_cipher=NONE
- * group_cipher=NONE
- * key_mgmt=NONE
- * wpa_state=COMPLETED
- * ip_address=X.X.X.X
- */
- public String status() {
- return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
- }
-
- public void enableRssiPolling(boolean enabled) {
- sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
- }
- /**
- * Get RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssi() {
- return sendSyncMessage(CMD_GET_RSSI).intValue;
- }
-
- /**
- * Get approx RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssiApprox() {
- return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
- }
-
- /**
- * Get link speed to currently connected network
- *
- * @return link speed, -1 on failure
- */
- public int getLinkSpeed() {
- return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
- }
-
- /**
- * Get MAC address of radio
- *
- * @return MAC address, null on failure
- */
- public String getMacAddress() {
- return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
- }
-
- /**
- * Start packet filtering
- */
- public void startPacketFiltering() {
- sendMessage(CMD_START_PACKET_FILTERING);
- }
-
- /**
- * Stop packet filtering
- */
- public void stopPacketFiltering() {
- sendMessage(CMD_STOP_PACKET_FILTERING);
- }
-
- /**
- * Set power mode
- * @param mode
- * DRIVER_POWER_MODE_AUTO
- * DRIVER_POWER_MODE_ACTIVE
- */
- public void setPowerMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
- }
-
- /**
- * Set the number of allowed radio frequency channels from the system
- * setting value, if any.
- */
- public void setNumAllowedChannels() {
- try {
- setNumAllowedChannels(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
- } catch (Settings.SettingNotFoundException e) {
- if (mNumAllowedChannels != 0) {
- setNumAllowedChannels(mNumAllowedChannels);
- }
- // otherwise, use the driver default
- }
- }
-
- /**
- * Set the number of radio frequency channels that are allowed to be used
- * in the current regulatory domain.
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- */
- public void setNumAllowedChannels(int numChannels) {
- sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
- }
-
- /**
- * Get number of allowed channels
- *
- * @return channel count, -1 on failure
- *
- * TODO: this is not a public API and needs to be removed in favor
- * of asynchronous reporting. unused for now.
- */
- public int getNumAllowedChannels() {
- return -1;
- }
-
- /**
- * Set bluetooth coex mode:
- *
- * @param mode
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- public void setBluetoothCoexistenceMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
- }
-
- /**
- * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
- * some of the low-level scan parameters used by the driver are changed to
- * reduce interference with A2DP streaming.
- *
- * @param isBluetoothPlaying whether to enable or disable this mode
- */
- public void setBluetoothScanMode(boolean isBluetoothPlaying) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
- }
-
- /**
- * Save configuration on supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- *
- * TODO: deprecate this
- */
- public boolean saveConfig() {
- return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void requestCmWakeLock() {
- sendMessage(CMD_REQUEST_CM_WAKELOCK);
- }
-
- /*********************************************************
- * Internal private functions
- ********************************************************/
-
- class SyncReturn {
- boolean boolValue;
- int intValue;
- String stringValue;
- Object objValue;
- List<WifiConfiguration> configList;
- }
-
- class SyncParams {
- Object mParameter;
- SyncReturn mSyncReturn;
- SyncParams() {
- mSyncReturn = new SyncReturn();
- }
- SyncParams(Object p) {
- mParameter = p;
- mSyncReturn = new SyncReturn();
- }
- }
-
- /**
- * message.arg2 is reserved to indicate synchronized
- * message.obj is used to store SyncParams
- */
- private SyncReturn syncedSend(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- msg.arg2 = SYNCHRONOUS_CALL;
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "syncedSend " + msg);
- sendMessage(msg);
- try {
- syncParams.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
- return null;
- }
- }
- return syncParams.mSyncReturn;
- }
-
- private SyncReturn sendSyncMessage(Message msg) {
- SyncParams syncParams = new SyncParams();
- msg.obj = syncParams;
- return syncedSend(msg);
- }
-
- private SyncReturn sendSyncMessage(int what, Object param) {
- SyncParams syncParams = new SyncParams(param);
- Message msg = obtainMessage(what, syncParams);
- return syncedSend(msg);
- }
-
-
- private SyncReturn sendSyncMessage(int what) {
- return sendSyncMessage(obtainMessage(what));
- }
-
- private void notifyOnMsgObject(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- if (syncParams != null) {
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
- syncParams.notify();
- }
- }
- else {
- Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
- }
- }
-
- private void setWifiState(int wifiState) {
- final int previousWifiState = mWifiState.get();
-
- try {
- if (wifiState == WIFI_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastEnableUid.get());
- } else if (wifiState == WIFI_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to note battery stats in wifi");
- }
-
- mWifiState.set(wifiState);
-
- if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
-
- final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void setWifiApState(int wifiApState) {
- final int previousWifiApState = mWifiApState.get();
-
- try {
- if (wifiApState == WIFI_AP_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastApEnableUid.get());
- } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastApEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to note battery stats in wifi");
- }
-
- // Update state
- mWifiApState.set(wifiApState);
-
- if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
-
- // Broadcast
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
- }
-
- /**
- * Parse the scan result line passed to us by wpa_supplicant (helper).
- * @param line the line to parse
- * @return the {@link ScanResult} object
- */
- private ScanResult parseScanResult(String line) {
- ScanResult scanResult = null;
- if (line != null) {
- /*
- * Cache implementation (LinkedHashMap) is not synchronized, thus,
- * must synchronized here!
- */
- synchronized (mScanResultCache) {
- String[] result = scanResultPattern.split(line);
- if (3 <= result.length && result.length <= 5) {
- String bssid = result[0];
- // bssid | frequency | level | flags | ssid
- int frequency;
- int level;
- try {
- frequency = Integer.parseInt(result[1]);
- level = Integer.parseInt(result[2]);
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (level > 0) level -= 256;
- } catch (NumberFormatException e) {
- frequency = 0;
- level = 0;
- }
-
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
- flags = result[3];
- ssid = result[4];
- } else {
- // Here, we must have 3 fields: no flags and ssid
- // set
- flags = "";
- ssid = "";
- }
-
- // bssid + ssid is the hash key
- String key = bssid + ssid;
- scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- scanResult.level = level;
- scanResult.SSID = ssid;
- scanResult.capabilities = flags;
- scanResult.frequency = frequency;
- } else {
- // Do not add scan results that have no SSID set
- if (0 < ssid.trim().length()) {
- scanResult =
- new ScanResult(
- ssid, bssid, flags, level, frequency);
- mScanResultCache.put(key, scanResult);
- }
- }
- } else {
- Log.w(TAG, "Misformatted scan result text with " +
- result.length + " fields: " + line);
- }
- }
- }
-
- return scanResult;
- }
-
- /**
- * scanResults input format
- * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
- * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
- */
- private void setScanResults(String scanResults) {
- if (scanResults == null) {
- return;
- }
-
- List<ScanResult> scanList = new ArrayList<ScanResult>();
-
- int lineCount = 0;
-
- int scanResultsLen = scanResults.length();
- // Parse the result string, keeping in mind that the last line does
- // not end with a newline.
- for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
- if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
- ++lineCount;
-
- if (lineCount == 1) {
- lineBeg = lineEnd + 1;
- continue;
- }
- if (lineEnd > lineBeg) {
- String line = scanResults.substring(lineBeg, lineEnd);
- ScanResult scanResult = parseScanResult(line);
- if (scanResult != null) {
- scanList.add(scanResult);
- } else {
- Log.w(TAG, "misformatted scan result for: " + line);
- }
- }
- lineBeg = lineEnd + 1;
- }
- }
-
- mScanResults = scanList;
- }
-
- private void checkAndSetNotification() {
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
-
- State state = mNetworkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
- // Look for an open network
- List<ScanResult> scanResults = mScanResults;
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- if (TextUtils.isEmpty(scanResult.capabilities)) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
- int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification mainly so we can remove the
- // EVENT_NOTIFICATION_CHANGED message with this Notification from
- // the queue later
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
- ICON_NETWORKS_AVAILABLE, mNotification);
-
- } else {
-
- // Remove any pending messages to show the notification
- mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
- ICON_NETWORKS_AVAILABLE);
- }
-
- mCsHandler.sendMessageDelayed(message, delay);
-
- mNotificationShown = visible;
- }
-
- private void configureNetworkProperties() {
- try {
- mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
- } catch (SocketException e) {
- Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
- ". e=" + e);
- return;
- } catch (NullPointerException e) {
- Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
- return;
- }
- // TODO - fix this for v6
- try {
- mNetworkProperties.addAddress(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
- mDhcpInfo.gateway)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
- }
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
-
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
- }
- // TODO - add proxy info
- }
-
-
- private void checkUseStaticIp() {
- mUseStaticIp = false;
- final ContentResolver cr = mContext.getContentResolver();
- try {
- if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
- return;
- }
- } catch (Settings.SettingNotFoundException e) {
- return;
- }
-
- try {
- String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
- if (addr != null) {
- mDhcpInfo.ipAddress = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
- if (addr != null) {
- mDhcpInfo.gateway = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
- if (addr != null) {
- mDhcpInfo.netmask = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
- if (addr != null) {
- mDhcpInfo.dns1 = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
- if (addr != null) {
- mDhcpInfo.dns2 = stringToIpAddr(addr);
- } else {
- mDhcpInfo.dns2 = 0;
- }
- } catch (UnknownHostException e) {
- return;
- }
- mUseStaticIp = true;
- }
-
- private static int stringToIpAddr(String addrString) throws UnknownHostException {
- try {
- String[] parts = addrString.split("\\.");
- if (parts.length != 4) {
- throw new UnknownHostException(addrString);
- }
-
- int a = Integer.parseInt(parts[0]) ;
- int b = Integer.parseInt(parts[1]) << 8;
- int c = Integer.parseInt(parts[2]) << 16;
- int d = Integer.parseInt(parts[3]) << 24;
-
- return a | b | c | d;
- } catch (NumberFormatException ex) {
- throw new UnknownHostException(addrString);
- }
- }
-
- private int getMaxDhcpRetries() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
- DEFAULT_MAX_DHCP_RETRIES);
- }
-
- private class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_USE_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_GATEWAY), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_NETMASK), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS1), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS2), false, this);
- }
-
+ private class WifiStateReceiver extends BroadcastReceiver {
@Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- boolean wasStaticIp = mUseStaticIp;
- int oIp, oGw, oMsk, oDns1, oDns2;
- oIp = oGw = oMsk = oDns1 = oDns2 = 0;
- if (wasStaticIp) {
- oIp = mDhcpInfo.ipAddress;
- oGw = mDhcpInfo.gateway;
- oMsk = mDhcpInfo.netmask;
- oDns1 = mDhcpInfo.dns1;
- oDns2 = mDhcpInfo.dns2;
- }
- checkUseStaticIp();
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- return;
- }
-
- boolean changed =
- (wasStaticIp != mUseStaticIp) ||
- (wasStaticIp && (
- oIp != mDhcpInfo.ipAddress ||
- oGw != mDhcpInfo.gateway ||
- oMsk != mDhcpInfo.netmask ||
- oDns1 != mDhcpInfo.dns1 ||
- oDns2 != mDhcpInfo.dns2));
-
- if (changed) {
- sendMessage(CMD_RECONFIGURE_IP);
- if (mUseStaticIp) {
- mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
- }
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
+ } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
}
}
}
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently.
- * <p>
- * After calling this method, the timer that prevents notifications from
- * being shown too often will be cleared.
- */
- private void resetNotificationTimer() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- }
-
-
- /**
- * Whether to disable coexistence mode while obtaining IP address. This
- * logic will return true only if the current bluetooth
- * headset/handsfree state is disconnected. This means if it is in an
- * error state, we will NOT disable coexistence mode to err on the side
- * of safety.
- *
- * @return Whether to disable coexistence mode.
- */
- private boolean shouldDisableCoexistenceMode() {
- int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
- return state == BluetoothHeadset.STATE_DISCONNECTED;
- }
-
- private void checkIsBluetoothPlaying() {
- boolean isBluetoothPlaying = false;
- Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
-
- for (BluetoothDevice device : connected) {
- if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
- isBluetoothPlaying = true;
- break;
- }
- }
- setBluetoothScanMode(isBluetoothPlaying);
- }
-
- private void sendScanResultsAvailableBroadcast() {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
-
- private void sendRssiChangeBroadcast(final int newRssi) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
- }
-
- private void sendNetworkStateChangeBroadcast(String bssid) {
- Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
- if (bssid != null)
- intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
- Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
- if (failedAuth) {
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
- mContext.sendBroadcast(intent);
- }
-
- /**
- * Record the detailed state of a network, and if it is a
- * change from the previous state, send a notification to
- * any listeners.
- * @param state the new @{code DetailedState}
- */
- private void setDetailedState(NetworkInfo.DetailedState state) {
- Log.d(TAG, "setDetailed state, old ="
- + mNetworkInfo.getDetailedState() + " and new state=" + state);
- if (state != mNetworkInfo.getDetailedState()) {
- mNetworkInfo.setDetailedState(state, null, null);
- Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string.length() <= 2) return "";
- return string.substring(1, string.length() - 1);
- }
-
- private static String convertToQuotedString(String string) {
- return "\"" + string + "\"";
- }
-
- private static String makeString(BitSet set, String[] strings) {
- StringBuffer buf = new StringBuffer();
- int nextSetBit = -1;
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- set = set.get(0, strings.length);
-
- while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
- buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
- }
-
- // remove trailing space
- if (set.cardinality() > 0) {
- buf.setLength(buf.length() - 1);
- }
-
- return buf.toString();
- }
-
- private static int lookupString(String string, String[] strings) {
- int size = strings.length;
-
- string = string.replace('-', '_');
-
- for (int i = 0; i < size; i++)
- if (string.equals(strings[i]))
- return i;
-
- // if we ever get here, we should probably add the
- // value to WifiConfiguration to reflect that it's
- // supported by the WPA supplicant
- Log.w(TAG, "Failed to look-up a string: " + string);
-
- return -1;
- }
-
- private int addOrUpdateNetworkNative(WifiConfiguration config) {
- /*
- * If the supplied networkId is -1, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
- int netId = config.networkId;
- boolean newNetwork = netId == -1;
- // networkId of -1 means we want to create a new network
-
- if (newNetwork) {
- netId = WifiNative.addNetworkCommand();
- if (netId < 0) {
- Log.e(TAG, "Failed to add a network!");
- return -1;
- }
- }
-
- setVariables: {
-
- if (config.SSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.ssidVarName,
- config.SSID)) {
- Log.d(TAG, "failed to set SSID: "+config.SSID);
- break setVariables;
- }
-
- if (config.BSSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.bssidVarName,
- config.BSSID)) {
- Log.d(TAG, "failed to set BSSID: "+config.BSSID);
- break setVariables;
- }
-
- String allowedKeyManagementString =
- makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
- if (config.allowedKeyManagement.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.KeyMgmt.varName,
- allowedKeyManagementString)) {
- Log.d(TAG, "failed to set key_mgmt: "+
- allowedKeyManagementString);
- break setVariables;
- }
-
- String allowedProtocolsString =
- makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
- if (config.allowedProtocols.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.Protocol.varName,
- allowedProtocolsString)) {
- Log.d(TAG, "failed to set proto: "+
- allowedProtocolsString);
- break setVariables;
- }
-
- String allowedAuthAlgorithmsString =
- makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
- if (config.allowedAuthAlgorithms.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.AuthAlgorithm.varName,
- allowedAuthAlgorithmsString)) {
- Log.d(TAG, "failed to set auth_alg: "+
- allowedAuthAlgorithmsString);
- break setVariables;
- }
-
- String allowedPairwiseCiphersString =
- makeString(config.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.strings);
- if (config.allowedPairwiseCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.PairwiseCipher.varName,
- allowedPairwiseCiphersString)) {
- Log.d(TAG, "failed to set pairwise: "+
- allowedPairwiseCiphersString);
- break setVariables;
- }
-
- String allowedGroupCiphersString =
- makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
- if (config.allowedGroupCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.GroupCipher.varName,
- allowedGroupCiphersString)) {
- Log.d(TAG, "failed to set group: "+
- allowedGroupCiphersString);
- break setVariables;
- }
-
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.pskVarName,
- config.preSharedKey)) {
- Log.d(TAG, "failed to set psk: "+config.preSharedKey);
- break setVariables;
- }
-
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepKeyVarNames[i],
- config.wepKeys[i])) {
- Log.d(TAG,
- "failed to set wep_key"+i+": " +
- config.wepKeys[i]);
- break setVariables;
- }
- hasSetKey = true;
- }
- }
- }
-
- if (hasSetKey) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepTxKeyIdxVarName,
- Integer.toString(config.wepTxKeyIndex))) {
- Log.d(TAG,
- "failed to set wep_tx_keyidx: "+
- config.wepTxKeyIndex);
- break setVariables;
- }
- }
-
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.priorityVarName,
- Integer.toString(config.priority))) {
- Log.d(TAG, config.SSID + ": failed to set priority: "
- +config.priority);
- break setVariables;
- }
-
- if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.hiddenSSIDVarName,
- Integer.toString(config.hiddenSSID ? 1 : 0))) {
- Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
- config.hiddenSSID);
- break setVariables;
- }
-
- for (WifiConfiguration.EnterpriseField field
- : config.enterpriseFields) {
- String varName = field.varName();
- String value = field.value();
- if (value != null) {
- if (field != config.eap) {
- value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
- }
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- varName,
- value)) {
- Log.d(TAG, config.SSID + ": failed to set " + varName +
- ": " + value);
- break setVariables;
- }
- }
- }
- return netId;
- }
-
- if (newNetwork) {
- WifiNative.removeNetworkCommand(netId);
- Log.d(TAG,
- "Failed to set a network variable, removed network: "
- + netId);
- }
-
- return -1;
- }
-
- private List<WifiConfiguration> getConfiguredNetworksNative() {
- String listStr = WifiNative.listNetworksCommand();
-
- List<WifiConfiguration> networks =
- new ArrayList<WifiConfiguration>();
- if (listStr == null)
- return networks;
-
- String[] lines = listStr.split("\n");
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- continue;
- }
- if (result.length > 3) {
- if (result[3].indexOf("[CURRENT]") != -1)
- config.status = WifiConfiguration.Status.CURRENT;
- else if (result[3].indexOf("[DISABLED]") != -1)
- config.status = WifiConfiguration.Status.DISABLED;
- else
- config.status = WifiConfiguration.Status.ENABLED;
- } else {
- config.status = WifiConfiguration.Status.ENABLED;
- }
- readNetworkVariables(config);
- networks.add(config);
- }
- return networks;
- }
-
- /**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- *
- * @param config the {@link WifiConfiguration} object to be filled in.
- */
- private void readNetworkVariables(WifiConfiguration config) {
-
- int netId = config.networkId;
- if (netId < 0)
- return;
-
- /*
- * TODO: maybe should have a native method that takes an array of
- * variable names and returns an array of values. But we'd still
- * be doing a round trip to the supplicant daemon for each variable.
- */
- String value;
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.SSID = removeDoubleQuotes(value);
- } else {
- config.SSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.BSSID = value;
- } else {
- config.BSSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
- config.priority = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.priority = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
- config.hiddenSSID = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.hiddenSSID = Integer.parseInt(value) != 0;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
- config.wepTxKeyIndex = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.wepTxKeyIndex = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- for (int i = 0; i < 4; i++) {
- value = WifiNative.getNetworkVariableCommand(netId,
- WifiConfiguration.wepKeyVarNames[i]);
- if (!TextUtils.isEmpty(value)) {
- config.wepKeys[i] = value;
- } else {
- config.wepKeys[i] = null;
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
- if (!TextUtils.isEmpty(value)) {
- config.preSharedKey = value;
- } else {
- config.preSharedKey = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.Protocol.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.Protocol.strings);
- if (0 <= index) {
- config.allowedProtocols.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.KeyMgmt.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.KeyMgmt.strings);
- if (0 <= index) {
- config.allowedKeyManagement.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.AuthAlgorithm.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
- if (0 <= index) {
- config.allowedAuthAlgorithms.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.PairwiseCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.PairwiseCipher.strings);
- if (0 <= index) {
- config.allowedPairwiseCiphers.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.GroupCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.GroupCipher.strings);
- if (0 <= index) {
- config.allowedGroupCiphers.set(index);
- }
- }
- }
-
- for (WifiConfiguration.EnterpriseField field :
- config.enterpriseFields) {
- value = WifiNative.getNetworkVariableCommand(netId,
- field.varName());
- if (!TextUtils.isEmpty(value)) {
- if (field != config.eap) value = removeDoubleQuotes(value);
- field.setValue(value);
- }
- }
-
- }
-
- /**
- * Poll for info not reported via events
- * RSSI & Linkspeed
- */
- private void requestPolledInfo() {
- int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (newRssi > 0) newRssi -= 256;
- mWifiInfo.setRssi(newRssi);
- /*
- * Rather then sending the raw RSSI out every time it
- * changes, we precalculate the signal level that would
- * be displayed in the status bar, and only send the
- * broadcast if that much more coarse-grained number
- * changes. This cuts down greatly on the number of
- * broadcasts, at the cost of not mWifiInforming others
- * interested in RSSI of all the changes in signal
- * level.
- */
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
- if (newSignalLevel != mLastSignalLevel) {
- sendRssiChangeBroadcast(newRssi);
- }
- mLastSignalLevel = newSignalLevel;
- } else {
- mWifiInfo.setRssi(-200);
- }
- int newLinkSpeed = WifiNative.getLinkSpeedCommand();
- if (newLinkSpeed != -1) {
- mWifiInfo.setLinkSpeed(newLinkSpeed);
- }
- }
-
- /**
- * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
- * using the interface, stopping DHCP & disabling interface
- */
- private void handleNetworkDisconnect() {
- Log.d(TAG, "Reset connections and stopping DHCP");
-
- /*
- * Reset connections & stop DHCP
- */
- NetworkUtils.resetConnections(mInterfaceName);
-
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Could not stop DHCP");
- }
-
- /* Disable interface */
- NetworkUtils.disableInterface(mInterfaceName);
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.DISCONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- /* Reset data structures */
- mWifiInfo.setIpAddress(0);
- mWifiInfo.setBSSID(null);
- mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(-1);
-
- /* Clear network properties */
- mNetworkProperties.clear();
-
- mLastBssid= null;
- mLastNetworkId = -1;
-
- }
-
-
- /*********************************************************
- * Notifications from WifiMonitor
- ********************************************************/
-
- /**
- * A structure for supplying information about a supplicant state
- * change in the STATE_CHANGE event message that comes from the
- * WifiMonitor
- * thread.
- */
- private static class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, Object state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- Object state;
- }
-
- /**
- * Send the tracker a notification that a user-entered password key
- * may be incorrect (i.e., caused authentication to fail).
- */
- void notifyPasswordKeyMayBeIncorrect() {
- sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendMessage(SUP_CONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantLost() {
- sendMessage(SUP_DISCONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that the state of Wifi connectivity
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new network state
- * @param BSSID when the new state is {@link DetailedState#CONNECTED
- * NetworkInfo.DetailedState.CONNECTED},
- * this is the MAC address of the access point. Otherwise, it
- * is {@code null}.
- */
- void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
- if (newState == NetworkInfo.DetailedState.CONNECTED) {
- sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- } else {
- sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
- }
-
- /**
- * Send the tracker a notification that the state of the supplicant
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new {@code SupplicantState}
- */
- void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
- sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- /**
- * Switch scan mode over to passive.
- * Turning off scan-only mode happens only in "Connect" mode
- */
- setScanType(false);
- sendMessage(SCAN_RESULTS_EVENT);
- }
-
- void notifyDriverStarted() {
- sendMessage(DRIVER_START_EVENT);
- }
-
- void notifyDriverStopped() {
- sendMessage(DRIVER_STOP_EVENT);
- }
-
- void notifyDriverHung() {
- setWifiEnabled(false);
- setWifiEnabled(true);
- }
-
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class DefaultState extends HierarchicalState {
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch (message.what) {
- /* Synchronous call returns */
- case CMD_PING_SUPPLICANT:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case CMD_REMOVE_NETWORK:
- case CMD_ENABLE_NETWORK:
- case CMD_DISABLE_NETWORK:
- case CMD_ADD_OR_UPDATE_NETWORK:
- case CMD_GET_RSSI:
- case CMD_GET_RSSI_APPROX:
- case CMD_GET_LINK_SPEED:
- case CMD_GET_MAC_ADDR:
- case CMD_SAVE_CONFIG:
- case CMD_CONNECTION_STATUS:
- case CMD_GET_NETWORK_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = false;
- syncParams.mSyncReturn.intValue = -1;
- syncParams.mSyncReturn.stringValue = null;
- syncParams.mSyncReturn.configList = null;
- notifyOnMsgObject(message);
- }
- break;
- case CM_CMD_TEARDOWN:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(true);
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- /* Mark wifi available when CM tears down */
- mNetworkInfo.setIsAvailable(true);
- break;
- case CM_CMD_RECONNECT:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(false);
- sendMessage(CMD_START_DRIVER);
- sendMessage(CMD_RECONNECT);
- break;
- case CMD_ENABLE_RSSI_POLL:
- mEnableRssiPolling = (message.arg1 == 1);
- mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
- break;
- default:
- if (DBG) Log.w(TAG, "Unhandled " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class InitialState extends HierarchicalState {
- @Override
- //TODO: could move logging into a common class
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- // [31-8] Reserved for future use
- // [7 - 0] HSM state change
- // 50021 wifi_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (WifiNative.isDriverLoaded()) {
- transitionTo(mDriverLoadedState);
- }
- else {
- transitionTo(mDriverUnloadedState);
- }
- }
- }
-
- class DriverLoadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- sWakeLock.acquire();
- //enabling state
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_ENABLING);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_ENABLING);
- break;
- }
-
- if(WifiNative.loadDriver()) {
- Log.d(TAG, "Driver load successful");
- sendMessage(CMD_LOAD_DRIVER_SUCCESS);
- } else {
- Log.e(TAG, "Failed to load driver!");
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- sendMessage(CMD_LOAD_DRIVER_FAILURE);
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER_SUCCESS:
- transitionTo(mDriverLoadedState);
- break;
- case CMD_LOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverLoadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_UNLOAD_DRIVER:
- transitionTo(mDriverUnloadingState);
- break;
- case CMD_START_SUPPLICANT:
- if(WifiNative.startSupplicant()) {
- Log.d(TAG, "Supplicant start successful");
- mWifiMonitor.startMonitoring();
- setWifiState(WIFI_STATE_ENABLED);
- transitionTo(mWaitForSupState);
- } else {
- Log.e(TAG, "Failed to start supplicant!");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_AP:
- try {
- nwService.startAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in startAccessPoint()");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- break;
- }
- Log.d(TAG, "Soft AP start successful");
- setWifiApState(WIFI_AP_STATE_ENABLED);
- transitionTo(mSoftApStartedState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- sWakeLock.acquire();
- if(WifiNative.unloadDriver()) {
- Log.d(TAG, "Driver unload successful");
- sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(message.arg1);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(message.arg1);
- break;
- }
- } else {
- Log.e(TAG, "Failed to unload driver!");
- sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_UNLOAD_DRIVER_SUCCESS:
- transitionTo(mDriverUnloadedState);
- break;
- case CMD_UNLOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER:
- transitionTo(mDriverLoadingState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverFailedState extends HierarchicalState {
- @Override
- public void enter() {
- Log.e(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
-
- class WaitForSupState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case SUP_CONNECTION_EVENT:
- Log.d(TAG, "Supplicant connection established");
- mSupplicantStateTracker.resetSupplicantState();
- /* Initialize data structures */
- resetNotificationTimer();
- setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
-
- //TODO: initialize and fix multicast filtering
- //mWM.initializeMulticastFiltering();
-
- if (mBluetoothA2dp == null) {
- mBluetoothA2dp = new BluetoothA2dp(mContext);
- }
- checkIsBluetoothPlaying();
-
- checkUseStaticIp();
- sendSupplicantConnectionChangedBroadcast(true);
- transitionTo(mDriverSupReadyState);
- break;
- case CMD_STOP_SUPPLICANT:
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- transitionTo(mDriverLoadedState);
- break;
- /* Fail soft ap when waiting for supplicant start */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- case CMD_STOP_AP:
- case CMD_START_SUPPLICANT:
- case CMD_UNLOAD_DRIVER:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverSupReadyState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- /* Initialize for connect mode operation at start */
- mIsScanMode = false;
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- //$FALL-THROUGH$
- case SUP_DISCONNECTION_EVENT: /* Supplicant died */
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- //Remove any notifications on disconnection
- setNotificationVisible(false, 0, false, 0);
- WifiNative.closeSupplicantConnection();
- handleNetworkDisconnect();
- sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.resetSupplicantState();
- transitionTo(mDriverLoadedState);
-
- /* When supplicant dies, unload driver and enter failed state */
- //TODO: consider bringing up supplicant again
- if (message.what == SUP_DISCONNECTION_EVENT) {
- Log.d(TAG, "Supplicant died, unloading driver");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_DRIVER:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.startDriverCommand();
- transitionTo(mDriverStartingState);
- break;
- case SCAN_RESULTS_EVENT:
- setScanResults(WifiNative.scanResultsCommand());
- sendScanResultsAvailableBroadcast();
- checkAndSetNotification();
- break;
- case CMD_PING_SUPPLICANT:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_ADD_OR_UPDATE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- syncParams = (SyncParams) message.obj;
- WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
- syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
- notifyOnMsgObject(message);
- break;
- case CMD_REMOVE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.removeNetworkCommand(message.arg1);
- }
- break;
- case CMD_ENABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
- syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
- enableNetParams.netId, enableNetParams.disableOthers);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
- }
- break;
- case CMD_DISABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disableNetworkCommand(message.arg1);
- }
- break;
- case CMD_BLACKLIST_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.addToBlacklistCommand((String)message.obj);
- break;
- case CMD_CLEAR_BLACKLIST:
- WifiNative.clearBlacklistCommand();
- break;
- case CMD_GET_NETWORK_CONFIG:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
- notifyOnMsgObject(message);
- break;
- case CMD_SAVE_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.saveConfigCommand();
- }
- // Inform the backup manager about a data change
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (ibm != null) {
- try {
- ibm.dataChanged("com.android.providers.settings");
- } catch (Exception e) {
- // Try again later
- }
- }
- break;
- case CMD_CONNECTION_STATUS:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_MAC_ADDR:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
- notifyOnMsgObject(message);
- break;
- /* Cannot start soft AP while in client mode */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_SET_SCAN_MODE:
- mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStartingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_START_EVENT:
- transitionTo(mDriverStartedState);
- break;
- /* Queue driver commands & connection events */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- try {
- mBatteryStats.noteWifiRunning();
- } catch (RemoteException ignore) {}
-
- /* Initialize channel count */
- setNumAllowedChannels();
-
- if (mIsScanMode) {
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.disconnectCommand();
- transitionTo(mScanModeState);
- } else {
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* If supplicant has already connected, before we could finish establishing
- * the control channel connection, we miss all the supplicant events.
- * Disconnect and reconnect when driver has started to ensure we receive
- * all supplicant events.
- *
- * TODO: This is a bit unclean, ideally the supplicant should never
- * connect until told to do so by the framework
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mConnectModeState);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_TYPE:
- if (message.arg1 == SCAN_ACTIVE) {
- WifiNative.setScanModeCommand(true);
- } else {
- WifiNative.setScanModeCommand(false);
- }
- break;
- case CMD_SET_POWER_MODE:
- WifiNative.setPowerModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
- break;
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- mNumAllowedChannels = message.arg1;
- WifiNative.setNumAllowedChannelsCommand(message.arg1);
- break;
- case CMD_START_DRIVER:
- /* Ignore another driver start */
- break;
- case CMD_STOP_DRIVER:
- WifiNative.stopDriverCommand();
- transitionTo(mDriverStoppingState);
- break;
- case CMD_REQUEST_CM_WAKELOCK:
- if (mCm == null) {
- mCm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
- mCm.requestNetworkTransitionWakelock(TAG);
- break;
- case CMD_START_PACKET_FILTERING:
- WifiNative.startPacketFiltering();
- break;
- case CMD_STOP_PACKET_FILTERING:
- WifiNative.stopPacketFiltering();
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- @Override
- public void exit() {
- if (DBG) Log.d(TAG, getName() + "\n");
- try {
- mBatteryStats.noteWifiStopped();
- } catch (RemoteException ignore) { }
- }
- }
-
- class DriverStoppingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_STOP_EVENT:
- transitionTo(mDriverStoppedState);
- break;
- /* Queue driver commands */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStoppedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- // Take down any open network notifications on driver stop
- setNotificationVisible(false, 0, false, 0);
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
- class ScanModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- /* Ignore */
- return HANDLED;
- } else {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- WifiNative.reconnectCommand();
- mIsScanMode = false;
- transitionTo(mDisconnectedState);
- }
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
- message.arg1 == SCAN_ACTIVE);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- }
- break;
- /* Ignore */
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- StateChangeResult stateChangeResult;
- switch(message.what) {
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- mPasswordKeyMayBeIncorrect = true;
- break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
- stateChangeResult = (StateChangeResult) message.obj;
- mSupplicantStateTracker.handleEvent(stateChangeResult);
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = true;
- notifyOnMsgObject(message);
- }
- /* We need to set scan type in completed state */
- Message newMsg = obtainMessage();
- newMsg.copyFrom(message);
- mSupplicantStateTracker.sendMessage(newMsg);
- break;
- /* Do a redundant disconnect without transition */
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- break;
- case CMD_RECONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reconnectCommand();
- }
- break;
- case CMD_REASSOCIATE:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reassociateCommand();
- }
- break;
- case SCAN_RESULTS_EVENT:
- /* Set the scan setting back to "connect" mode */
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* Handle scan results */
- return NOT_HANDLED;
- case NETWORK_CONNECTION_EVENT:
- Log.d(TAG,"Network connection established");
- stateChangeResult = (StateChangeResult) message.obj;
-
- /* Remove any notifications */
- setNotificationVisible(false, 0, false, 0);
- resetNotificationTimer();
-
- mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- mLastNetworkId = stateChangeResult.networkId;
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.OBTAINING_IPADDR);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- transitionTo(mConnectingState);
- break;
- case NETWORK_DISCONNECTION_EVENT:
- Log.d(TAG,"Network connection lost");
- handleNetworkDisconnect();
- transitionTo(mDisconnectedState);
- break;
- case CMD_GET_RSSI:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_RSSI_APPROX:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_LINK_SPEED:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
- notifyOnMsgObject(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectingState extends HierarchicalState {
- boolean modifiedBluetoothCoexistenceMode;
- int powerMode;
- Thread mDhcpThread;
-
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (!mUseStaticIp) {
-
- mDhcpThread = null;
- modifiedBluetoothCoexistenceMode = false;
- powerMode = DRIVER_POWER_MODE_AUTO;
-
- if (shouldDisableCoexistenceMode()) {
- /*
- * There are problems setting the Wi-Fi driver's power
- * mode to active when bluetooth coexistence mode is
- * enabled or sense.
- * <p>
- * We set Wi-Fi to active mode when
- * obtaining an IP address because we've found
- * compatibility issues with some routers with low power
- * mode.
- * <p>
- * In order for this active power mode to properly be set,
- * we disable coexistence mode until we're done with
- * obtaining an IP address. One exception is if we
- * are currently connected to a headset, since disabling
- * coexistence would interrupt that connection.
- */
- modifiedBluetoothCoexistenceMode = true;
-
- // Disable the coexistence mode
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
- }
-
- powerMode = WifiNative.getPowerModeCommand();
- if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
- powerMode = DRIVER_POWER_MODE_AUTO;
- }
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
- }
-
- Log.d(TAG, "DHCP request started");
- mDhcpThread = new Thread(new Runnable() {
- public void run() {
- if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
- Log.d(TAG, "DHCP request succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.d(TAG, "DHCP request failed: " +
- NetworkUtils.getDhcpError());
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- });
- mDhcpThread.start();
- } else {
- if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- Log.v(TAG, "Static IP configuration succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.v(TAG, "Static IP configuration failed");
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-
- switch(message.what) {
- case CMD_IP_CONFIG_SUCCESS:
- mReconnectCount = 0;
- mLastSignalLevel = -1; // force update of signal strength
- mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
- Log.d(TAG, "IP configuration: " + mDhcpInfo);
- configureNetworkProperties();
- setDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectedState);
- break;
- case CMD_IP_CONFIG_FAILURE:
- mWifiInfo.setIpAddress(0);
-
- Log.e(TAG, "IP configuration failed");
- /**
- * If we've exceeded the maximum number of retries for DHCP
- * to a given network, disable the network
- */
- if (++mReconnectCount > getMaxDhcpRetries()) {
- Log.e(TAG, "Failed " +
- mReconnectCount + " times, Disabling " + mLastNetworkId);
- WifiNative.disableNetworkCommand(mLastNetworkId);
- }
-
- /* DHCP times out after about 30 seconds, we do a
- * disconnect and an immediate reconnect to try again
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mDisconnectingState);
- break;
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- case CMD_RECONFIGURE_IP:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
-
- @Override
- public void exit() {
- /* reset power state & bluetooth coexistence if on DHCP */
- if (!mUseStaticIp) {
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(powerMode);
- }
-
- if (modifiedBluetoothCoexistenceMode) {
- // Set the coexistence mode back to its default value
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
- }
- }
-
- }
- }
-
- class ConnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- case CMD_RECONFIGURE_IP:
- Log.d(TAG,"Reconfiguring IP on connection");
- NetworkUtils.resetConnections(mInterfaceName);
- transitionTo(mConnectingState);
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- //Supplicant disconnect to prevent further connects
- WifiNative.disconnectCommand();
- mIsScanMode = true;
- transitionTo(mScanModeState);
- }
- break;
- /* Ignore network disconnect */
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class SoftApStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_STOP_AP:
- Log.d(TAG,"Stopping Soft AP");
- setWifiApState(WIFI_AP_STATE_DISABLING);
- try {
- nwService.stopAccessPoint();
- } catch(Exception e) {
- Log.e(TAG, "Exception in stopAccessPoint()");
- }
- transitionTo(mDriverLoadedState);
- break;
- case CMD_START_AP:
- Log.d(TAG,"SoftAP set on a running access point");
- try {
- nwService.setAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in nwService during soft AP set");
- try {
- nwService.stopAccessPoint();
- } catch (Exception ee) {
- Slog.e(TAG, "Could not stop AP, :" + ee);
- }
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- }
- break;
- /* Fail client mode operation when soft AP is enabled */
- case CMD_START_SUPPLICANT:
- Log.e(TAG,"Cannot start supplicant with a running soft AP");
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
-
- class SupplicantStateTracker extends HierarchicalStateMachine {
-
- private int mRssiPollToken = 0;
-
- /**
- * The max number of the WPA supplicant loop iterations before we
- * decide that the loop should be terminated:
- */
- private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
- private int mLoopDetectIndex = 0;
- private int mLoopDetectCount = 0;
-
- /**
- * Supplicant state change commands follow
- * the ordinal values defined in SupplicantState.java
- */
- private static final int DISCONNECTED = 0;
- private static final int INACTIVE = 1;
- private static final int SCANNING = 2;
- private static final int ASSOCIATING = 3;
- private static final int ASSOCIATED = 4;
- private static final int FOUR_WAY_HANDSHAKE = 5;
- private static final int GROUP_HANDSHAKE = 6;
- private static final int COMPLETED = 7;
- private static final int DORMANT = 8;
- private static final int UNINITIALIZED = 9;
- private static final int INVALID = 10;
-
- private HierarchicalState mUninitializedState;
- private HierarchicalState mInitializedState;
- private HierarchicalState mInactiveState;
- private HierarchicalState mDisconnectState;
- private HierarchicalState mScanState;
- private HierarchicalState mConnectState;
- private HierarchicalState mHandshakeState;
- private HierarchicalState mCompletedState;
- private HierarchicalState mDormantState;
-
-
- public SupplicantStateTracker(Context context, Handler target) {
- super(TAG, target.getLooper());
-
- mUninitializedState = new UninitializedState();
- mInitializedState = new InitializedState();
- mInactiveState = new InactiveState();
- mDisconnectState = new DisconnectedState();
- mScanState = new ScanState();
- mConnectState = new ConnectState();
- mHandshakeState = new HandshakeState();
- mCompletedState = new CompletedState();
- mDormantState = new DormantState();
-
-
- addState(mUninitializedState);
- addState(mInitializedState);
- addState(mInactiveState, mInitializedState);
- addState(mDisconnectState, mInitializedState);
- addState(mScanState, mInitializedState);
- addState(mConnectState, mInitializedState);
- addState(mHandshakeState, mConnectState);
- addState(mCompletedState, mConnectState);
- addState(mDormantState, mInitializedState);
-
- setInitialState(mUninitializedState);
-
- //start the state machine
- start();
- }
-
- public void handleEvent(StateChangeResult stateChangeResult) {
- SupplicantState newState = (SupplicantState) stateChangeResult.state;
-
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
-
- sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
- }
-
- public void resetSupplicantState() {
- transitionTo(mUninitializedState);
- }
-
- private void resetLoopDetection() {
- mLoopDetectCount = 0;
- mLoopDetectIndex = 0;
- }
-
- private boolean handleTransition(Message msg) {
- if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
- switch (msg.what) {
- case DISCONNECTED:
- transitionTo(mDisconnectState);
- break;
- case SCANNING:
- transitionTo(mScanState);
- break;
- case ASSOCIATING:
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- //$FALL-THROUGH$
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- transitionTo(mHandshakeState);
- break;
- case COMPLETED:
- transitionTo(mCompletedState);
- break;
- case DORMANT:
- transitionTo(mDormantState);
- break;
- case INACTIVE:
- transitionTo(mInactiveState);
- break;
- case UNINITIALIZED:
- case INVALID:
- transitionTo(mUninitializedState);
- break;
- default:
- return NOT_HANDLED;
- }
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- SupplicantState supState = (SupplicantState) stateChangeResult.state;
- setDetailedState(WifiInfo.getDetailedStateOf(supState));
- mWifiInfo.setSupplicantState(supState);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- //TODO: Modify WifiMonitor to report SSID on events
- //mWifiInfo.setSSID()
- return HANDLED;
- }
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class InitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- if (DBG) Log.w(TAG, "Ignoring " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class UninitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- default:
- if (!handleTransition(message)) {
- if (DBG) Log.w(TAG, "Ignoring " + message);
- }
- break;
- }
- return HANDLED;
- }
- }
-
- class InactiveState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
-
- /* If a disconnect event happens after a password key failure
- * event, disable the network
- */
- if (mPasswordKeyMayBeIncorrect) {
- Log.d(TAG, "Failed to authenticate, disabling network " +
- mWifiInfo.getNetworkId());
- WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, true);
- }
- else {
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ScanState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- mPasswordKeyMayBeIncorrect = false;
- resetLoopDetection();
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ConnectState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class HandshakeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- final Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- if (mLoopDetectIndex > message.what) {
- mLoopDetectCount++;
- }
- if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
- WifiNative.disableNetworkCommand(stateChangeResult.networkId);
- mLoopDetectCount = 0;
- }
-
- mLoopDetectIndex = message.what;
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class CompletedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
-
- resetLoopDetection();
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case ASSOCIATING:
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- case COMPLETED:
- break;
- case CMD_RSSI_POLL:
- if (message.arg1 == mRssiPollToken) {
- // Get Info and continue polling
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- } else {
- // Polling has completed
- }
- break;
- case CMD_ENABLE_RSSI_POLL:
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- // first poll
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
- break;
- default:
- return handleTransition(message);
- }
- return HANDLED;
- }
- }
-
- class DormantState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
-
- /* TODO: reconnect is now being handled at DHCP failure handling
- * If we run into issues with staying in Dormant state, might
- * need a reconnect here
- */
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
-
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- mNotificationEnabled = getValue();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- mNotificationEnabled = getValue();
- if (!mNotificationEnabled) {
- // Remove any notification that may be showing
- setNotificationVisible(false, 0, true, 0);
- }
-
- resetNotificationTimer();
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
- }
-}
+}
\ No newline at end of file