Merge "Preparing for build system change. Fix the build error in http://b/issue?id=2781522."
diff --git a/Android.mk b/Android.mk
index ca81fc1..27f9e1e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,7 @@
# Instead, it depends on the R.stamp file, which lists the corresponding
# R.java file as a prerequisite.
# TODO: find a more appropriate way to do this.
-framework-res-source-path := APPS/framework-res_intermediates/src
+framework_res_source_path := APPS/framework-res_intermediates/src
# the library
# ============================================================
@@ -189,9 +189,9 @@
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
LOCAL_INTERMEDIATE_SOURCES := \
- $(framework-res-source-path)/android/R.java \
- $(framework-res-source-path)/android/Manifest.java \
- $(framework-res-source-path)/com/android/internal/R.java
+ $(framework_res_source_path)/android/R.java \
+ $(framework_res_source_path)/android/Manifest.java \
+ $(framework_res_source_path)/com/android/internal/R.java
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit ext
@@ -337,9 +337,9 @@
$(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
framework_docs_LOCAL_INTERMEDIATE_SOURCES := \
- $(framework-res-source-path)/android/R.java \
- $(framework-res-source-path)/android/Manifest.java \
- $(framework-res-source-path)/com/android/internal/R.java
+ $(framework_res_source_path)/android/R.java \
+ $(framework_res_source_path)/android/Manifest.java \
+ $(framework_res_source_path)/com/android/internal/R.java
framework_docs_LOCAL_JAVA_LIBRARIES := \
bouncycastle \
diff --git a/api/current.xml b/api/current.xml
index 1fadcfb..355b3cd 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4893,6 +4893,17 @@
visibility="public"
>
</field>
+<field name="immersive"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843457"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="inAnimation"
type="int"
transient="false"
@@ -6092,17 +6103,6 @@
visibility="public"
>
</field>
-<field name="kraken_resource_pad64"
- type="int"
- transient="false"
- volatile="false"
- value="16843457"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="kraken_resource_pad7"
type="int"
transient="false"
@@ -13955,7 +13955,7 @@
value="17301636"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13966,7 +13966,7 @@
value="17301637"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13977,7 +13977,7 @@
value="17301638"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -14021,7 +14021,7 @@
value="17301671"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -14032,7 +14032,7 @@
value="17301672"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -15341,6 +15341,17 @@
visibility="public"
>
</field>
+<field name="list_content"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367073"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="preference_category"
type="int"
transient="false"
@@ -19457,6 +19468,19 @@
visibility="public"
>
</constructor>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
<method name="finishContextMode"
return="void"
abstract="true"
@@ -19523,6 +19547,84 @@
visibility="public"
>
</method>
+<method name="insertTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="newTab"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="removeTabAt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="selectTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="selectTabAt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="setBackgroundDrawable"
return="void"
abstract="true"
@@ -19644,6 +19746,30 @@
<parameter name="subtitle" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setTabNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setTabNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+</method>
<method name="setTitle"
return="void"
abstract="true"
@@ -19764,6 +19890,17 @@
visibility="public"
>
</method>
+<method name="getCustomView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getMenu"
return="android.view.Menu"
abstract="true"
@@ -19775,6 +19912,28 @@
visibility="public"
>
</method>
+<method name="getSubtitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="invalidate"
return="void"
abstract="true"
@@ -19915,6 +20074,128 @@
</parameter>
</method>
</interface>
+<class name="ActionBar.Tab"
+ extends="java.lang.Object"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionBar.Tab"
+ type="android.app.ActionBar.Tab"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getFragment"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPosition"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="select"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setFragment"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="setIcon"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setText"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<field name="INVALID_POSITION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="Activity"
extends="android.view.ContextThemeWrapper"
abstract="false"
@@ -20272,6 +20553,17 @@
visibility="public"
>
</method>
+<method name="getLoaderManager"
+ return="android.app.LoaderManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getLocalClassName"
return="java.lang.String"
abstract="false"
@@ -20523,6 +20815,19 @@
<parameter name="data" type="android.content.Intent">
</parameter>
</method>
+<method name="onAttachFragment"
+ 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>
+</method>
<method name="onAttachedToWindow"
return="void"
abstract="false"
@@ -25740,6 +26045,17 @@
visibility="public"
>
</method>
+<method name="getLoaderManager"
+ return="android.app.LoaderManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getRetainInstance"
return="boolean"
abstract="false"
@@ -25806,6 +26122,17 @@
visibility="public"
>
</method>
+<method name="isResumed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isVisible"
return="boolean"
abstract="false"
@@ -25817,6 +26144,19 @@
visibility="public"
>
</method>
+<method name="onActivityCreated"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
<method name="onActivityResult"
return="void"
abstract="false"
@@ -26076,19 +26416,6 @@
<parameter name="menu" type="android.view.Menu">
</parameter>
</method>
-<method name="onReady"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="savedInstanceState" type="android.os.Bundle">
-</parameter>
-</method>
<method name="onResume"
return="void"
abstract="false"
@@ -28182,7 +28509,18 @@
>
<parameter name="shown" type="boolean">
</parameter>
-<parameter name="animate" type="boolean">
+</method>
+<method name="setListShownNoAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shown" type="boolean">
</parameter>
</method>
<method name="setSelection"
@@ -28199,6 +28537,112 @@
</parameter>
</method>
</class>
+<interface name="LoaderManager"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getLoader"
+ return="android.content.Loader<D>"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="initLoader"
+ return="android.content.Loader<D>"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.app.LoaderManager.LoaderCallbacks<D>">
+</parameter>
+</method>
+<method name="restartLoader"
+ return="android.content.Loader<D>"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.app.LoaderManager.LoaderCallbacks<D>">
+</parameter>
+</method>
+<method name="stopLoader"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+</interface>
+<interface name="LoaderManager.LoaderCallbacks"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onCreateLoader"
+ return="android.content.Loader<D>"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onLoadFinished"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader<D>">
+</parameter>
+<parameter name="data" type="D">
+</parameter>
+</method>
+</interface>
<class name="LoaderManagingFragment"
extends="android.app.Fragment"
abstract="true"
@@ -28494,7 +28938,9 @@
>
<implements name="android.view.InputQueue.Callback">
</implements>
-<implements name="android.view.SurfaceHolder.Callback">
+<implements name="android.view.SurfaceHolder.Callback2">
+</implements>
+<implements name="android.view.ViewTreeObserver.OnGlobalLayoutListener">
</implements>
<constructor name="NativeActivity"
type="android.app.NativeActivity"
@@ -28504,6 +28950,17 @@
visibility="public"
>
</constructor>
+<method name="onGlobalLayout"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onInputQueueCreated"
return="void"
abstract="false"
@@ -28575,6 +29032,19 @@
<parameter name="holder" type="android.view.SurfaceHolder">
</parameter>
</method>
+<method name="surfaceRedrawNeeded"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="holder" type="android.view.SurfaceHolder">
+</parameter>
+</method>
<field name="META_DATA_LIB_NAME"
type="java.lang.String"
transient="false"
@@ -68176,6 +68646,17 @@
visibility="public"
>
</field>
+<field name="YV12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="842094169"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="Interpolator"
extends="java.lang.Object"
@@ -146736,6 +147217,19 @@
<parameter name="holder" type="android.view.SurfaceHolder">
</parameter>
</method>
+<method name="onSurfaceRedrawNeeded"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="holder" type="android.view.SurfaceHolder">
+</parameter>
+</method>
<method name="onTouchEvent"
return="void"
abstract="false"
@@ -158163,6 +158657,21 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="setPackageObbPath"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
</class>
<class name="MockResources"
extends="android.content.res.Resources"
@@ -165917,6 +166426,29 @@
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
+<method name="setCursorController"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursorController" type="android.widget.TextView.CursorController">
+</parameter>
+</method>
+<field name="mCursorController"
+ type="android.widget.TextView.CursorController"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
</class>
<class name="BaseKeyListener"
extends="android.text.method.MetaKeyKeyListener"
@@ -173792,6 +174324,19 @@
<parameter name="key" type="int">
</parameter>
</method>
+<method name="removeAt"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
<method name="setValueAt"
return="void"
abstract="false"
@@ -177948,6 +178493,171 @@
visibility="public"
>
</field>
+<field name="KEYCODE_BUTTON_A"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="96"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_B"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="97"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_C"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="98"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_L1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="102"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_L2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="104"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_MODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="110"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_R1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="103"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_R2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="105"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_SELECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="109"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_START"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="108"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_THUMBL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="106"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_THUMBR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="107"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_X"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="99"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_Y"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="100"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_BUTTON_Z"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="101"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="KEYCODE_C"
type="int"
transient="false"
@@ -182601,6 +183311,29 @@
</parameter>
</method>
</interface>
+<interface name="SurfaceHolder.Callback2"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.SurfaceHolder.Callback">
+</implements>
+<method name="surfaceRedrawNeeded"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="holder" type="android.view.SurfaceHolder">
+</parameter>
+</method>
+</interface>
<class name="SurfaceView"
extends="android.view.View"
abstract="false"
@@ -183544,6 +184277,17 @@
visibility="public"
>
</method>
+<method name="getAlpha"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getAnimation"
return="android.view.animation.Animation"
abstract="false"
@@ -183977,6 +184721,17 @@
<parameter name="location" type="int[]">
</parameter>
</method>
+<method name="getMatrix"
+ return="android.graphics.Matrix"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getMeasuredHeight"
return="int"
abstract="false"
@@ -184109,6 +184864,28 @@
visibility="public"
>
</method>
+<method name="getPivotX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPivotY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getResources"
return="android.content.res.Resources"
abstract="false"
@@ -184164,6 +184941,39 @@
visibility="public"
>
</method>
+<method name="getRotation"
+ 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"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaleY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getScrollBarStyle"
return="int"
abstract="false"
@@ -184410,6 +185220,28 @@
<parameter name="outRect" type="android.graphics.Rect">
</parameter>
</method>
+<method name="getX"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getY"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="hasFocus"
return="boolean"
abstract="false"
@@ -185740,6 +186572,19 @@
<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
</parameter>
</method>
+<method name="setAlpha"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="alpha" type="float">
+</parameter>
+</method>
<method name="setAnimation"
return="void"
abstract="false"
@@ -185792,6 +186637,19 @@
<parameter name="resid" type="int">
</parameter>
</method>
+<method name="setBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bottom" type="int">
+</parameter>
+</method>
<method name="setClickable"
return="void"
abstract="false"
@@ -186000,6 +186858,19 @@
<parameter name="params" type="android.view.ViewGroup.LayoutParams">
</parameter>
</method>
+<method name="setLeft"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="left" type="int">
+</parameter>
+</method>
<method name="setLongClickable"
return="void"
abstract="false"
@@ -186203,6 +187074,32 @@
<parameter name="bottom" type="int">
</parameter>
</method>
+<method name="setPivotX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pivotX" type="float">
+</parameter>
+</method>
+<method name="setPivotY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pivotY" type="float">
+</parameter>
+</method>
<method name="setPressed"
return="void"
abstract="false"
@@ -186216,6 +187113,32 @@
<parameter name="pressed" type="boolean">
</parameter>
</method>
+<method name="setRight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="right" type="int">
+</parameter>
+</method>
+<method name="setRotation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotation" type="float">
+</parameter>
+</method>
<method name="setSaveEnabled"
return="void"
abstract="false"
@@ -186242,6 +187165,32 @@
<parameter name="enabled" type="boolean">
</parameter>
</method>
+<method name="setScaleX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="scaleX" type="float">
+</parameter>
+</method>
+<method name="setScaleY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="scaleY" type="float">
+</parameter>
+</method>
<method name="setScrollBarStyle"
return="void"
abstract="false"
@@ -186335,6 +187284,19 @@
<parameter name="tag" type="java.lang.Object">
</parameter>
</method>
+<method name="setTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="top" type="int">
+</parameter>
+</method>
<method name="setTouchDelegate"
return="void"
abstract="false"
@@ -186413,6 +187375,32 @@
<parameter name="willNotDraw" type="boolean">
</parameter>
</method>
+<method name="setX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="x" type="int">
+</parameter>
+</method>
+<method name="setY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="y" type="int">
+</parameter>
+</method>
<method name="showContextMenu"
return="boolean"
abstract="false"
@@ -191240,7 +192228,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="callback" type="android.view.SurfaceHolder.Callback">
+<parameter name="callback" type="android.view.SurfaceHolder.Callback2">
</parameter>
</method>
<method name="togglePanel"
@@ -203758,6 +204746,36 @@
<parameter name="outState" type="android.os.Bundle">
</parameter>
</method>
+<method name="saveWebArchive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+</method>
+<method name="saveWebArchive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="basename" type="java.lang.String">
+</parameter>
+<parameter name="autoname" type="boolean">
+</parameter>
+<parameter name="callback" type="android.webkit.ValueCallback<java.lang.String>">
+</parameter>
+</method>
<method name="setCertificate"
return="void"
abstract="false"
@@ -213462,6 +214480,19 @@
<parameter name="d" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<method name="setContentWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+</method>
<method name="setHeight"
return="void"
abstract="false"
@@ -222423,6 +223454,108 @@
>
</method>
</class>
+<interface name="TextView.CursorController"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="draw"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
+<method name="getOffsetX"
+ return="float"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOffsetY"
+ return="float"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ 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="onTouchEvent"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="updatePosition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<field name="FADE_OUT_DURATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="400"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
<interface name="TextView.OnEditorActionListener"
abstract="true"
static="true"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 301883f..fb60fdf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,6 +98,8 @@
sendBroadcast();
} else if (op.equals("profile")) {
runProfile();
+ } else if (op.equals("dumpheap")) {
+ runDumpHeap();
} else {
throw new IllegalArgumentException("Unknown command: " + op);
}
@@ -424,6 +426,28 @@
}
}
+ private void runDumpHeap() throws Exception {
+ boolean managed = !"-n".equals(nextOption());
+ String process = nextArgRequired();
+ String heapFile = nextArgRequired();
+ ParcelFileDescriptor fd = null;
+
+ try {
+ fd = ParcelFileDescriptor.open(
+ new File(heapFile),
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_READ_WRITE);
+ } catch (FileNotFoundException e) {
+ System.err.println("Error: Unable to open file: " + heapFile);
+ return;
+ }
+
+ if (!mAm.dumpHeap(process, managed, heapFile, fd)) {
+ throw new AndroidException("HEAP DUMP FAILED on process " + process);
+ }
+ }
+
private class IntentReceiver extends IIntentReceiver.Stub {
private boolean mFinished = false;
@@ -593,6 +617,8 @@
"\n" +
" start profiling: am profile <PROCESS> start <FILE>\n" +
" stop profiling: am profile <PROCESS> stop\n" +
+ " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
+ " -n: dump native heap instead of managed heap\n" +
"\n" +
" <INTENT> specifications include these flags:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk
index bfa58a1..1df32bb 100644
--- a/cmds/surfaceflinger/Android.mk
+++ b/cmds/surfaceflinger/Android.mk
@@ -10,7 +10,7 @@
libutils
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../libs/surfaceflinger
+ $(LOCAL_PATH)/../../services/surfaceflinger
LOCAL_MODULE:= surfaceflinger
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index d650721..78b1007 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -1,18 +1,25 @@
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
+/*
+ * 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 <binder/BinderService.h>
#include <SurfaceFlinger.h>
using namespace android;
-int main(int argc, char** argv)
-{
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- LOGI("ServiceManager: %p", sm.get());
- SurfaceFlinger::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+int main(int argc, char** argv) {
+ SurfaceFlinger::publishAndJoinThreadPool();
+ return 0;
}
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 1813d3e..a880a91 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -7,9 +7,9 @@
base = $(LOCAL_PATH)/../../..
LOCAL_C_INCLUDES := \
- $(base)/camera/libcameraservice \
- $(base)/libs/audioflinger \
- $(base)/libs/surfaceflinger \
+ $(base)/services/camera/libcameraservice \
+ $(base)/services/audioflinger \
+ $(base)/services/surfaceflinger \
$(base)/media/libmediaplayerservice \
$(JNI_H_INCLUDE)
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 2ead976..e6b1c08 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -399,19 +399,19 @@
private boolean insertAccountIntoDatabase(Account account, String password, Bundle extras) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ if (account == null) {
+ return false;
+ }
+ final boolean noBroadcast = account.type.equals(GOOGLE_ACCOUNT_TYPE)
+ && extras != null && extras.getBoolean(NO_BROADCAST_FLAG, false);
+ // Remove the 'nobroadcast' flag since we don't want it to persist in the db. It is instead
+ // used as a control signal to indicate whether or not this insertion should result in
+ // an accounts changed broadcast being sent.
+ if (extras != null) {
+ extras.remove(NO_BROADCAST_FLAG);
+ }
db.beginTransaction();
try {
- if (account == null) {
- return false;
- }
- boolean noBroadcast = false;
- if (account.type.equals(GOOGLE_ACCOUNT_TYPE)) {
- // Look for the 'nobroadcast' flag and remove it since we don't want it to persist
- // in the db.
- noBroadcast = extras.getBoolean(NO_BROADCAST_FLAG, false);
- extras.remove(NO_BROADCAST_FLAG);
- }
-
long numMatches = DatabaseUtils.longForQuery(db,
"select count(*) from " + TABLE_ACCOUNTS
+ " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
@@ -436,13 +436,13 @@
}
}
db.setTransactionSuccessful();
- if (!noBroadcast) {
- sendAccountsChangedBroadcast();
- }
- return true;
} finally {
db.endTransaction();
}
+ if (!noBroadcast) {
+ sendAccountsChangedBroadcast();
+ }
+ return true;
}
private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
@@ -1681,11 +1681,11 @@
try {
db.execSQL("DELETE from " + TABLE_AUTHTOKENS);
db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''");
- sendAccountsChangedBroadcast();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
+ sendAccountsChangedBroadcast();
}
setMetaValue("imsi", imsi);
}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 67133e0..3cd2b9e 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -206,9 +206,11 @@
*
* @return The current navigation mode.
*
+ * @see #setStandardNavigationMode()
* @see #setStandardNavigationMode(CharSequence)
* @see #setStandardNavigationMode(CharSequence, CharSequence)
* @see #setDropdownNavigationMode(SpinnerAdapter)
+ * @see #setTabNavigationMode()
* @see #setCustomNavigationMode(View)
*/
public abstract int getNavigationMode();
@@ -217,10 +219,98 @@
* @return The current set of display options.
*/
public abstract int getDisplayOptions();
-
+
+ /**
+ * Start a context mode controlled by <code>callback</code>.
+ * The {@link ContextModeCallback} will receive lifecycle events for the duration
+ * of the context mode.
+ *
+ * @param callback Callback handler that will manage this context mode.
+ */
public abstract void startContextMode(ContextModeCallback callback);
+
+ /**
+ * Finish the current context mode.
+ */
public abstract void finishContextMode();
-
+
+ /**
+ * Set the action bar into tabbed navigation mode.
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ * @see #removeTab(Tab)
+ * @see #removeTabAt(int)
+ */
+ public abstract void setTabNavigationMode();
+
+ /**
+ * Set the action bar into tabbed navigation mode.
+ *
+ * @param containerViewId Id of the container view where tab content fragments should appear.
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ * @see #removeTab(Tab)
+ * @see #removeTabAt(int)
+ */
+ public abstract void setTabNavigationMode(int containerViewId);
+
+ /**
+ * Create and return a new {@link Tab}.
+ * This tab will not be included in the action bar until it is added.
+ *
+ * @return A new Tab
+ *
+ * @see #addTab(Tab)
+ * @see #insertTab(Tab, int)
+ */
+ public abstract Tab newTab();
+
+ /**
+ * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+ *
+ * @param tab Tab to add
+ */
+ public abstract void addTab(Tab tab);
+
+ /**
+ * Insert a tab for use in tabbed navigation mode. The tab will be inserted at
+ * <code>position</code>.
+ *
+ * @param tab The tab to add
+ * @param position The new position of the tab
+ */
+ public abstract void insertTab(Tab tab, int position);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param tab The tab to remove
+ */
+ public abstract void removeTab(Tab tab);
+
+ /**
+ * Remove a tab from the action bar.
+ *
+ * @param position Position of the tab to remove
+ */
+ public abstract void removeTabAt(int position);
+
+ /**
+ * Select the specified tab. If it is not a child of this action bar it will be added.
+ *
+ * @param tab Tab to select
+ */
+ public abstract void selectTab(Tab tab);
+
+ /**
+ * Select the tab at <code>position</code>
+ *
+ * @param position Position of the tab to select
+ */
+ public abstract void selectTabAt(int position);
+
/**
* Represents a contextual mode of the Action Bar. Context modes can be used for
* modal interactions with activity content and replace the normal Action Bar until finished.
@@ -278,6 +368,24 @@
* @return The context mode's menu.
*/
public abstract Menu getMenu();
+
+ /**
+ * Returns the current title of this context mode.
+ * @return Title text
+ */
+ public abstract CharSequence getTitle();
+
+ /**
+ * Returns the current subtitle of this context mode.
+ * @return Subtitle text
+ */
+ public abstract CharSequence getSubtitle();
+
+ /**
+ * Returns the current custom view for this context mode.
+ * @return The current custom view
+ */
+ public abstract View getCustomView();
}
/**
@@ -350,4 +458,74 @@
*/
public boolean onNavigationItemSelected(int itemPosition, long itemId);
}
+
+ /**
+ * A tab in the action bar.
+ *
+ * <p>Tabs manage the hiding and showing of {@link Fragment}s.
+ */
+ public static abstract class Tab {
+ /**
+ * An invalid position for a tab.
+ *
+ * @see #getPosition()
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Return the current position of this tab in the action bar.
+ *
+ * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+ * the action bar.
+ */
+ public abstract int getPosition();
+
+ /**
+ * Return the icon associated with this tab.
+ *
+ * @return The tab's icon
+ */
+ public abstract Drawable getIcon();
+
+ /**
+ * Return the text of this tab.
+ *
+ * @return The tab's text
+ */
+ public abstract CharSequence getText();
+
+ /**
+ * Set the icon displayed on this tab.
+ *
+ * @param icon The drawable to use as an icon
+ */
+ public abstract void setIcon(Drawable icon);
+
+ /**
+ * Set the text displayed on this tab. Text may be truncated if there is not
+ * room to display the entire string.
+ *
+ * @param text The text to display
+ */
+ public abstract void setText(CharSequence text);
+
+ /**
+ * Returns the fragment that will be shown when this tab is selected.
+ *
+ * @return Fragment associated with this tab
+ */
+ public abstract Fragment getFragment();
+
+ /**
+ * Set the fragment that will be shown when this tab is selected.
+ *
+ * @param fragment Fragment to associate with this tab
+ */
+ public abstract void setFragment(Fragment fragment);
+
+ /**
+ * Select this tab. Only valid if the tab has been added to the action bar.
+ */
+ public abstract void select();
+ }
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8825b8c..1cdd423 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -71,6 +71,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.internal.app.ActionBarImpl;
@@ -635,6 +636,7 @@
/*package*/ ActivityThread mMainThread;
Activity mParent;
boolean mCalled;
+ boolean mStarted;
private boolean mResumed;
private boolean mStopped;
boolean mFinished;
@@ -649,6 +651,7 @@
Object activity;
HashMap<String, Object> children;
ArrayList<Fragment> fragments;
+ SparseArray<LoaderManagerImpl> loaders;
}
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
@@ -666,6 +669,9 @@
final FragmentManager mFragments = new FragmentManager();
+ SparseArray<LoaderManagerImpl> mAllLoaderManagers;
+ LoaderManagerImpl mLoaderManager;
+
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -763,6 +769,29 @@
}
/**
+ * Return the LoaderManager for this fragment, creating it if needed.
+ */
+ public LoaderManager getLoaderManager() {
+ if (mLoaderManager != null) {
+ return mLoaderManager;
+ }
+ mLoaderManager = getLoaderManager(-1, false);
+ return mLoaderManager;
+ }
+
+ LoaderManagerImpl getLoaderManager(int index, boolean started) {
+ if (mAllLoaderManagers == null) {
+ mAllLoaderManagers = new SparseArray<LoaderManagerImpl>();
+ }
+ LoaderManagerImpl lm = mAllLoaderManagers.get(index);
+ if (lm == null) {
+ lm = new LoaderManagerImpl(started);
+ mAllLoaderManagers.put(index, lm);
+ }
+ return lm;
+ }
+
+ /**
* Calls {@link android.view.Window#getCurrentFocus} on the
* Window of this Activity to return the currently focused view.
*
@@ -816,6 +845,9 @@
protected void onCreate(Bundle savedInstanceState) {
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
+ if (mLastNonConfigurationInstances != null) {
+ mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
+ }
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
@@ -958,6 +990,10 @@
*/
protected void onStart() {
mCalled = true;
+ mStarted = true;
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ }
}
/**
@@ -1235,19 +1271,37 @@
* @see #onPause
*/
public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
- final View view = mDecor;
- if (view == null) {
+ if (mDecor == null) {
return false;
}
- final int vw = view.getWidth();
- final int vh = view.getHeight();
- final int dw = outBitmap.getWidth();
- final int dh = outBitmap.getHeight();
+ int paddingLeft = 0;
+ int paddingRight = 0;
+ int paddingTop = 0;
+ int paddingBottom = 0;
+
+ // Find System window and use padding so we ignore space reserved for decorations
+ // like the status bar and such.
+ final FrameLayout top = (FrameLayout) mDecor;
+ for (int i = 0; i < top.getChildCount(); i++) {
+ View child = top.getChildAt(i);
+ if (child.isFitsSystemWindowsFlagSet()) {
+ paddingLeft = child.getPaddingLeft();
+ paddingRight = child.getPaddingRight();
+ paddingTop = child.getPaddingTop();
+ paddingBottom = child.getPaddingBottom();
+ break;
+ }
+ }
+
+ final int visibleWidth = mDecor.getWidth() - paddingLeft - paddingRight;
+ final int visibleHeight = mDecor.getHeight() - paddingTop - paddingBottom;
canvas.save();
- canvas.scale(((float)dw)/vw, ((float)dh)/vh);
- view.draw(canvas);
+ canvas.scale( (float) outBitmap.getWidth() / visibleWidth,
+ (float) outBitmap.getHeight() / visibleHeight);
+ canvas.translate(-paddingLeft, -paddingTop);
+ mDecor.draw(canvas);
canvas.restore();
return true;
@@ -1495,7 +1549,20 @@
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
ArrayList<Fragment> fragments = mFragments.retainNonConfig();
- if (activity == null && children == null && fragments == null) {
+ boolean retainLoaders = false;
+ if (mAllLoaderManagers != null) {
+ // prune out any loader managers that were already stopped, so
+ // have nothing useful to retain.
+ for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
+ LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i);
+ if (lm.mRetaining) {
+ retainLoaders = true;
+ } else {
+ mAllLoaderManagers.removeAt(i);
+ }
+ }
+ }
+ if (activity == null && children == null && fragments == null && !retainLoaders) {
return null;
}
@@ -1503,6 +1570,7 @@
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
+ nci.loaders = mAllLoaderManagers;
return nci;
}
@@ -1518,6 +1586,20 @@
return new BackStackEntry(mFragments);
}
+ void invalidateFragmentIndex(int index) {
+ if (mAllLoaderManagers != null) {
+ mAllLoaderManagers.remove(index);
+ }
+ }
+
+ /**
+ * Called when a Fragment is being attached to this activity, immediately
+ * after the call to its {@link Fragment#onAttach Fragment.onAttach()}
+ * method and before {@link Fragment#onCreate Fragment.onCreate()}.
+ */
+ public void onAttachFragment(Fragment fragment) {
+ }
+
/**
* Wrapper around
* {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
@@ -1676,11 +1758,11 @@
*/
private void initActionBar() {
Window window = getWindow();
- if (!window.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
- mActionBar = new ActionBarImpl(getWindow().getDecorView());
+ mActionBar = new ActionBarImpl(this);
}
/**
@@ -2060,13 +2142,6 @@
}
public void onContentChanged() {
- // First time content is available, let the fragment manager
- // attach all of the fragments to it. Don't do this if the
- // activity is no longer attached (because it is being destroyed).
- if (mFragments.mCurState < Fragment.CONTENT
- && mFragments.mActivity != null) {
- mFragments.moveToState(Fragment.CONTENT, false);
- }
}
/**
@@ -4024,6 +4099,7 @@
final void performCreate(Bundle icicle) {
onCreate(icicle);
+ mFragments.dispatchActivityCreated();
}
final void performStart() {
@@ -4036,6 +4112,11 @@
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
+ if (mAllLoaderManagers != null) {
+ for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
+ mAllLoaderManagers.valueAt(i).finishRetain();
+ }
+ }
}
final void performRestart() {
@@ -4107,6 +4188,17 @@
}
final void performStop() {
+ if (mStarted) {
+ mStarted = false;
+ if (mLoaderManager != null) {
+ if (!mChangingConfigurations) {
+ mLoaderManager.doStop();
+ } else {
+ mLoaderManager.doRetain();
+ }
+ }
+ }
+
if (!mStopped) {
if (mWindow != null) {
mWindow.closeAllPanels();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index eb7520f..d66e98b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -285,24 +285,54 @@
* @param maxNum The maximum number of entries to return in the list. The
* actual number returned may be smaller, depending on how many tasks the
* user has started.
- *
+ *
+ * @param flags Optional flags
+ * @param receiver Optional receiver for delayed thumbnails
+ *
* @return Returns a list of RunningTaskInfo records describing each of
* the running tasks.
*
+ * Some thumbnails may not be available at the time of this call. The optional
+ * receiver may be used to receive those thumbnails.
+ *
* @throws SecurityException Throws SecurityException if the caller does
* not hold the {@link android.Manifest.permission#GET_TASKS} permission.
+ *
+ * @hide
*/
- public List<RunningTaskInfo> getRunningTasks(int maxNum)
+ public List<RunningTaskInfo> getRunningTasks(int maxNum, int flags, IThumbnailReceiver receiver)
throws SecurityException {
try {
- return (List<RunningTaskInfo>)ActivityManagerNative.getDefault()
- .getTasks(maxNum, 0, null);
+ return ActivityManagerNative.getDefault().getTasks(maxNum, flags, receiver);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
}
}
-
+
+ /**
+ * Return a list of the tasks that are currently running, with
+ * the most recent being first and older ones after in order. Note that
+ * "running" does not mean any of the task's code is currently loaded or
+ * activity -- the task may have been frozen by the system, so that it
+ * can be restarted in its previous state when next brought to the
+ * foreground.
+ *
+ * @param maxNum The maximum number of entries to return in the list. The
+ * actual number returned may be smaller, depending on how many tasks the
+ * user has started.
+ *
+ * @return Returns a list of RunningTaskInfo records describing each of
+ * the running tasks.
+ *
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
+ */
+ public List<RunningTaskInfo> getRunningTasks(int maxNum)
+ throws SecurityException {
+ return getRunningTasks(maxNum, 0, null);
+ }
+
/**
* Information you can retrieve about a particular Service that is
* currently running in the system.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1fe85e6..43a08b5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1294,6 +1294,19 @@
return true;
}
+ case DUMP_HEAP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String process = data.readString();
+ boolean managed = data.readInt() != 0;
+ String path = data.readString();
+ ParcelFileDescriptor fd = data.readInt() != 0
+ ? data.readFileDescriptor() : null;
+ boolean res = dumpHeap(process, managed, path, fd);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -2874,6 +2887,28 @@
data.recycle();
reply.recycle();
}
-
+
+ public boolean dumpHeap(String process, boolean managed,
+ String path, ParcelFileDescriptor fd) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(process);
+ data.writeInt(managed ? 1 : 0);
+ data.writeString(path);
+ if (fd != null) {
+ data.writeInt(1);
+ fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
+ mRemote.transact(DUMP_HEAP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d788be8..c800fbe 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -116,6 +116,7 @@
*/
public final class ActivityThread {
static final String TAG = "ActivityThread";
+ private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
private static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
static final boolean DEBUG_BROADCAST = false;
@@ -356,6 +357,11 @@
ParcelFileDescriptor fd;
}
+ private static final class DumpHeapData {
+ String path;
+ ParcelFileDescriptor fd;
+ }
+
private final class ApplicationThread extends ApplicationThreadNative {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -623,6 +629,13 @@
queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
}
+ public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
+ DumpHeapData dhd = new DumpHeapData();
+ dhd.path = path;
+ dhd.fd = fd;
+ queueOrSendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0);
+ }
+
public void setSchedulingGroup(int group) {
// Note: do this immediately, since going into the foreground
// should happen regardless of what pending work we have to do
@@ -874,6 +887,7 @@
public static final int ENABLE_JIT = 132;
public static final int DISPATCH_PACKAGE_BROADCAST = 133;
public static final int SCHEDULE_CRASH = 134;
+ public static final int DUMP_HEAP = 135;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -912,6 +926,7 @@
case ENABLE_JIT: return "ENABLE_JIT";
case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
+ case DUMP_HEAP: return "DUMP_HEAP";
}
}
return "(unknown)";
@@ -1037,6 +1052,9 @@
break;
case SCHEDULE_CRASH:
throw new RemoteServiceException((String)msg.obj);
+ case DUMP_HEAP:
+ handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj);
+ break;
}
}
@@ -2228,13 +2246,24 @@
h = mThumbnailHeight;
}
- // XXX Only set hasAlpha if needed?
- thumbnail = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
- thumbnail.eraseColor(0);
- Canvas cv = new Canvas(thumbnail);
- if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
- thumbnail = null;
+ // On platforms where we don't want thumbnails, set dims to (0,0)
+ if ((w > 0) && (h > 0)) {
+ View topView = r.activity.getWindow().getDecorView();
+
+ // Maximize bitmap by capturing in native aspect.
+ if (topView.getWidth() >= topView.getHeight()) {
+ thumbnail = Bitmap.createBitmap(w, h, THUMBNAIL_FORMAT);
+ } else {
+ thumbnail = Bitmap.createBitmap(h, w, THUMBNAIL_FORMAT);
+ }
+
+ thumbnail.eraseColor(0);
+ Canvas cv = new Canvas(thumbnail);
+ if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
+ thumbnail = null;
+ }
}
+
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -2365,7 +2394,7 @@
if (info != null) {
try {
// First create a thumbnail for the activity...
- //info.thumbnail = createThumbnailBitmap(r);
+ info.thumbnail = createThumbnailBitmap(r);
info.description = r.activity.onCreateDescription();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
@@ -3015,6 +3044,25 @@
}
}
+ final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+ if (managed) {
+ try {
+ Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
+ } catch (IOException e) {
+ Slog.w(TAG, "Managed heap dump failed on path " + dhd.path
+ + " -- can the process access this path?");
+ } finally {
+ try {
+ dhd.fd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure closing profile fd", e);
+ }
+ }
+ } else {
+ Debug.dumpNativeHeap(dhd.fd.getFileDescriptor());
+ }
+ }
+
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
boolean hasPkgInfo = false;
if (packages != null) {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 1c20062..dc2145f 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -403,6 +403,17 @@
scheduleCrash(msg);
return true;
}
+
+ case DUMP_HEAP_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ boolean managed = data.readInt() != 0;
+ String path = data.readString();
+ ParcelFileDescriptor fd = data.readInt() != 0
+ ? data.readFileDescriptor() : null;
+ dumpHeap(managed, path, fd);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -829,5 +840,22 @@
data.recycle();
}
+
+ public void dumpHeap(boolean managed, String path,
+ ParcelFileDescriptor fd) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeInt(managed ? 1 : 0);
+ data.writeString(path);
+ if (fd != null) {
+ data.writeInt(1);
+ fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
+ mRemote.transact(DUMP_HEAP_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4ff8e1a..a2a74f8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -53,7 +53,6 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageParser.Package;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -68,6 +67,7 @@
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
+import android.net.DownloadManager;
import android.net.ThrottleManager;
import android.net.IThrottleManager;
import android.net.Uri;
@@ -86,11 +86,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StatFs;
import android.os.Vibrator;
import android.os.FileUtils.FileStatus;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -200,6 +198,7 @@
private DropBoxManager mDropBoxManager = null;
private DevicePolicyManager mDevicePolicyManager = null;
private UiModeManager mUiModeManager = null;
+ private DownloadManager mDownloadManager = null;
private final Object mSync = new Object();
@@ -209,7 +208,7 @@
private File mCacheDir;
private File mExternalFilesDir;
private File mExternalCacheDir;
-
+
private static long sInstanceCount = 0;
private static final String[] EMPTY_FILE_LIST = {};
@@ -261,18 +260,18 @@
public Looper getMainLooper() {
return mMainThread.getLooper();
}
-
+
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
-
+
@Override
public void setTheme(int resid) {
mThemeResource = resid;
}
-
+
@Override
public Resources.Theme getTheme() {
if (mTheme == null) {
@@ -322,7 +321,7 @@
}
throw new RuntimeException("Not supported in system context");
}
-
+
private static File makeBackupFile(File prefsFile) {
return new File(prefsFile.getPath() + ".bak");
}
@@ -342,7 +341,7 @@
return sp;
}
}
-
+
FileInputStream str = null;
File backup = makeBackupFile(f);
if (backup.exists()) {
@@ -354,7 +353,7 @@
if (f.exists() && !f.canRead()) {
Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
}
-
+
Map map = null;
if (f.exists() && f.canRead()) {
try {
@@ -438,7 +437,7 @@
}
if (!mFilesDir.exists()) {
if(!mFilesDir.mkdirs()) {
- Log.w(TAG, "Unable to create files directory");
+ Log.w(TAG, "Unable to create files directory " + mFilesDir.getPath());
return null;
}
FileUtils.setPermissions(
@@ -449,7 +448,7 @@
return mFilesDir;
}
}
-
+
@Override
public File getExternalFilesDir(String type) {
synchronized (mSync) {
@@ -481,7 +480,7 @@
return dir;
}
}
-
+
@Override
public File getCacheDir() {
synchronized (mSync) {
@@ -501,7 +500,7 @@
}
return mCacheDir;
}
-
+
@Override
public File getExternalCacheDir() {
synchronized (mSync) {
@@ -523,7 +522,7 @@
return mExternalCacheDir;
}
}
-
+
@Override
public File getFileStreamPath(String name) {
return makeFilename(getFilesDir(), name);
@@ -573,7 +572,7 @@
return (list != null) ? list : EMPTY_FILE_LIST;
}
-
+
private File getDatabasesDir() {
synchronized (mSync) {
if (mDatabasesDir == null) {
@@ -585,7 +584,7 @@
return mDatabasesDir;
}
}
-
+
@Override
public Drawable getWallpaper() {
return getWallpaperManager().getDrawable();
@@ -654,7 +653,7 @@
} catch (RemoteException e) {
}
}
-
+
@Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
@@ -984,6 +983,8 @@
return getDevicePolicyManager();
} else if (UI_MODE_SERVICE.equals(name)) {
return getUiModeManager();
+ } else if (DOWNLOAD_SERVICE.equals(name)) {
+ return getDownloadManager();
}
return null;
@@ -1202,6 +1203,15 @@
return mUiModeManager;
}
+ private DownloadManager getDownloadManager() {
+ synchronized (mSync) {
+ if (mDownloadManager == null) {
+ mDownloadManager = new DownloadManager(getContentResolver());
+ }
+ }
+ return mDownloadManager;
+ }
+
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
@@ -1556,15 +1566,15 @@
final void setActivityToken(IBinder token) {
mActivityToken = token;
}
-
+
final void setOuterContext(Context context) {
mOuterContext = context;
}
-
+
final Context getOuterContext() {
return mOuterContext;
}
-
+
final IBinder getActivityToken() {
return mActivityToken;
}
@@ -1641,7 +1651,7 @@
{
return mMainThread.releaseProvider(provider);
}
-
+
private final ActivityThread mMainThread;
}
@@ -1674,7 +1684,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public String[] canonicalToCurrentPackageNames(String[] names) {
try {
@@ -1683,7 +1693,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public Intent getLaunchIntentForPackage(String packageName) {
// First see if the package has an INFO activity; the existence of
@@ -1705,8 +1715,9 @@
if (resolveInfo == null) {
return null;
}
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(packageName, resolveInfo.activityInfo.name);
+ Intent intent = new Intent(intentToResolve);
+ intent.setClassName(resolveInfo.activityInfo.applicationInfo.packageName,
+ resolveInfo.activityInfo.name);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
@@ -1857,7 +1868,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public boolean hasSystemFeature(String name) {
try {
@@ -1866,7 +1877,7 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
public int checkPermission(String permName, String pkgName) {
try {
@@ -1938,9 +1949,9 @@
throw new RuntimeException("Package manager has died", e);
}
}
-
+
@Override
- public int getUidForSharedUser(String sharedUserName)
+ public int getUidForSharedUser(String sharedUserName)
throws NameNotFoundException {
try {
int uid = mPM.getUidForSharedUser(sharedUserName);
@@ -2344,7 +2355,7 @@
}
}
}
-
+
private static final class ResourceName {
final String packageName;
final int iconId;
@@ -2516,7 +2527,7 @@
}
}
@Override
- public void clearApplicationUserData(String packageName,
+ public void clearApplicationUserData(String packageName,
IPackageDataObserver observer) {
try {
mPM.clearApplicationUserData(packageName, observer);
@@ -2525,7 +2536,7 @@
}
}
@Override
- public void deleteApplicationCacheFiles(String packageName,
+ public void deleteApplicationCacheFiles(String packageName,
IPackageDataObserver observer) {
try {
mPM.deleteApplicationCacheFiles(packageName, observer);
@@ -2550,9 +2561,9 @@
// Should never happen!
}
}
-
+
@Override
- public void getPackageSizeInfo(String packageName,
+ public void getPackageSizeInfo(String packageName,
IPackageStatsObserver observer) {
try {
mPM.getPackageSizeInfo(packageName, observer);
@@ -2597,7 +2608,7 @@
// Should never happen!
}
}
-
+
@Override
public void replacePreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
@@ -2616,7 +2627,7 @@
// Should never happen!
}
}
-
+
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
@@ -2627,7 +2638,7 @@
}
return 0;
}
-
+
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
@@ -2657,7 +2668,7 @@
// Should never happen!
}
}
-
+
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
@@ -2668,6 +2679,15 @@
return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
+ @Override
+ public void setPackageObbPath(String packageName, String path) {
+ try {
+ mPM.setPackageObbPath(packageName, path);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
@@ -2714,7 +2734,7 @@
return mTimestamp != mFileStatus.mtime;
}
}
-
+
public void replace(Map newContents) {
if (newContents != null) {
synchronized (this) {
@@ -2722,7 +2742,7 @@
}
}
}
-
+
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
mListeners.put(listener, mContent);
@@ -2901,7 +2921,7 @@
public Editor edit() {
return new EditorImpl();
}
-
+
private FileOutputStream createFileOutputStream(File file) {
FileOutputStream str = null;
try {
@@ -2938,7 +2958,7 @@
mFile.delete();
}
}
-
+
// Attempt to write the file, delete the backup and return true as atomically as
// possible. If any exception occurs, delete the new file; next time we will restore
// from the backup.
@@ -2953,7 +2973,7 @@
if (FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
mTimestamp = mFileStatus.mtime;
}
-
+
// Writing was successful, delete the backup file if there is one.
mBackupFile.delete();
return true;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 231d6ab..daf6ce0 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -134,14 +134,14 @@
* that can be placed in an {@link Activity}.
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
- private static final HashMap<String, Class> sClassMap =
- new HashMap<String, Class>();
+ private static final HashMap<String, Class<?>> sClassMap =
+ new HashMap<String, Class<?>>();
- static final int INITIALIZING = 0; // Not yet created.
- static final int CREATED = 1; // Created.
- static final int CONTENT = 2; // View hierarchy content available.
- static final int STARTED = 3; // Created and started, not resumed.
- static final int RESUMED = 4; // Created started and resumed.
+ static final int INITIALIZING = 0; // Not yet created.
+ static final int CREATED = 1; // Created.
+ static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
+ static final int STARTED = 3; // Created and started, not resumed.
+ static final int RESUMED = 4; // Created started and resumed.
int mState = INITIALIZING;
@@ -158,6 +158,9 @@
// True if the fragment is in the list of added fragments.
boolean mAdded;
+ // True if the fragment is in the resumed state.
+ boolean mResumed;
+
// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;
@@ -210,6 +213,17 @@
// The View generated for this fragment.
View mView;
+ LoaderManagerImpl mLoaderManager;
+ boolean mStarted;
+
+ /**
+ * Default constructor. <strong>Every</string> 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)}.
+ */
public Fragment() {
}
@@ -217,7 +231,7 @@
throws NoSuchMethodException, ClassNotFoundException,
IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException {
- Class clazz = sClassMap.get(fname);
+ Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
@@ -310,6 +324,14 @@
}
/**
+ * Return true if the fragment is in the resumed state. This is true
+ * for the duration of {@link #onResume()} and {@link #onPause()} as well.
+ */
+ final public boolean isResumed() {
+ return mResumed;
+ }
+
+ /**
* Return true if the fragment is currently visible to the user. This means
* it: (1) has been added, (2) has its view attached to the window, and
* (3) is not hidden.
@@ -350,7 +372,7 @@
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
- * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b>
+ * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
@@ -379,6 +401,17 @@
}
/**
+ * Return the LoaderManager for this fragment, creating it if needed.
+ */
+ public LoaderManager getLoaderManager() {
+ if (mLoaderManager != null) {
+ return mLoaderManager;
+ }
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted);
+ return mLoaderManager;
+ }
+
+ /**
* Call {@link Activity#startActivity(Intent)} on the fragment's
* containing Activity.
*/
@@ -446,7 +479,15 @@
/**
* Called to do initial creation of a fragment. This is called after
- * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}.
+ * {@link #onAttach(Activity)} and before
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+ *
+ * <p>Note that this can be called while the fragment's activity is
+ * still in the process of being created. As such, you can not rely
+ * on things like the activity's content view hierarchy being initialized
+ * at this point. If you want to do work once the activity itself is
+ * created, see {@link #onActivityCreated(Bundle)}.
+ *
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
@@ -458,7 +499,7 @@
* Called to have the fragment instantiate its user interface view.
* This is optional, and non-graphical fragments can return null (which
* is the default implementation). This will be called between
- * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}.
+ * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}.
*
* <p>If you return a View from here, you will later be called in
* {@link #onDestroyView} when the view is being released.
@@ -483,16 +524,19 @@
}
/**
- * Called when the activity is ready for the fragment to run. This is
- * most useful for fragments that use {@link #setRetainInstance(boolean)}
- * instance, as this tells the fragment when it is fully associated with
+ * Called when the fragment's activity has been created and this
+ * fragment's view hierarchy instantiated. It can be used to do final
+ * initialization once these pieces are in place, such as retrieving
+ * views or restoring state. It is also useful for fragments that use
+ * {@link #setRetainInstance(boolean)} to retain their instance,
+ * as this callback tells the fragment when it is fully associated with
* the new activity instance. This is called after {@link #onCreateView}
* and before {@link #onStart()}.
*
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
- public void onReady(Bundle savedInstanceState) {
+ public void onActivityCreated(Bundle savedInstanceState) {
mCalled = true;
}
@@ -503,6 +547,10 @@
*/
public void onStart() {
mCalled = true;
+ mStarted = true;
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ }
}
/**
@@ -561,6 +609,9 @@
*/
public void onDestroy() {
mCalled = true;
+ if (mLoaderManager != null) {
+ mLoaderManager.doDestroy();
+ }
}
/**
@@ -702,4 +753,18 @@
public boolean onContextItemSelected(MenuItem item) {
return false;
}
+
+ void performStop() {
+ onStop();
+ if (mStarted) {
+ mStarted = false;
+ if (mLoaderManager != null) {
+ if (mActivity == null || !mActivity.mChangingConfigurations) {
+ mLoaderManager.doStop();
+ } else {
+ mLoaderManager.doRetain();
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index b8eeb094..4f3043c 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -162,6 +162,7 @@
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
+ mActivity.onAttachFragment(f);
if (!f.mRetaining) {
f.mCalled = false;
@@ -216,15 +217,15 @@
}
f.mCalled = false;
- f.onReady(f.mSavedFragmentState);
+ f.onActivityCreated(f.mSavedFragmentState);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onReady()");
}
f.mSavedFragmentState = null;
}
- case Fragment.CONTENT:
- if (newState > Fragment.CONTENT) {
+ case Fragment.ACTIVITY_CREATED:
+ if (newState > Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.mCalled = false;
f.onStart();
@@ -237,6 +238,7 @@
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.mCalled = false;
+ f.mResumed = true;
f.onResume();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
@@ -255,19 +257,20 @@
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onPause()");
}
+ f.mResumed = false;
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
f.mCalled = false;
- f.onStop();
+ f.performStop();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onStop()");
}
}
- case Fragment.CONTENT:
- if (newState < Fragment.CONTENT) {
+ case Fragment.ACTIVITY_CREATED:
+ if (newState < Fragment.ACTIVITY_CREATED) {
if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
if (f.mView != null) {
f.mCalled = false;
@@ -373,6 +376,7 @@
mAvailIndices = new ArrayList<Integer>();
}
mAvailIndices.add(f.mIndex);
+ mActivity.invalidateFragmentIndex(f.mIndex);
f.clearIndex();
}
@@ -783,6 +787,10 @@
moveToState(Fragment.CREATED, false);
}
+ public void dispatchActivityCreated() {
+ moveToState(Fragment.ACTIVITY_CREATED, false);
+ }
+
public void dispatchStart() {
moveToState(Fragment.STARTED, false);
}
@@ -796,7 +804,7 @@
}
public void dispatchStop() {
- moveToState(Fragment.CONTENT, false);
+ moveToState(Fragment.ACTIVITY_CREATED, false);
}
public void dispatchDestroy() {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 20c9a80..8ea59a7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -316,7 +316,11 @@
public void crashApplication(int uid, int initialPid, String packageName,
String message) throws RemoteException;
-
+
+ // Cause the specified process to dump the specified heap.
+ public boolean dumpHeap(String process, boolean managed, String path,
+ ParcelFileDescriptor fd) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -533,4 +537,5 @@
int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
+ int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index c8ef17f..039bcb9 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -97,6 +97,8 @@
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
throws RemoteException;
+ void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
+ throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
static final int PACKAGE_REMOVED = 0;
@@ -140,4 +142,5 @@
int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
+ int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
}
diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java
index 4bf5518..d49968f 100644
--- a/core/java/android/app/ListActivity.java
+++ b/core/java/android/app/ListActivity.java
@@ -309,7 +309,7 @@
if (mList != null) {
return;
}
- setContentView(com.android.internal.R.layout.list_content);
+ setContentView(com.android.internal.R.layout.list_content_simple);
}
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index a6be329..73ef869 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -160,7 +160,6 @@
TextView mStandardEmptyView;
View mProgressContainer;
View mListContainer;
- boolean mSetEmptyView;
boolean mListShown;
public ListFragment() {
@@ -173,11 +172,17 @@
* is {@link android.R.id#list android.R.id.list} and can optionally
* have a sibling view id {@link android.R.id#empty android.R.id.empty}
* that is to be shown when the list is empty.
+ *
+ * <p>If you are overriding this method with your own custom content,
+ * consider including the standard layout {@link android.R.layout#list_content}
+ * in your layout file, so that you continue to retain all of the standard
+ * behavior of ListFragment. In particular, this is currently the only
+ * way to have the built-in indeterminant progress state be shown.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(com.android.internal.R.layout.list_content_rich,
+ return inflater.inflate(com.android.internal.R.layout.list_content,
container, false);
}
@@ -185,8 +190,8 @@
* Attach to list view once Fragment is ready to run.
*/
@Override
- public void onReady(Bundle savedInstanceState) {
- super.onReady(savedInstanceState);
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
ensureList();
}
@@ -218,9 +223,15 @@
* Provide the cursor for the list view.
*/
public void setListAdapter(ListAdapter adapter) {
+ boolean hadAdapter = mAdapter != null;
mAdapter = adapter;
if (mList != null) {
mList.setAdapter(adapter);
+ if (!mListShown && !hadAdapter) {
+ // The list was hidden, and previously didn't have an
+ // adapter. It is now time to show it.
+ setListShown(true, getView().getWindowToken() != null);
+ }
}
}
@@ -269,10 +280,33 @@
if (mStandardEmptyView == null) {
throw new IllegalStateException("Can't be used with a custom content view");
}
- if (!mSetEmptyView) {
- mSetEmptyView = true;
- mList.setEmptyView(mStandardEmptyView);
- }
+ mList.setEmptyView(mStandardEmptyView);
+ }
+
+ /**
+ * Control whether the list is being displayed. You can make it not
+ * displayed if you are waiting for the initial data to show in it. During
+ * this time an indeterminant progress indicator will be shown instead.
+ *
+ * <p>Applications do not normally need to use this themselves. The default
+ * behavior of ListFragment is to start with the list not being shown, only
+ * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.
+ * If the list at that point had not been shown, when it does get shown
+ * it will be do without the user ever seeing the hidden state.
+ *
+ * @param shown If true, the list view is shown; if false, the progress
+ * indicator. The initial value is true.
+ */
+ public void setListShown(boolean shown) {
+ setListShown(shown, true);
+ }
+
+ /**
+ * Like {@link #setListShown(boolean)}, but no animation is used when
+ * transitioning from the previous state.
+ */
+ public void setListShownNoAnimation(boolean shown) {
+ setListShown(shown, false);
}
/**
@@ -285,7 +319,7 @@
* @param animate If true, an animation will be used to transition to the
* new state.
*/
- public void setListShown(boolean shown, boolean animate) {
+ private void setListShown(boolean shown, boolean animate) {
ensureList();
if (mProgressContainer == null) {
throw new IllegalStateException("Can't be used with a custom content view");
@@ -360,6 +394,12 @@
mList.setOnItemClickListener(mOnClickListener);
if (mAdapter != null) {
setListAdapter(mAdapter);
+ } else {
+ // We are starting without an adapter, so assume we won't
+ // have our data right away and start with the progress indicator.
+ if (mProgressContainer != null) {
+ setListShown(false, false);
+ }
}
mHandler.post(mRequestFocus);
}
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
new file mode 100644
index 0000000..c9fdfba
--- /dev/null
+++ b/core/java/android/app/LoaderManager.java
@@ -0,0 +1,337 @@
+/*
+ * 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.app;
+
+import android.content.Loader;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+/**
+ * Interface associated with an {@link Activity} or {@link Fragment} for managing
+ * one or more {@link android.content.Loader} instances associated with it.
+ */
+public interface LoaderManager {
+ /**
+ * Callback interface for a client to interact with the manager.
+ */
+ public interface LoaderCallbacks<D> {
+ /**
+ * Instantiate and return a new Loader for the given ID.
+ *
+ * @param id The ID whose loader is to be created.
+ * @param args Any arguments supplied by the caller.
+ * @return Return a new Loader instance that is ready to start loading.
+ */
+ public Loader<D> onCreateLoader(int id, Bundle args);
+
+ /**
+ * Called when a previously created loader has finished its load.
+ * @param loader The Loader that has finished.
+ * @param data The data generated by the Loader.
+ */
+ public void onLoadFinished(Loader<D> loader, D data);
+ }
+
+ /**
+ * Ensures a loader is initialized and active. If the loader doesn't
+ * already exist, one is created and (if the activity/fragment is currently
+ * started) starts the loader. Otherwise the last created
+ * loader is re-used.
+ *
+ * <p>In either case, the given callback is associated with the loader, and
+ * will be called as the loader state changes. If at the point of call
+ * the caller is in its started state, and the requested loader
+ * already exists and has generated its data, then
+ * callback. {@link LoaderCallbacks#onLoadFinished} will
+ * be called immediately (inside of this function), so you must be prepared
+ * for this to happen.
+ */
+ public <D> Loader<D> initLoader(int id, Bundle args,
+ LoaderManager.LoaderCallbacks<D> callback);
+
+ /**
+ * Creates a new loader in this manager, registers the callbacks to it,
+ * and (if the activity/fragment is currently started) starts loading it.
+ * If a loader with the same id has previously been
+ * started it will automatically be destroyed when the new loader completes
+ * its work. The callback will be delivered before the old loader
+ * is destroyed.
+ */
+ public <D> Loader<D> restartLoader(int id, Bundle args,
+ LoaderManager.LoaderCallbacks<D> callback);
+
+ /**
+ * Stops and removes the loader with the given ID.
+ */
+ public void stopLoader(int id);
+
+ /**
+ * Return the Loader with the given id or null if no matching Loader
+ * is found.
+ */
+ public <D> Loader<D> getLoader(int id);
+}
+
+class LoaderManagerImpl implements LoaderManager {
+ final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
+ final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
+ boolean mStarted;
+ boolean mRetaining;
+ boolean mRetainingStarted;
+
+ final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
+ final int mId;
+ final Bundle mArgs;
+ LoaderManager.LoaderCallbacks<Object> mCallbacks;
+ Loader<Object> mLoader;
+ Object mData;
+ boolean mStarted;
+ boolean mRetaining;
+ boolean mRetainingStarted;
+ boolean mDestroyed;
+ boolean mListenerRegistered;
+
+ public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
+ mId = id;
+ mArgs = args;
+ mCallbacks = callbacks;
+ }
+
+ void start() {
+ if (mRetaining && mRetainingStarted) {
+ // Our owner is started, but we were being retained from a
+ // previous instance in the started state... so there is really
+ // nothing to do here, since the loaders are still started.
+ mStarted = true;
+ return;
+ }
+
+ if (mStarted) {
+ // If loader already started, don't restart.
+ return;
+ }
+
+ if (mLoader == null && mCallbacks != null) {
+ mLoader = mCallbacks.onCreateLoader(mId, mArgs);
+ }
+ if (mLoader != null) {
+ if (!mListenerRegistered) {
+ mLoader.registerListener(mId, this);
+ mListenerRegistered = true;
+ }
+ mLoader.startLoading();
+ mStarted = true;
+ }
+ }
+
+ void retain() {
+ mRetaining = true;
+ mRetainingStarted = mStarted;
+ mStarted = false;
+ mCallbacks = null;
+ }
+
+ void finishRetain() {
+ if (mRetaining) {
+ mRetaining = false;
+ if (mStarted != mRetainingStarted) {
+ if (!mStarted) {
+ // This loader was retained in a started state, but
+ // at the end of retaining everything our owner is
+ // no longer started... so make it stop.
+ stop();
+ }
+ }
+ if (mStarted && mData != null && mCallbacks != null) {
+ // This loader was retained, and now at the point of
+ // finishing the retain we find we remain started, have
+ // our data, and the owner has a new callback... so
+ // let's deliver the data now.
+ mCallbacks.onLoadFinished(mLoader, mData);
+ }
+ }
+ }
+
+ void stop() {
+ mStarted = false;
+ if (mLoader != null && mListenerRegistered) {
+ // Let the loader know we're done with it
+ mListenerRegistered = false;
+ mLoader.unregisterListener(this);
+ }
+ }
+
+ void destroy() {
+ mDestroyed = true;
+ mCallbacks = null;
+ if (mLoader != null) {
+ if (mListenerRegistered) {
+ mListenerRegistered = false;
+ mLoader.unregisterListener(this);
+ }
+ mLoader.destroy();
+ }
+ }
+
+ @Override public void onLoadComplete(Loader<Object> loader, Object data) {
+ if (mDestroyed) {
+ return;
+ }
+
+ // Notify of the new data so the app can switch out the old data before
+ // we try to destroy it.
+ mData = data;
+ if (mCallbacks != null) {
+ mCallbacks.onLoadFinished(loader, data);
+ }
+
+ // Look for an inactive loader and destroy it if found
+ LoaderInfo info = mInactiveLoaders.get(mId);
+ if (info != null) {
+ Loader<Object> oldLoader = info.mLoader;
+ if (oldLoader != null) {
+ if (info.mListenerRegistered) {
+ oldLoader.unregisterListener(info);
+ }
+ oldLoader.destroy();
+ }
+ mInactiveLoaders.remove(mId);
+ }
+ }
+ }
+
+ LoaderManagerImpl(boolean started) {
+ mStarted = started;
+ }
+
+ private LoaderInfo createLoader(int id, Bundle args,
+ LoaderManager.LoaderCallbacks<Object> callback) {
+ LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
+ mLoaders.put(id, info);
+ Loader<Object> loader = callback.onCreateLoader(id, args);
+ info.mLoader = (Loader<Object>)loader;
+ if (mStarted) {
+ // The activity will start all existing loaders in it's onStart(),
+ // so only start them here if we're past that point of the activitiy's
+ // life cycle
+ info.start();
+ }
+ return info;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+ LoaderInfo info = mLoaders.get(id);
+
+ if (info == null) {
+ // Loader doesn't already exist; create.
+ info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
+ } else {
+ info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
+ }
+
+ if (info.mData != null && mStarted) {
+ // If the loader has already generated its data, report it now.
+ info.mCallbacks.onLoadFinished(info.mLoader, info.mData);
+ }
+
+ return (Loader<D>)info.mLoader;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+ LoaderInfo info = mLoaders.get(id);
+ if (info != null) {
+ if (mInactiveLoaders.get(id) != null) {
+ // We already have an inactive loader for this ID that we are
+ // waiting for! Now we have three active loaders... let's just
+ // drop the one in the middle, since we are still waiting for
+ // its result but that result is already out of date.
+ info.destroy();
+ } else {
+ // Keep track of the previous instance of this loader so we can destroy
+ // it when the new one completes.
+ mInactiveLoaders.put(id, info);
+ }
+ }
+
+ info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
+ return (Loader<D>)info.mLoader;
+ }
+
+ public void stopLoader(int id) {
+ int idx = mLoaders.indexOfKey(id);
+ if (idx >= 0) {
+ LoaderInfo info = mLoaders.valueAt(idx);
+ mLoaders.removeAt(idx);
+ info.destroy();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <D> Loader<D> getLoader(int id) {
+ LoaderInfo loaderInfo = mLoaders.get(id);
+ if (loaderInfo != null) {
+ return (Loader<D>)mLoaders.get(id).mLoader;
+ }
+ return null;
+ }
+
+ void doStart() {
+ // Call out to sub classes so they can start their loaders
+ // Let the existing loaders know that we want to be notified when a load is complete
+ for (int i = mLoaders.size()-1; i >= 0; i--) {
+ mLoaders.valueAt(i).start();
+ }
+ mStarted = true;
+ }
+
+ void doStop() {
+ for (int i = mLoaders.size()-1; i >= 0; i--) {
+ mLoaders.valueAt(i).stop();
+ }
+ mStarted = false;
+ }
+
+ void doRetain() {
+ mRetaining = true;
+ mStarted = false;
+ for (int i = mLoaders.size()-1; i >= 0; i--) {
+ mLoaders.valueAt(i).retain();
+ }
+ }
+
+ void finishRetain() {
+ mRetaining = false;
+ for (int i = mLoaders.size()-1; i >= 0; i--) {
+ mLoaders.valueAt(i).finishRetain();
+ }
+ }
+
+ void doDestroy() {
+ if (!mRetaining) {
+ for (int i = mLoaders.size()-1; i >= 0; i--) {
+ mLoaders.valueAt(i).destroy();
+ }
+ }
+
+ for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
+ mInactiveLoaders.valueAt(i).destroy();
+ }
+ mInactiveLoaders.clear();
+ }
+}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index d72dda7..ccc9ae3 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -2,6 +2,7 @@
import dalvik.system.PathClassLoader;
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -11,12 +12,16 @@
import android.os.Environment;
import android.os.Looper;
import android.os.MessageQueue;
+import android.util.AttributeSet;
import android.view.InputChannel;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
+import android.view.WindowManager;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.inputmethod.InputMethodManager;
import java.io.File;
@@ -24,15 +29,26 @@
* Convenience for implementing an activity that will be implemented
* purely in native code. That is, a game (or game-like thing).
*/
-public class NativeActivity extends Activity implements SurfaceHolder.Callback,
- InputQueue.Callback {
+public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
+ InputQueue.Callback, OnGlobalLayoutListener {
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
+ private NativeContentView mNativeContentView;
+ private InputMethodManager mIMM;
+
private int mNativeHandle;
private InputQueue mCurInputQueue;
private SurfaceHolder mCurSurfaceHolder;
+ final int[] mLocation = new int[2];
+ int mLastContentX;
+ int mLastContentY;
+ int mLastContentWidth;
+ int mLastContentHeight;
+
+ private boolean mDispatchingUnhandledKey;
+
private boolean mDestroyed;
private native int loadNativeCode(String path, MessageQueue queue,
@@ -49,18 +65,44 @@
private native void onSurfaceCreatedNative(int handle, Surface surface);
private native void onSurfaceChangedNative(int handle, Surface surface,
int format, int width, int height);
+ private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
private native void onSurfaceDestroyedNative(int handle);
private native void onInputChannelCreatedNative(int handle, InputChannel channel);
private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
+ private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
+ private native void dispatchKeyEventNative(int handle, KeyEvent event);
+
+ static class NativeContentView extends View {
+ NativeActivity mActivity;
+
+ public NativeContentView(Context context) {
+ super(context);
+ }
+
+ public NativeContentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
String libname = "main";
ActivityInfo ai;
+ mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+
getWindow().takeSurface(this);
getWindow().takeInputQueue(this);
getWindow().setFormat(PixelFormat.RGB_565);
+ getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+
+ mNativeContentView = new NativeContentView(this);
+ mNativeContentView.mActivity = this;
+ setContentView(mNativeContentView);
+ mNativeContentView.requestFocus();
+ mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this);
try {
ai = getPackageManager().getActivityInfo(
@@ -165,6 +207,18 @@
}
}
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mDispatchingUnhandledKey) {
+ return super.dispatchKeyEvent(event);
+ } else {
+ // Key events from the IME do not go through the input channel;
+ // we need to intercept them here to hand to the application.
+ dispatchKeyEventNative(mNativeHandle, event);
+ return true;
+ }
+ }
+
public void surfaceCreated(SurfaceHolder holder) {
if (!mDestroyed) {
mCurSurfaceHolder = holder;
@@ -179,6 +233,13 @@
}
}
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ if (!mDestroyed) {
+ mCurSurfaceHolder = holder;
+ onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface());
+ }
+ }
+
public void surfaceDestroyed(SurfaceHolder holder) {
mCurSurfaceHolder = null;
if (!mDestroyed) {
@@ -200,10 +261,32 @@
}
}
+ public void onGlobalLayout() {
+ mNativeContentView.getLocationInWindow(mLocation);
+ int w = mNativeContentView.getWidth();
+ int h = mNativeContentView.getHeight();
+ if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY
+ || w != mLastContentWidth || h != mLastContentHeight) {
+ mLastContentX = mLocation[0];
+ mLastContentY = mLocation[1];
+ mLastContentWidth = w;
+ mLastContentHeight = h;
+ if (!mDestroyed) {
+ onContentRectChangedNative(mNativeHandle, mLastContentX,
+ mLastContentY, mLastContentWidth, mLastContentHeight);
+ }
+ }
+ }
+
void dispatchUnhandledKeyEvent(KeyEvent event) {
- View decor = getWindow().getDecorView();
- if (decor != null) {
- decor.dispatchKeyEvent(event);
+ try {
+ mDispatchingUnhandledKey = true;
+ View decor = getWindow().getDecorView();
+ if (decor != null) {
+ decor.dispatchKeyEvent(event);
+ }
+ } finally {
+ mDispatchingUnhandledKey = false;
}
}
@@ -214,4 +297,12 @@
void setWindowFormat(int format) {
getWindow().setFormat(format);
}
+
+ void showIme(int mode) {
+ mIMM.showSoftInput(mNativeContentView, mode);
+ }
+
+ void hideIme(int mode) {
+ mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode);
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d4ce6a1..3f12bf9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -22,7 +22,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.TypedValue;
import android.widget.RemoteViews;
@@ -149,7 +148,7 @@
* instances as possible.</td>
* </tr>
* </table>
- *
+ *
* @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
*/
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
@@ -163,7 +162,7 @@
/**
* Sent when an instance of an AppWidget is removed from the last host.
- *
+ *
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
*/
public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -172,7 +171,7 @@
* Sent when an instance of an AppWidget is added to a host for the first time.
* This broadcast is sent at boot time if there is a AppWidgetHost installed with
* an instance for this provider.
- *
+ *
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
*/
public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
@@ -183,20 +182,21 @@
* @see AppWidgetProviderInfo
*/
public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
-
+
/**
* Field for the manifest meta-data tag used to indicate any previous name for the
* app widget receiver.
*
* @see AppWidgetProviderInfo
- *
+ *
* @hide Pending API approval
*/
public static final String META_DATA_APPWIDGET_OLD_NAME = "android.appwidget.oldName";
- static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache = new WeakHashMap();
+ static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
+ new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
static IAppWidgetService sService;
-
+
Context mContext;
private DisplayMetrics mDisplayMetrics;
@@ -219,7 +219,7 @@
}
if (result == null) {
result = new AppWidgetManager(context);
- sManagerCache.put(context, new WeakReference(result));
+ sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
}
return result;
}
@@ -292,7 +292,15 @@
*/
public List<AppWidgetProviderInfo> getInstalledProviders() {
try {
- return sService.getInstalledProviders();
+ List<AppWidgetProviderInfo> providers = sService.getInstalledProviders();
+ for (AppWidgetProviderInfo info : providers) {
+ // Converting complex to dp.
+ info.minWidth =
+ TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
+ info.minHeight =
+ TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+ }
+ return providers;
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -310,7 +318,7 @@
AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
if (info != null) {
// Converting complex to dp.
- info.minWidth =
+ info.minWidth =
TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
info.minHeight =
TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
@@ -344,7 +352,7 @@
/**
* Get the list of appWidgetIds that have been bound to the given AppWidget
* provider.
- *
+ *
* @param provider The {@link android.content.BroadcastReceiver} that is the
* AppWidget provider to find appWidgetIds for.
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 86ddee4..b49d801 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1561,6 +1561,15 @@
public static final String UI_MODE_SERVICE = "uimode";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.net.DownloadManager} for requesting HTTP downloads.
+ *
+ * @see #getSystemService
+ * @hide (TODO) for now
+ */
+ public static final String DOWNLOAD_SERVICE = "download";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index e230394..01bf968 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -50,7 +50,9 @@
public void deliverResult(Cursor cursor) {
if (mStopped) {
// An async query came in while the loader is stopped
- cursor.close();
+ if (cursor != null) {
+ cursor.close();
+ }
return;
}
mCursor = cursor;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9939478..160a481 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -319,4 +319,6 @@
boolean setInstallLocation(int loc);
int getInstallLocation();
+
+ void setPackageObbPath(String packageName, String path);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1a5b419..15a446b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2193,4 +2193,17 @@
*/
public abstract void movePackage(
String packageName, IPackageMoveObserver observer, int flags);
+
+ /**
+ * Sets the Opaque Binary Blob (OBB) file location.
+ * <p>
+ * NOTE: The existence or format of this file is not currently checked, but
+ * it may be in the future.
+ *
+ * @param packageName Name of the package with which to associate the .obb
+ * file
+ * @param path Path on the filesystem to the .obb file
+ * @hide
+ */
+ public abstract void setPackageObbPath(String packageName, String path);
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 3c5a029..c0226f8 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -613,7 +613,10 @@
// This thread didn't already have the lock, so begin a database
// transaction now.
- if (exclusive) {
+ // STOPSHIP - uncomment the following 1 line
+ // if (exclusive) {
+ // STOPSHIP - remove the following 1 line
+ if (exclusive && mConnectionPool == null) {
execSQL("BEGIN EXCLUSIVE;");
} else {
execSQL("BEGIN IMMEDIATE;");
@@ -946,7 +949,10 @@
sBlockSize = new StatFs("/data").getBlockSize();
}
sqliteDatabase.setPageSize(sBlockSize);
- sqliteDatabase.setJournalMode(path, "TRUNCATE");
+ //STOPSHIP - uncomment the following line
+ //sqliteDatabase.setJournalMode(path, "TRUNCATE");
+ // STOPSHIP remove the following lines
+ sqliteDatabase.enableWriteAheadLogging();
// add this database to the list of databases opened in this process
ActiveDatabases.addActiveDatabase(sqliteDatabase);
@@ -1041,6 +1047,7 @@
closeClosable();
// finalize ALL statements queued up so far
closePendingStatements();
+ releaseCustomFunctions();
// close this database instance - regardless of its reference count value
dbclose();
if (mConnectionPool != null) {
@@ -1077,6 +1084,54 @@
private native void dbclose();
/**
+ * A callback interface for a custom sqlite3 function.
+ * This can be used to create a function that can be called from
+ * sqlite3 database triggers.
+ * @hide
+ */
+ public interface CustomFunction {
+ public void callback(String[] args);
+ }
+
+ /**
+ * Registers a CustomFunction callback as a function that can be called from
+ * sqlite3 database triggers.
+ * @param name the name of the sqlite3 function
+ * @param numArgs the number of arguments for the function
+ * @param function callback to call when the function is executed
+ * @hide
+ */
+ public void addCustomFunction(String name, int numArgs, CustomFunction function) {
+ verifyDbIsOpen();
+ synchronized (mCustomFunctions) {
+ int ref = native_addCustomFunction(name, numArgs, function);
+ if (ref != 0) {
+ // save a reference to the function for cleanup later
+ mCustomFunctions.add(new Integer(ref));
+ } else {
+ throw new SQLiteException("failed to add custom function " + name);
+ }
+ }
+ }
+
+ private void releaseCustomFunctions() {
+ synchronized (mCustomFunctions) {
+ for (int i = 0; i < mCustomFunctions.size(); i++) {
+ Integer function = mCustomFunctions.get(i);
+ native_releaseCustomFunction(function.intValue());
+ }
+ mCustomFunctions.clear();
+ }
+ }
+
+ // list of CustomFunction references so we can clean up when the database closes
+ private final ArrayList<Integer> mCustomFunctions =
+ new ArrayList<Integer>();
+
+ private native int native_addCustomFunction(String name, int numArgs, CustomFunction function);
+ private native void native_releaseCustomFunction(int function);
+
+ /**
* Gets the database version.
*
* @return the database version
@@ -1953,12 +2008,17 @@
}
@Override
- protected void finalize() {
- if (isOpen()) {
- Log.e(TAG, "close() was never explicitly called on database '" +
- mPath + "' ", mStackTrace);
- closeClosable();
- onAllReferencesReleased();
+ protected void finalize() throws Throwable {
+ try {
+ if (isOpen()) {
+ Log.e(TAG, "close() was never explicitly called on database '" +
+ mPath + "' ", mStackTrace);
+ closeClosable();
+ onAllReferencesReleased();
+ releaseCustomFunctions();
+ }
+ } finally {
+ super.finalize();
}
}
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index fb3f428..017b65f 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -17,6 +17,9 @@
package android.database.sqlite;
import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
/**
* A base class for compiled SQLite programs.
@@ -29,7 +32,7 @@
private static final String TAG = "SQLiteProgram";
/** the type of sql statement being processed by this object */
- private static final int SELECT_STMT = 1;
+ /* package */ static final int SELECT_STMT = 1;
private static final int UPDATE_STMT = 2;
private static final int OTHER_STMT = 3;
@@ -63,24 +66,46 @@
@Deprecated
protected int nStatement = 0;
+ /**
+ * In the case of {@link SQLiteStatement}, this member stores the bindargs passed
+ * to the following methods, instead of actually doing the binding.
+ * <ul>
+ * <li>{@link #bindBlob(int, byte[])}</li>
+ * <li>{@link #bindDouble(int, double)}</li>
+ * <li>{@link #bindLong(int, long)}</li>
+ * <li>{@link #bindNull(int)}</li>
+ * <li>{@link #bindString(int, String)}</li>
+ * </ul>
+ * <p>
+ * Each entry in the array is a Pair of
+ * <ol>
+ * <li>bind arg position number</li>
+ * <li>the value to be bound to the bindarg</li>
+ * </ol>
+ * <p>
+ * It is lazily initialized in the above bind methods
+ * and it is cleared in {@link #clearBindings()} method.
+ * <p>
+ * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this
+ */
+ private ArrayList<Pair<Integer, Object>> bindArgs = null;
+
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
this(db, sql, true);
}
/* package */ SQLiteProgram(SQLiteDatabase db, String sql, boolean compileFlag) {
mSql = sql.trim();
- attachObjectToDatabase(db);
+ db.acquireReference();
+ db.addSQLiteClosable(this);
+ mDatabase = db;
+ nHandle = db.mNativeHandle;
if (compileFlag) {
compileSql();
}
}
private void compileSql() {
- if (nStatement > 0) {
- // already compiled.
- return;
- }
-
// only cache CRUD statements
if (getSqlStatementType(mSql) == OTHER_STMT) {
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
@@ -125,7 +150,7 @@
nStatement = mCompiledSql.nStatement;
}
- private int getSqlStatementType(String sql) {
+ /* package */ int getSqlStatementType(String sql) {
if (mSql.length() < 6) {
return OTHER_STMT;
}
@@ -141,42 +166,11 @@
return OTHER_STMT;
}
- private synchronized void attachObjectToDatabase(SQLiteDatabase db) {
- db.acquireReference();
- db.addSQLiteClosable(this);
- mDatabase = db;
- nHandle = db.mNativeHandle;
- }
-
- private synchronized void detachObjectFromDatabase() {
- mDatabase.removeSQLiteClosable(this);
- mDatabase.releaseReference();
- }
-
- /* package */ synchronized void verifyDbAndCompileSql() {
- mDatabase.verifyDbIsOpen();
- // use pooled database connection handles for SELECT SQL statements
- SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase
- : mDatabase.getDbConnection(mSql);
- if (!db.equals(mDatabase)) {
- // the database connection handle to be used is not the same as the one supplied
- // in the constructor. do some housekeeping.
- detachObjectFromDatabase();
- attachObjectToDatabase(db);
- }
- // compile the sql statement
- mDatabase.lock();
- try {
- compileSql();
- } finally {
- mDatabase.unlock();
- }
- }
-
@Override
protected void onAllReferencesReleased() {
releaseCompiledSqlIfNotInCache();
- detachObjectFromDatabase();
+ mDatabase.removeSQLiteClosable(this);
+ mDatabase.releaseReference();
}
@Override
@@ -247,11 +241,17 @@
* @param index The 1-based index to the parameter to bind null to
*/
public void bindNull(int index) {
+ mDatabase.verifyDbIsOpen();
synchronized (this) {
- verifyDbAndCompileSql();
acquireReference();
try {
- native_bind_null(index);
+ if (this.nStatement == 0) {
+ // since the SQL statement is not compiled, don't do the binding yet.
+ // can be done before executing the SQL statement
+ addToBindArgs(index, null);
+ } else {
+ native_bind_null(index);
+ }
} finally {
releaseReference();
}
@@ -266,11 +266,15 @@
* @param value The value to bind
*/
public void bindLong(int index, long value) {
+ mDatabase.verifyDbIsOpen();
synchronized (this) {
- verifyDbAndCompileSql();
acquireReference();
try {
- native_bind_long(index, value);
+ if (this.nStatement == 0) {
+ addToBindArgs(index, value);
+ } else {
+ native_bind_long(index, value);
+ }
} finally {
releaseReference();
}
@@ -285,11 +289,15 @@
* @param value The value to bind
*/
public void bindDouble(int index, double value) {
+ mDatabase.verifyDbIsOpen();
synchronized (this) {
- verifyDbAndCompileSql();
acquireReference();
try {
- native_bind_double(index, value);
+ if (this.nStatement == 0) {
+ addToBindArgs(index, value);
+ } else {
+ native_bind_double(index, value);
+ }
} finally {
releaseReference();
}
@@ -307,11 +315,15 @@
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
+ mDatabase.verifyDbIsOpen();
synchronized (this) {
- verifyDbAndCompileSql();
acquireReference();
try {
- native_bind_string(index, value);
+ if (this.nStatement == 0) {
+ addToBindArgs(index, value);
+ } else {
+ native_bind_string(index, value);
+ }
} finally {
releaseReference();
}
@@ -329,11 +341,15 @@
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
+ mDatabase.verifyDbIsOpen();
synchronized (this) {
- verifyDbAndCompileSql();
acquireReference();
try {
- native_bind_blob(index, value);
+ if (this.nStatement == 0) {
+ addToBindArgs(index, value);
+ } else {
+ native_bind_blob(index, value);
+ }
} finally {
releaseReference();
}
@@ -345,6 +361,7 @@
*/
public void clearBindings() {
synchronized (this) {
+ bindArgs = null;
if (this.nStatement == 0) {
return;
}
@@ -363,6 +380,7 @@
*/
public void close() {
synchronized (this) {
+ bindArgs = null;
if (nHandle == 0 || !mDatabase.isOpen()) {
return;
}
@@ -370,6 +388,34 @@
}
}
+ private synchronized void addToBindArgs(int index, Object value) {
+ if (bindArgs == null) {
+ bindArgs = new ArrayList<Pair<Integer, Object>>();
+ }
+ bindArgs.add(new Pair<Integer, Object>(index, value));
+ }
+
+ /* package */ synchronized void compileAndbindAllArgs() {
+ assert nStatement == 0;
+ compileSql();
+ if (bindArgs == null) {
+ return;
+ }
+ for (Pair<Integer, Object> p : bindArgs) {
+ if (p.second == null) {
+ native_bind_null(p.first);
+ } else if (p.second instanceof Long) {
+ native_bind_long(p.first, (Long)p.second);
+ } else if (p.second instanceof Double) {
+ native_bind_double(p.first, (Double)p.second);
+ } else if (p.second instanceof byte[]) {
+ native_bind_blob(p.first, (byte[])p.second);
+ } else {
+ native_bind_string(p.first, (String)p.second);
+ }
+ }
+ }
+
/**
* @deprecated This method is deprecated and must not be used.
* Compiles SQL into a SQLite program.
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 9664593..b902803 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -35,6 +35,8 @@
private static final boolean READ = true;
private static final boolean WRITE = false;
+ private SQLiteDatabase mOrigDb;
+
/**
* Don't use SQLiteStatement constructor directly, please use
* {@link SQLiteDatabase#compileStatement(String)}
@@ -53,12 +55,14 @@
* some reason
*/
public void execute() {
- long timeStart = acquireAndLock(WRITE);
- try {
- native_execute();
- mDatabase.logTimeStat(mSql, timeStart);
- } finally {
- releaseAndUnlock();
+ synchronized(this) {
+ long timeStart = acquireAndLock(WRITE);
+ try {
+ native_execute();
+ mDatabase.logTimeStat(mSql, timeStart);
+ } finally {
+ releaseAndUnlock();
+ }
}
}
@@ -72,13 +76,15 @@
* some reason
*/
public long executeInsert() {
- long timeStart = acquireAndLock(WRITE);
- try {
- native_execute();
- mDatabase.logTimeStat(mSql, timeStart);
- return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
- } finally {
- releaseAndUnlock();
+ synchronized(this) {
+ long timeStart = acquireAndLock(WRITE);
+ try {
+ native_execute();
+ mDatabase.logTimeStat(mSql, timeStart);
+ return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
+ } finally {
+ releaseAndUnlock();
+ }
}
}
@@ -91,13 +97,15 @@
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
public long simpleQueryForLong() {
- long timeStart = acquireAndLock(READ);
- try {
- long retValue = native_1x1_long();
- mDatabase.logTimeStat(mSql, timeStart);
- return retValue;
- } finally {
- releaseAndUnlock();
+ synchronized(this) {
+ long timeStart = acquireAndLock(READ);
+ try {
+ long retValue = native_1x1_long();
+ mDatabase.logTimeStat(mSql, timeStart);
+ return retValue;
+ } finally {
+ releaseAndUnlock();
+ }
}
}
@@ -110,13 +118,15 @@
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
public String simpleQueryForString() {
- long timeStart = acquireAndLock(READ);
- try {
- String retValue = native_1x1_string();
- mDatabase.logTimeStat(mSql, timeStart);
- return retValue;
- } finally {
- releaseAndUnlock();
+ synchronized(this) {
+ long timeStart = acquireAndLock(READ);
+ try {
+ String retValue = native_1x1_string();
+ mDatabase.logTimeStat(mSql, timeStart);
+ return retValue;
+ } finally {
+ releaseAndUnlock();
+ }
}
}
@@ -125,6 +135,7 @@
* this method does the following:
* <ul>
* <li>make sure the database is open</li>
+ * <li>get a database connection from the connection pool,if possible</li>
* <li>notifies {@link BlockGuard} of read/write</li>
* <li>get lock on the database</li>
* <li>acquire reference on this object</li>
@@ -135,7 +146,14 @@
* methods in this class.
*/
private long acquireAndLock(boolean rwFlag) {
- verifyDbAndCompileSql();
+ // use pooled database connection handles for SELECT SQL statements
+ mDatabase.verifyDbIsOpen();
+ SQLiteDatabase db = (getSqlStatementType(mSql) != SELECT_STMT) ? mDatabase
+ : mDatabase.getDbConnection(mSql);
+ // use the database connection obtained above
+ mOrigDb = mDatabase;
+ mDatabase = db;
+ nHandle = mDatabase.mNativeHandle;
if (rwFlag == WRITE) {
BlockGuard.getThreadPolicy().onWriteToDisk();
} else {
@@ -145,6 +163,7 @@
mDatabase.lock();
acquireReference();
mDatabase.closePendingStatements();
+ compileAndbindAllArgs();
return startTime;
}
@@ -158,6 +177,9 @@
// release the compiled sql statement so that the caller's SQLiteStatement no longer
// has a hard reference to a database object that may get deallocated at any point.
releaseCompiledSqlIfNotInCache();
+ // restore the database connection handle to the original value
+ mDatabase = mOrigDb;
+ nHandle = mDatabase.mNativeHandle;
}
private final native void native_execute();
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 70519ff..aaf3898 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -84,14 +84,14 @@
* sensor itself (<b>Fs</b>) using the relation:
* </p>
*
- * <b><center>Ad = - ·Fs / mass</center></b>
+ * <b><center>Ad = - ∑Fs / mass</center></b>
*
* <p>
* In particular, the force of gravity is always influencing the measured
* acceleration:
* </p>
*
- * <b><center>Ad = -g - ·F / mass</center></b>
+ * <b><center>Ad = -g - ∑F / mass</center></b>
*
* <p>
* For this reason, when the device is sitting on a table (and obviously not
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
new file mode 100644
index 0000000..00b6864
--- /dev/null
+++ b/core/java/android/net/DownloadManager.java
@@ -0,0 +1,729 @@
+/*
+ * 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;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.os.ParcelFileDescriptor;
+import android.provider.Downloads;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The download manager is a system service that handles long-running HTTP downloads. Clients may
+ * request that a URI be downloaded to a particular destination file. The download manager will
+ * conduct the download in the background, taking care of HTTP interactions and retrying downloads
+ * after failures or across connectivity changes and system reboots.
+ *
+ * Instances of this class should be obtained through
+ * {@link android.content.Context#getSystemService(String)} by passing
+ * {@link android.content.Context#DOWNLOAD_SERVICE}.
+ *
+ * @hide
+ */
+public class DownloadManager {
+ /**
+ * An identifier for a particular download, unique across the system. Clients use this ID to
+ * make subsequent calls related to the download.
+ */
+ public final static String COLUMN_ID = "id";
+
+ /**
+ * The client-supplied title for this download. This will be displayed in system notifications,
+ * if enabled.
+ */
+ public final static String COLUMN_TITLE = "title";
+
+ /**
+ * The client-supplied description of this download. This will be displayed in system
+ * notifications, if enabled.
+ */
+ public final static String COLUMN_DESCRIPTION = "description";
+
+ /**
+ * URI to be downloaded.
+ */
+ public final static String COLUMN_URI = "uri";
+
+ /**
+ * Internet Media Type of the downloaded file. This will be filled in based on the server's
+ * response once the download has started.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
+ */
+ public final static String COLUMN_MEDIA_TYPE = "media_type";
+
+ /**
+ * Total size of the download in bytes. This will be filled in once the download starts.
+ */
+ public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
+
+ /**
+ * Uri where downloaded file will be stored. If a destination is supplied by client, that URI
+ * will be used here. Otherwise, the value will be filled in with a generated URI once the
+ * download has started.
+ */
+ public final static String COLUMN_LOCAL_URI = "local_uri";
+
+ /**
+ * Current status of the download, as one of the STATUS_* constants.
+ */
+ public final static String COLUMN_STATUS = "status";
+
+ /**
+ * Indicates the type of error that occurred, when {@link #COLUMN_STATUS} is
+ * {@link #STATUS_FAILED}. If an HTTP error occurred, this will hold the HTTP status code as
+ * defined in RFC 2616. Otherwise, it will hold one of the ERROR_* constants.
+ *
+ * If {@link #COLUMN_STATUS} is not {@link #STATUS_FAILED}, this column's value is undefined.
+ *
+ * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">RFC 2616
+ * status codes</a>
+ */
+ public final static String COLUMN_ERROR_CODE = "error_code";
+
+ /**
+ * Number of bytes download so far.
+ */
+ public final static String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far";
+
+ /**
+ * Timestamp when the download was last modified, in {@link System#currentTimeMillis
+ * System.currentTimeMillis()} (wall clock time in UTC).
+ */
+ public final static String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
+
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is waiting to start.
+ */
+ public final static int STATUS_PENDING = 1 << 0;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is currently running.
+ */
+ public final static int STATUS_RUNNING = 1 << 1;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download is waiting to retry or resume.
+ */
+ public final static int STATUS_PAUSED = 1 << 2;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download has successfully completed.
+ */
+ public final static int STATUS_SUCCESSFUL = 1 << 3;
+
+ /**
+ * Value of {@link #COLUMN_STATUS} when the download has failed (and will not be retried).
+ */
+ public final static int STATUS_FAILED = 1 << 4;
+
+
+ /**
+ * Value of COLUMN_ERROR_CODE when the download has completed with an error that doesn't fit
+ * under any other error code.
+ */
+ public final static int ERROR_UNKNOWN = 1000;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when a storage issue arises which doesn't fit under any
+ * other error code. Use the more specific {@link #ERROR_INSUFFICIENT_SPACE} and
+ * {@link #ERROR_DEVICE_NOT_FOUND} when appropriate.
+ */
+ public final static int ERROR_FILE_ERROR = 1001;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when an HTTP code was received that download manager
+ * can't handle.
+ */
+ public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when an error receiving or processing data occurred at
+ * the HTTP level.
+ */
+ public final static int ERROR_HTTP_DATA_ERROR = 1004;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when there were too many redirects.
+ */
+ public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when there was insufficient storage space. Typically,
+ * this is because the SD card is full.
+ */
+ public final static int ERROR_INSUFFICIENT_SPACE = 1006;
+
+ /**
+ * Value of {@link #COLUMN_ERROR_CODE} when no external storage device was found. Typically,
+ * this is because the SD card is not mounted.
+ */
+ public final static int ERROR_DEVICE_NOT_FOUND = 1007;
+
+
+ // this array must contain all public columns
+ private static final String[] COLUMNS = new String[] {
+ COLUMN_ID,
+ COLUMN_TITLE,
+ COLUMN_DESCRIPTION,
+ COLUMN_URI,
+ COLUMN_MEDIA_TYPE,
+ COLUMN_TOTAL_SIZE_BYTES,
+ COLUMN_LOCAL_URI,
+ COLUMN_STATUS,
+ COLUMN_ERROR_CODE,
+ COLUMN_BYTES_DOWNLOADED_SO_FAR,
+ COLUMN_LAST_MODIFIED_TIMESTAMP
+ };
+
+ // columns to request from DownloadProvider
+ private static final String[] UNDERLYING_COLUMNS = new String[] {
+ Downloads.Impl._ID,
+ Downloads.COLUMN_TITLE,
+ Downloads.COLUMN_DESCRIPTION,
+ Downloads.COLUMN_URI,
+ Downloads.COLUMN_MIME_TYPE,
+ Downloads.COLUMN_TOTAL_BYTES,
+ Downloads._DATA,
+ Downloads.COLUMN_STATUS,
+ Downloads.COLUMN_CURRENT_BYTES,
+ Downloads.COLUMN_LAST_MODIFICATION,
+ };
+
+ private static final Set<String> LONG_COLUMNS = new HashSet<String>(
+ Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_ERROR_CODE,
+ COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
+
+ /**
+ * This class contains all the information necessary to request a new download. The URI is the
+ * only required parameter.
+ */
+ public static class Request {
+ /**
+ * Bit flag for setShowNotification indicated a notification should be created while the
+ * download is running.
+ */
+ private static final int NOTIFICATION_WHEN_RUNNING = 1;
+
+ Uri mUri;
+ Uri mDestinationUri;
+ Map<String, String> mRequestHeaders = new HashMap<String, String>();
+ String mTitle;
+ String mDescription;
+ int mNotificationFlags;
+
+ private String mMediaType;
+
+ /**
+ * @param uri the HTTP URI to download.
+ */
+ public Request(Uri uri) {
+ if (uri == null) {
+ throw new NullPointerException();
+ }
+ String scheme = uri.getScheme();
+ if (scheme == null || !scheme.equals("http")) {
+ throw new IllegalArgumentException("Can only download HTTP URIs: " + uri);
+ }
+ mUri = uri;
+ }
+
+ /**
+ * Set the local destination for the downloaded data. Must be a file URI to a path on
+ * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
+ * permission.
+ *
+ * By default, downloads are saved to a generated file in the download cache and may be
+ * deleted by the download manager at any time.
+ *
+ * @return this object
+ */
+ public Request setDestinationUri(Uri uri) {
+ mDestinationUri = uri;
+ return this;
+ }
+
+ /**
+ * Set an HTTP header to be included with the download request.
+ * @param header HTTP header name
+ * @param value header value
+ * @return this object
+ */
+ public Request setRequestHeader(String header, String value) {
+ mRequestHeaders.put(header, value);
+ return this;
+ }
+
+ /**
+ * Set the title of this download, to be displayed in notifications (if enabled)
+ * @return this object
+ */
+ public Request setTitle(String title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Set a description of this download, to be displayed in notifications (if enabled)
+ * @return this object
+ */
+ public Request setDescription(String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Set the Internet Media Type of this download. This will override the media type declared
+ * in the server's response.
+ * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
+ * @return this object
+ */
+ public Request setMediaType(String mediaType) {
+ mMediaType = mediaType;
+ return this;
+ }
+
+ /**
+ * Control system notifications posted by the download manager for this download. If
+ * enabled, the download manager posts notifications about downloads through the system
+ * {@link android.app.NotificationManager}.
+ *
+ * @param flags any combination of the NOTIFICATION_* bit flags
+ * @return this object
+ */
+ public Request setShowNotification(int flags) {
+ mNotificationFlags = flags;
+ return this;
+ }
+
+ public Request setAllowedNetworkTypes(int flags) {
+ // TODO allowed networks support
+ throw new UnsupportedOperationException();
+ }
+
+ public Request setAllowedOverRoaming(boolean allowed) {
+ // TODO roaming support
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @return ContentValues to be passed to DownloadProvider.insert()
+ */
+ ContentValues toContentValues() {
+ ContentValues values = new ContentValues();
+ assert mUri != null;
+ values.put(Downloads.COLUMN_URI, mUri.toString());
+
+ if (mDestinationUri != null) {
+ values.put(Downloads.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
+ values.put(Downloads.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+ } else {
+ values.put(Downloads.COLUMN_DESTINATION,
+ Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE);
+ }
+
+ if (!mRequestHeaders.isEmpty()) {
+ encodeHttpHeaders(values);
+ }
+
+ putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
+ putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
+ putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
+
+ int visibility = Downloads.VISIBILITY_HIDDEN;
+ if ((mNotificationFlags & NOTIFICATION_WHEN_RUNNING) != 0) {
+ visibility = Downloads.VISIBILITY_VISIBLE;
+ }
+ values.put(Downloads.COLUMN_VISIBILITY, visibility);
+
+ return values;
+ }
+
+ private void encodeHttpHeaders(ContentValues values) {
+ int index = 0;
+ for (Map.Entry<String, String> entry : mRequestHeaders.entrySet()) {
+ String headerString = entry.getKey() + ": " + entry.getValue();
+ values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
+ index++;
+ }
+ }
+
+ private void putIfNonNull(ContentValues contentValues, String key, String value) {
+ if (value != null) {
+ contentValues.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * This class may be used to filter download manager queries.
+ */
+ public static class Query {
+ private Long mId;
+ private Integer mStatusFlags = null;
+
+ /**
+ * Include only the download with the given ID.
+ * @return this object
+ */
+ public Query setFilterById(long id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * Include only downloads with status matching any the given status flags.
+ * @param flags any combination of the STATUS_* bit flags
+ * @return this object
+ */
+ public Query setFilterByStatus(int flags) {
+ mStatusFlags = flags;
+ return this;
+ }
+
+ /**
+ * Run this query using the given ContentResolver.
+ * @param projection the projection to pass to ContentResolver.query()
+ * @return the Cursor returned by ContentResolver.query()
+ */
+ Cursor runQuery(ContentResolver resolver, String[] projection) {
+ Uri uri = Downloads.CONTENT_URI;
+ String selection = null;
+
+ if (mId != null) {
+ uri = Uri.withAppendedPath(uri, mId.toString());
+ }
+
+ if (mStatusFlags != null) {
+ List<String> parts = new ArrayList<String>();
+ if ((mStatusFlags & STATUS_PENDING) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_PENDING));
+ }
+ if ((mStatusFlags & STATUS_RUNNING) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_RUNNING));
+ }
+ if ((mStatusFlags & STATUS_PAUSED) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_PENDING_PAUSED));
+ parts.add(statusClause("=", Downloads.STATUS_RUNNING_PAUSED));
+ }
+ if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
+ parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
+ }
+ if ((mStatusFlags & STATUS_FAILED) != 0) {
+ parts.add("(" + statusClause(">=", 400)
+ + " AND " + statusClause("<", 600) + ")");
+ }
+ selection = joinStrings(" OR ", parts);
+ Log.w("DownloadManagerPublic", selection);
+ }
+ String orderBy = Downloads.COLUMN_LAST_MODIFICATION + " DESC";
+ return resolver.query(uri, projection, selection, null, orderBy);
+ }
+
+ private String joinStrings(String joiner, Iterable<String> parts) {
+ StringBuilder builder = new StringBuilder();
+ boolean first = true;
+ for (String part : parts) {
+ if (!first) {
+ builder.append(joiner);
+ }
+ builder.append(part);
+ first = false;
+ }
+ return builder.toString();
+ }
+
+ private String statusClause(String operator, int value) {
+ return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
+ }
+ }
+
+ private ContentResolver mResolver;
+
+ /**
+ * @hide
+ */
+ public DownloadManager(ContentResolver resolver) {
+ mResolver = resolver;
+ }
+
+ /**
+ * Enqueue a new download. The download will start automatically once the download manager is
+ * ready to execute it and connectivity is available.
+ *
+ * @param request the parameters specifying this download
+ * @return an ID for the download, unique across the system. This ID is used to make future
+ * calls related to this download.
+ */
+ public long enqueue(Request request) {
+ ContentValues values = request.toContentValues();
+ Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
+ long id = Long.parseLong(downloadUri.getLastPathSegment());
+ return id;
+ }
+
+ /**
+ * Cancel a download and remove it from the download manager. The download will be stopped if
+ * it was running, and it will no longer be accessible through the download manager. If a file
+ * was already downloaded, it will not be deleted.
+ *
+ * @param id the ID of the download
+ */
+ public void remove(long id) {
+ int numDeleted = mResolver.delete(getDownloadUri(id), null, null);
+ if (numDeleted == 0) {
+ throw new IllegalArgumentException("Download " + id + " does not exist");
+ }
+ }
+
+ /**
+ * Query the download manager about downloads that have been requested.
+ * @param query parameters specifying filters for this query
+ * @return a Cursor over the result set of downloads, with columns consisting of all the
+ * COLUMN_* constants.
+ */
+ public Cursor query(Query query) {
+ Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS);
+ return new CursorTranslator(underlyingCursor);
+ }
+
+ /**
+ * Open a downloaded file for reading. The download must have completed.
+ * @param id the ID of the download
+ * @return a read-only {@link ParcelFileDescriptor}
+ * @throws FileNotFoundException if the destination file does not already exist
+ */
+ public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
+ return mResolver.openFileDescriptor(getDownloadUri(id), "r");
+ }
+
+ /**
+ * Get the DownloadProvider URI for the download with the given ID.
+ */
+ private Uri getDownloadUri(long id) {
+ Uri downloadUri = Uri.withAppendedPath(Downloads.CONTENT_URI, Long.toString(id));
+ return downloadUri;
+ }
+
+ /**
+ * This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
+ * presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
+ * Some columns correspond directly to underlying values while others are computed from
+ * underlying data.
+ */
+ private static class CursorTranslator extends CursorWrapper {
+ public CursorTranslator(Cursor cursor) {
+ super(cursor);
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ return Arrays.asList(COLUMNS).indexOf(columnName);
+ }
+
+ @Override
+ public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
+ int index = getColumnIndex(columnName);
+ if (index == -1) {
+ throw new IllegalArgumentException();
+ }
+ return index;
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ int numColumns = COLUMNS.length;
+ if (columnIndex < 0 || columnIndex >= numColumns) {
+ throw new IllegalArgumentException("Invalid column index " + columnIndex + ", "
+ + numColumns + " columns exist");
+ }
+ return COLUMNS[columnIndex];
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ String[] returnColumns = new String[COLUMNS.length];
+ System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length);
+ return returnColumns;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return COLUMNS.length;
+ }
+
+ @Override
+ public byte[] getBlob(int columnIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double getDouble(int columnIndex) {
+ return getLong(columnIndex);
+ }
+
+ private boolean isLongColumn(String column) {
+ return LONG_COLUMNS.contains(column);
+ }
+
+ @Override
+ public float getFloat(int columnIndex) {
+ return (float) getDouble(columnIndex);
+ }
+
+ @Override
+ public int getInt(int columnIndex) {
+ return (int) getLong(columnIndex);
+ }
+
+ @Override
+ public long getLong(int columnIndex) {
+ return translateLong(getColumnName(columnIndex));
+ }
+
+ @Override
+ public short getShort(int columnIndex) {
+ return (short) getLong(columnIndex);
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ return translateString(getColumnName(columnIndex));
+ }
+
+ private String translateString(String column) {
+ if (isLongColumn(column)) {
+ return Long.toString(translateLong(column));
+ }
+ if (column.equals(COLUMN_TITLE)) {
+ return getUnderlyingString(Downloads.COLUMN_TITLE);
+ }
+ if (column.equals(COLUMN_DESCRIPTION)) {
+ return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
+ }
+ if (column.equals(COLUMN_URI)) {
+ return getUnderlyingString(Downloads.COLUMN_URI);
+ }
+ if (column.equals(COLUMN_MEDIA_TYPE)) {
+ return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
+ }
+ assert column.equals(COLUMN_LOCAL_URI);
+ return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString();
+ }
+
+ private long translateLong(String column) {
+ if (!isLongColumn(column)) {
+ // mimic behavior of underlying cursor -- most likely, throw NumberFormatException
+ return Long.valueOf(translateString(column));
+ }
+
+ if (column.equals(COLUMN_ID)) {
+ return getUnderlyingLong(Downloads.Impl._ID);
+ }
+ if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
+ return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
+ }
+ if (column.equals(COLUMN_STATUS)) {
+ return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+ }
+ if (column.equals(COLUMN_ERROR_CODE)) {
+ return translateErrorCode((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+ }
+ if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
+ return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
+ }
+ assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
+ return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
+ }
+
+ private long translateErrorCode(int status) {
+ if (translateStatus(status) != STATUS_FAILED) {
+ return 0; // arbitrary value when status is not an error
+ }
+ if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+ // HTTP status code
+ return status;
+ }
+
+ switch (status) {
+ case Downloads.STATUS_FILE_ERROR:
+ return ERROR_FILE_ERROR;
+
+ case Downloads.STATUS_UNHANDLED_HTTP_CODE:
+ case Downloads.STATUS_UNHANDLED_REDIRECT:
+ return ERROR_UNHANDLED_HTTP_CODE;
+
+ case Downloads.STATUS_HTTP_DATA_ERROR:
+ return ERROR_HTTP_DATA_ERROR;
+
+ case Downloads.STATUS_TOO_MANY_REDIRECTS:
+ return ERROR_TOO_MANY_REDIRECTS;
+
+ case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
+ return ERROR_INSUFFICIENT_SPACE;
+
+ case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
+ return ERROR_DEVICE_NOT_FOUND;
+
+ default:
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ private long getUnderlyingLong(String column) {
+ return super.getLong(super.getColumnIndex(column));
+ }
+
+ private String getUnderlyingString(String column) {
+ return super.getString(super.getColumnIndex(column));
+ }
+
+ private long translateStatus(int status) {
+ switch (status) {
+ case Downloads.STATUS_PENDING:
+ return STATUS_PENDING;
+
+ case Downloads.STATUS_RUNNING:
+ return STATUS_RUNNING;
+
+ case Downloads.STATUS_PENDING_PAUSED:
+ case Downloads.STATUS_RUNNING_PAUSED:
+ return STATUS_PAUSED;
+
+ case Downloads.STATUS_SUCCESS:
+ return STATUS_SUCCESSFUL;
+
+ default:
+ assert Downloads.isStatusError(status);
+ return STATUS_FAILED;
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 5fd5315..f067392 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -29,6 +29,7 @@
import com.android.internal.telephony.TelephonyIntents;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo;
+import android.net.NetworkProperties;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.text.TextUtils;
@@ -55,7 +56,7 @@
private boolean mTeardownRequested = false;
private Handler mTarget;
private Context mContext;
- private String mInterfaceName;
+ private NetworkProperties mNetworkProperties;
private boolean mPrivateDnsRouteSet = false;
private int mDefaultGatewayAddr = 0;
private boolean mDefaultRouteSet = false;
@@ -101,14 +102,6 @@
return sDnsPropNames;
}
- /**
- * Return the name of our network interface.
- * @return the name of our interface.
- */
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
public boolean isPrivateDnsRouteSet() {
return mPrivateDnsRouteSet;
}
@@ -211,9 +204,11 @@
}
setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
- if (mInterfaceName != null) {
- NetworkUtils.resetConnections(mInterfaceName);
+ if (mNetworkProperties != null) {
+ String iface = mNetworkProperties.getInterfaceName();
+ if (iface != null) NetworkUtils.resetConnections(iface);
}
+ // TODO - check this
// can't do this here - ConnectivityService needs it to clear stuff
// it's ok though - just leave it to be refreshed next time
// we connect.
@@ -229,9 +224,11 @@
setDetailedState(DetailedState.SUSPENDED, reason, apnName);
break;
case CONNECTED:
- mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
- if (mInterfaceName == null) {
- Log.d(TAG, "CONNECTED event did not supply interface name.");
+ mNetworkProperties = intent.getParcelableExtra(
+ Phone.DATA_NETWORK_PROPERTIES_KEY);
+ if (mNetworkProperties == null) {
+ Log.d(TAG,
+ "CONNECTED event did not supply network properties.");
}
setDetailedState(DetailedState.CONNECTED, reason, apnName);
break;
@@ -565,4 +562,8 @@
return null;
}
}
+
+ public NetworkProperties getNetworkProperties() {
+ return mNetworkProperties;
+ }
}
diff --git a/core/java/android/net/NetworkProperties.aidl b/core/java/android/net/NetworkProperties.aidl
new file mode 100644
index 0000000..07aac6e
--- /dev/null
+++ b/core/java/android/net/NetworkProperties.aidl
@@ -0,0 +1,22 @@
+/*
+**
+** Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved.
+** 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.net;
+
+parcelable NetworkProperties;
+
diff --git a/core/java/android/net/NetworkProperties.java b/core/java/android/net/NetworkProperties.java
new file mode 100644
index 0000000..56e1f1a
--- /dev/null
+++ b/core/java/android/net/NetworkProperties.java
@@ -0,0 +1,196 @@
+/*
+ * 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.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Describes the properties of a network interface or single address
+ * of an interface.
+ * TODO - consider adding optional fields like Apn and ApnType
+ * @hide
+ */
+public class NetworkProperties implements Parcelable {
+
+ private NetworkInterface mIface;
+ private Collection<InetAddress> mAddresses;
+ private Collection<InetAddress> mDnses;
+ private InetAddress mGateway;
+ private ProxyProperties mHttpProxy;
+
+ public NetworkProperties() {
+ clear();
+ }
+
+ public synchronized void setInterface(NetworkInterface iface) {
+ mIface = iface;
+ }
+ public synchronized NetworkInterface getInterface() {
+ return mIface;
+ }
+ public synchronized String getInterfaceName() {
+ return (mIface == null ? null : mIface.getName());
+ }
+
+ public synchronized void addAddress(InetAddress address) {
+ mAddresses.add(address);
+ }
+ public synchronized Collection<InetAddress> getAddresses() {
+ return mAddresses;
+ }
+
+ public synchronized void addDns(InetAddress dns) {
+ mDnses.add(dns);
+ }
+ public synchronized Collection<InetAddress> getDnses() {
+ return mDnses;
+ }
+
+ public synchronized void setGateway(InetAddress gateway) {
+ mGateway = gateway;
+ }
+ public synchronized InetAddress getGateway() {
+ return mGateway;
+ }
+
+ public synchronized void setHttpProxy(ProxyProperties proxy) {
+ mHttpProxy = proxy;
+ }
+ public synchronized ProxyProperties getHttpProxy() {
+ return mHttpProxy;
+ }
+
+ public synchronized void clear() {
+ mIface = null;
+ mAddresses = new ArrayList<InetAddress>();
+ mDnses = new ArrayList<InetAddress>();
+ mGateway = null;
+ mHttpProxy = null;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ public synchronized String toString() {
+ String ifaceName = (mIface == null ? "" : "InterfaceName: " + mIface.getName() + " ");
+
+ String ip = "IpAddresses: [";
+ for (InetAddress addr : mAddresses) ip += addr.toString() + ",";
+ ip += "] ";
+
+ String dns = "DnsAddresses: [";
+ for (InetAddress addr : mDnses) dns += addr.toString() + ",";
+ dns += "] ";
+
+ String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
+ String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.toString() + " ");
+
+ return ifaceName + ip + gateway + dns + proxy;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public synchronized void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(getInterfaceName());
+ dest.writeInt(mAddresses.size());
+ for(InetAddress a : mAddresses) {
+ dest.writeString(a.getHostName());
+ dest.writeByteArray(a.getAddress());
+ }
+ dest.writeInt(mDnses.size());
+ for(InetAddress d : mDnses) {
+ dest.writeString(d.getHostName());
+ dest.writeByteArray(d.getAddress());
+ }
+ if (mGateway != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mGateway.getHostName());
+ dest.writeByteArray(mGateway.getAddress());
+ } else {
+ dest.writeByte((byte)0);
+ }
+ if (mHttpProxy != null) {
+ dest.writeByte((byte)1);
+ dest.writeParcelable(mHttpProxy, flags);
+ } else {
+ dest.writeByte((byte)0);
+ }
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public static final Creator<NetworkProperties> CREATOR =
+ new Creator<NetworkProperties>() {
+ public NetworkProperties createFromParcel(Parcel in) {
+ NetworkProperties netProp = new NetworkProperties();
+ String iface = in.readString();
+ if (iface != null) {
+ try {
+ netProp.setInterface(NetworkInterface.getByName(iface));
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ int addressCount = in.readInt();
+ for (int i=0; i<addressCount; i++) {
+ try {
+ netProp.addAddress(InetAddress.getByAddress(in.readString(),
+ in.createByteArray()));
+ } catch (UnknownHostException e) { }
+ }
+ addressCount = in.readInt();
+ for (int i=0; i<addressCount; i++) {
+ try {
+ netProp.addDns(InetAddress.getByAddress(in.readString(),
+ in.createByteArray()));
+ } catch (UnknownHostException e) { }
+ }
+ if (in.readByte() == 1) {
+ try {
+ netProp.setGateway(InetAddress.getByAddress(in.readString(),
+ in.createByteArray()));
+ } catch (UnknownHostException e) {}
+ }
+ if (in.readByte() == 1) {
+ netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+ }
+ return netProp;
+ }
+
+ public NetworkProperties[] newArray(int size) {
+ return new NetworkProperties[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index cd8e7f1..44215e7 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -46,22 +46,17 @@
public NetworkInfo getNetworkInfo();
/**
+ * Fetch NetworkProperties for the network
+ */
+ public NetworkProperties getNetworkProperties();
+
+ /**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
*/
public String getTcpBufferSizesPropName();
/**
- * Return the DNS property names for this network.
- */
- public String[] getDnsPropNames();
-
- /**
- * Fetch interface name of the interface
- */
- public String getInterfaceName();
-
- /**
* Check if private DNS route is set for the network
*/
public boolean isPrivateDnsRouteSet();
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index a3ae01b..564bc1f 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -32,13 +32,37 @@
public native static int disableInterface(String interfaceName);
/** Add a route to the specified host via the named interface. */
- public native static int addHostRoute(String interfaceName, int hostaddr);
+ public static int addHostRoute(String interfaceName, InetAddress hostaddr) {
+ int v4Int = v4StringToInt(hostaddr.getHostAddress());
+ if (v4Int != 0) {
+ return addHostRouteNative(interfaceName, v4Int);
+ } else {
+ return -1;
+ }
+ }
+ private native static int addHostRouteNative(String interfaceName, int hostaddr);
/** Add a default route for the named interface. */
- public native static int setDefaultRoute(String interfaceName, int gwayAddr);
+ public static int setDefaultRoute(String interfaceName, InetAddress gwayAddr) {
+ int v4Int = v4StringToInt(gwayAddr.getHostAddress());
+ if (v4Int != 0) {
+ return setDefaultRouteNative(interfaceName, v4Int);
+ } else {
+ return -1;
+ }
+ }
+ private native static int setDefaultRouteNative(String interfaceName, int hostaddr);
/** Return the gateway address for the default route for the named interface. */
- public native static int getDefaultRoute(String interfaceName);
+ public static InetAddress getDefaultRoute(String interfaceName) {
+ int addr = getDefaultRouteNative(interfaceName);
+ try {
+ return InetAddress.getByAddress(v4IntToArray(addr));
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+ private native static int getDefaultRouteNative(String interfaceName);
/** Remove host routes that uses the named interface. */
public native static int removeHostRoutes(String interfaceName);
@@ -105,27 +129,30 @@
private native static boolean configureNative(
String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
- /**
- * Look up a host name and return the result as an int. Works if the argument
- * is an IP address in dot notation. Obviously, this can only be used for IPv4
- * addresses.
- * @param hostname the name of the host (or the IP address)
- * @return the IP address as an {@code int} in network byte order
- */
- public static int lookupHost(String hostname) {
- InetAddress inetAddress;
+ // The following two functions are glue to tie the old int-based address scheme
+ // to the new InetAddress scheme. They should go away when we go fully to InetAddress
+ // TODO - remove when we switch fully to InetAddress
+ public static byte[] v4IntToArray(int addr) {
+ byte[] addrBytes = new byte[4];
+ addrBytes[0] = (byte)(addr & 0xff);
+ addrBytes[1] = (byte)((addr >> 8) & 0xff);
+ addrBytes[2] = (byte)((addr >> 16) & 0xff);
+ addrBytes[3] = (byte)((addr >> 24) & 0xff);
+ return addrBytes;
+ }
+
+ public static int v4StringToInt(String str) {
+ int result = 0;
+ String[] array = str.split("\\.");
+ if (array.length != 4) return 0;
try {
- inetAddress = InetAddress.getByName(hostname);
- } catch (UnknownHostException e) {
- return -1;
+ result = Integer.parseInt(array[3]);
+ result = (result << 8) + Integer.parseInt(array[2]);
+ result = (result << 8) + Integer.parseInt(array[1]);
+ result = (result << 8) + Integer.parseInt(array[0]);
+ } catch (NumberFormatException e) {
+ return 0;
}
- byte[] addrBytes;
- int addr;
- addrBytes = inetAddress.getAddress();
- addr = ((addrBytes[3] & 0xff) << 24)
- | ((addrBytes[2] & 0xff) << 16)
- | ((addrBytes[1] & 0xff) << 8)
- | (addrBytes[0] & 0xff);
- return addr;
+ return result;
}
}
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
new file mode 100644
index 0000000..6828dd4
--- /dev/null
+++ b/core/java/android/net/ProxyProperties.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A container class for the http proxy info
+ * @hide
+ */
+public class ProxyProperties implements Parcelable {
+
+ private InetAddress mProxy;
+ private int mPort;
+ private String mExclusionList;
+
+ public ProxyProperties() {
+ }
+
+ public synchronized InetAddress getAddress() {
+ return mProxy;
+ }
+ public synchronized void setAddress(InetAddress proxy) {
+ mProxy = proxy;
+ }
+
+ public synchronized int getPort() {
+ return mPort;
+ }
+ public synchronized void setPort(int port) {
+ mPort = port;
+ }
+
+ public synchronized String getExclusionList() {
+ return mExclusionList;
+ }
+ public synchronized void setExclusionList(String exclusionList) {
+ mExclusionList = exclusionList;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public synchronized void writeToParcel(Parcel dest, int flags) {
+ if (mProxy != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mProxy.getHostName());
+ dest.writeByteArray(mProxy.getAddress());
+ } else {
+ dest.writeByte((byte)0);
+ }
+ dest.writeInt(mPort);
+ dest.writeString(mExclusionList);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public static final Creator<ProxyProperties> CREATOR =
+ new Creator<ProxyProperties>() {
+ public ProxyProperties createFromParcel(Parcel in) {
+ ProxyProperties proxyProperties = new ProxyProperties();
+ if (in.readByte() == 1) {
+ try {
+ proxyProperties.setAddress(InetAddress.getByAddress(in.readString(),
+ in.createByteArray()));
+ } catch (UnknownHostException e) {}
+ }
+ proxyProperties.setPort(in.readInt());
+ proxyProperties.setExclusionList(in.readString());
+ return proxyProperties;
+ }
+
+ public ProxyProperties[] newArray(int size) {
+ return new ProxyProperties[size];
+ }
+ };
+
+};
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 9ad125b..31acb5b 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -247,13 +247,16 @@
/**
* {@inheritDoc}
*
- * <p>This method verifies the peer's certificate hostname after connecting.
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
*/
@Override
public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
- verifyHostname(s, host);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
@@ -305,7 +308,8 @@
/**
* {@inheritDoc}
*
- * <p>This method verifies the peer's certificate hostname after connecting.
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
*/
@Override
public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
@@ -313,20 +317,25 @@
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
host, port, localAddr, localPort);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
- verifyHostname(s, host);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
/**
* {@inheritDoc}
*
- * <p>This method verifies the peer's certificate hostname after connecting.
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
*/
@Override
public Socket createSocket(String host, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
- verifyHostname(s, host);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2e14667..d23b161 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -730,7 +730,7 @@
}
/**
- * Dump "hprof" data to the specified file. This will cause a GC.
+ * Dump "hprof" data to the specified file. This may cause a GC.
*
* @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
* @throws UnsupportedOperationException if the VM was built without
@@ -742,11 +742,24 @@
}
/**
- * Collect "hprof" and send it to DDMS. This will cause a GC.
+ * Like dumpHprofData(String), but takes an already-opened
+ * FileDescriptor to which the trace is written. The file name is also
+ * supplied simply for logging. Makes a dup of the file descriptor.
+ *
+ * Primarily for use by the "am" shell command.
+ *
+ * @hide
+ */
+ public static void dumpHprofData(String fileName, FileDescriptor fd)
+ throws IOException {
+ VMDebug.dumpHprofData(fileName, fd);
+ }
+
+ /**
+ * Collect "hprof" and send it to DDMS. This may cause a GC.
*
* @throws UnsupportedOperationException if the VM was built without
* HPROF support.
- *
* @hide
*/
public static void dumpHprofDataDdms() {
@@ -754,6 +767,13 @@
}
/**
+ * Writes native heap data to the specified file descriptor.
+ *
+ * @hide
+ */
+ public static native void dumpNativeHeap(FileDescriptor fd);
+
+ /**
* Returns the number of sent transactions from this process.
* @return The number of sent transactions or -1 if it could not read t.
*/
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index a23a5a7..10f1d2b 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -289,6 +289,7 @@
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC3);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC4);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 348e9e8..2a612fe 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -899,6 +899,12 @@
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
/**
+ * This download will be saved to the location given by the file URI in
+ * {@link #COLUMN_FILE_NAME_HINT}.
+ */
+ public static final int DESTINATION_FILE_URI = 4;
+
+ /**
* This download is allowed to run.
*/
public static final int CONTROL_RUN = 0;
@@ -1109,5 +1115,26 @@
* This download doesn't show in the UI or in the notifications.
*/
public static final int VISIBILITY_HIDDEN = 2;
+
+ /**
+ * Constants related to HTTP request headers associated with each download.
+ */
+ public static class RequestHeaders {
+ public static final String HEADERS_DB_TABLE = "request_headers";
+ public static final String COLUMN_DOWNLOAD_ID = "download_id";
+ public static final String COLUMN_HEADER = "header";
+ public static final String COLUMN_VALUE = "value";
+
+ /**
+ * Path segment to add to a download URI to retrieve request headers
+ */
+ public static final String URI_SEGMENT = "headers";
+
+ /**
+ * Prefix for ContentValues keys that contain HTTP header lines, to be passed to
+ * DownloadProvider.insert().
+ */
+ public static final String INSERT_KEY_PREFIX = "http_header_";
+ }
}
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 9a3c618..293d31c 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -237,8 +237,67 @@
* <P>Type: TEXT</P>
*/
public static final String MIME_TYPE = "mime_type";
+
+ /**
+ * The MTP object handle of a newly transfered file.
+ * Used internally by the MediaScanner
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String MTP_OBJECT_HANDLE = "mtp_object_handle";
}
+
+
+ /**
+ * Media provider interface used by MTP implementation.
+ * @hide
+ */
+ public static final class MtpObjects {
+
+ public static Uri getContentUri(String volumeName) {
+ return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+ "/object");
+ }
+
+ public static final Uri getContentUri(String volumeName,
+ long objectId) {
+ return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
+ + "/object/" + objectId);
+ }
+
+ /**
+ * Fields for master table for all media files.
+ * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
+ */
+ public interface ObjectColumns extends MediaColumns {
+ /**
+ * The MTP format code of the file
+ * <P>Type: INTEGER</P>
+ */
+ public static final String FORMAT = "format";
+
+ /**
+ * The index of the parent directory of the file
+ * <P>Type: INTEGER</P>
+ */
+ public static final String PARENT = "parent";
+
+ /**
+ * Identifier for the media table containing the object.
+ * Used internally by MediaProvider
+ * <P>Type: INTEGER</P>
+ */
+ public static final String MEDIA_TABLE = "media_table";
+
+ /**
+ * The ID of the object in its media table.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String MEDIA_ID = "media_id";
+ }
+ }
+
/**
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index ec99b0d..23219a3 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1340,9 +1340,7 @@
}
- /*package*/ void handleInputDevicePropertyChange(String path, boolean connected) {
- String address = getAddressFromObjectPath(path);
- if (address == null) return;
+ /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
BluetoothInputDevice.STATE_DISCONNECTED;
BluetoothDevice device = mAdapter.getRemoteDevice(address);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 249ad62..e26a090 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -216,20 +216,7 @@
@Override
public void handleTouch(MotionEvent event, Runnable finishedCallback) {
try {
- synchronized (mLock) {
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- if (mPendingMove != null) {
- mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
- mPendingMove.recycle();
- }
- mPendingMove = event;
- } else {
- mPendingMove = null;
- }
- Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
- event);
- mCaller.sendMessage(msg);
- }
+ dispatchPointer(event);
} finally {
finishedCallback.run();
}
@@ -238,26 +225,6 @@
final BaseIWindow mWindow = new BaseIWindow() {
@Override
- public boolean onDispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- synchronized (mLock) {
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- if (mPendingMove != null) {
- mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
- mPendingMove.recycle();
- }
- mPendingMove = event;
- } else {
- mPendingMove = null;
- }
- Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
- event);
- mCaller.sendMessage(msg);
- }
- return false;
- }
-
- @Override
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
@@ -369,7 +336,7 @@
? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
: (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
if (mCreated) {
- updateSurface(false, false);
+ updateSurface(false, false, false);
}
}
@@ -454,6 +421,13 @@
}
/**
+ * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
+ * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
+ */
+ public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+ }
+
+ /**
* Convenience for {@link SurfaceHolder.Callback#surfaceCreated
* SurfaceHolder.Callback.surfaceCreated()}.
*/
@@ -466,8 +440,24 @@
*/
public void onSurfaceDestroyed(SurfaceHolder holder) {
}
+
+ private void dispatchPointer(MotionEvent event) {
+ synchronized (mLock) {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (mPendingMove != null) {
+ mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
+ mPendingMove.recycle();
+ }
+ mPendingMove = event;
+ } else {
+ mPendingMove = null;
+ }
+ Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
+ mCaller.sendMessage(msg);
+ }
+ }
- void updateSurface(boolean forceRelayout, boolean forceReport) {
+ void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface: destroyed");
}
@@ -484,7 +474,7 @@
final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
- || typeChanged || flagsChanged) {
+ || typeChanged || flagsChanged || redrawNeeded) {
if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged);
@@ -523,10 +513,8 @@
mInputChannel);
mCreated = true;
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
- }
+ InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+ Looper.myQueue());
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -574,6 +562,10 @@
}
}
}
+
+ redrawNeeded |= creating
+ || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0;
+
if (forceReport || creating || surfaceCreating
|| formatChanged || sizeChanged) {
if (DEBUG) {
@@ -597,10 +589,24 @@
}
}
}
+
+ if (redrawNeeded) {
+ onSurfaceRedrawNeeded(mSurfaceHolder);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ if (c instanceof SurfaceHolder.Callback2) {
+ ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
+ mSurfaceHolder);
+ }
+ }
+ }
+ }
+
} finally {
mIsCreating = false;
mSurfaceCreated = true;
- if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+ if (redrawNeeded) {
mSession.finishDrawing(mWindow);
}
}
@@ -637,7 +643,7 @@
onCreate(mSurfaceHolder);
mInitializing = false;
- updateSurface(false, false);
+ updateSurface(false, false, false);
}
void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
@@ -666,7 +672,7 @@
// If becoming visible, in preview mode the surface
// may have been destroyed so now we need to make
// sure it is re-created.
- updateSurface(false, false);
+ updateSurface(false, false, false);
}
onVisibilityChanged(visible);
}
@@ -770,10 +776,8 @@
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
+ mSurfaceHolder.getSurface() + " of: " + this);
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- if (mInputChannel != null) {
- InputQueue.unregisterInputChannel(mInputChannel);
- }
+ if (mInputChannel != null) {
+ InputQueue.unregisterInputChannel(mInputChannel);
}
mSession.remove(mWindow);
@@ -782,13 +786,11 @@
mSurfaceHolder.mSurface.release();
mCreated = false;
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- // Dispose the input channel after removing the window so the Window Manager
- // doesn't interpret the input channel being closed as an abnormal termination.
- if (mInputChannel != null) {
- mInputChannel.dispose();
- mInputChannel = null;
- }
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
}
}
}
@@ -841,7 +843,7 @@
public void dispatchPointer(MotionEvent event) {
if (mEngine != null) {
- mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false);
+ mEngine.dispatchPointer(event);
}
}
@@ -875,7 +877,7 @@
return;
}
case MSG_UPDATE_SURFACE:
- mEngine.updateSurface(true, false);
+ mEngine.updateSurface(true, false, false);
break;
case MSG_VISIBILITY_CHANGED:
if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
@@ -891,14 +893,8 @@
} break;
case MSG_WINDOW_RESIZED: {
final boolean reportDraw = message.arg1 != 0;
- mEngine.updateSurface(true, false);
+ mEngine.updateSurface(true, false, reportDraw);
mEngine.doOffsetsChanged();
- if (reportDraw) {
- try {
- mEngine.mSession.finishDrawing(mEngine.mWindow);
- } catch (RemoteException e) {
- }
- }
} break;
case MSG_TOUCH_EVENT: {
MotionEvent ev = (MotionEvent)message.obj;
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index bb98bce..13cb5e6 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -417,8 +417,8 @@
}
}
- private static final class START implements NoCopySpan { };
- private static final class END implements NoCopySpan { };
+ private static final class START implements NoCopySpan { }
+ private static final class END implements NoCopySpan { }
/*
* Public constants
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 33c9554..0e3522e 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -21,8 +21,8 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.RectF;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.RectF;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
import android.text.style.CharacterStyle;
@@ -583,7 +583,7 @@
/**
* Returns the next valid offset within this directional run, skipping
* conjuncts and zero-width characters. This should not be called to walk
- * off the end of the line, since the the returned values might not be valid
+ * off the end of the line, since the returned values might not be valid
* on neighboring lines. If the returned offset is less than zero or
* greater than the line length, the offset should be recomputed on the
* preceding or following line, respectively.
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 8eae111..c05a8fe 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -32,7 +32,7 @@
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z";
private static final String Y_M_D = "%Y-%m-%d";
-
+
public static final String TIMEZONE_UTC = "UTC";
/**
@@ -170,11 +170,11 @@
public Time() {
this(TimeZone.getDefault().getID());
}
-
+
/**
* A copy constructor. Construct a Time object by copying the given
* Time object. No normalization occurs.
- *
+ *
* @param other
*/
public Time(Time other) {
@@ -185,17 +185,17 @@
* Ensures the values in each field are in range. For example if the
* current value of this calendar is March 32, normalize() will convert it
* to April 1. It also fills in weekDay, yearDay, isDst and gmtoff.
- *
+ *
* <p>
* If "ignoreDst" is true, then this method sets the "isDst" field to -1
* (the "unknown" value) before normalizing. It then computes the
* correct value for "isDst".
- *
+ *
* <p>
* See {@link #toMillis(boolean)} for more information about when to
* use <tt>true</tt> or <tt>false</tt> for "ignoreDst".
- *
- * @return the UTC milliseconds since the epoch
+ *
+ * @return the UTC milliseconds since the epoch
*/
native public long normalize(boolean ignoreDst);
@@ -379,13 +379,13 @@
* Parses a date-time string in either the RFC 2445 format or an abbreviated
* format that does not include the "time" field. For example, all of the
* following strings are valid:
- *
+ *
* <ul>
* <li>"20081013T160000Z"</li>
* <li>"20081013T160000"</li>
* <li>"20081013"</li>
* </ul>
- *
+ *
* Returns whether or not the time is in UTC (ends with Z). If the string
* ends with "Z" then the timezone is set to UTC. If the date-time string
* included only a date and no time field, then the <code>allDay</code>
@@ -396,10 +396,10 @@
* <code>yearDay</code>, and <code>gmtoff</code> are always set to zero,
* and the field <code>isDst</code> is set to -1 (unknown). To set those
* fields, call {@link #normalize(boolean)} after parsing.
- *
+ *
* To parse a date-time string and convert it to UTC milliseconds, do
* something like this:
- *
+ *
* <pre>
* Time time = new Time();
* String date = "20081013T160000Z";
@@ -428,25 +428,25 @@
* Parse a time in RFC 3339 format. This method also parses simple dates
* (that is, strings that contain no time or time offset). For example,
* all of the following strings are valid:
- *
+ *
* <ul>
* <li>"2008-10-13T16:00:00.000Z"</li>
* <li>"2008-10-13T16:00:00.000+07:00"</li>
* <li>"2008-10-13T16:00:00.000-07:00"</li>
* <li>"2008-10-13"</li>
* </ul>
- *
+ *
* <p>
* If the string contains a time and time offset, then the time offset will
* be used to convert the time value to UTC.
* </p>
- *
+ *
* <p>
* If the given string contains just a date (with no time field), then
* the {@link #allDay} field is set to true and the {@link #hour},
* {@link #minute}, and {@link #second} fields are set to zero.
* </p>
- *
+ *
* <p>
* Returns true if the resulting time value is in UTC time.
* </p>
@@ -462,7 +462,7 @@
}
return false;
}
-
+
native private boolean nativeParse3339(String s);
/**
@@ -484,13 +484,13 @@
* <em>not</em> change any of the fields in this Time object. If you want
* to normalize the fields in this Time object and also get the milliseconds
* then use {@link #normalize(boolean)}.
- *
+ *
* <p>
* If "ignoreDst" is false, then this method uses the current setting of the
* "isDst" field and will adjust the returned time if the "isDst" field is
* wrong for the given time. See the sample code below for an example of
* this.
- *
+ *
* <p>
* If "ignoreDst" is true, then this method ignores the current setting of
* the "isDst" field in this Time object and will instead figure out the
@@ -499,27 +499,27 @@
* correct value of the "isDst" field is when the time is inherently
* ambiguous because it falls in the hour that is repeated when switching
* from Daylight-Saving Time to Standard Time.
- *
+ *
* <p>
* Here is an example where <tt>toMillis(true)</tt> adjusts the time,
* assuming that DST changes at 2am on Sunday, Nov 4, 2007.
- *
+ *
* <pre>
* Time time = new Time();
- * time.set(2007, 10, 4); // set the date to Nov 4, 2007, 12am
+ * time.set(4, 10, 2007); // set the date to Nov 4, 2007, 12am
* time.normalize(); // this sets isDst = 1
* time.monthDay += 1; // changes the date to Nov 5, 2007, 12am
* millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm
* millis = time.toMillis(true); // millis is Nov 5, 2007, 12am
* </pre>
- *
+ *
* <p>
* To avoid this problem, use <tt>toMillis(true)</tt>
* after adding or subtracting days or explicitly setting the "monthDay"
* field. On the other hand, if you are adding
* or subtracting hours or minutes, then you should use
* <tt>toMillis(false)</tt>.
- *
+ *
* <p>
* You should also use <tt>toMillis(false)</tt> if you want
* to read back the same milliseconds that you set with {@link #set(long)}
@@ -531,14 +531,14 @@
* Sets the fields in this Time object given the UTC milliseconds. After
* this method returns, all the fields are normalized.
* This also sets the "isDst" field to the correct value.
- *
+ *
* @param millis the time in UTC milliseconds since the epoch.
*/
native public void set(long millis);
/**
* Format according to RFC 2445 DATETIME type.
- *
+ *
* <p>
* The same as format("%Y%m%dT%H%M%S").
*/
@@ -584,7 +584,7 @@
* Sets the date from the given fields. Also sets allDay to true.
* Sets weekDay, yearDay and gmtoff to 0, and isDst to -1.
* Call {@link #normalize(boolean)} if you need those.
- *
+ *
* @param monthDay the day of the month (in the range [1,31])
* @param month the zero-based month number (in the range [0,11])
* @param year the year
@@ -606,7 +606,7 @@
/**
* Returns true if the time represented by this Time object occurs before
* the given time.
- *
+ *
* @param that a given Time object to compare against
* @return true if this time is less than the given time
*/
@@ -618,7 +618,7 @@
/**
* Returns true if the time represented by this Time object occurs after
* the given time.
- *
+ *
* @param that a given Time object to compare against
* @return true if this time is greater than the given time
*/
@@ -632,12 +632,12 @@
* closest Thursday yearDay.
*/
private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 };
-
+
/**
* Computes the week number according to ISO 8601. The current Time
* object must already be normalized because this method uses the
* yearDay and weekDay fields.
- *
+ *
* <p>
* In IS0 8601, weeks start on Monday.
* The first week of the year (week 1) is defined by ISO 8601 as the
@@ -645,12 +645,12 @@
* Or equivalently, the week containing January 4. Or equivalently,
* the week with the year's first Thursday in it.
* </p>
- *
+ *
* <p>
* The week number can be calculated by counting Thursdays. Week N
* contains the Nth Thursday of the year.
* </p>
- *
+ *
* @return the ISO week number.
*/
public int getWeekNumber() {
@@ -661,7 +661,7 @@
if (closestThursday >= 0 && closestThursday <= 364) {
return closestThursday / 7 + 1;
}
-
+
// The week crosses a year boundary.
Time temp = new Time(this);
temp.monthDay += sThursdayOffset[weekDay];
@@ -670,7 +670,7 @@
}
/**
- * Return a string in the RFC 3339 format.
+ * Return a string in the RFC 3339 format.
* <p>
* If allDay is true, expresses the time as Y-M-D</p>
* <p>
@@ -691,13 +691,13 @@
int offset = (int)Math.abs(gmtoff);
int minutes = (offset % 3600) / 60;
int hours = offset / 3600;
-
+
return String.format("%s%s%02d:%02d", base, sign, hours, minutes);
}
}
-
+
/**
- * Returns true if the day of the given time is the epoch on the Julian Calendar
+ * Returns true if the day of the given time is the epoch on the Julian Calendar
* (January 1, 1970 on the Gregorian calendar).
*
* @param time the time to test
@@ -707,7 +707,7 @@
long millis = time.toMillis(true);
return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY;
}
-
+
/**
* Computes the Julian day number, given the UTC milliseconds
* and the offset (in seconds) from UTC. The Julian day for a given
@@ -716,10 +716,10 @@
* what timezone is being used. The Julian day is useful for testing
* if two events occur on the same day and for determining the relative
* time of an event from the present ("yesterday", "3 days ago", etc.).
- *
+ *
* <p>
* Use {@link #toMillis(boolean)} to get the milliseconds.
- *
+ *
* @param millis the time in UTC milliseconds
* @param gmtoff the offset from UTC in seconds
* @return the Julian day
@@ -729,7 +729,7 @@
long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS;
return (int) julianDay + EPOCH_JULIAN_DAY;
}
-
+
/**
* <p>Sets the time from the given Julian day number, which must be based on
* the same timezone that is set in this Time object. The "gmtoff" field
@@ -738,7 +738,7 @@
* After this method returns all the fields will be normalized and the time
* will be set to 12am at the beginning of the given Julian day.
* </p>
- *
+ *
* <p>
* The only exception to this is if 12am does not exist for that day because
* of daylight saving time. For example, Cairo, Eqypt moves time ahead one
@@ -746,7 +746,7 @@
* also change daylight saving time at 12am. In those cases, the time
* will be set to 1am.
* </p>
- *
+ *
* @param julianDay the Julian day in the timezone for this Time object
* @return the UTC milliseconds for the beginning of the Julian day
*/
@@ -756,13 +756,13 @@
// the day.
long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS;
set(millis);
-
+
// Figure out how close we are to the requested Julian day.
// We can't be off by more than a day.
int approximateDay = getJulianDay(millis, gmtoff);
int diff = julianDay - approximateDay;
monthDay += diff;
-
+
// Set the time to 12am and re-normalize.
hour = 0;
minute = 0;
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 9af42cc..79a0c37 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,30 +16,38 @@
package android.text.method;
-import android.util.Log;
+import android.text.Layout;
+import android.text.Selection;
+import android.text.Spannable;
import android.view.KeyEvent;
-import android.graphics.Rect;
-import android.text.*;
-import android.widget.TextView;
-import android.view.View;
-import android.view.ViewConfiguration;
import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.TextView.CursorController;
// XXX this doesn't extend MetaKeyKeyListener because the signatures
// don't match. Need to figure that out. Meanwhile the meta keys
// won't work in fields that don't take input.
-public class
-ArrowKeyMovementMethod
-implements MovementMethod
-{
+public class ArrowKeyMovementMethod implements MovementMethod {
+ /**
+ * An optional controller for the cursor.
+ * Use {@link #setCursorController(CursorController)} to set this field.
+ */
+ protected CursorController mCursorController;
+
+ private boolean isCap(Spannable buffer) {
+ return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
+ (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
+ }
+
+ private boolean isAlt(Spannable buffer) {
+ return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1;
+ }
+
private boolean up(TextView widget, Spannable buffer) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
- boolean alt = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_ALT_ON) == 1;
+ boolean cap = isCap(buffer);
+ boolean alt = isAlt(buffer);
Layout layout = widget.getLayout();
if (cap) {
@@ -60,12 +68,8 @@
}
private boolean down(TextView widget, Spannable buffer) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
- boolean alt = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_ALT_ON) == 1;
+ boolean cap = isCap(buffer);
+ boolean alt = isAlt(buffer);
Layout layout = widget.getLayout();
if (cap) {
@@ -86,12 +90,8 @@
}
private boolean left(TextView widget, Spannable buffer) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
- boolean alt = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_ALT_ON) == 1;
+ boolean cap = isCap(buffer);
+ boolean alt = isAlt(buffer);
Layout layout = widget.getLayout();
if (cap) {
@@ -110,12 +110,8 @@
}
private boolean right(TextView widget, Spannable buffer) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
- boolean alt = MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_ALT_ON) == 1;
+ boolean cap = isCap(buffer);
+ boolean alt = isAlt(buffer);
Layout layout = widget.getLayout();
if (cap) {
@@ -133,35 +129,6 @@
}
}
- private int getOffset(int x, int y, TextView widget){
- // Converts the absolute X,Y coordinates to the character offset for the
- // character whose position is closest to the specified
- // horizontal position.
- x -= widget.getTotalPaddingLeft();
- y -= widget.getTotalPaddingTop();
-
- // Clamp the position to inside of the view.
- if (x < 0) {
- x = 0;
- } else if (x >= (widget.getWidth()-widget.getTotalPaddingRight())) {
- x = widget.getWidth()-widget.getTotalPaddingRight() - 1;
- }
- if (y < 0) {
- y = 0;
- } else if (y >= (widget.getHeight()-widget.getTotalPaddingBottom())) {
- y = widget.getHeight()-widget.getTotalPaddingBottom() - 1;
- }
-
- x += widget.getScrollX();
- y += widget.getScrollY();
-
- Layout layout = widget.getLayout();
- int line = layout.getLineForVertical(y);
-
- int offset = layout.getOffsetForHorizontal(line, x);
- return offset;
- }
-
public boolean onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
if (executeDown(widget, buffer, keyCode)) {
MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
@@ -193,10 +160,9 @@
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
- if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
- if (widget.showContextMenu()) {
+ if ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) &&
+ (widget.showContextMenu())) {
handled = true;
- }
}
}
@@ -214,8 +180,7 @@
public boolean onKeyOther(TextView view, Spannable text, KeyEvent event) {
int code = event.getKeyCode();
- if (code != KeyEvent.KEYCODE_UNKNOWN
- && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
+ if (code != KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
int repeat = event.getRepeatCount();
boolean handled = false;
while ((--repeat) > 0) {
@@ -226,13 +191,22 @@
return false;
}
- public boolean onTrackballEvent(TextView widget, Spannable text,
- MotionEvent event) {
+ public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
+ if (mCursorController != null) {
+ mCursorController.hide();
+ }
return false;
}
- public boolean onTouchEvent(TextView widget, Spannable buffer,
- MotionEvent event) {
+ public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+ if (mCursorController != null) {
+ return onTouchEventCursor(widget, buffer, event);
+ } else {
+ return onTouchEventStandard(widget, buffer, event);
+ }
+ }
+
+ private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1, initialScrollY = -1;
if (event.getAction() == MotionEvent.ACTION_UP) {
initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -243,53 +217,20 @@
if (widget.isFocused() && !widget.didTouchFocusSelect()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
- int x = (int) event.getX();
- int y = (int) event.getY();
- int offset = getOffset(x, y, widget);
-
+ boolean cap = isCap(buffer);
if (cap) {
- buffer.setSpan(LAST_TAP_DOWN, offset, offset,
- Spannable.SPAN_POINT_POINT);
+ int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+
+ buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
// Disallow intercepting of the touch events, so that
// users can scroll and select at the same time.
// without this, users would get booted out of select
// mode once the view detected it needed to scroll.
widget.getParent().requestDisallowInterceptTouchEvent(true);
- } else {
- OnePointFiveTapState[] tap = buffer.getSpans(0, buffer.length(),
- OnePointFiveTapState.class);
-
- if (tap.length > 0) {
- if (event.getEventTime() - tap[0].mWhen <=
- ViewConfiguration.getDoubleTapTimeout() &&
- sameWord(buffer, offset, Selection.getSelectionEnd(buffer))) {
-
- tap[0].active = true;
- MetaKeyKeyListener.startSelecting(widget, buffer);
- widget.getParent().requestDisallowInterceptTouchEvent(true);
- buffer.setSpan(LAST_TAP_DOWN, offset, offset,
- Spannable.SPAN_POINT_POINT);
- }
-
- tap[0].mWhen = event.getEventTime();
- } else {
- OnePointFiveTapState newtap = new OnePointFiveTapState();
- newtap.mWhen = event.getEventTime();
- newtap.active = false;
- buffer.setSpan(newtap, 0, buffer.length(),
- Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- }
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
+ boolean cap = isCap(buffer);
if (cap && handled) {
// Before selecting, make sure we've moved out of the "slop".
@@ -297,45 +238,15 @@
// OUT of the slop
// Turn long press off while we're selecting. User needs to
- // re-tap on the selection to enable longpress
+ // re-tap on the selection to enable long press
widget.cancelLongPress();
// Update selection as we're moving the selection area.
// Get the current touch position
- int x = (int) event.getX();
- int y = (int) event.getY();
- int offset = getOffset(x, y, widget);
+ int offset = widget.getOffset((int) event.getX(), (int) event.getY());
- final OnePointFiveTapState[] tap = buffer.getSpans(0, buffer.length(),
- OnePointFiveTapState.class);
-
- if (tap.length > 0 && tap[0].active) {
- // Get the last down touch position (the position at which the
- // user started the selection)
- int lastDownOffset = buffer.getSpanStart(LAST_TAP_DOWN);
-
- // Compute the selection boundaries
- int spanstart;
- int spanend;
- if (offset >= lastDownOffset) {
- // Expand from word start of the original tap to new word
- // end, since we are selecting "forwards"
- spanstart = findWordStart(buffer, lastDownOffset);
- spanend = findWordEnd(buffer, offset);
- } else {
- // Expand to from new word start to word end of the original
- // tap since we are selecting "backwards".
- // The spanend will always need to be associated with the touch
- // up position, so that refining the selection with the
- // trackball will work as expected.
- spanstart = findWordEnd(buffer, lastDownOffset);
- spanend = findWordStart(buffer, offset);
- }
- Selection.setSelection(buffer, spanstart, spanend);
- } else {
- Selection.extendSelection(buffer, offset);
- }
+ Selection.extendSelection(buffer, offset);
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
@@ -344,70 +255,17 @@
// the current scroll offset to avoid the scroll jumping later
// to show it.
if ((initialScrollY >= 0 && initialScrollY != widget.getScrollY()) ||
- (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) {
+ (initialScrollX >= 0 && initialScrollX != widget.getScrollX())) {
widget.moveCursorToVisibleOffset();
return true;
}
- int x = (int) event.getX();
- int y = (int) event.getY();
- int off = getOffset(x, y, widget);
-
- // XXX should do the same adjust for x as we do for the line.
-
- OnePointFiveTapState[] onepointfivetap = buffer.getSpans(0, buffer.length(),
- OnePointFiveTapState.class);
- if (onepointfivetap.length > 0 && onepointfivetap[0].active &&
- Selection.getSelectionStart(buffer) == Selection.getSelectionEnd(buffer)) {
- // If we've set select mode, because there was a onepointfivetap,
- // but there was no ensuing swipe gesture, undo the select mode
- // and remove reference to the last onepointfivetap.
- MetaKeyKeyListener.stopSelecting(widget, buffer);
- for (int i=0; i < onepointfivetap.length; i++) {
- buffer.removeSpan(onepointfivetap[i]);
- }
+ int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+ if (isCap(buffer)) {
buffer.removeSpan(LAST_TAP_DOWN);
- }
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- KeyEvent.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
-
- DoubleTapState[] tap = buffer.getSpans(0, buffer.length(),
- DoubleTapState.class);
- boolean doubletap = false;
-
- if (tap.length > 0) {
- if (event.getEventTime() - tap[0].mWhen <=
- ViewConfiguration.getDoubleTapTimeout() &&
- sameWord(buffer, off, Selection.getSelectionEnd(buffer))) {
-
- doubletap = true;
- }
-
- tap[0].mWhen = event.getEventTime();
+ Selection.extendSelection(buffer, offset);
} else {
- DoubleTapState newtap = new DoubleTapState();
- newtap.mWhen = event.getEventTime();
- buffer.setSpan(newtap, 0, buffer.length(),
- Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- }
-
- if (cap) {
- buffer.removeSpan(LAST_TAP_DOWN);
- if (onepointfivetap.length > 0 && onepointfivetap[0].active) {
- // If we selecting something with the onepointfivetap-and
- // swipe gesture, stop it on finger up.
- MetaKeyKeyListener.stopSelecting(widget, buffer);
- } else {
- Selection.extendSelection(buffer, off);
- }
- } else if (doubletap) {
- Selection.setSelection(buffer,
- findWordStart(buffer, off),
- findWordEnd(buffer, off));
- } else {
- Selection.setSelection(buffer, off);
+ Selection.setSelection(buffer, offset);
}
MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
@@ -420,73 +278,36 @@
return handled;
}
- private static class DoubleTapState implements NoCopySpan {
- long mWhen;
- }
+ private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
+ if (widget.isFocused() && !widget.didTouchFocusSelect()) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ widget.cancelLongPress();
- /* We check for a onepointfive tap. This is similar to
- * doubletap gesture (where a finger goes down, up, down, up, in a short
- * time period), except in the onepointfive tap, a users finger only needs
- * to go down, up, down in a short time period. We detect this type of tap
- * to implement the onepointfivetap-and-swipe selection gesture.
- * This gesture allows users to select a segment of text without going
- * through the "select text" option in the context menu.
- */
- private static class OnePointFiveTapState implements NoCopySpan {
- long mWhen;
- boolean active;
- }
+ // Offset the current touch position (from controller to cursor)
+ final float x = event.getX() + mCursorController.getOffsetX();
+ final float y = event.getY() + mCursorController.getOffsetY();
+ int offset = widget.getOffset((int) x, (int) y);
+ mCursorController.updatePosition(offset);
+ return true;
- private static boolean sameWord(CharSequence text, int one, int two) {
- int start = findWordStart(text, one);
- int end = findWordEnd(text, one);
-
- if (end == start) {
- return false;
- }
-
- return start == findWordStart(text, two) &&
- end == findWordEnd(text, two);
- }
-
- // TODO: Unify with TextView.getWordForDictionary()
- private static int findWordStart(CharSequence text, int start) {
- for (; start > 0; start--) {
- char c = text.charAt(start - 1);
- int type = Character.getType(c);
-
- if (c != '\'' &&
- type != Character.UPPERCASE_LETTER &&
- type != Character.LOWERCASE_LETTER &&
- type != Character.TITLECASE_LETTER &&
- type != Character.MODIFIER_LETTER &&
- type != Character.DECIMAL_DIGIT_NUMBER) {
- break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mCursorController = null;
+ return true;
}
}
-
- return start;
+ return false;
}
- // TODO: Unify with TextView.getWordForDictionary()
- private static int findWordEnd(CharSequence text, int end) {
- int len = text.length();
-
- for (; end < len; end++) {
- char c = text.charAt(end);
- int type = Character.getType(c);
-
- if (c != '\'' &&
- type != Character.UPPERCASE_LETTER &&
- type != Character.LOWERCASE_LETTER &&
- type != Character.TITLECASE_LETTER &&
- type != Character.MODIFIER_LETTER &&
- type != Character.DECIMAL_DIGIT_NUMBER) {
- break;
- }
- }
-
- return end;
+ /**
+ * Defines the cursor controller.
+ *
+ * When set, this object can be used to handle events, that can be translated in cursor updates.
+ * @param cursorController A cursor controller implementation
+ */
+ public void setCursorController(CursorController cursorController) {
+ mCursorController = cursorController;
}
public boolean canSelectArbitrarily() {
@@ -525,8 +346,9 @@
}
public static MovementMethod getInstance() {
- if (sInstance == null)
+ if (sInstance == null) {
sInstance = new ArrowKeyMovementMethod();
+ }
return sInstance;
}
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index c30db20..3b98fc3 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -17,9 +17,9 @@
package android.text.method;
import android.text.Layout;
+import android.text.Layout.Alignment;
import android.text.NoCopySpan;
import android.text.Spannable;
-import android.text.Layout.Alignment;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -102,7 +102,7 @@
MotionEvent event) {
DragState[] ds;
- switch (event.getAction()) {
+ switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
ds = buffer.getSpans(0, buffer.length(), DragState.class);
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 1c8b330..7fc43b9 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -90,6 +90,16 @@
delete(key);
}
+ /**
+ * Removes the mapping at the specified index.
+ */
+ public void removeAt(int index) {
+ if (mValues[index] != DELETED) {
+ mValues[index] = DELETED;
+ mGarbage = true;
+ }
+ }
+
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
diff --git a/core/java/android/view/AbsSavedState.java b/core/java/android/view/AbsSavedState.java
index 840d7c1..6ad33dd 100644
--- a/core/java/android/view/AbsSavedState.java
+++ b/core/java/android/view/AbsSavedState.java
@@ -54,7 +54,7 @@
*/
protected AbsSavedState(Parcel source) {
// FIXME need class loader
- Parcelable superState = (Parcelable) source.readParcelable(null);
+ Parcelable superState = source.readParcelable(null);
mSuperState = superState != null ? superState : EMPTY_STATE;
}
@@ -75,7 +75,7 @@
= new Parcelable.Creator<AbsSavedState>() {
public AbsSavedState createFromParcel(Parcel in) {
- Parcelable superState = (Parcelable) in.readParcelable(null);
+ Parcelable superState = in.readParcelable(null);
if (superState != null) {
throw new IllegalStateException("superState must be null");
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 00dc128..a2ab8bd 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -17,16 +17,21 @@
package android.view;
import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
+import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
import javax.microedition.khronos.opengles.GL;
@@ -45,6 +50,10 @@
private final float[] mPoint = new float[2];
private final float[] mLine = new float[4];
+
+ private final Rect mClipBounds = new Rect();
+
+ private DrawFilter mFilter;
///////////////////////////////////////////////////////////////////////////
// Constructors
@@ -164,6 +173,7 @@
@Override
public boolean clipRect(Rect rect, Region.Op op) {
+ // TODO: Implement
throw new UnsupportedOperationException();
}
@@ -174,6 +184,7 @@
@Override
public boolean clipRect(RectF rect, Region.Op op) {
+ // TODO: Implement
throw new UnsupportedOperationException();
}
@@ -336,12 +347,14 @@
@Override
public void setDrawFilter(DrawFilter filter) {
- throw new UnsupportedOperationException();
+ // Don't crash, but ignore the draw filter
+ // TODO: Implement PaintDrawFilter
+ mFilter = filter;
}
@Override
public DrawFilter getDrawFilter() {
- throw new UnsupportedOperationException();
+ return mFilter;
}
///////////////////////////////////////////////////////////////////////////
@@ -361,71 +374,77 @@
@Override
public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+ // Shaders are ignored when drawing patches
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top, dst.right,
- dst.bottom, nativePaint, bitmap.getDensity(), mDensity, mScreenDensity);
+ nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top,
+ dst.right, dst.bottom, nativePaint);
}
private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top,
- float right, float bottom, int paint, int bitmapDensity, int canvasDensity, int screenDensity);
+ float right, float bottom, int paint);
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+ // Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint,
- bitmap.getDensity(), mDensity, mScreenDensity);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
}
+ private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint);
+
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+ // Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
- nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint,
- bitmap.getDensity(), mDensity, mScreenDensity);
+ nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
}
- private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint,
- int bitmapDensity, int canvasDensity, int screenDensity);
+ private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+ // Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint,
- bitmap.getDensity(), mDensity, mScreenDensity);
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint
+ );
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+ // Shaders are ignored when drawing bitmaps
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
- dst.left, dst.top, dst.right, dst.bottom, nativePaint,
- bitmap.getDensity(), mDensity, mScreenDensity);
+ dst.left, dst.top, dst.right, dst.bottom, nativePaint
+ );
}
- private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint,
- int bitmapDensity, int canvasDensity, int screenDensity);
-
private native void nDrawBitmap(int renderer, int bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
- float left, float top, float right, float bottom, int paint,
- int bitmapDensity, int canvasDensity, int screenDensity);
+ float left, float top, float right, float bottom, int paint);
@Override
public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
int width, int height, boolean hasAlpha, Paint paint) {
- // TODO: Implement
+ // Shaders are ignored when drawing bitmaps
+ final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+ final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
+ final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawBitmap(mRenderer, b.mNativeBitmap, x, y, nativePaint);
+ b.recycle();
}
@Override
public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
int width, int height, boolean hasAlpha, Paint paint) {
+ // Shaders are ignored when drawing bitmaps
drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
}
@Override
public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
int vertOffset, int[] colors, int colorOffset, Paint paint) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
}
@Override
@@ -471,7 +490,9 @@
@Override
public void drawPaint(Paint paint) {
- // TODO: Implement
+ final Rect r = mClipBounds;
+ nGetClipBounds(mRenderer, r);
+ drawRect(r.left, r.top, r.right, r.bottom, paint);
}
@Override
@@ -523,7 +544,9 @@
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+ boolean hasShader = setupShader(paint);
nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
+ if (hasShader) nResetShader(mRenderer);
}
private native void nDrawRect(int renderer, float left, float top, float right, float bottom,
@@ -546,7 +569,7 @@
@Override
public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
}
@Override
@@ -596,6 +619,29 @@
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
int indexOffset, int indexCount, Paint paint) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
}
+
+ private boolean setupShader(Paint paint) {
+ final Shader shader = paint.getShader();
+ if (shader != null) {
+ if (shader instanceof BitmapShader) {
+ final BitmapShader bs = (BitmapShader) shader;
+ nSetupBitmapShader(mRenderer, bs.native_instance, bs.mBitmap.mNativeBitmap,
+ bs.mTileX, bs.mTileY, bs.mLocalMatrix);
+ return true;
+ } else if (shader instanceof LinearGradient) {
+ // TODO: Implement
+ } else if (shader instanceof RadialGradient) {
+ // TODO: Implement
+ } else if (shader instanceof SweepGradient) {
+ // TODO: Implement
+ }
+ }
+ return false;
+ }
+
+ private native void nSetupBitmapShader(int renderer, int shader, int bitmap,
+ int tileX, int tileY, int matrix);
+ private native void nResetShader(int renderer);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d30bce9..090a743 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,10 +17,8 @@
package android.view;
-import android.content.res.CompatibilityInfo;
import android.graphics.Canvas;
import android.os.SystemClock;
-import android.util.DisplayMetrics;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
@@ -74,14 +72,8 @@
*
* @param view The view to draw.
* @param attachInfo AttachInfo tied to the specified view.
- * @param translator Translator used to draw applications in compatibility mode.
- * @param yoff The vertical offset for the drawing.
- * @param density The density of the application
- * @param scalingRequired Whether drawing should be scaled.
*/
- abstract void draw(View view, View.AttachInfo attachInfo,
- CompatibilityInfo.Translator translator, int yoff, int density,
- boolean scalingRequired);
+ abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
/**
* Initializes the hardware renderer for the specified surface and setup the
@@ -371,9 +363,7 @@
}
@Override
- void draw(View view, View.AttachInfo attachInfo, CompatibilityInfo.Translator translator,
- int yoff, int density, boolean scalingRequired) {
-
+ void draw(View view, View.AttachInfo attachInfo, int yOffset) {
if (canDraw()) {
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
attachInfo.mIgnoreDirtyState = true;
@@ -383,14 +373,9 @@
Canvas canvas = mCanvas;
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.translate(0, -yOffset);
+
try {
- canvas.translate(0, -yoff);
- if (translator != null) {
- translator.translateCanvas(canvas);
- }
- canvas.setDensity(density);
- canvas.setScreenDensity(scalingRequired ? DisplayMetrics.DENSITY_DEVICE : 0);
-
view.draw(canvas);
} finally {
canvas.restoreToCount(saveCount);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3b09808..921018a 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -46,9 +46,6 @@
void resized(int w, int h, in Rect coveredInsets, in Rect visibleInsets,
boolean reportDraw, in Configuration newConfig);
- void dispatchKey(in KeyEvent event);
- void dispatchPointer(in MotionEvent event, long eventTime, boolean callWhenDone);
- void dispatchTrackball(in MotionEvent event, long eventTime, boolean callWhenDone);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 4647fb4..7f10b76 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -109,10 +109,6 @@
void getDisplayFrame(IWindow window, out Rect outDisplayFrame);
void finishDrawing(IWindow window);
-
- void finishKey(IWindow window);
- MotionEvent getPendingPointerMove(IWindow window);
- MotionEvent getPendingTrackballMove(IWindow window);
void setInTouchMode(boolean showFocus);
boolean getInTouchMode();
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0bfb6d6..9c05008 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -124,11 +124,27 @@
public static final int KEYCODE_PAGE_DOWN = 93;
public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
+ public static final int KEYCODE_BUTTON_A = 96;
+ public static final int KEYCODE_BUTTON_B = 97;
+ public static final int KEYCODE_BUTTON_C = 98;
+ public static final int KEYCODE_BUTTON_X = 99;
+ public static final int KEYCODE_BUTTON_Y = 100;
+ public static final int KEYCODE_BUTTON_Z = 101;
+ public static final int KEYCODE_BUTTON_L1 = 102;
+ public static final int KEYCODE_BUTTON_R1 = 103;
+ public static final int KEYCODE_BUTTON_L2 = 104;
+ public static final int KEYCODE_BUTTON_R2 = 105;
+ public static final int KEYCODE_BUTTON_THUMBL = 106;
+ public static final int KEYCODE_BUTTON_THUMBR = 107;
+ public static final int KEYCODE_BUTTON_START = 108;
+ public static final int KEYCODE_BUTTON_SELECT = 109;
+ public static final int KEYCODE_BUTTON_MODE = 110;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// native/include/android/keycodes.h
// frameworks/base/include/ui/KeycodeLabels.h
+ // external/webkit/WebKit/android/plugins/ANPKeyCodes.h
// tools/puppet_master/PuppetMaster/nav_keys.py
// frameworks/base/core/res/res/values/attrs.xml
// commands/monkey/Monkey.java
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index ae8c21d..35e229a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -722,7 +722,7 @@
*
* @param pointerId The identifier of the pointer to be found.
* @return Returns either the index of the pointer (for use with
- * {@link #getX(int) et al.), or -1 if there is no data available for
+ * {@link #getX(int)} et al.), or -1 if there is no data available for
* that pointer identifier.
*/
public final int findPointerIndex(int pointerId) {
diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java
deleted file mode 100644
index 3bbfea8..0000000
--- a/core/java/android/view/RawInputEvent.java
+++ /dev/null
@@ -1,201 +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.view;
-
-/**
- * @hide
- * This really belongs in services.jar; WindowManagerPolicy should go there too.
- */
-public class RawInputEvent {
- // Event class as defined by EventHub.
- public static final int CLASS_KEYBOARD = 0x00000001;
- public static final int CLASS_ALPHAKEY = 0x00000002;
- public static final int CLASS_TOUCHSCREEN = 0x00000004;
- public static final int CLASS_TRACKBALL = 0x00000008;
- public static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
- public static final int CLASS_DPAD = 0x00000020;
-
- // More special classes for QueuedEvent below.
- public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000;
-
- // Event types.
-
- public static final int EV_SYN = 0x00;
- public static final int EV_KEY = 0x01;
- public static final int EV_REL = 0x02;
- public static final int EV_ABS = 0x03;
- public static final int EV_MSC = 0x04;
- public static final int EV_SW = 0x05;
- public static final int EV_LED = 0x11;
- public static final int EV_SND = 0x12;
- public static final int EV_REP = 0x14;
- public static final int EV_FF = 0x15;
- public static final int EV_PWR = 0x16;
- public static final int EV_FF_STATUS = 0x17;
-
- // Platform-specific event types.
-
- public static final int EV_DEVICE_ADDED = 0x10000000;
- public static final int EV_DEVICE_REMOVED = 0x20000000;
-
- // Special key (EV_KEY) scan codes for pointer buttons.
-
- public static final int BTN_FIRST = 0x100;
-
- public static final int BTN_MISC = 0x100;
- public static final int BTN_0 = 0x100;
- public static final int BTN_1 = 0x101;
- public static final int BTN_2 = 0x102;
- public static final int BTN_3 = 0x103;
- public static final int BTN_4 = 0x104;
- public static final int BTN_5 = 0x105;
- public static final int BTN_6 = 0x106;
- public static final int BTN_7 = 0x107;
- public static final int BTN_8 = 0x108;
- public static final int BTN_9 = 0x109;
-
- public static final int BTN_MOUSE = 0x110;
- public static final int BTN_LEFT = 0x110;
- public static final int BTN_RIGHT = 0x111;
- public static final int BTN_MIDDLE = 0x112;
- public static final int BTN_SIDE = 0x113;
- public static final int BTN_EXTRA = 0x114;
- public static final int BTN_FORWARD = 0x115;
- public static final int BTN_BACK = 0x116;
- public static final int BTN_TASK = 0x117;
-
- public static final int BTN_JOYSTICK = 0x120;
- public static final int BTN_TRIGGER = 0x120;
- public static final int BTN_THUMB = 0x121;
- public static final int BTN_THUMB2 = 0x122;
- public static final int BTN_TOP = 0x123;
- public static final int BTN_TOP2 = 0x124;
- public static final int BTN_PINKIE = 0x125;
- public static final int BTN_BASE = 0x126;
- public static final int BTN_BASE2 = 0x127;
- public static final int BTN_BASE3 = 0x128;
- public static final int BTN_BASE4 = 0x129;
- public static final int BTN_BASE5 = 0x12a;
- public static final int BTN_BASE6 = 0x12b;
- public static final int BTN_DEAD = 0x12f;
-
- public static final int BTN_GAMEPAD = 0x130;
- public static final int BTN_A = 0x130;
- public static final int BTN_B = 0x131;
- public static final int BTN_C = 0x132;
- public static final int BTN_X = 0x133;
- public static final int BTN_Y = 0x134;
- public static final int BTN_Z = 0x135;
- public static final int BTN_TL = 0x136;
- public static final int BTN_TR = 0x137;
- public static final int BTN_TL2 = 0x138;
- public static final int BTN_TR2 = 0x139;
- public static final int BTN_SELECT = 0x13a;
- public static final int BTN_START = 0x13b;
- public static final int BTN_MODE = 0x13c;
- public static final int BTN_THUMBL = 0x13d;
- public static final int BTN_THUMBR = 0x13e;
-
- public static final int BTN_DIGI = 0x140;
- public static final int BTN_TOOL_PEN = 0x140;
- public static final int BTN_TOOL_RUBBER = 0x141;
- public static final int BTN_TOOL_BRUSH = 0x142;
- public static final int BTN_TOOL_PENCIL = 0x143;
- public static final int BTN_TOOL_AIRBRUSH = 0x144;
- public static final int BTN_TOOL_FINGER = 0x145;
- public static final int BTN_TOOL_MOUSE = 0x146;
- public static final int BTN_TOOL_LENS = 0x147;
- public static final int BTN_TOUCH = 0x14a;
- public static final int BTN_STYLUS = 0x14b;
- public static final int BTN_STYLUS2 = 0x14c;
- public static final int BTN_TOOL_DOUBLETAP = 0x14d;
- public static final int BTN_TOOL_TRIPLETAP = 0x14e;
-
- public static final int BTN_WHEEL = 0x150;
- public static final int BTN_GEAR_DOWN = 0x150;
- public static final int BTN_GEAR_UP = 0x151;
-
- public static final int BTN_LAST = 0x15f;
-
- // Relative axes (EV_REL) scan codes.
-
- public static final int REL_X = 0x00;
- public static final int REL_Y = 0x01;
- public static final int REL_Z = 0x02;
- public static final int REL_RX = 0x03;
- public static final int REL_RY = 0x04;
- public static final int REL_RZ = 0x05;
- public static final int REL_HWHEEL = 0x06;
- public static final int REL_DIAL = 0x07;
- public static final int REL_WHEEL = 0x08;
- public static final int REL_MISC = 0x09;
- public static final int REL_MAX = 0x0f;
-
- // Absolute axes (EV_ABS) scan codes.
-
- public static final int ABS_X = 0x00;
- public static final int ABS_Y = 0x01;
- public static final int ABS_Z = 0x02;
- public static final int ABS_RX = 0x03;
- public static final int ABS_RY = 0x04;
- public static final int ABS_RZ = 0x05;
- public static final int ABS_THROTTLE = 0x06;
- public static final int ABS_RUDDER = 0x07;
- public static final int ABS_WHEEL = 0x08;
- public static final int ABS_GAS = 0x09;
- public static final int ABS_BRAKE = 0x0a;
- public static final int ABS_HAT0X = 0x10;
- public static final int ABS_HAT0Y = 0x11;
- public static final int ABS_HAT1X = 0x12;
- public static final int ABS_HAT1Y = 0x13;
- public static final int ABS_HAT2X = 0x14;
- public static final int ABS_HAT2Y = 0x15;
- public static final int ABS_HAT3X = 0x16;
- public static final int ABS_HAT3Y = 0x17;
- public static final int ABS_PRESSURE = 0x18;
- public static final int ABS_DISTANCE = 0x19;
- public static final int ABS_TILT_X = 0x1a;
- public static final int ABS_TILT_Y = 0x1b;
- public static final int ABS_TOOL_WIDTH = 0x1c;
- public static final int ABS_VOLUME = 0x20;
- public static final int ABS_MISC = 0x28;
- public static final int ABS_MT_TOUCH_MAJOR = 0x30;
- public static final int ABS_MT_TOUCH_MINOR = 0x31;
- public static final int ABS_MT_WIDTH_MAJOR = 0x32;
- public static final int ABS_MT_WIDTH_MINOR = 0x33;
- public static final int ABS_MT_ORIENTATION = 0x34;
- public static final int ABS_MT_POSITION_X = 0x35;
- public static final int ABS_MT_POSITION_Y = 0x36;
- public static final int ABS_MT_TOOL_TYPE = 0x37;
- public static final int ABS_MT_BLOB_ID = 0x38;
- public static final int ABS_MAX = 0x3f;
-
- // Switch events
- public static final int SW_LID = 0x00;
-
- public static final int SYN_REPORT = 0;
- public static final int SYN_CONFIG = 1;
- public static final int SYN_MT_REPORT = 2;
-
- public int deviceId;
- public int type;
- public int scancode;
- public int keycode;
- public int flags;
- public int value;
- public long when;
-}
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 34e4638..0d38f7b 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -119,6 +119,23 @@
}
/**
+ * Additional callbacks that can be received for {@link Callback}.
+ */
+ public interface Callback2 extends Callback {
+ /**
+ * Called when the application needs to redraw the content of its
+ * surface, after it is resized or for some other reason. By not
+ * returning here until the redraw is complete, you can ensure that
+ * the user will not see your surface in a bad state (at its new
+ * size before it has been correctly drawn that way). This will
+ * typically be preceeded by a call to {@link #surfaceChanged}.
+ *
+ * @param holder The SurfaceHolder whose surface has changed.
+ */
+ public void surfaceRedrawNeeded(SurfaceHolder holder);
+ }
+
+ /**
* Add a Callback interface for this holder. There can several Callback
* interfaces associated to a holder.
*
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d1a0f75..54cb4ca 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -123,7 +123,7 @@
handleGetNewSurface();
} break;
case UPDATE_WINDOW_MSG: {
- updateWindow(false);
+ updateWindow(false, false);
} break;
}
}
@@ -132,7 +132,7 @@
final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
= new ViewTreeObserver.OnScrollChangedListener() {
public void onScrollChanged() {
- updateWindow(false);
+ updateWindow(false, false);
}
};
@@ -210,7 +210,7 @@
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
- updateWindow(false);
+ updateWindow(false, false);
}
@Override
@@ -218,7 +218,7 @@
super.setVisibility(visibility);
mViewVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
- updateWindow(false);
+ updateWindow(false, false);
}
/**
@@ -232,7 +232,7 @@
*/
protected void showSurface() {
if (mSession != null) {
- updateWindow(true);
+ updateWindow(true, false);
}
}
@@ -265,7 +265,7 @@
protected void onDetachedFromWindow() {
getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener);
mRequestedVisible = false;
- updateWindow(false);
+ updateWindow(false, false);
mHaveFrame = false;
if (mWindow != null) {
try {
@@ -290,7 +290,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- updateWindow(false);
+ updateWindow(false, false);
}
@Override
@@ -343,7 +343,7 @@
}
// reposition ourselves where the surface is
mHaveFrame = true;
- updateWindow(false);
+ updateWindow(false, false);
super.dispatchDraw(canvas);
}
@@ -397,7 +397,7 @@
mWindowType = type;
}
- private void updateWindow(boolean force) {
+ private void updateWindow(boolean force, boolean redrawNeeded) {
if (!mHaveFrame) {
return;
}
@@ -425,7 +425,7 @@
final boolean typeChanged = mType != mRequestedType;
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]
- || mUpdateWindowNeeded || mReportDrawNeeded) {
+ || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
@@ -524,6 +524,8 @@
}
try {
+ redrawNeeded |= creating | reportDrawNeeded;
+
if (visible) {
mDestroyReportNeeded = true;
@@ -545,12 +547,20 @@
c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
}
}
+ if (redrawNeeded) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ if (c instanceof SurfaceHolder.Callback2) {
+ ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
+ mSurfaceHolder);
+ }
+ }
+ }
} else {
mSurface.release();
}
} finally {
mIsCreating = false;
- if (creating || reportDrawNeeded) {
+ if (redrawNeeded) {
mSession.finishDrawing(mWindow);
}
}
@@ -580,7 +590,7 @@
void handleGetNewSurface() {
mNewSurfaceNeeded = true;
- updateWindow(false);
+ updateWindow(false, false);
}
/**
@@ -625,41 +635,6 @@
}
}
- public void dispatchKey(KeyEvent event) {
- SurfaceView surfaceView = mSurfaceView.get();
- if (surfaceView != null) {
- //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
- if (surfaceView.mSession != null && surfaceView.mSurface != null) {
- try {
- surfaceView.mSession.finishKey(surfaceView.mWindow);
- } catch (RemoteException ex) {
- }
- }
- }
- }
-
- public void dispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
- //if (mSession != null && mSurface != null) {
- // try {
- // //mSession.finishKey(mWindow);
- // } catch (RemoteException ex) {
- // }
- //}
- }
-
- public void dispatchTrackball(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
- //if (mSession != null && mSurface != null) {
- // try {
- // //mSession.finishKey(mWindow);
- // } catch (RemoteException ex) {
- // }
- //}
- }
-
public void dispatchAppVisibility(boolean visible) {
// The point of SurfaceView is to let the app control the surface.
}
@@ -686,7 +661,6 @@
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
private static final String LOG_TAG = "SurfaceHolder";
- private int mSaveCount;
public boolean isCreating() {
return mIsCreating;
@@ -732,7 +706,7 @@
mRequestedFormat = format;
if (mWindow != null) {
- updateWindow(false);
+ updateWindow(false, false);
}
}
@@ -749,7 +723,7 @@
case SURFACE_TYPE_PUSH_BUFFERS:
mRequestedType = type;
if (mWindow != null) {
- updateWindow(false);
+ updateWindow(false, false);
}
break;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3461cbf..3a3ad8c9 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.RectF;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -1585,6 +1586,87 @@
int mViewFlags;
/**
+ * The transform matrix for the View. This transform is calculated internally
+ * based on the rotation, scaleX, and scaleY properties. The identity matrix
+ * is used by default. Do *not* use this variable directly; instead call
+ * getMatrix(), which will automatically recalculate the matrix if necessary
+ * to get the correct matrix based on the latest rotation and scale properties.
+ */
+ private final Matrix mMatrix = new Matrix();
+
+ /**
+ * The transform matrix for the View. This transform is calculated internally
+ * based on the rotation, scaleX, and scaleY properties. The identity matrix
+ * is used by default. Do *not* use this variable directly; instead call
+ * getMatrix(), which will automatically recalculate the matrix if necessary
+ * to get the correct matrix based on the latest rotation and scale properties.
+ */
+ private Matrix mInverseMatrix;
+
+ /**
+ * An internal variable that tracks whether we need to recalculate the
+ * transform matrix, based on whether the rotation or scaleX/Y properties
+ * have changed since the matrix was last calculated.
+ */
+ private boolean mMatrixDirty = false;
+
+ /**
+ * An internal variable that tracks whether we need to recalculate the
+ * transform matrix, based on whether the rotation or scaleX/Y properties
+ * have changed since the matrix was last calculated.
+ */
+ private boolean mInverseMatrixDirty = true;
+
+ /**
+ * A variable that tracks whether we need to recalculate the
+ * transform matrix, based on whether the rotation or scaleX/Y properties
+ * have changed since the matrix was last calculated. This variable
+ * is only valid after a call to getMatrix().
+ */
+ boolean mMatrixIsIdentity = true;
+
+ /**
+ * The degrees rotation around the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotation = 0f;
+
+ /**
+ * The amount of scale in the x direction around the pivot point. A
+ * value of 1 means no scaling is applied.
+ */
+ @ViewDebug.ExportedProperty
+ private float mScaleX = 1f;
+
+ /**
+ * The amount of scale in the y direction around the pivot point. A
+ * value of 1 means no scaling is applied.
+ */
+ @ViewDebug.ExportedProperty
+ private float mScaleY = 1f;
+
+ /**
+ * The amount of scale in the x direction around the pivot point. A
+ * value of 1 means no scaling is applied.
+ */
+ @ViewDebug.ExportedProperty
+ private float mPivotX = 0f;
+
+ /**
+ * The amount of scale in the y direction around the pivot point. A
+ * value of 1 means no scaling is applied.
+ */
+ @ViewDebug.ExportedProperty
+ private float mPivotY = 0f;
+
+ /**
+ * The opacity of the View. This is a value from 0 to 1, where 0 means
+ * completely transparent and 1 means completely opaque.
+ */
+ @ViewDebug.ExportedProperty
+ private float mAlpha = 1f;
+
+ /**
* The distance in pixels from the left edge of this view's parent
* to the left edge of this view.
* {@hide}
@@ -3019,6 +3101,16 @@
}
/**
+ * Determine if this view has the FITS_SYSTEM_WINDOWS flag set.
+ * @return True if window has FITS_SYSTEM_WINDOWS set
+ *
+ * @hide
+ */
+ public boolean isFitsSystemWindowsFlagSet() {
+ return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
+ }
+
+ /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -4406,9 +4498,7 @@
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
- int slop = mTouchSlop;
- if ((x < 0 - slop) || (x >= getWidth() + slop) ||
- (y < 0 - slop) || (y >= getHeight() + slop)) {
+ if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
@@ -4754,6 +4844,234 @@
}
/**
+ * The transform matrix of this view, which is calculated based on the current
+ * roation, scale, and pivot properties.
+ *
+ * @see #getRotation()
+ * @see #getScaleX()
+ * @see #getScaleY()
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The current transform matrix for the view
+ */
+ public Matrix getMatrix() {
+ if (mMatrixDirty) {
+ // transform-related properties have changed since the last time someone
+ // asked for the matrix; recalculate it with the current values
+ mMatrix.reset();
+ mMatrix.setRotate(mRotation, mPivotX, mPivotY);
+ mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ mMatrixDirty = false;
+ mMatrixIsIdentity = mMatrix.isIdentity();
+ mInverseMatrixDirty = true;
+ }
+ return mMatrix;
+ }
+
+ /**
+ * Utility method to retrieve the inverse of the current mMatrix property.
+ * We cache the matrix to avoid recalculating it when transform properties
+ * have not changed.
+ *
+ * @return The inverse of the current matrix of this view.
+ */
+ Matrix getInverseMatrix() {
+ if (mInverseMatrixDirty) {
+ if (mInverseMatrix == null) {
+ mInverseMatrix = new Matrix();
+ }
+ mMatrix.invert(mInverseMatrix);
+ mInverseMatrixDirty = false;
+ }
+ return mInverseMatrix;
+ }
+
+ /**
+ * The degrees that the view is rotated around the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of rotation.
+ */
+ public float getRotation() {
+ return mRotation;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the pivot point.
+ *
+ * @param rotation The degrees of rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotation(float rotation) {
+ if (mRotation != rotation) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotation = rotation;
+ 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.
+ *
+ * @default 1.0f
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The scaling factor.
+ */
+ public float getScaleX() {
+ return mScaleX;
+ }
+
+ /**
+ * Sets 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 means that no scaling is applied.
+ *
+ * @param scaleX The scaling factor.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setScaleX(float scaleX) {
+ if (mScaleX != scaleX) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mScaleX = scaleX;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The amount that the view is scaled in y around the pivot point, as a proportion of
+ * the view's unscaled height. A value of 1, the default, means that no scaling is applied.
+ *
+ * @default 1.0f
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The scaling factor.
+ */
+ public float getScaleY() {
+ return mScaleY;
+ }
+
+ /**
+ * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of
+ * the view's unscaled width. A value of 1 means that no scaling is applied.
+ *
+ * @param scaleY The scaling factor.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setScaleY(float scaleY) {
+ if (mScaleY != scaleY) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mScaleY = scaleY;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The x location of the point around which the view is {@link #setRotation(float) rotated}
+ * and {@link #setScaleX(float) scaled}.
+ *
+ * @see #getRotation()
+ * @see #getScaleX()
+ * @see #getScaleY()
+ * @see #getPivotY()
+ * @return The x location of the pivot point.
+ */
+ public float getPivotX() {
+ return mPivotX;
+ }
+
+ /**
+ * Sets the x location of the point around which the view is
+ * {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
+ *
+ * @param pivotX The x location of the pivot point.
+ * @see #getRotation()
+ * @see #getScaleX()
+ * @see #getScaleY()
+ * @see #getPivotY()
+ */
+ public void setPivotX(float pivotX) {
+ if (mPivotX != pivotX) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mPivotX = pivotX;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The y location of the point around which the view is {@link #setRotation(float) rotated}
+ * and {@link #setScaleY(float) scaled}.
+ *
+ * @see #getRotation()
+ * @see #getScaleX()
+ * @see #getScaleY()
+ * @see #getPivotY()
+ * @return The y location of the pivot point.
+ */
+ public float getPivotY() {
+ return mPivotY;
+ }
+
+ /**
+ * Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
+ * and {@link #setScaleY(float) scaled}.
+ *
+ * @param pivotY The y location of the pivot point.
+ * @see #getRotation()
+ * @see #getScaleX()
+ * @see #getScaleY()
+ * @see #getPivotY()
+ */
+ public void setPivotY(float pivotY) {
+ if (mPivotY != pivotY) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mPivotY = pivotY;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The opacity of the view. This is a value from 0 to 1, where 0 means the view is
+ * completely transparent and 1 means the view is completely opaque.
+ *
+ * @default 1.0f
+ * @return The opacity of the view.
+ */
+ public float getAlpha() {
+ return mAlpha;
+ }
+
+ /**
+ * Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
+ * completely transparent and 1 means the view is completely opaque.
+ *
+ * @param alpha The opacity of the view.
+ */
+ public void setAlpha(float alpha) {
+ mAlpha = alpha;
+ invalidate();
+ }
+
+ /**
* Top position of this view relative to its parent.
*
* @return The top of this view, in pixels.
@@ -4764,6 +5082,37 @@
}
/**
+ * Sets the top position of this view relative to its parent.
+ *
+ * @param top The top of this view, in pixels.
+ */
+ public final void setTop(int top) {
+ if (top != mTop) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int minTop = Math.min(mTop, top);
+ location[0] = mLeft;
+ location[1] = minTop;
+ r.set(0, 0, mRight - mLeft, mBottom - minTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ }
+ mTop = top;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+ }
+
+ /**
* Bottom position of this view relative to its parent.
*
* @return The bottom of this view, in pixels.
@@ -4774,6 +5123,37 @@
}
/**
+ * Sets the bottom position of this view relative to its parent.
+ *
+ * @param bottom The bottom of this view, in pixels.
+ */
+ public final void setBottom(int bottom) {
+ if (bottom != mBottom) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int maxBottom = Math.max(mBottom, bottom);
+ location[0] = mLeft;
+ location[1] = mTop;
+ r.set(0, 0, mRight - mLeft, maxBottom - mTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ }
+ mBottom = bottom;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+ }
+
+ /**
* Left position of this view relative to its parent.
*
* @return The left edge of this view, in pixels.
@@ -4784,6 +5164,37 @@
}
/**
+ * Sets the left position of this view relative to its parent.
+ *
+ * @param left The bottom of this view, in pixels.
+ */
+ public final void setLeft(int left) {
+ if (left != mLeft) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int minLeft = Math.min(mLeft, left);
+ location[0] = minLeft;
+ location[1] = mTop;
+ r.set(0, 0, mRight - minLeft, mBottom - mTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ }
+ mLeft = left;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+ }
+
+ /**
* Right position of this view relative to its parent.
*
* @return The right edge of this view, in pixels.
@@ -4794,12 +5205,149 @@
}
/**
+ * Sets the right position of this view relative to its parent.
+ *
+ * @param right The bottom of this view, in pixels.
+ */
+ public final void setRight(int right) {
+ if (right != mRight) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int maxRight = Math.max(mRight, right);
+ location[0] = mLeft;
+ location[1] = mTop;
+ r.set(0, 0, maxRight - mLeft, mBottom - mTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ }
+ mRight = right;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+ }
+
+ /**
+ * The horizontal location of this view relative to its parent. This value is equivalent to the
+ * {@link #getLeft() left} property.
+ *
+ * @return The horizontal position of this view, in pixels.
+ */
+ public int getX() {
+ return mLeft;
+ }
+
+ /**
+ * Sets the horizontal location of this view relative to its parent. Setting this value will
+ * affect both the {@link #setLeft(int) left} and {@link #setRight(int) right} properties
+ * of this view.
+ *
+ * @param x The horizontal position of this view, in pixels.
+ */
+ public void setX(int x) {
+ offsetLeftAndRight(x - mLeft);
+ }
+
+ /**
+ * The vertical location of this view relative to its parent. This value is equivalent to the
+ * {@link #getTop() left} property.
+ *
+ * @return The vertical position of this view, in pixels.
+ */
+ public int getY() {
+ return mTop;
+ }
+
+ /**
+ * Sets the vertical location of this view relative to its parent. Setting this value will
+ * affect both the {@link #setTop(int) left} and {@link #setBottom(int) right} properties
+ * of this view.
+ *
+ * @param y The vertical position of this view, in pixels.
+ */
+ public void setY(int y) {
+ offsetTopAndBottom(y - mTop);
+ }
+
+ /**
* Hit rectangle in parent's coordinates
*
* @param outRect The hit rectangle of the view.
*/
public void getHitRect(Rect outRect) {
- outRect.set(mLeft, mTop, mRight, mBottom);
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity || mAttachInfo == null) {
+ outRect.set(mLeft, mTop, mRight, mBottom);
+ } else {
+ final RectF tmpRect = mAttachInfo.mTmpTransformRect;
+ tmpRect.set(-mPivotX, -mPivotY,
+ getWidth() - mPivotX, getHeight() - mPivotY);
+ m.mapRect(tmpRect);
+ outRect.set((int)tmpRect.left + mLeft, (int)tmpRect.top + mTop,
+ (int)tmpRect.right + mLeft, (int)tmpRect.bottom + mTop);
+ }
+ }
+
+ /**
+ * This method detects whether the given event is inside the view and, if so,
+ * handles it via the dispatchEvent(MotionEvent) method.
+ *
+ * @param ev The event that is being dispatched.
+ * @param parentX The x location of the event in the parent's coordinates.
+ * @param parentY The y location of the event in the parent's coordinates.
+ * @return true if the event was inside this view, false otherwise.
+ */
+ boolean dispatchTouchEvent(MotionEvent ev, float parentX, float parentY) {
+ float localX = parentX - mLeft;
+ float localY = parentY - mTop;
+ Matrix m = getMatrix();
+ if (!mMatrixIsIdentity && 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];
+ }
+ if (localX >= 0 && localY >= 0 &&
+ localX < (mRight - mLeft) && localY < (mBottom - mTop)) {
+ ev.setLocation(localX, localY);
+ return dispatchTouchEvent(ev);
+ }
+ return false;
+ }
+
+ /**
+ * Utility method to determine whether the given point, in local coordinates,
+ * is inside the view, where the area of the view is expanded by the slop factor.
+ * This method is called while processing touch-move events to determine if the event
+ * is still within the view.
+ */
+ private boolean pointInView(float localX, float localY, float slop) {
+ Matrix m = getMatrix();
+ if (!mMatrixIsIdentity && 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];
+ }
+ if (localX > -slop && localY > -slop &&
+ localX < ((mRight - mLeft) + slop) && localY < ((mBottom - mTop) + slop)) {
+ return true;
+ }
+ return false;
}
/**
@@ -4862,8 +5410,30 @@
* @param offset the number of pixels to offset the view by
*/
public void offsetTopAndBottom(int offset) {
- mTop += offset;
- mBottom += offset;
+ if (offset != 0) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int minTop = offset < 0 ? mTop + offset : mTop;
+ int maxBottom = offset < 0 ? mBottom : mBottom + offset;
+ location[0] = mLeft;
+ location[1] = minTop;
+ r.set(0, 0, mRight - mLeft, maxBottom - minTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ invalidate();
+ }
+ mTop += offset;
+ mBottom += offset;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
}
/**
@@ -4872,8 +5442,30 @@
* @param offset the numer of pixels to offset the view by
*/
public void offsetLeftAndRight(int offset) {
- mLeft += offset;
- mRight += offset;
+ if (offset != 0) {
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity) {
+ final ViewParent p = mParent;
+ if (p != null && mAttachInfo != null) {
+ final int[] location = mAttachInfo.mInvalidateChildLocation;
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ int minLeft = offset < 0 ? mLeft + offset : mLeft;
+ int maxRight = offset < 0 ? mRight : mRight + offset;
+ location[0] = minLeft;
+ location[1] = mTop;
+ r.set(0, 0, maxRight - minLeft, mBottom - mTop);
+ p.invalidateChildInParent(location, r);
+ }
+ } else {
+ invalidate();
+ }
+ mLeft += offset;
+ mRight += offset;
+ if (!mMatrixIsIdentity) {
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
}
/**
@@ -6848,16 +7440,16 @@
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
- drawTop = topFadeStrength >= 0.0f;
+ drawTop = topFadeStrength > 0.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
- drawBottom = bottomFadeStrength >= 0.0f;
+ drawBottom = bottomFadeStrength > 0.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
- drawLeft = leftFadeStrength >= 0.0f;
+ drawLeft = leftFadeStrength > 0.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
- drawRight = rightFadeStrength >= 0.0f;
+ drawRight = rightFadeStrength > 0.0f;
}
saveCount = canvas.getSaveCount();
@@ -7936,7 +8528,9 @@
* without resorting to another data structure.
*
* The specified key should be an id declared in the resources of the
- * application to ensure it is unique. Keys identified as belonging to
+ * application to ensure it is unique (see the <a
+ * href={@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>).
+ * Keys identified as belonging to
* the Android framework or not associated with any package will cause
* an {@link IllegalArgumentException} to be thrown.
*
@@ -9191,6 +9785,13 @@
*/
final int[] mInvalidateChildLocation = new int[2];
+
+ /**
+ * Global to the view hierarchy used as a temporary for dealing with
+ * x/y location when view is transformed.
+ */
+ final float[] mTmpTransformLocation = new float[2];
+
/**
* The view tree observer used to dispatch global events like
* layout, pre-draw, touch mode change, etc.
@@ -9227,6 +9828,16 @@
final Rect mTmpInvalRect = new Rect();
/**
+ * Temporary for use in computing hit areas with transformed views
+ */
+ final RectF mTmpTransformRect = new RectF();
+
+ /**
+ * Temporary for use in computing invalidation areas with transformed views
+ */
+ final float[] mTmpTransformBounds = new float[8];
+
+ /**
* Temporary list for use in collecting focusable descendents of a view.
*/
final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 34777ce..22ad27a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Matrix;
import com.android.internal.R;
import android.content.Context;
@@ -867,21 +868,10 @@
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
- child.getHitRect(frame);
- if (frame.contains(scrolledXInt, scrolledYInt)) {
- // offset the event to the view's coordinate system
- final float xc = scrolledXFloat - child.mLeft;
- final float yc = scrolledYFloat - child.mTop;
- ev.setLocation(xc, yc);
+ if (child.dispatchTouchEvent(ev, scrolledXFloat, scrolledYFloat)) {
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- if (child.dispatchTouchEvent(ev)) {
- // Event handled, we have a target now.
- mMotionTarget = child;
- return true;
- }
- // The event didn't get handled, try the next view.
- // Don't reset the event's location, it's not
- // necessary here.
+ mMotionTarget = child;
+ return true;
}
}
}
@@ -937,8 +927,21 @@
// finally offset the event to the target's coordinate system and
// dispatch the event.
- final float xc = scrolledXFloat - (float) target.mLeft;
- final float yc = scrolledYFloat - (float) target.mTop;
+ float xc;
+ float yc;
+ Matrix m = getMatrix();
+ if (mMatrixIsIdentity || mAttachInfo == null) {
+ xc = scrolledXFloat - (float) target.mLeft;
+ yc = scrolledYFloat - (float) target.mTop;
+ } else {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = scrolledXFloat;
+ localXY[1] = scrolledYFloat;
+ getInverseMatrix().mapPoints(localXY);
+ xc = localXY[0] - (float) target.mLeft;
+ yc = localXY[1] - (float) target.mTop;
+ }
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
@@ -1609,25 +1612,36 @@
}
}
- float alpha = 1.0f;
+ float alpha = child.getAlpha();
+ Matrix childMatrix = child.getMatrix();
- if (transformToApply != null) {
- if (concatMatrix) {
- int transX = 0;
- int transY = 0;
- if (hasNoCache) {
- transX = -sx;
- transY = -sy;
- }
- // Undo the scroll translation, apply the transformation matrix,
- // then redo the scroll translate to get the correct result.
- canvas.translate(-transX, -transY);
- canvas.concat(transformToApply.getMatrix());
- canvas.translate(transX, transY);
- mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+ if (transformToApply != null || alpha < 1.0f || !child.mMatrixIsIdentity) {
+ int transX = 0;
+ int transY = 0;
+ if (hasNoCache) {
+ transX = -sx;
+ transY = -sy;
}
-
- alpha = transformToApply.getAlpha();
+ if (transformToApply != null) {
+ if (concatMatrix) {
+ // Undo the scroll translation, apply the transformation matrix,
+ // then redo the scroll translate to get the correct result.
+ canvas.translate(-transX, -transY);
+ canvas.concat(transformToApply.getMatrix());
+ canvas.translate(transX, transY);
+ mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+ }
+ float transformAlpha = transformToApply.getAlpha();
+ if (transformAlpha < 1.0f) {
+ alpha *= transformToApply.getAlpha();
+ mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+ }
+ }
+ if (!child.mMatrixIsIdentity) {
+ canvas.translate(-transX, -transY);
+ canvas.concat(child.getMatrix());
+ canvas.translate(transX, transY);
+ }
if (alpha < 1.0f) {
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
}
@@ -2498,6 +2512,41 @@
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
+ Matrix childMatrix = child.getMatrix();
+ if (!childMatrix.isIdentity()) {
+ float[] boundingRectPoints = attachInfo.mTmpTransformBounds;
+ boundingRectPoints[0] = dirty.left; // upper left
+ boundingRectPoints[1] = dirty.top;
+ boundingRectPoints[2] = dirty.right; // upper right
+ boundingRectPoints[3] = dirty.top;
+ boundingRectPoints[4] = dirty.right; // lower right
+ boundingRectPoints[5] = dirty.bottom;
+ boundingRectPoints[6] = dirty.left; // lower left
+ boundingRectPoints[7] = dirty.bottom;
+ childMatrix.mapPoints(boundingRectPoints);
+ // find the mind/max points to get the bounding rect
+ float left = Float.MAX_VALUE;
+ float top = Float.MAX_VALUE;
+ float right = -Float.MAX_VALUE;
+ float bottom = -Float.MAX_VALUE;
+ for (int i = 0; i < 8; i += 2) {
+ float x = boundingRectPoints[i];
+ float y = boundingRectPoints[i+1];
+ if (x < left) {
+ left = x;
+ }
+ if (x > right) {
+ right = x;
+ }
+ if (y < top) {
+ top = y;
+ }
+ if (y > bottom) {
+ bottom = y;
+ }
+ }
+ dirty.set((int)left, (int)top, (int)(right + .5f), (int)(bottom + .5f));
+ }
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
@@ -2532,6 +2581,39 @@
}
parent = parent.invalidateChildInParent(location, dirty);
+ Matrix m = getMatrix();
+ if (!m.isIdentity()) {
+ float[] boundingRectPoints = {
+ dirty.left - mLeft, dirty.top - mTop, // upper left
+ dirty.right - mLeft, dirty.top - mTop, // upper right
+ dirty.right - mLeft, dirty.bottom - mTop, // lower right
+ dirty.left - mLeft, dirty.bottom - mTop // lower left
+ };
+ m.mapPoints(boundingRectPoints);
+ // find the mind/max points to get the bounding rect
+ float left = Float.MAX_VALUE;
+ float top = Float.MAX_VALUE;
+ float right = Float.MIN_VALUE;
+ float bottom = Float.MIN_VALUE;
+ for (int i = 0; i < 8; i += 2) {
+ float x = boundingRectPoints[i];
+ float y = boundingRectPoints[i+1];
+ if (x < left) {
+ left = x;
+ }
+ if (x > right) {
+ right = x;
+ }
+ if (y < top) {
+ top = y;
+ }
+ if (y > bottom) {
+ bottom = y;
+ }
+ }
+ dirty.set((int)left + mLeft, (int)top + mTop, (int)(right + .5f) + mLeft,
+ (int)(bottom + .5f) + mTop);
+ }
} while (parent != null);
}
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index e63ecef..a89e7f6 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -130,7 +130,7 @@
int mViewVisibility;
boolean mAppVisible = true;
- SurfaceHolder.Callback mSurfaceHolderCallback;
+ SurfaceHolder.Callback2 mSurfaceHolderCallback;
BaseSurfaceHolder mSurfaceHolder;
boolean mIsCreating;
boolean mDrawingAllowed;
@@ -336,6 +336,7 @@
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
+ mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
}
}
Resources resources = mView.getContext().getResources();
@@ -440,18 +441,16 @@
"Unable to add window -- unknown error code " + res);
}
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- if (view instanceof RootViewSurfaceTaker) {
- mInputQueueCallback =
- ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
- }
- if (mInputQueueCallback != null) {
- mInputQueue = new InputQueue(mInputChannel);
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- } else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
- }
+ if (view instanceof RootViewSurfaceTaker) {
+ mInputQueueCallback =
+ ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
+ }
+ if (mInputQueueCallback != null) {
+ mInputQueue = new InputQueue(mInputChannel);
+ mInputQueueCallback.onInputQueueCreated(mInputQueue);
+ } else {
+ InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+ Looper.myQueue());
}
view.assignParent(this);
@@ -652,7 +651,7 @@
// object is not initialized to its backing store, but soon it
// will be (assuming the window is visible).
attachInfo.mSurface = mSurface;
- attachInfo.mTranslucentWindow = lp.format != PixelFormat.OPAQUE;
+ attachInfo.mTranslucentWindow = PixelFormat.formatHasAlpha(lp.format);
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
attachInfo.mRecomputeGlobalAttributes = false;
@@ -818,8 +817,6 @@
if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
- lp.format = mSurfaceHolder.getRequestedFormat();
- lp.type = mSurfaceHolder.getRequestedType();
}
boolean hwIntialized = false;
@@ -1155,6 +1152,18 @@
Log.v("ViewRoot", "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
+ if (mSurfaceHolder != null && mSurface.isValid()) {
+ mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
+ SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ if (c instanceof SurfaceHolder.Callback2) {
+ ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
+ mSurfaceHolder);
+ }
+ }
+ }
+ }
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
@@ -1264,7 +1273,7 @@
if (mHwRenderer != null && mHwRenderer.isEnabled()) {
if (!dirty.isEmpty()) {
- mHwRenderer.draw(mView, mAttachInfo, mTranslator, yoff, mDensity, scalingRequired);
+ mHwRenderer.draw(mView, mAttachInfo, yoff);
}
if (scrolling) {
@@ -1596,16 +1605,12 @@
}
mSurface.release();
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- if (mInputChannel != null) {
- if (mInputQueueCallback != null) {
- mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
- mInputQueueCallback = null;
- } else {
- InputQueue.unregisterInputChannel(mInputChannel);
- }
- mInputChannel.dispose();
- mInputChannel = null;
+ if (mInputChannel != null) {
+ if (mInputQueueCallback != null) {
+ mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
+ mInputQueueCallback = null;
+ } else {
+ InputQueue.unregisterInputChannel(mInputChannel);
}
}
@@ -1614,13 +1619,11 @@
} catch (RemoteException e) {
}
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- // Dispose the input channel after removing the window so the Window Manager
- // doesn't interpret the input channel being closed as an abnormal termination.
- if (mInputChannel != null) {
- mInputChannel.dispose();
- mInputChannel = null;
- }
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
}
}
@@ -1720,109 +1723,22 @@
deliverKeyEvent((KeyEvent)msg.obj, true);
break;
case DISPATCH_POINTER: {
- MotionEvent event = (MotionEvent)msg.obj;
- boolean callWhenDone = msg.arg1 != 0;
-
- if (event == null) {
- long timeBeforeGettingEvents;
- if (MEASURE_LATENCY) {
- timeBeforeGettingEvents = System.nanoTime();
- }
-
- event = getPendingPointerMotionEvent();
-
- if (MEASURE_LATENCY && event != null) {
- lt.sample("9 Client got events ",
- System.nanoTime() - event.getEventTimeNano());
- lt.sample("8 Client getting events ",
- timeBeforeGettingEvents - event.getEventTimeNano());
- }
- callWhenDone = false;
- }
- if (event != null && mTranslator != null) {
- mTranslator.translateEventInScreenToAppWindow(event);
- }
+ MotionEvent event = (MotionEvent) msg.obj;
try {
- boolean handled;
- if (mView != null && mAdded && event != null) {
-
- // enter touch mode on the down
- boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
- if (isDown) {
- ensureTouchMode(true);
- }
- if(Config.LOGV) {
- captureMotionLog("captureDispatchPointer", event);
- }
- if (mCurScrollY != 0) {
- event.offsetLocation(0, mCurScrollY);
- }
- if (MEASURE_LATENCY) {
- lt.sample("A Dispatching TouchEvents",
- System.nanoTime() - event.getEventTimeNano());
- }
- handled = mView.dispatchTouchEvent(event);
- if (MEASURE_LATENCY) {
- lt.sample("B Dispatched TouchEvents ",
- System.nanoTime() - event.getEventTimeNano());
- }
- if (!handled && isDown) {
- int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
-
- final int edgeFlags = event.getEdgeFlags();
- int direction = View.FOCUS_UP;
- int x = (int)event.getX();
- int y = (int)event.getY();
- final int[] deltas = new int[2];
-
- if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
- direction = View.FOCUS_DOWN;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
- direction = View.FOCUS_UP;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- direction = View.FOCUS_RIGHT;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- direction = View.FOCUS_LEFT;
- }
-
- if (edgeFlags != 0 && mView instanceof ViewGroup) {
- View nearest = FocusFinder.getInstance().findNearestTouchable(
- ((ViewGroup) mView), x, y, direction, deltas);
- if (nearest != null) {
- event.offsetLocation(deltas[0], deltas[1]);
- event.setEdgeFlags(0);
- mView.dispatchTouchEvent(event);
- }
- }
- }
- }
+ deliverPointerEvent(event);
} finally {
- if (callWhenDone) {
- finishMotionEvent();
- }
- recycleMotionEvent(event);
+ event.recycle();
if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
- // Let the exception fall through -- the looper will catch
- // it and take care of the bad app for us.
}
} break;
- case DISPATCH_TRACKBALL:
- deliverTrackballEvent((MotionEvent)msg.obj, msg.arg1 != 0);
- break;
+ case DISPATCH_TRACKBALL: {
+ MotionEvent event = (MotionEvent) msg.obj;
+ try {
+ deliverTrackballEvent(event);
+ } finally {
+ event.recycle();
+ }
+ } break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
@@ -1945,61 +1861,12 @@
}
private void finishKeyEvent(KeyEvent event) {
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- if (mFinishedCallback != null) {
- mFinishedCallback.run();
- mFinishedCallback = null;
- }
- } else {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ if (mFinishedCallback != null) {
+ mFinishedCallback.run();
+ mFinishedCallback = null;
}
}
- private void finishMotionEvent() {
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be reachable with native input dispatch.");
- }
-
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
- }
-
- private void recycleMotionEvent(MotionEvent event) {
- if (event != null) {
- event.recycle();
- }
- }
-
- private MotionEvent getPendingPointerMotionEvent() {
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be reachable with native input dispatch.");
- }
-
- try {
- return sWindowSession.getPendingPointerMove(mWindow);
- } catch (RemoteException e) {
- return null;
- }
- }
-
- private MotionEvent getPendingTrackballMotionEvent() {
- if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be reachable with native input dispatch.");
- }
-
- try {
- return sWindowSession.getPendingTrackballMove(mWindow);
- } catch (RemoteException e) {
- return null;
- }
- }
-
-
/**
* Something in the current window tells us we need to change the touch mode. For
* example, we are not in touch mode, and the user touches the screen.
@@ -2121,42 +1988,95 @@
return false;
}
-
- private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) {
- if (event == null) {
- event = getPendingTrackballMotionEvent();
- callWhenDone = false;
+ private void deliverPointerEvent(MotionEvent event) {
+ if (mTranslator != null) {
+ mTranslator.translateEventInScreenToAppWindow(event);
}
+
+ boolean handled;
+ if (mView != null && mAdded) {
+ // enter touch mode on the down
+ boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
+ if (isDown) {
+ ensureTouchMode(true);
+ }
+ if(Config.LOGV) {
+ captureMotionLog("captureDispatchPointer", event);
+ }
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+ if (MEASURE_LATENCY) {
+ lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+ }
+ handled = mView.dispatchTouchEvent(event);
+ if (MEASURE_LATENCY) {
+ lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+ }
+ if (!handled && isDown) {
+ int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
+
+ final int edgeFlags = event.getEdgeFlags();
+ int direction = View.FOCUS_UP;
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+ final int[] deltas = new int[2];
+
+ if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
+ direction = View.FOCUS_DOWN;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
+ direction = View.FOCUS_UP;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ direction = View.FOCUS_RIGHT;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ direction = View.FOCUS_LEFT;
+ }
+
+ if (edgeFlags != 0 && mView instanceof ViewGroup) {
+ View nearest = FocusFinder.getInstance().findNearestTouchable(
+ ((ViewGroup) mView), x, y, direction, deltas);
+ if (nearest != null) {
+ event.offsetLocation(deltas[0], deltas[1]);
+ event.setEdgeFlags(0);
+ mView.dispatchTouchEvent(event);
+ }
+ }
+ }
+ }
+ }
+
+ private void deliverTrackballEvent(MotionEvent event) {
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
boolean handled = false;
- try {
- if (event == null) {
- handled = true;
- } else if (mView != null && mAdded) {
- handled = mView.dispatchTrackballEvent(event);
- if (!handled) {
- // we could do something here, like changing the focus
- // or something?
- }
- }
- } finally {
+ if (mView != null && mAdded) {
+ handled = mView.dispatchTrackballEvent(event);
if (handled) {
- if (callWhenDone) {
- finishMotionEvent();
- }
- recycleMotionEvent(event);
// If we reach this, we delivered a trackball event to mView and
// mView consumed it. Because we will not translate the trackball
// event into a key event, touch mode will not exit, so we exit
// touch mode here.
ensureTouchMode(false);
- //noinspection ReturnInsideFinallyBlock
return;
}
- // Let the exception fall through -- the looper will catch
- // it and take care of the bad app for us.
+
+ // Otherwise we could do something here, like changing the focus
+ // or something?
}
final TrackballAxis x = mTrackballAxisX;
@@ -2171,95 +2091,86 @@
mLastTrackballTime = curTime;
}
- try {
- final int action = event.getAction();
- final int metastate = event.getMetaState();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- x.reset(2);
- y.reset(2);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
- 0, metastate), false);
- break;
- case MotionEvent.ACTION_UP:
- x.reset(2);
- y.reset(2);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
- 0, metastate), false);
- break;
- }
+ final int action = event.getAction();
+ final int metastate = event.getMetaState();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ x.reset(2);
+ y.reset(2);
+ deliverKeyEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
+ 0, metastate), false);
+ break;
+ case MotionEvent.ACTION_UP:
+ x.reset(2);
+ y.reset(2);
+ deliverKeyEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
+ 0, metastate), false);
+ break;
+ }
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
- + x.step + " dir=" + x.dir + " acc=" + x.acceleration
- + " move=" + event.getX()
- + " / Y=" + y.position + " step="
- + y.step + " dir=" + y.dir + " acc=" + y.acceleration
- + " move=" + event.getY());
- final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
- final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+ if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
+ + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+ + " move=" + event.getX()
+ + " / Y=" + y.position + " step="
+ + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+ + " move=" + event.getY());
+ final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
+ final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
- // Generate DPAD events based on the trackball movement.
- // We pick the axis that has moved the most as the direction of
- // the DPAD. When we generate DPAD events for one axis, then the
- // other axis is reset -- we don't want to perform DPAD jumps due
- // to slight movements in the trackball when making major movements
- // along the other axis.
- int keycode = 0;
- int movement = 0;
- float accel = 1;
- if (xOff > yOff) {
- movement = x.generate((2/event.getXPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
- : KeyEvent.KEYCODE_DPAD_LEFT;
- accel = x.acceleration;
- y.reset(2);
- }
- } else if (yOff > 0) {
- movement = y.generate((2/event.getYPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
- : KeyEvent.KEYCODE_DPAD_UP;
- accel = y.acceleration;
- x.reset(2);
- }
+ // Generate DPAD events based on the trackball movement.
+ // We pick the axis that has moved the most as the direction of
+ // the DPAD. When we generate DPAD events for one axis, then the
+ // other axis is reset -- we don't want to perform DPAD jumps due
+ // to slight movements in the trackball when making major movements
+ // along the other axis.
+ int keycode = 0;
+ int movement = 0;
+ float accel = 1;
+ if (xOff > yOff) {
+ movement = x.generate((2/event.getXPrecision()));
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
+ : KeyEvent.KEYCODE_DPAD_LEFT;
+ accel = x.acceleration;
+ y.reset(2);
}
+ } else if (yOff > 0) {
+ movement = y.generate((2/event.getYPrecision()));
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
+ : KeyEvent.KEYCODE_DPAD_UP;
+ accel = y.acceleration;
+ x.reset(2);
+ }
+ }
- if (keycode != 0) {
- if (movement < 0) movement = -movement;
- int accelMovement = (int)(movement * accel);
- if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
- + " accelMovement=" + accelMovement
- + " accel=" + accel);
- if (accelMovement > movement) {
- if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
- + keycode);
- movement--;
- deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_MULTIPLE, keycode,
- accelMovement-movement, metastate), false);
- }
- while (movement > 0) {
- if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
- + keycode);
- movement--;
- curTime = SystemClock.uptimeMillis();
- deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, keycode, 0, metastate), false);
- }
- mLastTrackballTime = curTime;
+ if (keycode != 0) {
+ if (movement < 0) movement = -movement;
+ int accelMovement = (int)(movement * accel);
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+ + " accelMovement=" + accelMovement
+ + " accel=" + accel);
+ if (accelMovement > movement) {
+ if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ deliverKeyEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_MULTIPLE, keycode,
+ accelMovement-movement, metastate), false);
}
- } finally {
- if (callWhenDone) {
- finishMotionEvent();
- recycleMotionEvent(event);
+ while (movement > 0) {
+ if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ curTime = SystemClock.uptimeMillis();
+ deliverKeyEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
+ deliverKeyEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, keycode, 0, metastate), false);
}
- // Let the exception fall through -- the looper will catch
- // it and take care of the bad app for us.
+ mLastTrackballTime = curTime;
}
}
@@ -2704,45 +2615,20 @@
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, Runnable finishedCallback) {
mFinishedCallback = finishedCallback;
-
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- //noinspection ConstantConditions
- if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
- if (Config.LOGD) Log.d("keydisp",
- "===================================================");
- if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
- debug();
- if (Config.LOGD) Log.d("keydisp",
- "===================================================");
- }
- }
-
- Message msg = obtainMessage(DISPATCH_KEY);
- msg.obj = event;
-
- if (LOCAL_LOGV) Log.v(
- "ViewRoot", "sending key " + event + " to " + mView);
-
- sendMessageAtTime(msg, event.getEventTime());
+ dispatchKey(event);
}
public void handleTouch(MotionEvent event, Runnable finishedCallback) {
finishedCallback.run();
- Message msg = obtainMessage(DISPATCH_POINTER);
- msg.obj = event;
- msg.arg1 = 0;
- sendMessageAtTime(msg, event.getEventTime());
+ dispatchPointer(event);
}
public void handleTrackball(MotionEvent event, Runnable finishedCallback) {
finishedCallback.run();
- Message msg = obtainMessage(DISPATCH_TRACKBALL);
- msg.obj = event;
- msg.arg1 = 0;
- sendMessageAtTime(msg, event.getEventTime());
+ dispatchTrackball(event);
}
};
@@ -2768,22 +2654,18 @@
sendMessageAtTime(msg, event.getEventTime());
}
- public void dispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
+ public void dispatchPointer(MotionEvent event) {
Message msg = obtainMessage(DISPATCH_POINTER);
msg.obj = event;
- msg.arg1 = callWhenDone ? 1 : 0;
- sendMessageAtTime(msg, eventTime);
+ sendMessageAtTime(msg, event.getEventTime());
}
- public void dispatchTrackball(MotionEvent event, long eventTime,
- boolean callWhenDone) {
+ public void dispatchTrackball(MotionEvent event) {
Message msg = obtainMessage(DISPATCH_TRACKBALL);
msg.obj = event;
- msg.arg1 = callWhenDone ? 1 : 0;
- sendMessageAtTime(msg, eventTime);
+ sendMessageAtTime(msg, event.getEventTime());
}
-
+
public void dispatchAppVisibility(boolean visible) {
Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
msg.arg1 = visible ? 1 : 0;
@@ -2914,56 +2796,11 @@
}
}
- class EventCompletion extends Handler {
- final IWindow mWindow;
- final KeyEvent mKeyEvent;
- final boolean mIsPointer;
- final MotionEvent mMotionEvent;
-
- EventCompletion(Looper looper, IWindow window, KeyEvent key,
- boolean isPointer, MotionEvent motion) {
- super(looper);
- mWindow = window;
- mKeyEvent = key;
- mIsPointer = isPointer;
- mMotionEvent = motion;
- sendEmptyMessage(0);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (mKeyEvent != null) {
- finishKeyEvent(mKeyEvent);
- } else if (mIsPointer) {
- boolean didFinish;
- MotionEvent event = mMotionEvent;
- if (event == null) {
- event = getPendingPointerMotionEvent();
- didFinish = true;
- } else {
- didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
- }
- if (!didFinish) {
- finishMotionEvent();
- }
- } else {
- MotionEvent event = mMotionEvent;
- if (event == null) {
- event = getPendingTrackballMotionEvent();
- } else {
- finishMotionEvent();
- }
- }
- }
- }
-
static class W extends IWindow.Stub {
private final WeakReference<ViewRoot> mViewRoot;
- private final Looper mMainLooper;
public W(ViewRoot viewRoot, Context context) {
mViewRoot = new WeakReference<ViewRoot>(viewRoot);
- mMainLooper = context.getMainLooper();
}
public void resized(int w, int h, Rect coveredInsets,
@@ -2975,40 +2812,6 @@
}
}
- public void dispatchKey(KeyEvent event) {
- final ViewRoot viewRoot = mViewRoot.get();
- if (viewRoot != null) {
- viewRoot.dispatchKey(event);
- } else {
- Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!");
- viewRoot.new EventCompletion(mMainLooper, this, event, false, null);
- }
- }
-
- public void dispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- final ViewRoot viewRoot = mViewRoot.get();
- if (viewRoot != null) {
- if (MEASURE_LATENCY) {
- // Note: eventTime is in milliseconds
- ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000);
- }
- viewRoot.dispatchPointer(event, eventTime, callWhenDone);
- } else {
- viewRoot.new EventCompletion(mMainLooper, this, null, true, event);
- }
- }
-
- public void dispatchTrackball(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- final ViewRoot viewRoot = mViewRoot.get();
- if (viewRoot != null) {
- viewRoot.dispatchTrackball(event, eventTime, callWhenDone);
- } else {
- viewRoot.new EventCompletion(mMainLooper, this, null, false, event);
- }
- }
-
public void dispatchAppVisibility(boolean visible) {
final ViewRoot viewRoot = mViewRoot.get();
if (viewRoot != null) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9b31b9c..be681cc 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -485,7 +485,7 @@
* to operate (such as for receiving input events). The given SurfaceHolder
* callback will be used to tell you about state changes to the surface.
*/
- public abstract void takeSurface(SurfaceHolder.Callback callback);
+ public abstract void takeSurface(SurfaceHolder.Callback2 callback);
/**
* Take ownership of this window's InputQueue. The window will no
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index be1f6d2..33757f0 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -78,12 +78,6 @@
public final static int FLAG_BRIGHT_HERE = 0x20000000;
public final static boolean WATCH_POINTER = false;
-
- /**
- * Temporary flag added during the transition to the new native input dispatcher.
- * This will be removed when the old input dispatch code is deleted.
- */
- public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = true;
// flags for interceptKeyTq
/**
@@ -555,23 +549,26 @@
public Animation createForceHideEnterAnimation();
/**
- * Called from the key queue thread before a key is dispatched to the
- * input thread.
+ * Called from the input reader thread before a key is enqueued.
*
* <p>There are some actions that need to be handled here because they
* affect the power state of the device, for example, the power keys.
* Generally, it's best to keep as little as possible in the queue thread
* because it's the most fragile.
+ * @param whenNanos The event time in uptime nanoseconds.
+ * @param keyCode The key code.
+ * @param down True if the key is down.
+ * @param policyFlags The policy flags associated with the key.
+ * @param isScreenOn True if the screen is already on
*
- * @param event the raw input event as read from the driver
- * @param screenIsOn true if the screen is already on
* @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
* {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
*/
- public int interceptKeyTq(RawInputEvent event, boolean screenIsOn);
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags,
+ boolean isScreenOn);
/**
- * Called from the input thread before a key is dispatched to a window.
+ * Called from the input dispatcher thread before a key is dispatched to a window.
*
* <p>Allows you to define
* behavior for keys that can not be overridden by applications or redirect
@@ -583,16 +580,17 @@
*
* @param win The window that currently has focus. This is where the key
* event will normally go.
- * @param code Key code.
- * @param metaKeys bit mask of meta keys that are held.
- * @param down Is this a key press (true) or release (false)?
+ * @param action The key event action.
+ * @param flags The key event flags.
+ * @param keyCode The key code.
+ * @param metaState bit mask of meta keys that are held.
* @param repeatCount Number of times a key down has repeated.
- * @param flags event's flags.
+ * @param policyFlags The policy flags associated with the key.
* @return Returns true if the policy consumed the event and it should
* not be further dispatched.
*/
- public boolean interceptKeyTi(WindowState win, int code,
- int metaKeys, boolean down, int repeatCount, int flags);
+ public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
+ int keyCode, int metaState, int repeatCount, int policyFlags);
/**
* Called when layout of the windows is about to start.
@@ -701,85 +699,15 @@
* Return whether the screen is currently on.
*/
public boolean isScreenOn();
-
+
/**
- * Perform any initial processing of a low-level input event before the
- * window manager handles special keys and generates a high-level event
- * that is dispatched to the application.
- *
- * @param event The input event that has occurred.
- *
- * @return Return true if you have consumed the event and do not want
- * further processing to occur; return false for normal processing.
+ * Tell the policy that the lid switch has changed state.
+ * @param whenNanos The time when the change occurred in uptime nanoseconds.
+ * @param lidOpen True if the lid is now open.
*/
- public boolean preprocessInputEventTq(RawInputEvent event);
-
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
/**
- * Determine whether a given key code is used to cause an app switch
- * to occur (most often the HOME key, also often ENDCALL). If you return
- * true, then the system will go into a special key processing state
- * where it drops any pending events that it cans and adjusts timeouts to
- * try to get to this key as quickly as possible.
- *
- * <p>Note that this function is called from the low-level input queue
- * thread, with either/or the window or input lock held; be very careful
- * about what you do here. You absolutely should never acquire a lock
- * that you would ever hold elsewhere while calling out into the window
- * manager or view hierarchy.
- *
- * @param keycode The key that should be checked for performing an
- * app switch before delivering to the application.
- *
- * @return Return true if this is an app switch key and special processing
- * should happen; return false for normal processing.
- */
- public boolean isAppSwitchKeyTqTiLwLi(int keycode);
-
- /**
- * Determine whether a given key code is used for movement within a UI,
- * and does not generally cause actions to be performed (normally the DPAD
- * movement keys, NOT the DPAD center press key). This is called
- * when {@link #isAppSwitchKeyTiLi} returns true to remove any pending events
- * in the key queue that are not needed to switch applications.
- *
- * <p>Note that this function is called from the low-level input queue
- * thread; be very careful about what you do here.
- *
- * @param keycode The key that is waiting to be delivered to the
- * application.
- *
- * @return Return true if this is a purely navigation key and can be
- * dropped without negative consequences; return false to keep it.
- */
- public boolean isMovementKeyTi(int keycode);
-
- /**
- * Given the current state of the world, should this relative movement
- * wake up the device?
- *
- * @param device The device the movement came from.
- * @param classes The input classes associated with the device.
- * @param event The input event that occurred.
- * @return
- */
- public boolean isWakeRelMovementTq(int device, int classes,
- RawInputEvent event);
-
- /**
- * Given the current state of the world, should this absolute movement
- * wake up the device?
- *
- * @param device The device the movement came from.
- * @param classes The input classes associated with the device.
- * @param event The input event that occurred.
- * @return
- */
- public boolean isWakeAbsMovementTq(int device, int classes,
- RawInputEvent event);
-
- /**
* Tell the policy if anyone is requesting that keyguard not come on.
*
* @param enabled Whether keyguard can be on or not. does not actually
@@ -852,18 +780,6 @@
public void enableScreenAfterBoot();
/**
- * Returns true if the user's cheek has been pressed against the phone. This is
- * determined by comparing the event's size attribute with a threshold value.
- * For example for a motion event like down or up or move, if the size exceeds
- * the threshold, it is considered as cheek press.
- * @param ev the motion event generated when the cheek is pressed
- * against the phone
- * @return Returns true if the user's cheek has been pressed against the phone
- * screen resulting in an invalid motion event
- */
- public boolean isCheekPressedAgainstScreen(MotionEvent ev);
-
- /**
* Called every time the window manager is dispatching a pointer event.
*/
public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
@@ -876,13 +792,6 @@
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
/**
- * A special function that is called from the very low-level input queue
- * to provide feedback to the user. Currently only called for virtual
- * keys.
- */
- public void keyFeedbackFromInput(KeyEvent event);
-
- /**
* Called when we have stopped keeping the screen on because a window
* requesting this is no longer visible.
*/
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 7b1aab2..b021ded 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -301,6 +301,18 @@
}
/**
+ * Saves the contents of the frame as a web archive.
+ *
+ * @param basename The filename where the archive should be placed.
+ * @param autoname If false, takes filename to be a file. If true, filename
+ * is assumed to be a directory in which a filename will be
+ * chosen according to the url of the current page.
+ */
+ /* package */ String saveWebArchive(String basename, boolean autoname) {
+ return nativeSaveWebArchive(basename, autoname);
+ }
+
+ /**
* Go back or forward the number of steps given.
* @param steps A negative or positive number indicating the direction
* and number of steps to move.
@@ -1040,5 +1052,7 @@
*/
private native HashMap getFormTextData();
+ private native String nativeSaveWebArchive(String basename, boolean autoname);
+
private native void nativeOrientationChanged(int orientation);
}
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index c1ac180..6e9c70a 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -363,6 +363,7 @@
sMimeTypeMap.loadEntry("application/x-wais-source", "src");
sMimeTypeMap.loadEntry("application/x-wingz", "wz");
sMimeTypeMap.loadEntry("application/x-webarchive", "webarchive");
+ sMimeTypeMap.loadEntry("application/x-webarchive-xml", "webarchivexml");
sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt");
sMimeTypeMap.loadEntry("application/x-x509-user-cert", "crt");
sMimeTypeMap.loadEntry("application/x-xcf", "xcf");
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d5136ae9..0c8fc79 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -22,14 +22,15 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
-import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
+import android.graphics.DrawFilter;
import android.graphics.Interpolator;
import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
@@ -454,10 +455,6 @@
// true if onPause has been called (and not onResume)
private boolean mIsPaused;
- // true if, during a transition to a new page, we're delaying
- // deleting a root layer until there's something to draw of the new page.
- private boolean mDelayedDeleteRootLayer;
-
/**
* Customizable constant
*/
@@ -584,8 +581,8 @@
static final int SHOW_FULLSCREEN = 120;
static final int HIDE_FULLSCREEN = 121;
static final int DOM_FOCUS_CHANGED = 122;
- static final int IMMEDIATE_REPAINT_MSG_ID = 123;
- static final int SET_ROOT_LAYER_MSG_ID = 124;
+ static final int REPLACE_BASE_CONTENT = 123;
+ // 124;
static final int RETURN_LABEL = 125;
static final int FIND_AGAIN = 126;
static final int CENTER_FIT_RECT = 127;
@@ -593,6 +590,7 @@
static final int SET_SCROLLBAR_MODES = 129;
static final int SELECTION_STRING_CHANGED = 130;
static final int SET_TOUCH_HIGHLIGHT_RECTS = 131;
+ static final int SAVE_WEBARCHIVE_FINISHED = 132;
private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
@@ -633,15 +631,16 @@
"SHOW_FULLSCREEN", // = 120;
"HIDE_FULLSCREEN", // = 121;
"DOM_FOCUS_CHANGED", // = 122;
- "IMMEDIATE_REPAINT_MSG_ID", // = 123;
- "SET_ROOT_LAYER_MSG_ID", // = 124;
+ "REPLACE_BASE_CONTENT", // = 123;
+ "124", // = 124;
"RETURN_LABEL", // = 125;
"FIND_AGAIN", // = 126;
"CENTER_FIT_RECT", // = 127;
"REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
"SET_SCROLLBAR_MODES", // = 129;
"SELECTION_STRING_CHANGED", // = 130;
- "SET_TOUCH_HIGHLIGHT_RECTS" // = 131;
+ "SET_TOUCH_HIGHLIGHT_RECTS", // = 131;
+ "SAVE_WEBARCHIVE_FINISHED" // = 132;
};
// If the site doesn't use the viewport meta tag to specify the viewport,
@@ -1523,6 +1522,45 @@
}
/**
+ * Saves the current view as a web archive.
+ *
+ * @param filename The filename where the archive should be placed.
+ */
+ public void saveWebArchive(String filename) {
+ saveWebArchive(filename, false, null);
+ }
+
+ /* package */ static class SaveWebArchiveMessage {
+ SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
+ mBasename = basename;
+ mAutoname = autoname;
+ mCallback = callback;
+ }
+
+ /* package */ final String mBasename;
+ /* package */ final boolean mAutoname;
+ /* package */ final ValueCallback<String> mCallback;
+ /* package */ String mResultFile;
+ }
+
+ /**
+ * Saves the current view as a web archive.
+ *
+ * @param basename The filename where the archive should be placed.
+ * @param autoname If false, takes basename to be a file. If true, basename
+ * is assumed to be a directory in which a filename will be
+ * chosen according to the url of the current page.
+ * @param callback Called after the web archive has been saved. The
+ * parameter for onReceiveValue will either be the filename
+ * under which the file was saved, or null if saving the
+ * file failed.
+ */
+ public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
+ mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
+ new SaveWebArchiveMessage(basename, autoname, callback));
+ }
+
+ /**
* Stop the current load.
*/
public void stopLoading() {
@@ -1691,6 +1729,7 @@
public void clearView() {
mContentWidth = 0;
mContentHeight = 0;
+ nativeSetBaseLayer(0);
mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
}
@@ -1704,8 +1743,9 @@
* bounds of the view.
*/
public Picture capturePicture() {
- if (null == mWebViewCore) return null; // check for out of memory tab
- return mWebViewCore.copyContentPicture();
+ Picture result = new Picture();
+ nativeCopyBaseContentToPicture(result);
+ return result;
}
/**
@@ -3232,16 +3272,6 @@
}
}
- private void drawExtras(Canvas canvas, int extras, boolean animationsRunning) {
- // If mNativeClass is 0, we should not reach here, so we do not
- // need to check it again.
- if (animationsRunning) {
- canvas.setDrawFilter(mWebViewCore.mZoomFilter);
- }
- nativeDrawExtras(canvas, extras);
- canvas.setDrawFilter(null);
- }
-
private void onZoomAnimationStart() {
// If it is in password mode, turn it off so it does not draw misplaced.
if (inEditingMode() && nativeFocusCandidateIsPassword()) {
@@ -3268,6 +3298,18 @@
onZoomAnimationEnd();
}
+ private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
+ Paint.DITHER_FLAG |
+ Paint.SUBPIXEL_TEXT_FLAG;
+ private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
+ Paint.DITHER_FLAG;
+
+ private final DrawFilter mZoomFilter =
+ new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
+ // If we need to trade better quality for speed, set mScrollFilter to null
+ private final DrawFilter mScrollFilter =
+ new PaintFlagsDrawFilter(SCROLL_BITS, 0);
+
private void drawCoreAndCursorRing(Canvas canvas, int color,
boolean drawCursorRing) {
if (mDrawHistory) {
@@ -3275,6 +3317,7 @@
canvas.drawPicture(mHistoryPicture);
return;
}
+ if (mNativeClass == 0) return;
boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
boolean animateScroll = ((!mScroller.isFinished()
@@ -3309,10 +3352,7 @@
// we ask for a repaint.
invalidate();
}
- mWebViewCore.drawContentPicture(canvas, color,
- (mZoomManager.isZoomAnimating() || UIAnimationsRunning),
- animateScroll);
- if (mNativeClass == 0) return;
+
// decide which adornments to draw
int extras = DRAW_EXTRAS_NONE;
if (DebugFlags.WEB_VIEW) {
@@ -3332,7 +3372,18 @@
} else if (drawCursorRing) {
extras = DRAW_EXTRAS_CURSOR_RING;
}
- drawExtras(canvas, extras, UIAnimationsRunning);
+ DrawFilter df = null;
+ if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
+ df = mZoomFilter;
+ } else if (animateScroll) {
+ df = mScrollFilter;
+ }
+ canvas.setDrawFilter(df);
+ int content = nativeDraw(canvas, color, extras, true);
+ canvas.setDrawFilter(null);
+ if (content != 0) {
+ mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
+ }
if (extras == DRAW_EXTRAS_CURSOR_RING) {
if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
@@ -3368,7 +3419,7 @@
// Should only be called in UI thread
void switchOutDrawHistory() {
if (null == mWebViewCore) return; // CallbackProxy may trigger this
- if (mDrawHistory && mWebViewCore.pictureReady()) {
+ if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
mDrawHistory = false;
mHistoryPicture = null;
invalidate();
@@ -6036,16 +6087,14 @@
mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
break;
}
+ case REPLACE_BASE_CONTENT: {
+ nativeReplaceBaseContent(msg.arg1);
+ break;
+ }
case NEW_PICTURE_MSG_ID: {
- // If we've previously delayed deleting a root
- // layer, do it now.
- if (mDelayedDeleteRootLayer) {
- mDelayedDeleteRootLayer = false;
- nativeSetRootLayer(0);
- }
// called for new content
final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
-
+ nativeSetBaseLayer(draw.mBaseLayer);
final Point viewSize = draw.mViewPoint;
WebViewCore.ViewState viewState = draw.mViewState;
boolean isPictureAfterFirstLayout = viewState != null;
@@ -6166,23 +6215,6 @@
}
break;
}
- case IMMEDIATE_REPAINT_MSG_ID: {
- invalidate();
- break;
- }
- case SET_ROOT_LAYER_MSG_ID: {
- if (0 == msg.arg1) {
- // Null indicates deleting the old layer, but
- // don't actually do so until we've got the
- // new page to display.
- mDelayedDeleteRootLayer = true;
- } else {
- mDelayedDeleteRootLayer = false;
- nativeSetRootLayer(msg.arg1);
- invalidate();
- }
- break;
- }
case REQUEST_FORM_DATA:
AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
if (mWebTextView.isSameTextField(msg.arg1)) {
@@ -6444,6 +6476,13 @@
}
break;
+ case SAVE_WEBARCHIVE_FINISHED:
+ SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
+ if (saveMessage.mCallback != null) {
+ saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
+ }
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -6944,7 +6983,7 @@
* @hide only needs to be accessible to Browser and testing
*/
public void drawPage(Canvas canvas) {
- mWebViewCore.drawContentPicture(canvas, 0, false, false);
+ nativeDraw(canvas, 0, 0, false);
}
/**
@@ -6988,7 +7027,15 @@
private native boolean nativeCursorWantsKeyEvents();
private native void nativeDebugDump();
private native void nativeDestroy();
- private native void nativeDrawExtras(Canvas canvas, int extra);
+
+ /**
+ * Draw the picture set with a background color and extra. If
+ * "splitIfNeeded" is true and the return value is not 0, the return value
+ * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
+ * native allocation can be freed.
+ */
+ private native int nativeDraw(Canvas canvas, int color, int extra,
+ boolean splitIfNeeded);
private native void nativeDumpDisplayTree(String urlOrNull);
private native boolean nativeEvaluateLayersAnimations();
private native void nativeExtendSelection(int x, int y);
@@ -7053,7 +7100,10 @@
private native void nativeSetFindIsUp(boolean isUp);
private native void nativeSetFollowedLink(boolean followed);
private native void nativeSetHeightCanMeasure(boolean measure);
- private native void nativeSetRootLayer(int layer);
+ private native void nativeSetBaseLayer(int layer);
+ private native void nativeReplaceBaseContent(int content);
+ private native void nativeCopyBaseContentToPicture(Picture pict);
+ private native boolean nativeHasContent();
private native void nativeSetSelectionPointer(boolean set,
float scale, int x, int y);
private native boolean nativeStartSelection(int x, int y);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 54699d6..21af570 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -19,11 +19,6 @@
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
-import android.graphics.Canvas;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -33,7 +28,6 @@
import android.os.Message;
import android.os.Process;
import android.provider.MediaStore;
-import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
@@ -443,35 +437,18 @@
private native void nativeClearContent();
/**
- * Create a flat picture from the set of pictures.
- */
- private native void nativeCopyContentToPicture(Picture picture);
-
- /**
- * Draw the picture set with a background color. Returns true
- * if some individual picture took too long to draw and can be
- * split into parts. Called from the UI thread.
- */
- private native boolean nativeDrawContent(Canvas canvas, int color);
-
- /**
- * check to see if picture is blank and in progress
- */
- private native boolean nativePictureReady();
-
- /**
* Redraw a portion of the picture set. The Point wh returns the
* width and height of the overall picture.
*/
- private native boolean nativeRecordContent(Region invalRegion, Point wh);
+ private native int nativeRecordContent(Region invalRegion, Point wh);
private native boolean nativeFocusBoundsChanged();
/**
- * Splits slow parts of the picture set. Called from the webkit
- * thread after nativeDrawContent returns true.
+ * Splits slow parts of the picture set. Called from the webkit thread after
+ * WebView.nativeDraw() returns content to be split.
*/
- private native void nativeSplitContent();
+ private native void nativeSplitContent(int content);
private native boolean nativeKey(int keyCode, int unichar,
int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
@@ -797,6 +774,7 @@
"ON_RESUME", // = 144
"FREE_MEMORY", // = 145
"VALID_NODE_BOUNDS", // = 146
+ "SAVE_WEBARCHIVE", // = 147
};
class EventHub {
@@ -863,6 +841,9 @@
static final int FREE_MEMORY = 145;
static final int VALID_NODE_BOUNDS = 146;
+ // Load and save web archives
+ static final int SAVE_WEBARCHIVE = 147;
+
// Network-based messaging
static final int CLEAR_SSL_PREF_TABLE = 150;
@@ -1323,6 +1304,15 @@
nativeSetJsFlags((String)msg.obj);
break;
+ case SAVE_WEBARCHIVE:
+ WebView.SaveWebArchiveMessage saveMessage =
+ (WebView.SaveWebArchiveMessage)msg.obj;
+ saveMessage.mResultFile =
+ saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
+ mWebView.mPrivateHandler.obtainMessage(
+ WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
+ break;
+
case GEOLOCATION_PERMISSIONS_PROVIDE:
GeolocationPermissionsData data =
(GeolocationPermissionsData) msg.obj;
@@ -1336,7 +1326,9 @@
break;
case SPLIT_PICTURE_SET:
- nativeSplitContent();
+ nativeSplitContent(msg.arg1);
+ mWebView.mPrivateHandler.obtainMessage(
+ WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
mSplitPictureIsScheduled = false;
break;
@@ -1622,6 +1614,13 @@
mBrowserFrame.loadUrl(url, extraHeaders);
}
+ private String saveWebArchive(String filename, boolean autoname) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
+ }
+ return mBrowserFrame.saveWebArchive(filename, autoname);
+ }
+
private void key(KeyEvent evt, boolean isDown) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
@@ -1736,6 +1735,14 @@
return usedQuota;
}
+ // called from UI thread
+ void splitContent(int content) {
+ if (!mSplitPictureIsScheduled) {
+ mSplitPictureIsScheduled = true;
+ sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
+ }
+ }
+
// Used to avoid posting more than one draw message.
private boolean mDrawIsScheduled;
@@ -1762,9 +1769,11 @@
static class DrawData {
DrawData() {
+ mBaseLayer = 0;
mInvalRegion = new Region();
mWidthHeight = new Point();
}
+ int mBaseLayer;
Region mInvalRegion;
Point mViewPoint;
Point mWidthHeight;
@@ -1778,8 +1787,8 @@
mDrawIsScheduled = false;
DrawData draw = new DrawData();
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
- if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
- == false) {
+ draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight);
+ if (draw.mBaseLayer == 0) {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
return;
}
@@ -1812,51 +1821,6 @@
}
}
- ///////////////////////////////////////////////////////////////////////////
- // These are called from the UI thread, not our thread
-
- static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
- Paint.DITHER_FLAG |
- Paint.SUBPIXEL_TEXT_FLAG;
- static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
- Paint.DITHER_FLAG;
-
- final DrawFilter mZoomFilter =
- new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
- // If we need to trade better quality for speed, set mScrollFilter to null
- final DrawFilter mScrollFilter =
- new PaintFlagsDrawFilter(SCROLL_BITS, 0);
-
- /* package */ void drawContentPicture(Canvas canvas, int color,
- boolean animatingZoom,
- boolean animatingScroll) {
- DrawFilter df = null;
- if (animatingZoom) {
- df = mZoomFilter;
- } else if (animatingScroll) {
- df = mScrollFilter;
- }
- canvas.setDrawFilter(df);
- boolean tookTooLong = nativeDrawContent(canvas, color);
- canvas.setDrawFilter(null);
- if (tookTooLong && mSplitPictureIsScheduled == false) {
- mSplitPictureIsScheduled = true;
- sendMessage(EventHub.SPLIT_PICTURE_SET);
- }
- }
-
- /* package */ synchronized boolean pictureReady() {
- return 0 != mNativeClass ? nativePictureReady() : false;
- }
-
- /*package*/ synchronized Picture copyContentPicture() {
- Picture result = new Picture();
- if (0 != mNativeClass) {
- nativeCopyContentToPicture(result);
- }
- return result;
- }
-
static void reducePriority() {
// remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
@@ -2038,24 +2002,6 @@
mRepaintScheduled = false;
}
- // called by JNI
- private void sendImmediateRepaint() {
- if (mWebView != null && !mRepaintScheduled) {
- mRepaintScheduled = true;
- Message.obtain(mWebView.mPrivateHandler,
- WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget();
- }
- }
-
- // called by JNI
- private void setRootLayer(int layer) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.SET_ROOT_LAYER_MSG_ID,
- layer, 0).sendToTarget();
- }
- }
-
/* package */ WebView getWebView() {
return mWebView;
}
diff --git a/core/java/android/webruntime/WebRuntimeActivity.java b/core/java/android/webruntime/WebRuntimeActivity.java
index 07d9908..ec8c60c 100644
--- a/core/java/android/webruntime/WebRuntimeActivity.java
+++ b/core/java/android/webruntime/WebRuntimeActivity.java
@@ -141,7 +141,7 @@
@Override
public void onPageFinished(WebView view, String url) {
- if (mSplashScreen.getVisibility() == View.VISIBLE) {
+ if (mSplashScreen != null && mSplashScreen.getVisibility() == View.VISIBLE) {
mSplashScreen.setVisibility(View.GONE);
mSplashScreen = null;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1d5c5f9..70c1e15 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2651,7 +2651,8 @@
mMode = MOVE_OFFSET;
final int firstPos = mFirstPosition;
- final int lastPos = firstPos + getChildCount() - 1;
+ final int childCount = getChildCount();
+ final int lastPos = firstPos + childCount - 1;
int viewTravelCount = 0;
if (position < firstPos) {
@@ -2665,7 +2666,10 @@
return;
}
- mScrollDuration = SCROLL_DURATION / viewTravelCount;
+ // Estimate how many screens we should travel
+ final float screenTravelCount = viewTravelCount / childCount;
+ mScrollDuration = (int) (SCROLL_DURATION / screenTravelCount);
+ mLastSeenPos = INVALID_POSITION;
post(this);
}
@@ -2674,6 +2678,10 @@
}
public void run() {
+ if (mTouchMode != TOUCH_MODE_FLING && mLastSeenPos != INVALID_POSITION) {
+ return;
+ }
+
final int listHeight = getHeight();
final int firstPos = mFirstPosition;
@@ -2799,23 +2807,17 @@
}
case MOVE_OFFSET: {
- if (firstPos == mLastSeenPos) {
- // No new views, let things keep going.
- post(this);
- }
+ final int childCount = getChildCount();
+ mLastSeenPos = firstPos;
final int position = mTargetPos;
- final int lastPos = firstPos + getChildCount() - 1;
+ final int lastPos = firstPos + childCount - 1;
if (position < firstPos) {
- final View firstView = getChildAt(0);
- final int firstViewPixelsShowing = firstView.getHeight() + firstView.getTop();
- smoothScrollBy(-firstViewPixelsShowing, mScrollDuration);
+ smoothScrollBy(-getHeight(), mScrollDuration);
post(this);
} else if (position > lastPos) {
- final View lastView = getChildAt(getChildCount() - 1);
- final int lastViewPixelsShowing = lastView.getBottom() - lastView.getHeight();
- smoothScrollBy(lastViewPixelsShowing, mScrollDuration);
+ smoothScrollBy(getHeight(), mScrollDuration);
post(this);
} else {
// On-screen, just scroll.
@@ -3249,6 +3251,7 @@
mResurrectToPosition = INVALID_POSITION;
removeCallbacks(mFlingRunnable);
+ removeCallbacks(mPositionScroller);
mTouchMode = TOUCH_MODE_REST;
clearScrollingCache();
mSpecificTop = selectedTop;
@@ -4284,7 +4287,7 @@
final ArrayList<View> scrap = mScrapViews[i];
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
- scrap.get(i).setDrawingCacheBackgroundColor(color);
+ scrap.get(j).setDrawingCacheBackgroundColor(color);
}
}
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index e9de385..5c34c2c 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -78,7 +78,7 @@
private AdapterView.OnItemClickListener mItemClickListener;
private AdapterView.OnItemSelectedListener mItemSelectedListener;
- private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
+ private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
private final PopupScrollListener mScrollListener = new PopupScrollListener();
private final ListSelectorHider mHideSelector = new ListSelectorHider();
@@ -432,6 +432,19 @@
}
/**
+ * Sets the width of the popup window by the size of its content. The final width may be
+ * larger to accommodate styled window dressing.
+ *
+ * @param width Desired width of content in pixels.
+ */
+ public void setContentWidth(int width) {
+ Drawable popupBackground = mPopup.getBackground();
+ if (popupBackground != null) {
+ mDropDownWidth = popupBackground.getIntrinsicWidth() + width;
+ }
+ }
+
+ /**
* @return The height of the popup window in pixels.
*/
public int getHeight() {
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 91f4946..86913ae 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2968,7 +2968,7 @@
// fill a rect where the dividers would be for non-selectable items
// If the list is opaque and the background is also opaque, we don't
// need to draw anything since the background will do it for us
- final boolean fillForMissingDividers = drawDividers && isOpaque() && !super.isOpaque();
+ final boolean fillForMissingDividers = isOpaque() && !super.isOpaque();
if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) {
mDividerPaint = new Paint();
@@ -2978,7 +2978,7 @@
final int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
if (!mStackFromBottom) {
- int bottom = 0;
+ int bottom;
final int scrollY = mScrollY;
for (int i = 0; i < count; i++) {
@@ -2987,18 +2987,16 @@
View child = getChildAt(i);
bottom = child.getBottom();
// Don't draw dividers next to items that are not enabled
- if (drawDividers) {
- if ((areAllItemsSelectable ||
- (adapter.isEnabled(first + i) && (i == count - 1 ||
- adapter.isEnabled(first + i + 1))))) {
- bounds.top = bottom;
- bounds.bottom = bottom + dividerHeight;
- drawDivider(canvas, bounds, i);
- } else if (fillForMissingDividers) {
- bounds.top = bottom;
- bounds.bottom = bottom + dividerHeight;
- canvas.drawRect(bounds, paint);
- }
+ if ((areAllItemsSelectable ||
+ (adapter.isEnabled(first + i) && (i == count - 1 ||
+ adapter.isEnabled(first + i + 1))))) {
+ bounds.top = bottom;
+ bounds.bottom = bottom + dividerHeight;
+ drawDivider(canvas, bounds, i);
+ } else if (fillForMissingDividers) {
+ bounds.top = bottom;
+ bounds.bottom = bottom + dividerHeight;
+ canvas.drawRect(bounds, paint);
}
}
}
@@ -3014,7 +3012,7 @@
View child = getChildAt(i);
top = child.getTop();
// Don't draw dividers next to items that are not enabled
- if (drawDividers && top > listTop) {
+ if (top > listTop) {
if ((areAllItemsSelectable ||
(adapter.isEnabled(first + i) && (i == count - 1 ||
adapter.isEnabled(first + i + 1))))) {
@@ -3034,7 +3032,7 @@
}
}
- if (count > 0 && scrollY > 0 && drawDividers) {
+ if (count > 0 && scrollY > 0) {
bounds.top = listBottom;
bounds.bottom = listBottom + dividerHeight;
drawDivider(canvas, bounds, -1);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17967d9..0ce8164 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -61,6 +61,7 @@
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.text.method.ArrowKeyMovementMethod;
import android.text.method.DateKeyListener;
import android.text.method.DateTimeKeyListener;
import android.text.method.DialerKeyListener;
@@ -89,10 +90,11 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
import android.view.ViewRoot;
import android.view.ViewTreeObserver;
-import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
@@ -185,7 +187,7 @@
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
- static final String TAG = "TextView";
+ static final String LOG_TAG = "TextView";
static final boolean DEBUG_EXTRACT = false;
private static int PRIORITY = 100;
@@ -696,9 +698,9 @@
try {
setInputExtras(a.getResourceId(attr, 0));
} catch (XmlPullParserException e) {
- Log.w("TextView", "Failure reading input extras", e);
+ Log.w(LOG_TAG, "Failure reading input extras", e);
} catch (IOException e) {
- Log.w("TextView", "Failure reading input extras", e);
+ Log.w(LOG_TAG, "Failure reading input extras", e);
}
break;
}
@@ -924,6 +926,8 @@
setFocusable(focusable);
setClickable(clickable);
setLongClickable(longClickable);
+
+ prepareCursorController();
}
private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -1129,6 +1133,7 @@
setText(mText);
fixFocusableAndClickableSettings();
+ prepareCursorController();
}
private void fixFocusableAndClickableSettings() {
@@ -2371,8 +2376,8 @@
int end = 0;
if (mText != null) {
- start = Selection.getSelectionStart(mText);
- end = Selection.getSelectionEnd(mText);
+ start = getSelectionStart();
+ end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
save = true;
@@ -2444,7 +2449,7 @@
restored = "(restored) ";
}
- Log.e("TextView", "Saved cursor position " + ss.selStart +
+ Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
"/" + ss.selEnd + " out of range for " + restored +
"text " + mText);
} else {
@@ -2696,6 +2701,9 @@
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
}
+
+ // Depends on canSelectText, which depends on text
+ prepareCursorController();
}
/**
@@ -3009,7 +3017,7 @@
} else {
input = TextKeyListener.getInstance();
}
- mInputType = type;
+ setRawInputType(type);
if (direct) mInput = input;
else {
setKeyListenerOnly(input);
@@ -3613,7 +3621,7 @@
}
private void invalidateCursor() {
- int where = Selection.getSelectionEnd(mText);
+ int where = getSelectionEnd();
invalidateCursor(where, where, where);
}
@@ -3689,7 +3697,18 @@
boolean changed = false;
if (mMovement != null) {
- int curs = Selection.getSelectionEnd(mText);
+ /* This code also provides auto-scrolling when a cursor is moved using a
+ * CursorController (insertion point or selection limits).
+ * For selection, ensure start or end is visible depending on controller's state.
+ */
+ int curs = getSelectionEnd();
+ if (mSelectionModifierCursorController != null) {
+ SelectionModifierCursorController selectionController =
+ (SelectionModifierCursorController) mSelectionModifierCursorController;
+ if (selectionController.isSelectionStartDragged()) {
+ curs = getSelectionStart();
+ }
+ }
/*
* TODO: This should really only keep the end in view if
@@ -3982,8 +4001,8 @@
// XXX This is not strictly true -- a program could set the
// selection manually if it really wanted to.
if (mMovement != null && (isFocused() || isPressed())) {
- selStart = Selection.getSelectionStart(mText);
- selEnd = Selection.getSelectionEnd(mText);
+ selStart = getSelectionStart();
+ selEnd = getSelectionEnd();
if (mCursorVisible && selStart >= 0 && isEnabled()) {
if (mHighlightPath == null)
@@ -4089,6 +4108,13 @@
*/
canvas.restore();
+
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.draw(canvas);
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.draw(canvas);
+ }
}
@Override
@@ -4503,8 +4529,8 @@
outAttrs.hintText = mHint;
if (mText instanceof Editable) {
InputConnection ic = new EditableInputConnection(this);
- outAttrs.initialSelStart = Selection.getSelectionStart(mText);
- outAttrs.initialSelEnd = Selection.getSelectionEnd(mText);
+ outAttrs.initialSelStart = getSelectionStart();
+ outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
return ic;
}
@@ -4589,8 +4615,8 @@
outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
}
outText.startOffset = 0;
- outText.selectionStart = Selection.getSelectionStart(content);
- outText.selectionEnd = Selection.getSelectionEnd(content);
+ outText.selectionStart = getSelectionStart();
+ outText.selectionEnd = getSelectionEnd();
return true;
}
return false;
@@ -4607,7 +4633,7 @@
if (req != null) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
- if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Retrieving extracted start="
+ ims.mChangedStart + " end=" + ims.mChangedEnd
+ " delta=" + ims.mChangedDelta);
if (ims.mChangedStart < 0 && !contentChanged) {
@@ -4615,7 +4641,7 @@
}
if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
ims.mChangedDelta, ims.mTmpExtracted)) {
- if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Reporting extracted start="
+ ims.mTmpExtracted.partialStartOffset
+ " end=" + ims.mTmpExtracted.partialEndOffset
+ ": " + ims.mTmpExtracted.text);
@@ -4769,7 +4795,7 @@
void updateAfterEdit() {
invalidate();
- int curs = Selection.getSelectionStart(mText);
+ int curs = getSelectionStart();
if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) ==
Gravity.BOTTOM) {
@@ -4784,7 +4810,7 @@
makeBlink();
}
}
-
+
checkForResize();
}
@@ -4913,7 +4939,6 @@
w, alignment, mSpacingMult, mSpacingAdd,
boring, mIncludePad);
}
- // Log.e("aaa", "Boring: " + mTransformed);
mSavedLayout = (BoringLayout) mLayout;
} else if (shouldEllipsize && boring.width <= w) {
@@ -5508,7 +5533,7 @@
// FIXME: Is it okay to truncate this, or should we round?
final int x = (int)mLayout.getPrimaryHorizontal(offset);
final int top = mLayout.getLineTop(line);
- final int bottom = mLayout.getLineTop(line+1);
+ final int bottom = mLayout.getLineTop(line + 1);
int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
@@ -5645,8 +5670,8 @@
// viewport coordinates, but requestRectangleOnScreen()
// is in terms of content coordinates.
- Rect r = new Rect();
- getInterestingRect(r, x, top, bottom, line);
+ Rect r = new Rect(x, top, x + 1, bottom);
+ getInterestingRect(r, line);
r.offset(mScrollX, mScrollY);
if (requestRectangleOnScreen(r)) {
@@ -5669,8 +5694,8 @@
if (!(mText instanceof Spannable)) {
return false;
}
- int start = Selection.getSelectionStart(mText);
- int end = Selection.getSelectionEnd(mText);
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
if (start != end) {
return false;
}
@@ -5680,7 +5705,7 @@
int line = mLayout.getLineForOffset(start);
final int top = mLayout.getLineTop(line);
- final int bottom = mLayout.getLineTop(line+1);
+ final int bottom = mLayout.getLineTop(line + 1);
final int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
int vslack = (bottom - top) / 2;
if (vslack > vspace / 4)
@@ -5730,22 +5755,28 @@
}
}
- private void getInterestingRect(Rect r, int h, int top, int bottom,
- int line) {
+ private void getInterestingRect(Rect r, int line) {
+ convertFromViewportToContentCoordinates(r);
+
+ // Rectangle can can be expanded on first and last line to take
+ // padding into account.
+ // TODO Take left/right padding into account too?
+ if (line == 0) r.top -= getExtendedPaddingTop();
+ if (line == mLayout.getLineCount() - 1) r.bottom += getExtendedPaddingBottom();
+ }
+
+ private void convertFromViewportToContentCoordinates(Rect r) {
int paddingTop = getExtendedPaddingTop();
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
paddingTop += getVerticalOffset(false);
}
- top += paddingTop;
- bottom += paddingTop;
- h += getCompoundPaddingLeft();
+ r.top += paddingTop;
+ r.bottom += paddingTop;
- if (line == 0)
- top -= getExtendedPaddingTop();
- if (line == mLayout.getLineCount() - 1)
- bottom += getExtendedPaddingBottom();
+ int paddingLeft = getCompoundPaddingLeft();
+ r.left += paddingLeft;
+ r.right += paddingLeft;
- r.set(h, top, h+1, bottom);
r.offset(-mScrollX, -mScrollY);
}
@@ -5913,6 +5944,7 @@
} else if (mBlink != null) {
mBlink.removeCallbacks(mBlink);
}
+ prepareCursorController();
}
private boolean canMarquee() {
@@ -6327,7 +6359,7 @@
}
}
} else {
- if (DEBUG_EXTRACT) Log.v(TAG, "Span change outside of batch: "
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "Span change outside of batch: "
+ oldStart + "-" + oldEnd + ","
+ newStart + "-" + newEnd + what);
ims.mContentChanged = true;
@@ -6343,7 +6375,7 @@
public void beforeTextChanged(CharSequence buffer, int start,
int before, int after) {
- if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start
+ " before=" + before + " after=" + after + ": " + buffer);
if (AccessibilityManager.getInstance(mContext).isEnabled()
@@ -6356,7 +6388,7 @@
public void onTextChanged(CharSequence buffer, int start,
int before, int after) {
- if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start
+ " before=" + before + " after=" + after + ": " + buffer);
TextView.this.handleTextChanged(buffer, start, before, after);
@@ -6366,10 +6398,15 @@
sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
mBeforeText = null;
}
+
+ // TODO. The cursor controller should hide as soon as text is typed.
+ // But this method is also used for cosmetic changes (underline current word when
+ // spell corrections are displayed. There is currently no way to make the difference
+ // between these cosmetic changes and actual text modifications.
}
public void afterTextChanged(Editable buffer) {
- if (DEBUG_EXTRACT) Log.v(TAG, "afterTextChanged: " + buffer);
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer);
TextView.this.sendAfterTextChanged(buffer);
if (MetaKeyKeyListener.getMetaState(buffer,
@@ -6380,19 +6417,19 @@
public void onSpanChanged(Spannable buf,
Object what, int s, int e, int st, int en) {
- if (DEBUG_EXTRACT) Log.v(TAG, "onSpanChanged s=" + s + " e=" + e
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e
+ " st=" + st + " en=" + en + " what=" + what + ": " + buf);
TextView.this.spanChange(buf, what, s, st, e, en);
}
public void onSpanAdded(Spannable buf, Object what, int s, int e) {
- if (DEBUG_EXTRACT) Log.v(TAG, "onSpanAdded s=" + s + " e=" + e
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e
+ " what=" + what + ": " + buf);
TextView.this.spanChange(buf, what, -1, s, -1, e);
}
public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
- if (DEBUG_EXTRACT) Log.v(TAG, "onSpanRemoved s=" + s + " e=" + e
+ if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e
+ " what=" + what + ": " + buf);
TextView.this.spanChange(buf, what, s, -1, e, -1);
}
@@ -6502,6 +6539,13 @@
}
// Don't leave us in the middle of a batch edit.
onEndBatchEdit();
+
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.hide();
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.hide();
+ }
}
startStopMarquee(focused);
@@ -6589,6 +6633,8 @@
if (resultCode == InputMethodManager.RESULT_SHOWN) {
start = mPrevStart;
end = mPrevEnd;
+ } else if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.show();
}
final int len = mText.length();
@@ -6629,20 +6675,27 @@
boolean handled = false;
- int oldSelStart = Selection.getSelectionStart(mText);
- int oldSelEnd = Selection.getSelectionEnd(mText);
-
+ int oldSelStart = getSelectionStart();
+ int oldSelEnd = getSelectionEnd();
+
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.onTouchEvent(event);
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.onTouchEvent(event);
+ }
+
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
}
- if (mText instanceof Editable && onCheckIsTextEditor()) {
+ if (isTextEditable()) {
if (action == MotionEvent.ACTION_UP && isFocused() && !mScrolled) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- final int newSelStart = Selection.getSelectionStart(mText);
- final int newSelEnd = Selection.getSelectionEnd(mText);
+ final int newSelStart = getSelectionStart();
+ final int newSelEnd = getSelectionEnd();
CommitSelectionReceiver csr = null;
if (newSelStart != oldSelStart || newSelEnd != oldSelEnd) {
@@ -6650,7 +6703,7 @@
newSelStart, newSelEnd);
}
- handled = imm.showSoftInput(this, 0, csr) && (csr != null);
+ handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
}
}
@@ -6662,6 +6715,47 @@
return superResult;
}
+ private void prepareCursorController() {
+ boolean atLeastOneController = false;
+
+ // TODO Add an extra android:cursorController flag to disable the controller?
+ if (mCursorVisible) {
+ atLeastOneController = true;
+ if (mInsertionPointCursorController == null) {
+ mInsertionPointCursorController = new InsertionPointCursorController();
+ }
+ } else {
+ mInsertionPointCursorController = null;
+ }
+
+ if (canSelectText()) {
+ atLeastOneController = true;
+ if (mSelectionModifierCursorController == null) {
+ mSelectionModifierCursorController = new SelectionModifierCursorController();
+ }
+ } else {
+ mSelectionModifierCursorController = null;
+ }
+
+ if (atLeastOneController) {
+ if (sCursorControllerTempRect == null) {
+ sCursorControllerTempRect = new Rect();
+ }
+ Resources res = mContext.getResources();
+ mCursorControllerVerticalOffset = res.getDimensionPixelOffset(
+ com.android.internal.R.dimen.cursor_controller_vertical_offset);
+ } else {
+ sCursorControllerTempRect = null;
+ }
+ }
+
+ /**
+ * @return True iff this TextView contains a text that can be edited.
+ */
+ private boolean isTextEditable() {
+ return mText instanceof Editable && onCheckIsTextEditor();
+ }
+
/**
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
@@ -6712,8 +6806,8 @@
TextView tv = mView.get();
if (tv != null && tv.isFocused()) {
- int st = Selection.getSelectionStart(tv.mText);
- int en = Selection.getSelectionEnd(tv.mText);
+ int st = tv.getSelectionStart();
+ int en = tv.getSelectionEnd();
if (st == en && st >= 0 && en >= 0) {
if (tv.mLayout != null) {
@@ -6790,8 +6884,10 @@
@Override
protected int computeHorizontalScrollRange() {
- if (mLayout != null)
- return mLayout.getWidth();
+ if (mLayout != null) {
+ return mSingleLine && (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT ?
+ (int) mLayout.getLineWidth(0) : mLayout.getWidth();
+ }
return super.computeHorizontalScrollRange();
}
@@ -6903,6 +6999,9 @@
}
private boolean canSelectText() {
+ // prepareCursorController() relies on this method.
+ // If you change this condition, make sure prepareCursorController is called anywhere
+ // the value of this condition might be changed.
if (mText instanceof Spannable && mText.length() != 0 &&
mMovement != null && mMovement.canSelectArbitrarily()) {
return true;
@@ -6951,10 +7050,14 @@
}
/**
- * Returns a word to add to the dictionary from the context menu,
- * or null if there is no cursor or no word at the cursor.
+ * Returns the offsets delimiting the 'word' located at position offset.
+ *
+ * @param offset An offset in the text.
+ * @return The offsets for the start and end of the word located at <code>offset</code>.
+ * The two ints offsets are packed in a long, with the starting offset shifted by 32 bits.
+ * Returns a negative value if no valid word was found.
*/
- private String getWordForDictionary() {
+ private long getWordLimitsAt(int offset) {
/*
* Quick return if the input type is one where adding words
* to the dictionary doesn't make any sense.
@@ -6963,7 +7066,7 @@
if (klass == InputType.TYPE_CLASS_NUMBER ||
klass == InputType.TYPE_CLASS_PHONE ||
klass == InputType.TYPE_CLASS_DATETIME) {
- return null;
+ return -1;
}
int variation = mInputType & InputType.TYPE_MASK_VARIATION;
@@ -6972,13 +7075,13 @@
variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
- return null;
+ return -1;
}
- int end = getSelectionEnd();
+ int end = offset;
if (end < 0) {
- return null;
+ return -1;
}
int start = end;
@@ -7012,6 +7115,14 @@
}
}
+ if (start == end) {
+ return -1;
+ }
+
+ if (end - start > 48) {
+ return -1;
+ }
+
boolean hasLetter = false;
for (int i = start; i < end; i++) {
if (Character.isLetter(mTransformed.charAt(i))) {
@@ -7019,19 +7130,28 @@
break;
}
}
+
if (!hasLetter) {
- return null;
+ return -1;
}
- if (start == end) {
- return null;
- }
+ // Two ints packed in a long
+ return (((long) start) << 32) | end;
+ }
- if (end - start > 48) {
+ /**
+ * Returns a word to add to the dictionary from the context menu,
+ * or null if there is no cursor or no word at the cursor.
+ */
+ private String getWordForDictionary() {
+ long wordLimits = getWordLimitsAt(getSelectionEnd());
+ if (wordLimits < 0) {
return null;
+ } else {
+ int start = (int) (wordLimits >>> 32);
+ int end = (int) (wordLimits & 0x00000000FFFFFFFFL);
+ return TextUtils.substring(mTransformed, start, end);
}
-
- return TextUtils.substring(mTransformed, start, end);
}
@Override
@@ -7331,6 +7451,15 @@
@Override
public boolean performLongClick() {
+ // TODO This behavior should be moved to View
+ // TODO handle legacy code that added items to context menu
+ if (canSelectText()) {
+ if (startSelectionMode()) {
+ mEatTouchRelease = true;
+ return true;
+ }
+ }
+
if (super.performLongClick()) {
mEatTouchRelease = true;
return true;
@@ -7339,6 +7468,493 @@
return false;
}
+ private boolean startSelectionMode() {
+ if (mSelectionModifierCursorController != null) {
+ int offset = ((SelectionModifierCursorController) mSelectionModifierCursorController).
+ getTouchOffset();
+
+ int selectionStart, selectionEnd;
+
+ if (hasSelection()) {
+ selectionStart = getSelectionStart();
+ selectionEnd = getSelectionEnd();
+ if (selectionStart > selectionEnd) {
+ int tmp = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = tmp;
+ }
+ if ((offset >= selectionStart) && (offset <= selectionEnd)) {
+ // Long press in the current selection.
+ // Should initiate a drag. Return false, to rely on context menu for now.
+ return false;
+ }
+ }
+
+ long wordLimits = getWordLimitsAt(offset);
+ if (wordLimits >= 0) {
+ selectionStart = (int) (wordLimits >>> 32);
+ selectionEnd = (int) (wordLimits & 0x00000000FFFFFFFFL);
+ } else {
+ selectionStart = Math.max(offset - 5, 0);
+ selectionEnd = Math.min(offset + 5, mText.length());
+ }
+
+ Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+
+ // Has to be done AFTER selection has been changed to correctly position controllers.
+ mSelectionModifierCursorController.show();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the offset character closest to the specified absolute position.
+ *
+ * @param x The horizontal absolute position of a point on screen
+ * @param y The vertical absolute position of a point on screen
+ * @return the character offset for the character whose position is closest to the specified
+ * position.
+ *
+ * @hide
+ */
+ public int getOffset(int x, int y) {
+ x -= getTotalPaddingLeft();
+ y -= getTotalPaddingTop();
+
+ // Clamp the position to inside of the view.
+ if (x < 0) {
+ x = 0;
+ } else if (x >= (getWidth() - getTotalPaddingRight())) {
+ x = getWidth()-getTotalPaddingRight() - 1;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= (getHeight() - getTotalPaddingBottom())) {
+ y = getHeight()-getTotalPaddingBottom() - 1;
+ }
+
+ x += getScrollX();
+ y += getScrollY();
+
+ Layout layout = getLayout();
+ final int line = layout.getLineForVertical(y);
+ final int offset = layout.getOffsetForHorizontal(line, x);
+ return offset;
+ }
+
+ /**
+ * A CursorController instance can be used to control a cursor in the text.
+ *
+ * It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
+ * and send them to this object instead of the cursor.
+ */
+ public interface CursorController {
+ /* Cursor fade-out animation duration, in milliseconds. */
+ static final int FADE_OUT_DURATION = 400;
+
+ /**
+ * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
+ * See also {@link #hide()}.
+ */
+ public void show();
+
+ /**
+ * Hide the cursor controller from screen.
+ * See also {@link #show()}.
+ */
+ public void hide();
+
+ /**
+ * Update the controller's position.
+ */
+ public void updatePosition(int offset);
+
+ /**
+ * The controller and the cursor's positions can be link by a fixed offset,
+ * computed when the controller is touched, and then maintained as it moves
+ * @return Horizontal offset between the controller and the cursor.
+ */
+ public float getOffsetX();
+
+ /**
+ * @return Vertical offset between the controller and the cursor.
+ */
+ public float getOffsetY();
+
+ /**
+ * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
+ * a chance to become active and/or visible.
+ * @param event The touch event
+ */
+ public void onTouchEvent(MotionEvent event);
+
+ /**
+ * Draws a visual representation of the controller on the canvas.
+ *
+ * Called at the end of {@link #draw(Canvas)}, in the content coordinates system.
+ * @param canvas The Canvas used by this TextView.
+ */
+ public void draw(Canvas canvas);
+ }
+
+ class InsertionPointCursorController implements CursorController {
+ private static final int DELAY_BEFORE_FADE_OUT = 2100;
+
+ // Whether or not the cursor control is currently visible
+ private boolean mIsVisible = false;
+ // Starting time of the fade timer
+ private long mFadeOutTimerStart;
+ // The cursor controller image
+ private final Drawable mDrawable;
+ // Used to detect a tap (vs drag) on the controller
+ private long mOnDownTimerStart;
+ // Offset between finger hot point on cursor controller and actual cursor
+ private float mOffsetX, mOffsetY;
+
+ InsertionPointCursorController() {
+ Resources res = mContext.getResources();
+ mDrawable = res.getDrawable(com.android.internal.R.drawable.cursor_controller);
+ }
+
+ public void show() {
+ updateDrawablePosition();
+ // Has to be done after updatePosition, so that previous position invalidate
+ // in only done if necessary.
+ mIsVisible = true;
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.hide();
+ }
+ }
+
+ public void hide() {
+ if (mIsVisible) {
+ long time = System.currentTimeMillis();
+ // Start fading out, only if not already in progress
+ if (time - mFadeOutTimerStart < DELAY_BEFORE_FADE_OUT) {
+ mFadeOutTimerStart = time - DELAY_BEFORE_FADE_OUT;
+ postInvalidate(mDrawable);
+ }
+ }
+ }
+
+ public void draw(Canvas canvas) {
+ if (mIsVisible) {
+ int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
+ if (time <= DELAY_BEFORE_FADE_OUT) {
+ postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time, mDrawable);
+ } 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);
+ } else {
+ mDrawable.setAlpha(0);
+ mIsVisible = false;
+ }
+ }
+ mDrawable.draw(canvas);
+ }
+ }
+
+ public void updatePosition(int offset) {
+ Selection.setSelection((Spannable) mText, offset);
+ updateDrawablePosition();
+ }
+
+ private void updateDrawablePosition() {
+ if (mIsVisible) {
+ // Clear previous cursor controller before bounds are updated
+ postInvalidate(mDrawable);
+ }
+
+ final int offset = getSelectionStart();
+
+ if (offset < 0) {
+ // Should never happen, safety check.
+ Log.w(LOG_TAG, "Update cursor controller position called with no cursor");
+ mIsVisible = false;
+ return;
+ }
+
+ positionDrawableUnderCursor(offset, mDrawable);
+
+ mFadeOutTimerStart = System.currentTimeMillis();
+ mDrawable.setAlpha(255);
+ }
+
+ public void onTouchEvent(MotionEvent event) {
+ if (isFocused() && isTextEditable() && mIsVisible) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN : {
+ final float x = event.getX();
+ final float y = event.getY();
+
+ if (fingerIsOnDrawable(x, y, mDrawable)) {
+ show();
+
+ if (mMovement instanceof ArrowKeyMovementMethod) {
+ ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+ }
+
+ if (mParent != null) {
+ // Prevent possible scrollView parent from scrolling, so that
+ // we can use auto-scrolling.
+ mParent.requestDisallowInterceptTouchEvent(true);
+
+ final Rect bounds = mDrawable.getBounds();
+ mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+ mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+
+ mOnDownTimerStart = event.getEventTime();
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_UP : {
+ int time = (int) (event.getEventTime() - mOnDownTimerStart);
+
+ if (time <= ViewConfiguration.getTapTimeout()) {
+ // A tap on the controller is not grabbed, move the cursor instead
+ int offset = getOffset((int) event.getX(), (int) event.getY());
+ Selection.setSelection((Spannable) mText, offset);
+
+ // Modified by cancelLongPress and prevents the cursor from changing
+ mScrolled = false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public float getOffsetX() {
+ return mOffsetX;
+ }
+
+ public float getOffsetY() {
+ return mOffsetY;
+ }
+ }
+
+ class SelectionModifierCursorController implements CursorController {
+ // Whether or not the selection controls are currently visible
+ private boolean mIsVisible = false;
+ // Whether that start or the end of selection controller is dragged
+ private boolean mStartIsDragged = false;
+ // Starting time of the fade timer
+ private long mFadeOutTimerStart;
+ // The cursor controller images
+ private final Drawable mStartDrawable, mEndDrawable;
+ // Offset between finger hot point on active cursor controller and actual cursor
+ private float mOffsetX, mOffsetY;
+ // The offset of that last touch down event. Remembered to start selection there.
+ private int mTouchOffset;
+
+ 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);
+ }
+
+ public void show() {
+ updateDrawablesPositions();
+ // Has to be done after updatePosition, so that previous position invalidate
+ // in only done if necessary.
+ mIsVisible = true;
+ mFadeOutTimerStart = -1;
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.hide();
+ }
+ }
+
+ public void hide() {
+ if (mIsVisible && (mFadeOutTimerStart < 0)) {
+ mFadeOutTimerStart = System.currentTimeMillis();
+ postInvalidate(mStartDrawable);
+ postInvalidate(mEndDrawable);
+ }
+ }
+
+ public void draw(Canvas canvas) {
+ if (mIsVisible) {
+ if (mFadeOutTimerStart >= 0) {
+ 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);
+ } else {
+ mStartDrawable.setAlpha(0);
+ mEndDrawable.setAlpha(0);
+ mIsVisible = false;
+ }
+ }
+ mStartDrawable.draw(canvas);
+ mEndDrawable.draw(canvas);
+ }
+ }
+
+ public void updatePosition(int offset) {
+ int selectionStart = getSelectionStart();
+ int selectionEnd = getSelectionEnd();
+
+ // Handle the case where start and end are swapped, making sure start <= end
+ if (mStartIsDragged) {
+ if (offset <= selectionEnd) {
+ selectionStart = offset;
+ } else {
+ selectionStart = selectionEnd;
+ selectionEnd = offset;
+ mStartIsDragged = false;
+ }
+ } else {
+ if (offset >= selectionStart) {
+ selectionEnd = offset;
+ } else {
+ selectionEnd = selectionStart;
+ selectionStart = offset;
+ mStartIsDragged = true;
+ }
+ }
+
+ Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ updateDrawablesPositions();
+ }
+
+ private void updateDrawablesPositions() {
+ if (mIsVisible) {
+ // Clear previous cursor controller before bounds are updated
+ postInvalidate(mStartDrawable);
+ postInvalidate(mEndDrawable);
+ }
+
+ final int selectionStart = getSelectionStart();
+ final int selectionEnd = getSelectionEnd();
+
+ if ((selectionStart < 0) || (selectionEnd < 0)) {
+ // Should never happen, safety check.
+ Log.w(LOG_TAG, "Update selection controller position called with no cursor");
+ mIsVisible = false;
+ return;
+ }
+
+ positionDrawableUnderCursor(selectionStart, mStartDrawable);
+ positionDrawableUnderCursor(selectionEnd, mEndDrawable);
+
+ mStartDrawable.setAlpha(255);
+ mEndDrawable.setAlpha(255);
+ }
+
+ public void onTouchEvent(MotionEvent event) {
+ if (isFocused() && isTextEditable() &&
+ (event.getActionMasked() == MotionEvent.ACTION_DOWN)) {
+ 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
+ mTouchOffset = getOffset(x, y);
+
+ if (mIsVisible) {
+ if (mMovement instanceof ArrowKeyMovementMethod) {
+ boolean isOnStart = fingerIsOnDrawable(x, y, mStartDrawable);
+ boolean isOnEnd = fingerIsOnDrawable(x, y, mEndDrawable);
+ if (isOnStart || isOnEnd) {
+ if (mParent != null) {
+ // 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();
+ mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
+ mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+
+ ((ArrowKeyMovementMethod)mMovement).setCursorController(this);
+ }
+ }
+ }
+ }
+ }
+
+ public int getTouchOffset() {
+ return mTouchOffset;
+ }
+
+ public float getOffsetX() {
+ return mOffsetX;
+ }
+
+ public float getOffsetY() {
+ return mOffsetY;
+ }
+
+ /**
+ * @return true iff this controller is currently used to move the selection start.
+ */
+ public boolean isSelectionStartDragged() {
+ return mIsVisible && mStartIsDragged;
+ }
+ }
+
+ // 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);
+ }
+
@ViewDebug.ExportedProperty
private CharSequence mText;
private CharSequence mTransformed;
@@ -7360,13 +7976,21 @@
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
private final Paint mHighlightPaint;
- private int mHighlightColor = 0xFFBBDDFF;
+ private int mHighlightColor = 0xD077A14B;
private Layout mLayout;
private long mShowCursor;
private Blink mBlink;
private boolean mCursorVisible = true;
+ // Cursor Controllers. Null when disabled.
+ private CursorController mInsertionPointCursorController;
+ private CursorController mSelectionModifierCursorController;
+ // Stored once and for all.
+ private int mCursorControllerVerticalOffset;
+ // Created once and shared by different CursorController helper methods.
+ private static Rect sCursorControllerTempRect;
+
private boolean mSelectAllOnFocus = false;
private int mGravity = Gravity.TOP | Gravity.LEFT;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 6ac68aa..f37021b 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -22,6 +22,9 @@
import com.android.internal.widget.ActionBarView;
import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.Menu;
@@ -31,6 +34,9 @@
import android.widget.SpinnerAdapter;
import android.widget.ViewAnimator;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
/**
* ActionBarImpl is the ActionBar implementation used
* by devices of all screen sizes. If it detects a compatible decor,
@@ -42,10 +48,21 @@
private static final int NORMAL_VIEW = 0;
private static final int CONTEXT_VIEW = 1;
+ private static final int TAB_SWITCH_SHOW_HIDE = 0;
+ private static final int TAB_SWITCH_ADD_REMOVE = 1;
+
+ private Activity mActivity;
+
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
private ActionBarContextView mUpperContextView;
private LinearLayout mLowerContextView;
+
+ private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
+
+ private int mTabContainerViewId = android.R.id.content;
+ private TabImpl mSelectedTab;
+ private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
private ContextMode mContextMode;
@@ -54,6 +71,8 @@
private int mContextDisplayMode;
+ private boolean mClosingContext;
+
final Handler mHandler = new Handler();
final Runnable mCloseContext = new Runnable() {
public void run() {
@@ -61,10 +80,13 @@
if (mLowerContextView != null) {
mLowerContextView.removeAllViews();
}
+ mClosingContext = false;
}
};
- public ActionBarImpl(View decor) {
+ public ActionBarImpl(Activity activity) {
+ final View decor = activity.getWindow().getDecorView();
+ mActivity = activity;
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -83,32 +105,54 @@
}
public void setCustomNavigationMode(View view) {
+ cleanupTabs();
mActionView.setCustomNavigationView(view);
mActionView.setCallback(null);
}
public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+ cleanupTabs();
mActionView.setCallback(callback);
mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
mActionView.setDropdownAdapter(adapter);
}
public void setStandardNavigationMode() {
+ cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setCallback(null);
}
public void setStandardNavigationMode(CharSequence title) {
+ cleanupTabs();
setStandardNavigationMode(title, null);
}
public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
+ cleanupTabs();
mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
mActionView.setTitle(title);
mActionView.setSubtitle(subtitle);
mActionView.setCallback(null);
}
+ private void cleanupTabs() {
+ if (mSelectedTab != null) {
+ selectTab(null);
+ }
+ if (!mTabs.isEmpty()) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ final int tabCount = mTabs.size();
+ for (int i = 0; i < tabCount; i++) {
+ trans.remove(mTabs.get(i).getFragment());
+ }
+ trans.commit();
+ }
+ mTabs.clear();
+ }
+ }
+
public void setTitle(CharSequence title) {
mActionView.setTitle(title);
}
@@ -155,6 +199,14 @@
if (mContextMode != null) {
mContextMode.finish();
}
+
+ // Don't wait for the close context mode animation to finish.
+ if (mClosingContext) {
+ mAnimatorView.clearAnimation();
+ mHandler.removeCallbacks(mCloseContext);
+ mCloseContext.run();
+ }
+
mContextMode = new ContextMode(callback);
if (callback.onCreateContextMode(mContextMode, mContextMode.getMenu())) {
mContextMode.invalidate();
@@ -174,12 +226,120 @@
}
}
+ private void configureTab(Tab tab, int position) {
+ final TabImpl tabi = (TabImpl) tab;
+ final boolean isFirstTab = mTabs.isEmpty();
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ final Fragment frag = tabi.getFragment();
+
+ tabi.setPosition(position);
+ mTabs.add(position, tabi);
+
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ if (!frag.isAdded()) {
+ trans.add(mTabContainerViewId, frag);
+ }
+ }
+
+ if (isFirstTab) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.show(frag);
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.add(mTabContainerViewId, frag);
+ }
+ mSelectedTab = tabi;
+ } else {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.hide(frag);
+ }
+ }
+ trans.commit();
+ }
+
+ @Override
+ public void addTab(Tab tab) {
+ mActionView.addTab(tab);
+ configureTab(tab, mTabs.size());
+ }
+
+ @Override
+ public void insertTab(Tab tab, int position) {
+ mActionView.insertTab(tab, position);
+ configureTab(tab, position);
+ }
+
+ @Override
+ public Tab newTab() {
+ return new TabImpl();
+ }
+
+ @Override
+ public void removeTab(Tab tab) {
+ removeTabAt(tab.getPosition());
+ }
+
+ @Override
+ public void removeTabAt(int position) {
+ mActionView.removeTabAt(position);
+ mTabs.remove(position);
+
+ final int newTabCount = mTabs.size();
+ for (int i = position; i < newTabCount; i++) {
+ mTabs.get(i).setPosition(i);
+ }
+
+ selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+ }
+
+ @Override
+ public void setTabNavigationMode() {
+ mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
+ }
+
+ @Override
+ public void setTabNavigationMode(int containerViewId) {
+ mTabContainerViewId = containerViewId;
+ setTabNavigationMode();
+ }
+
+ @Override
+ public void selectTab(Tab tab) {
+ if (mSelectedTab == tab) {
+ return;
+ }
+
+ mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+ final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ if (mSelectedTab != null) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.hide(mSelectedTab.getFragment());
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.remove(mSelectedTab.getFragment());
+ }
+ }
+ if (tab != null) {
+ if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
+ trans.show(tab.getFragment());
+ } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
+ trans.add(mTabContainerViewId, tab.getFragment());
+ }
+ }
+ mSelectedTab = (TabImpl) tab;
+ trans.commit();
+ }
+
+ @Override
+ public void selectTabAt(int position) {
+ selectTab(mTabs.get(position));
+ }
+
/**
* @hide
*/
public class ContextMode extends ActionBar.ContextMode {
private ContextModeCallback mCallback;
private ActionMenu mMenu;
+ private WeakReference<View> mCustomView;
public ContextMode(ContextModeCallback callback) {
mCallback = callback;
@@ -197,6 +357,7 @@
mAnimatorView.setDisplayedChild(NORMAL_VIEW);
// Clear out the context mode views after the animation finishes
+ mClosingContext = true;
mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
@@ -216,6 +377,7 @@
@Override
public void setCustomView(View view) {
mUpperContextView.setCustomView(view);
+ mCustomView = new WeakReference<View>(view);
}
@Override
@@ -227,12 +389,81 @@
public void setTitle(CharSequence title) {
mUpperContextView.setTitle(title);
}
+
+ @Override
+ public CharSequence getTitle() {
+ return mUpperContextView.getTitle();
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return mUpperContextView.getSubtitle();
+ }
+ @Override
+ public View getCustomView() {
+ return mCustomView != null ? mCustomView.get() : null;
+ }
+
public void dispatchOnContextItemClicked(MenuItem item) {
ActionMenuItem actionItem = (ActionMenuItem) item;
if (!actionItem.invoke()) {
mCallback.onContextItemClicked(this, item);
}
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public class TabImpl extends ActionBar.Tab {
+ private Fragment mFragment;
+ private Drawable mIcon;
+ private CharSequence mText;
+ private int mPosition;
+
+ @Override
+ public Fragment getFragment() {
+ return mFragment;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ @Override
+ public int getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public CharSequence getText() {
+ return mText;
+ }
+
+ @Override
+ public void setFragment(Fragment fragment) {
+ mFragment = fragment;
+ }
+
+ @Override
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ @Override
+ public void setText(CharSequence text) {
+ mText = text;
+ }
+
+ @Override
+ public void select() {
+ selectTab(this);
}
}
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 107b145..4a0617c 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -435,6 +435,7 @@
View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
mIconView.setVisibility(View.GONE);
+ topPanel.setVisibility(View.GONE);
hasTitle = false;
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index b13d656..4da74e6 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -43,59 +43,6 @@
}
}
- public void dispatchKey(KeyEvent event) {
- try {
- mSession.finishKey(this);
- } catch (RemoteException ex) {
- }
- }
-
- public boolean onDispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- event.recycle();
- return false;
- }
-
- public void dispatchPointer(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- try {
- if (event == null) {
- event = mSession.getPendingPointerMove(this);
- onDispatchPointer(event, eventTime, false);
- } else if (callWhenDone) {
- if (!onDispatchPointer(event, eventTime, true)) {
- mSession.finishKey(this);
- }
- } else {
- onDispatchPointer(event, eventTime, false);
- }
- } catch (RemoteException ex) {
- }
- }
-
- public boolean onDispatchTrackball(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- event.recycle();
- return false;
- }
-
- public void dispatchTrackball(MotionEvent event, long eventTime,
- boolean callWhenDone) {
- try {
- if (event == null) {
- event = mSession.getPendingTrackballMove(this);
- onDispatchTrackball(event, eventTime, false);
- } else if (callWhenDone) {
- if (!onDispatchTrackball(event, eventTime, true)) {
- mSession.finishKey(this);
- }
- } else {
- onDispatchTrackball(event, eventTime, false);
- }
- } catch (RemoteException ex) {
- }
- }
-
public void dispatchAppVisibility(boolean visible) {
}
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index 7ff8d4c..9c1b558 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -5,7 +5,7 @@
/** hahahah */
public interface RootViewSurfaceTaker {
- SurfaceHolder.Callback willYouTakeTheSurface();
+ SurfaceHolder.Callback2 willYouTakeTheSurface();
void setSurfaceType(int type);
void setSurfaceFormat(int format);
void setSurfaceKeepScreenOn(boolean keepOn);
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index a962212..94a9f65 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -55,7 +55,7 @@
private static final String LOGTAG = "MenuBuilder";
/** The number of different menu types */
- public static final int NUM_TYPES = 4;
+ public static final int NUM_TYPES = 5;
/** The menu type that represents the icon menu view */
public static final int TYPE_ICON = 0;
/** The menu type that represents the expanded menu view */
@@ -66,20 +66,24 @@
* have an ItemView.
*/
public static final int TYPE_DIALOG = 2;
-
/**
* The menu type that represents a button in the application's action bar.
*/
public static final int TYPE_ACTION_BUTTON = 3;
+ /**
+ * The menu type that represents a menu popup.
+ */
+ public static final int TYPE_POPUP = 4;
private static final String VIEWS_TAG = "android:views";
-
+
// Order must be the same order as the TYPE_*
static final int THEME_RES_FOR_TYPE[] = new int[] {
com.android.internal.R.style.Theme_IconMenu,
com.android.internal.R.style.Theme_ExpandedMenu,
0,
0,
+ 0,
};
// Order must be the same order as the TYPE_*
@@ -88,6 +92,7 @@
com.android.internal.R.layout.expanded_menu_layout,
0,
com.android.internal.R.layout.action_menu_layout,
+ 0,
};
// Order must be the same order as the TYPE_*
@@ -96,6 +101,7 @@
com.android.internal.R.layout.list_menu_item_layout,
com.android.internal.R.layout.list_menu_item_layout,
com.android.internal.R.layout.action_menu_item_layout,
+ com.android.internal.R.layout.list_menu_item_layout,
};
private static final int[] sCategoryToOrder = new int[] {
@@ -1251,7 +1257,19 @@
}
public View getView(int position, View convertView, ViewGroup parent) {
- return ((MenuItemImpl) getItem(position)).getItemView(mMenuType, parent);
+ if (convertView != null) {
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ itemView.getItemData().setItemView(mMenuType, null);
+
+ MenuItemImpl item = (MenuItemImpl) getItem(position);
+ itemView.initialize(item, mMenuType);
+ item.setItemView(mMenuType, itemView);
+ return convertView;
+ } else {
+ MenuItemImpl item = (MenuItemImpl) getItem(position);
+ item.setItemView(mMenuType, null);
+ return item.getItemView(mMenuType, parent);
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 5fe75be..fecbd77 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -583,6 +583,10 @@
return (View) mItemViews[menuType].get();
}
+ void setItemView(int menuType, ItemView view) {
+ mItemViews[menuType] = new WeakReference<ItemView>(view);
+ }
+
/**
* Create and initializes a menu item view that implements {@link MenuView.ItemView}.
* @param menuType The type of menu to get a View for (must be one of
@@ -631,7 +635,10 @@
* @return Whether the given menu type should show icons for menu items.
*/
public boolean shouldShowIcon(int menuType) {
- return menuType == MenuBuilder.TYPE_ICON || mMenu.getOptionalIconsVisible();
+ return menuType == MenuBuilder.TYPE_ICON ||
+ menuType == MenuBuilder.TYPE_ACTION_BUTTON ||
+ menuType == MenuBuilder.TYPE_POPUP ||
+ mMenu.getOptionalIconsVisible();
}
public boolean isActionButton() {
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
new file mode 100644
index 0000000..751ecda
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -0,0 +1,91 @@
+/*
+ * 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.view.menu;
+
+import com.android.internal.view.menu.MenuBuilder.MenuAdapter;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.AdapterView;
+import android.widget.ListPopupWindow;
+
+/**
+ * @hide
+ */
+public class MenuPopupHelper implements AdapterView.OnItemClickListener {
+ private static final String TAG = "MenuPopupHelper";
+
+ private Context mContext;
+ private ListPopupWindow mPopup;
+ private SubMenuBuilder mSubMenu;
+ private int mPopupMaxWidth;
+
+ public MenuPopupHelper(Context context, SubMenuBuilder subMenu) {
+ mContext = context;
+ mSubMenu = subMenu;
+
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ mPopupMaxWidth = metrics.widthPixels / 2;
+ }
+
+ public void show() {
+ // TODO Use a style from the theme here
+ mPopup = new ListPopupWindow(mContext, null, 0,
+ com.android.internal.R.style.Widget_Spinner);
+ mPopup.setOnItemClickListener(this);
+
+ final MenuAdapter adapter = mSubMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP);
+ mPopup.setAdapter(adapter);
+ mPopup.setModal(true);
+
+ final MenuItemImpl itemImpl = (MenuItemImpl) mSubMenu.getItem();
+ final View anchorView = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null);
+ mPopup.setAnchorView(anchorView);
+
+ mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth));
+ mPopup.show();
+ }
+
+ public void dismiss() {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mSubMenu.performItemAction(mSubMenu.getItem(position), 0);
+ mPopup.dismiss();
+ }
+
+ private int measureContentWidth(MenuAdapter adapter) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int width = 0;
+ View itemView = null;
+ final int widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ itemView = adapter.getView(i, itemView, null);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+ return width;
+ }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 0f895f0..b57b7a8 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -105,6 +105,14 @@
initTitle();
}
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getSubtitle() {
+ return mSubtitle;
+ }
+
private void initTitle() {
if (mTitleLayout == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index e919f1b..fbff8ae 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -31,8 +31,10 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
@@ -85,9 +87,11 @@
private TextView mTitleView;
private TextView mSubtitleView;
private Spinner mSpinner;
+ private LinearLayout mTabLayout;
private View mCustomNavView;
private boolean mShowMenu;
+ private boolean mUserTitle;
private MenuBuilder mOptionsMenu;
private ActionMenuView mMenuView;
@@ -128,7 +132,8 @@
ApplicationInfo info = context.getApplicationInfo();
PackageManager pm = context.getPackageManager();
- mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_STANDARD);
+ mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
+ ActionBar.NAVIGATION_MODE_STANDARD);
mTitle = a.getText(R.styleable.ActionBar_title);
mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
mDisplayOptions = a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT);
@@ -208,7 +213,30 @@
return mTitle;
}
+ /**
+ * Set the action bar title. This will always replace or override window titles.
+ * @param title Title to set
+ *
+ * @see #setWindowTitle(CharSequence)
+ */
public void setTitle(CharSequence title) {
+ mUserTitle = true;
+ setTitleImpl(title);
+ }
+
+ /**
+ * Set the window title. A window title will always be replaced or overridden by a user title.
+ * @param title Title to set
+ *
+ * @see #setTitle(CharSequence)
+ */
+ public void setWindowTitle(CharSequence title) {
+ if (!mUserTitle) {
+ setTitleImpl(title);
+ }
+ }
+
+ private void setTitleImpl(CharSequence title) {
mTitle = title;
if (mTitleView != null) {
mTitleView.setText(title);
@@ -271,6 +299,11 @@
mCustomNavView = null;
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ removeView(mTabLayout);
+ mTabLayout = null;
+ }
}
switch (mode) {
@@ -286,6 +319,10 @@
case ActionBar.NAVIGATION_MODE_CUSTOM:
addView(mCustomNavView);
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ mTabLayout = new LinearLayout(getContext());
+ addView(mTabLayout);
+ break;
}
mNavigationMode = mode;
requestLayout();
@@ -308,6 +345,35 @@
return mDisplayOptions;
}
+ private TabView createTabView(ActionBar.Tab tab) {
+ final TabView tabView = new TabView(getContext(), tab);
+ tabView.setFocusable(true);
+ tabView.setOnClickListener(new TabClickListener());
+ return tabView;
+ }
+
+ public void addTab(ActionBar.Tab tab) {
+ final boolean isFirst = mTabLayout.getChildCount() == 0;
+ final TabView tabView = createTabView(tab);
+ mTabLayout.addView(tabView);
+ if (isFirst) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void insertTab(ActionBar.Tab tab, int position) {
+ final boolean isFirst = mTabLayout.getChildCount() == 0;
+ final TabView tabView = createTabView(tab);
+ mTabLayout.addView(tabView, position);
+ if (isFirst) {
+ tabView.setSelected(true);
+ }
+ }
+
+ public void removeTabAt(int position) {
+ mTabLayout.removeViewAt(position);
+ }
+
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom nav views if they don't supply layout params. Everything else
@@ -356,7 +422,7 @@
case ActionBar.NAVIGATION_MODE_TABS:
throw new UnsupportedOperationException(
- "Tab navigation isn't supported yet!");
+ "Inflating tab navigation isn't supported yet!");
case ActionBar.NAVIGATION_MODE_CUSTOM:
if (mCustomNavView != null) {
@@ -381,6 +447,14 @@
addView(mTitleLayout);
}
+ public void setTabSelected(int position) {
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(i == position);
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
@@ -442,6 +516,13 @@
MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ mTabLayout.measure(
+ MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+ break;
}
setMeasuredDimension(contentWidth, mContentHeight);
@@ -486,6 +567,10 @@
x += positionChild(mCustomNavView, x, y, contentHeight) + mSpacing;
}
break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabLayout != null) {
+ x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
+ }
}
x = r - l - getPaddingRight();
@@ -515,4 +600,59 @@
return childWidth;
}
+
+ private static class TabView extends LinearLayout {
+ private ActionBar.Tab mTab;
+
+ public TabView(Context context, ActionBar.Tab tab) {
+ super(context);
+ mTab = tab;
+
+ // TODO Style tabs based on the theme
+
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (icon != null) {
+ ImageView iconView = new ImageView(context);
+ iconView.setImageDrawable(icon);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView);
+ }
+
+ if (text != null) {
+ TextView textView = new TextView(context);
+ textView.setText(text);
+ textView.setSingleLine();
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ }
+
+ setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, 1));
+ }
+
+ public ActionBar.Tab getTab() {
+ return mTab;
+ }
+ }
+
+ private class TabClickListener implements OnClickListener {
+ public void onClick(View view) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ final int tabCount = mTabLayout.getChildCount();
+ for (int i = 0; i < tabCount; i++) {
+ final View child = mTabLayout.getChildAt(i);
+ child.setSelected(child == view);
+ }
+ }
+ }
}
diff --git a/core/jni/ActivityManager.cpp b/core/jni/ActivityManager.cpp
index 8950dfb..0bd14fa 100644
--- a/core/jni/ActivityManager.cpp
+++ b/core/jni/ActivityManager.cpp
@@ -39,7 +39,7 @@
data.writeString16(uri);
status_t ret = am->transact(OPEN_CONTENT_URI_TRANSACTION, data, &reply);
if (ret == NO_ERROR) {
- int32_t exceptionCode = reply.readInt32();
+ int32_t exceptionCode = reply.readExceptionCode();
if (!exceptionCode) {
// Success is indicated here by a nonzero int followed by the fd;
// failure by a zero int with no data following.
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 722dacb..d61d56e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -105,6 +105,7 @@
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
+ android/graphics/TextLayout.cpp \
android/graphics/Typeface.cpp \
android/graphics/Xfermode.cpp \
android/graphics/YuvToJpegEncoder.cpp \
@@ -187,7 +188,6 @@
libssl \
libicuuc \
libicui18n \
- libicudata \
libmedia \
libwpa_client \
libjpeg
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 2e49c64..558f5ff 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -30,6 +30,8 @@
#include "SkBoundaryPatch.h"
#include "SkMeshUtils.h"
+#include "TextLayout.h"
+
#include "unicode/ubidi.h"
#include "unicode/ushape.h"
@@ -57,24 +59,6 @@
class SkCanvasGlue {
public:
- enum {
- kDirection_LTR = 0,
- kDirection_RTL = 1
- };
-
- enum {
- kDirection_Mask = 0x1
- };
-
- enum {
- kBidi_LTR = 0,
- kBidi_RTL = 1,
- kBidi_Default_LTR = 2,
- kBidi_Default_RTL = 3,
- kBidi_Force_LTR = 4,
- kBidi_Force_RTL = 5
- };
-
static void finalizer(JNIEnv* env, jobject clazz, SkCanvas* canvas) {
canvas->unref();
}
@@ -767,192 +751,12 @@
indices, indexCount, *paint);
}
- /**
- * Character-based Arabic shaping.
- *
- * We'll use harfbuzz and glyph-based shaping instead once we're set up for it.
- *
- * @context the text context
- * @start the start of the text to render
- * @count the length of the text to render, start + count must be <= contextCount
- * @contextCount the length of the context
- * @shaped where to put the shaped text, must have capacity for count uchars
- * @return the length of the shaped text, or -1 if error
- */
- static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
- jchar* shaped, UErrorCode &status) {
- jchar buffer[contextCount];
-
- // Use fixed length since we need to keep start and count valid
- u_shapeArabic(context, contextCount, buffer, contextCount,
- U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
- U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
- U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
-
- if (U_SUCCESS(status)) {
- // trim out 0xffff following ligatures, if any
- int end = 0;
- for (int i = start, e = start + count; i < e; ++i) {
- if (buffer[i] != 0xffff) {
- buffer[end++] = buffer[i];
- }
- }
- count = end;
- // LOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount);
- ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE
- | UBIDI_KEEP_BASE_COMBINING, &status);
- if (U_SUCCESS(status)) {
- return count;
- }
- }
-
- return -1;
- }
-
- /**
- * Basic character-based layout supporting rtl and arabic shaping.
- * Runs bidi on the text and generates a reordered, shaped line in buffer, returning
- * the length.
- * @text the text
- * @len the length of the text in uchars
- * @dir receives the resolved paragraph direction
- * @buffer the buffer to receive the reordered, shaped line. Must have capacity of
- * at least len jchars.
- * @flags line bidi flags
- * @return the length of the reordered, shaped line, or -1 if error
- */
- static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
- UErrorCode &status) {
- static int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
- UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
-
- UBiDiLevel bidiReq = 0;
- switch (flags) {
- case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
- case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
- case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
- case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
- case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len;
- case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status);
- }
-
- int32_t result = -1;
-
- UBiDi* bidi = ubidi_open();
- if (bidi) {
- ubidi_setPara(bidi, text, len, bidiReq, NULL, &status);
- if (U_SUCCESS(status)) {
- dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl
-
- int rc = ubidi_countRuns(bidi, &status);
- if (U_SUCCESS(status)) {
- // LOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc);
-
- int32_t slen = 0;
- for (int i = 0; i < rc; ++i) {
- int32_t start;
- int32_t length;
- UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length);
- // LOG(LOG_INFO, "LAYOUT", " [%2d] runDir=%d start=%3d len=%3d\n", i, runDir, start, length);
- if (runDir == UBIDI_RTL) {
- slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status);
- } else {
- memcpy(buffer + slen, text + start, length * sizeof(jchar));
- slen += length;
- }
- }
- if (U_SUCCESS(status)) {
- result = slen;
- }
- }
- }
- ubidi_close(bidi);
- }
-
- return result;
- }
-
- // Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if
- // bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
- // looking for a character >= the first RTL character in unicode and assume we do if
- // we find one.
- static bool needsLayout(const jchar* text, jint len, jint bidiFlags) {
- if (bidiFlags == kBidi_Force_LTR) {
- return false;
- }
- if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
- bidiFlags == kBidi_Force_RTL) {
- return true;
- }
- for (int i = 0; i < len; ++i) {
- if (text[i] >= 0x0590) {
- return true;
- }
- }
- return false;
- }
-
- // Draws a paragraph of text on a single line, running bidi and shaping
- static void drawText(JNIEnv* env, SkCanvas* canvas, const jchar* text, jsize len,
- jfloat x, jfloat y, int bidiFlags, SkPaint* paint) {
-
- SkScalar x_ = SkFloatToScalar(x);
- SkScalar y_ = SkFloatToScalar(y);
-
- SkPaint::Align horiz = paint->getTextAlign();
-
- const jchar *workText = text;
- jchar *buffer = NULL;
- int dir = kDirection_LTR;
- if (needsLayout(text, len, bidiFlags)) {
- buffer =(jchar *) malloc(len * sizeof(jchar));
- if (!buffer) {
- return;
- }
- UErrorCode status = U_ZERO_ERROR;
- len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
- if (!U_SUCCESS(status)) {
- LOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status);
- free(buffer);
- return; // can't render
- }
-
- workText = buffer; // use the shaped text
- }
-
- bool trimLeft = false;
- bool trimRight = false;
-
- switch (horiz) {
- case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
- case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
- case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
- default: break;
- }
- const jchar* workLimit = workText + len;
-
- if (trimLeft) {
- while (workText < workLimit && *workText == ' ') {
- ++workText;
- }
- }
- if (trimRight) {
- while (workLimit > workText && *(workLimit - 1) == ' ') {
- --workLimit;
- }
- }
- int32_t workBytes = (workLimit - workText) << 1;
-
- canvas->drawText(workText, workBytes, x_, y_, *paint);
-
- free(buffer);
- }
static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- drawText(env, canvas, textArray + index, count, x, y, flags, paint);
+ TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
@@ -961,41 +765,18 @@
int start, int end,
jfloat x, jfloat y, int flags, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
- drawText(env, canvas, textArray + start, end - start, x, y, flags, paint);
+ TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
env->ReleaseStringChars(text, textArray);
}
- // Draws a unidirectional run of text.
- static void drawTextRun(JNIEnv* env, SkCanvas* canvas, const jchar* chars,
- jint start, jint count, jint contextCount,
- jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
-
- SkScalar x_ = SkFloatToScalar(x);
- SkScalar y_ = SkFloatToScalar(y);
-
- 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)) {
- 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);
- }
- }
-
static void drawTextRun___CIIIIFFIPaint(
JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
int count, int contextIndex, int contextCount,
jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
jchar* chars = env->GetCharArrayElements(text, NULL);
- drawTextRun(env, canvas, chars + contextIndex, index - contextIndex,
- count, contextCount, x, y, dirFlags, paint);
+ TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex,
+ count, contextCount, dirFlags, x, y, canvas);
env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
}
@@ -1007,8 +788,8 @@
jint count = end - start;
jint contextCount = contextEnd - contextStart;
const jchar* chars = env->GetStringChars(text, NULL);
- drawTextRun(env, canvas, chars + contextStart, start - contextStart,
- count, contextCount, x, y, dirFlags, paint);
+ TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart,
+ count, contextCount, dirFlags, x, y, canvas);
env->ReleaseStringChars(text, chars);
}
@@ -1059,31 +840,13 @@
delete[] posPtr;
}
- static void drawTextOnPath(JNIEnv *env, SkCanvas* canvas, const jchar* text, int count,
- int bidiFlags, SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
-
- if (!needsLayout(text, count, bidiFlags)) {
- canvas->drawTextOnPathHV(text, count << 1, *path,
- SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
- return;
- }
-
- SkAutoSTMalloc<80, jchar> buffer(count);
- int dir = kDirection_LTR;
- UErrorCode status = U_ZERO_ERROR;
- count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
- if (U_SUCCESS(status)) {
- canvas->drawTextOnPathHV(buffer.get(), count << 1, *path,
- SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
- }
- }
-
static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
SkCanvas* canvas, jcharArray text, int index, int count,
SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- drawTextOnPath(env, canvas, textArray, count, bidiFlags, path, hOffset, vOffset, paint);
+ TextLayout::drawTextOnPath(paint, textArray, count, bidiFlags, hOffset, vOffset,
+ path, canvas);
env->ReleaseCharArrayElements(text, textArray, 0);
}
@@ -1092,7 +855,8 @@
jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
const jchar* text_ = env->GetStringChars(text, NULL);
int count = env->GetStringLength(text);
- drawTextOnPath(env, canvas, text_, count, bidiFlags, path, hOffset, vOffset, paint);
+ TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+ path, canvas);
env->ReleaseStringChars(text, text_);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index ca9c9de..e4d4850 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -32,6 +32,7 @@
#include "SkTypeface.h"
#include "SkXfermode.h"
#include "unicode/ushape.h"
+#include "TextLayout.h"
// temporary for debugging
#include <utils/Log.h>
@@ -403,56 +404,14 @@
return count;
}
- static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text, jint start, jint count, jint contextCount, jint flags,
+ static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
+ jint start, jint count, jint contextCount, jint flags,
jfloatArray advances, jint advancesIndex) {
jfloat advancesArray[count];
- jchar buffer[contextCount];
+ jfloat totalAdvance;
- SkScalar* scalarArray = (SkScalar *)advancesArray;
- jfloat totalAdvance = 0;
-
- // this is where we'd call harfbuzz
- // for now we just use ushape.c
-
- int widths;
- if (flags & 0x1) { // rtl, call arabic shaping in case
- UErrorCode status = U_ZERO_ERROR;
- // Use fixed length since we need to keep start and count valid
- u_shapeArabic(text, contextCount, buffer, contextCount,
- U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
- U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
- U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
- // we shouldn't fail unless there's an out of memory condition,
- // in which case we're hosed anyway
- for (int i = start, e = i + count; i < e; ++i) {
- if (buffer[i] == 0xffff) {
- buffer[i] = 0x200b; // zero-width-space for skia
- }
- }
- widths = paint->getTextWidths(buffer + start, count << 1, scalarArray);
- } else {
- widths = paint->getTextWidths(text + start, count << 1, scalarArray);
- }
-
- if (widths < count) {
- // Skia operates on code points, not code units, so surrogate pairs return only
- // one value. Expand the result so we have one value per UTF-16 code unit.
-
- // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
- // leaving the remaining widths zero. Not nice.
- const jchar *chars = text + start;
- for (int i = 0, p = 0; i < widths; ++i) {
- totalAdvance += advancesArray[p++] = SkScalarToFloat(scalarArray[i]);
- if (p < count && chars[p] >= 0xdc00 && chars[p] < 0xe000 &&
- chars[p-1] >= 0xd800 && chars[p-1] < 0xdc00) {
- advancesArray[p++] = 0;
- }
- }
- } else {
- for (int i = 0; i < count; i++) {
- totalAdvance += advancesArray[i] = SkScalarToFloat(scalarArray[i]);
- }
- }
+ TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags,
+ advancesArray, totalAdvance);
if (advances != NULL) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
@@ -580,19 +539,25 @@
return result;
}
- static void getTextPath___CIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
- const jchar* textArray = env->GetCharArrayElements(text, NULL);
- paint->getTextPath(textArray + index, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), path);
- env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
- JNI_ABORT);
+ static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count,
+ jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+ TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path);
}
-
- static void getTextPath__StringIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
+
+ static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+ jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
+ const jchar* textArray = env->GetCharArrayElements(text, NULL);
+ getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path);
+ env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
+ }
+
+ static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+ jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
const jchar* textArray = env->GetStringChars(text, NULL);
- paint->getTextPath(textArray + start, (end - start) << 1, SkFloatToScalar(x), SkFloatToScalar(y), path);
+ getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path);
env->ReleaseStringChars(text, textArray);
}
-
+
static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius,
jfloat dx, jfloat dy, int color) {
NPE_CHECK_RETURN_VOID(env, jpaint);
@@ -767,8 +732,8 @@
{"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
{"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
(void*) SkPaintGlue::getTextRunCursor__String},
- {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___CIIFFPath},
- {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__StringIIFFPath},
+ {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
+ {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
{"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getStringBounds },
{"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
new file mode 100644
index 0000000..e2536ee
--- /dev/null
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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 "TextLayout.h"
+
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkTemplates.h"
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+#include <utils/Log.h>
+
+
+namespace android {
+// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if
+// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
+// looking for a character >= the first RTL character in unicode and assume we do if
+// we find one.
+bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) {
+ if (bidiFlags == kBidi_Force_LTR) {
+ return false;
+ }
+ if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
+ bidiFlags == kBidi_Force_RTL) {
+ return true;
+ }
+ for (int i = 0; i < len; ++i) {
+ if (text[i] >= 0x0590) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Character-based Arabic shaping.
+ *
+ * We'll use harfbuzz and glyph-based shaping instead once we're set up for it.
+ *
+ * @context the text context
+ * @start the start of the text to render
+ * @count the length of the text to render, start + count must be <= contextCount
+ * @contextCount the length of the context
+ * @shaped where to put the shaped text, must have capacity for count uchars
+ * @return the length of the shaped text, or -1 if error
+ */
+int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
+ jchar* shaped, UErrorCode &status) {
+ jchar buffer[contextCount];
+
+ // Use fixed length since we need to keep start and count valid
+ u_shapeArabic(context, contextCount, buffer, contextCount,
+ U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+ U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+ U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+
+ if (U_SUCCESS(status)) {
+ // trim out 0xffff following ligatures, if any
+ int end = 0;
+ for (int i = start, e = start + count; i < e; ++i) {
+ if (buffer[i] != 0xffff) {
+ buffer[end++] = buffer[i];
+ }
+ }
+ count = end;
+ // LOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount);
+ ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE
+ | UBIDI_KEEP_BASE_COMBINING, &status);
+ if (U_SUCCESS(status)) {
+ return count;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Basic character-based layout supporting rtl and arabic shaping.
+ * Runs bidi on the text and generates a reordered, shaped line in buffer, returning
+ * the length.
+ * @text the text
+ * @len the length of the text in uchars
+ * @dir receives the resolved paragraph direction
+ * @buffer the buffer to receive the reordered, shaped line. Must have capacity of
+ * at least len jchars.
+ * @flags line bidi flags
+ * @return the length of the reordered, shaped line, or -1 if error
+ */
+jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
+ UErrorCode &status) {
+ static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
+ UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
+
+ UBiDiLevel bidiReq = 0;
+ switch (flags) {
+ case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
+ case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
+ case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
+ case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
+ case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len;
+ case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status);
+ }
+
+ int32_t result = -1;
+
+ UBiDi* bidi = ubidi_open();
+ if (bidi) {
+ ubidi_setPara(bidi, text, len, bidiReq, NULL, &status);
+ if (U_SUCCESS(status)) {
+ dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl
+
+ int rc = ubidi_countRuns(bidi, &status);
+ if (U_SUCCESS(status)) {
+ // LOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc);
+
+ int32_t slen = 0;
+ for (int i = 0; i < rc; ++i) {
+ int32_t start;
+ int32_t length;
+ UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length);
+
+ if (runDir == UBIDI_RTL) {
+ slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status);
+ } else {
+ memcpy(buffer + slen, text + start, length * sizeof(jchar));
+ slen += length;
+ }
+ }
+ if (U_SUCCESS(status)) {
+ result = slen;
+ }
+ }
+ }
+ ubidi_close(bidi);
+ }
+
+ return result;
+}
+
+// Draws or gets the path of a paragraph of text on a single line, running bidi and shaping.
+// This will draw if canvas is not null, otherwise path must be non-null and it will create
+// a path representing the text that would have been drawn.
+void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
+ jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) {
+
+ const jchar *workText = text;
+ jchar *buffer = NULL;
+ int dir = kDirection_LTR;
+ if (needsLayout(text, len, bidiFlags)) {
+ buffer =(jchar *) malloc(len * sizeof(jchar));
+ if (!buffer) {
+ return;
+ }
+ UErrorCode status = U_ZERO_ERROR;
+ len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
+ if (!U_SUCCESS(status)) {
+ LOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status);
+ free(buffer);
+ return; // can't render
+ }
+
+ workText = buffer; // use the shaped text
+ }
+
+ bool trimLeft = false;
+ bool trimRight = false;
+
+ SkPaint::Align horiz = paint->getTextAlign();
+ switch (horiz) {
+ case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
+ case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
+ case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
+ default: break;
+ }
+ const jchar* workLimit = workText + len;
+
+ if (trimLeft) {
+ while (workText < workLimit && *workText == ' ') {
+ ++workText;
+ }
+ }
+ if (trimRight) {
+ while (workLimit > workText && *(workLimit - 1) == ' ') {
+ --workLimit;
+ }
+ }
+
+ int32_t workBytes = (workLimit - workText) << 1;
+ SkScalar x_ = SkFloatToScalar(x);
+ SkScalar y_ = SkFloatToScalar(y);
+ if (canvas) {
+ canvas->drawText(workText, workBytes, x_, y_, *paint);
+ } else {
+ paint->getTextPath(workText, workBytes, x_, y_, path);
+ }
+
+ free(buffer);
+}
+
+void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
+ jint start, jint count, jint contextCount,
+ int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
+
+ SkScalar x_ = SkFloatToScalar(x);
+ SkScalar y_ = SkFloatToScalar(y);
+
+ 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)) {
+ 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);
+ }
+ }
+
+void TextLayout::getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat *resultAdvances, jfloat &resultTotalAdvance) {
+ jchar buffer[contextCount];
+
+ SkScalar* scalarArray = (SkScalar *)resultAdvances;
+ resultTotalAdvance = 0;
+
+ // this is where we'd call harfbuzz
+ // for now we just use ushape.c
+
+ int widths;
+ const jchar* text;
+ if (dirFlags & 0x1) { // rtl, call arabic shaping in case
+ UErrorCode status = U_ZERO_ERROR;
+ // Use fixed length since we need to keep start and count valid
+ u_shapeArabic(chars, contextCount, buffer, contextCount,
+ U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+ U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+ U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+ // we shouldn't fail unless there's an out of memory condition,
+ // in which case we're hosed anyway
+ for (int i = start, e = i + count; i < e; ++i) {
+ if (buffer[i] == 0xffff) {
+ buffer[i] = 0x200b; // zero-width-space for skia
+ }
+ }
+ text = buffer + start;
+ widths = paint->getTextWidths(text, count << 1, scalarArray);
+ } else {
+ text = chars + start;
+ widths = paint->getTextWidths(text, count << 1, scalarArray);
+ }
+
+ if (widths < count) {
+ // Skia operates on code points, not code units, so surrogate pairs return only
+ // one value. Expand the result so we have one value per UTF-16 code unit.
+
+ // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
+ // leaving the remaining widths zero. Not nice.
+ for (int i = 0, p = 0; i < widths; ++i) {
+ resultTotalAdvance += resultAdvances[p++] = SkScalarToFloat(scalarArray[i]);
+ if (p < count && text[p] >= 0xdc00 && text[p] < 0xe000 &&
+ text[p-1] >= 0xd800 && text[p-1] < 0xdc00) {
+ resultAdvances[p++] = 0;
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ resultTotalAdvance += resultAdvances[i] = SkScalarToFloat(scalarArray[i]);
+ }
+ }
+}
+
+
+// Draws a paragraph of text on a single line, running bidi and shaping
+void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len,
+ int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) {
+
+ handleText(paint, text, len, bidiFlags, x, y, canvas, NULL);
+}
+
+void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len,
+ jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+ handleText(paint, text, len, bidiFlags, x, y, NULL, path);
+}
+
+
+void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
+ int bidiFlags, jfloat hOffset, jfloat vOffset,
+ SkPath* path, SkCanvas* canvas) {
+
+ SkScalar h_ = SkFloatToScalar(hOffset);
+ SkScalar v_ = SkFloatToScalar(vOffset);
+
+ if (!needsLayout(text, count, bidiFlags)) {
+ canvas->drawTextOnPathHV(text, count << 1, *path, h_, v_, *paint);
+ return;
+ }
+
+ SkAutoSTMalloc<80, jchar> buffer(count);
+ int dir = kDirection_LTR;
+ UErrorCode status = U_ZERO_ERROR;
+ count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
+ if (U_SUCCESS(status)) {
+ canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint);
+ }
+}
+
+}
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
new file mode 100644
index 0000000..c0d9f75
--- /dev/null
+++ b/core/jni/android/graphics/TextLayout.h
@@ -0,0 +1,77 @@
+/*
+ * 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 "jni.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "unicode/utypes.h"
+
+namespace android {
+
+class TextLayout {
+public:
+
+ enum {
+ kDirection_LTR = 0,
+ kDirection_RTL = 1,
+
+ kDirection_Mask = 0x1
+ };
+
+ enum {
+ kBidi_LTR = 0,
+ kBidi_RTL = 1,
+ kBidi_Default_LTR = 2,
+ kBidi_Default_RTL = 3,
+ kBidi_Force_LTR = 4,
+ kBidi_Force_RTL = 5,
+
+ kBidi_Mask = 0x7
+ };
+
+ /*
+ * Draws a unidirectional run of text.
+ */
+ static void drawTextRun(SkPaint* paint, const jchar* chars,
+ jint start, jint count, jint contextCount,
+ int dirFlags, jfloat x, jfloat y, SkCanvas* canvas);
+
+ static void getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat *resultAdvances, jfloat &resultTotalAdvance);
+
+ static void drawText(SkPaint* paint, const jchar* text, jsize len,
+ jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas);
+
+ static void getTextPath(SkPaint *paint, const jchar *text, jsize len,
+ jint bidiFlags, jfloat x, jfloat y, SkPath *path);
+
+ static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len,
+ int bidiFlags, jfloat hOffset, jfloat vOffset,
+ SkPath* path, SkCanvas* canvas);
+
+private:
+ static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
+ static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
+ jchar* shaped, UErrorCode &status);
+ static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
+ UErrorCode &status);
+ static void handleText(SkPaint *paint, const jchar* text, jsize len,
+ int bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path);
+};
+
+}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index af61b80..acbf854 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -19,9 +19,11 @@
#include <poll.h>
#include <dlfcn.h>
+#include <fcntl.h>
#include <android_runtime/AndroidRuntime.h>
-#include <android/native_activity.h>
+#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_app_NativeActivity.h>
#include <surfaceflinger/Surface.h>
#include <ui/egl/android_natives.h>
#include <ui/InputTransport.h>
@@ -31,7 +33,9 @@
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
-#include "android_view_Surface.h"
+
+//#define LOG_TRACE(...)
+#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
namespace android
{
@@ -42,58 +46,239 @@
jmethodID dispatchUnhandledKeyEvent;
jmethodID setWindowFlags;
jmethodID setWindowFormat;
+ jmethodID showIme;
+ jmethodID hideIme;
} gNativeActivityClassInfo;
// ------------------------------------------------------------------------
-/*
- * Specialized input queue that allows unhandled key events to be dispatched
- * back to the native activity's Java framework code.
- */
-struct MyInputQueue : AInputQueue {
- explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite)
- : AInputQueue(channel), mWorkWrite(workWrite) {
- }
-
- virtual void doDefaultKey(android::KeyEvent* keyEvent) {
- mLock.lock();
- LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
- if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
- int8_t cmd = 1;
- write(mWorkWrite, &cmd, sizeof(cmd));
- }
- mPendingKeys.add(keyEvent);
- mLock.unlock();
- }
-
- KeyEvent* getNextEvent() {
- KeyEvent* event = NULL;
-
- mLock.lock();
- if (mPendingKeys.size() > 0) {
- event = mPendingKeys[0];
- mPendingKeys.removeAt(0);
- }
- mLock.unlock();
-
- return event;
- }
-
- int mWorkWrite;
-
- Mutex mLock;
- Vector<KeyEvent*> mPendingKeys;
+struct ActivityWork {
+ int32_t cmd;
+ int32_t arg1;
+ int32_t arg2;
};
+enum {
+ CMD_DEF_KEY = 1,
+ CMD_SET_WINDOW_FORMAT,
+ CMD_SET_WINDOW_FLAGS,
+ CMD_SHOW_SOFT_INPUT,
+ CMD_HIDE_SOFT_INPUT,
+};
+
+static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
+ ActivityWork work;
+ work.cmd = cmd;
+ work.arg1 = arg1;
+ work.arg2 = arg2;
+
+ LOG_TRACE("write_work: cmd=%d", cmd);
+
+restart:
+ int res = write(fd, &work, sizeof(work));
+ if (res < 0 && errno == EINTR) {
+ goto restart;
+ }
+
+ if (res == sizeof(work)) return;
+
+ if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno));
+ else LOGW("Truncated writing to work fd: %d", res);
+}
+
+static bool read_work(int fd, ActivityWork* outWork) {
+ int res = read(fd, outWork, sizeof(ActivityWork));
+ // no need to worry about EINTR, poll loop will just come back again.
+ if (res == sizeof(ActivityWork)) return true;
+
+ if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno));
+ else LOGW("Truncated reading work fd: %d", res);
+ return false;
+}
+
+// ------------------------------------------------------------------------
+
+} // namespace android
+
+using namespace android;
+
+AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
+ mWorkWrite(workWrite), mConsumer(channel) {
+ int msgpipe[2];
+ if (pipe(msgpipe)) {
+ LOGW("could not create pipe: %s", strerror(errno));
+ mDispatchKeyRead = mDispatchKeyWrite = -1;
+ } else {
+ mDispatchKeyRead = msgpipe[0];
+ mDispatchKeyWrite = msgpipe[1];
+ int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make AInputQueue read pipe "
+ "non-blocking: %s", strerror(errno));
+ result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make AInputQueue write pipe "
+ "non-blocking: %s", strerror(errno));
+ }
+}
+
+AInputQueue::~AInputQueue() {
+ close(mDispatchKeyRead);
+ close(mDispatchKeyWrite);
+}
+
+void AInputQueue::attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data) {
+ mPollLoop = static_cast<android::PollLoop*>(looper);
+ mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(),
+ POLLIN, callback, data);
+ mPollLoop->setLooperCallback(mDispatchKeyRead,
+ POLLIN, callback, data);
+}
+
+void AInputQueue::detachLooper() {
+ mPollLoop->removeCallback(mConsumer.getChannel()->getReceivePipeFd());
+ mPollLoop->removeCallback(mDispatchKeyRead);
+}
+
+int32_t AInputQueue::hasEvents() {
+ struct pollfd pfd[2];
+
+ pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd();
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ pfd[1].fd = mDispatchKeyRead;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+
+ int nfd = poll(pfd, 2, 0);
+ if (nfd <= 0) return 0;
+ return (pfd[0].revents == POLLIN || pfd[1].revents == POLLIN) ? 1 : -1;
+}
+
+int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
+ *outEvent = NULL;
+
+ char byteread;
+ ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
+ if (nRead == 1) {
+ mLock.lock();
+ if (mDispatchingKeys.size() > 0) {
+ KeyEvent* kevent = mDispatchingKeys[0];
+ *outEvent = kevent;
+ mDispatchingKeys.removeAt(0);
+ mDeliveringKeys.add(kevent);
+ }
+ mLock.unlock();
+ if (*outEvent != NULL) {
+ return 0;
+ }
+ }
+
+ int32_t res = mConsumer.receiveDispatchSignal();
+ if (res != android::OK) {
+ LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
+ mConsumer.getChannel()->getName().string(), res);
+ return -1;
+ }
+
+ InputEvent* myEvent = NULL;
+ res = mConsumer.consume(&mInputEventFactory, &myEvent);
+ if (res != android::OK) {
+ LOGW("channel '%s' ~ Failed to consume input event. status=%d",
+ mConsumer.getChannel()->getName().string(), res);
+ mConsumer.sendFinishedSignal();
+ return -1;
+ }
+
+ *outEvent = myEvent;
+ return 0;
+}
+
+void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
+ bool needFinished = true;
+
+ if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY
+ && ((KeyEvent*)event)->hasDefaultAction()) {
+ // The app didn't handle this, but it may have a default action
+ // associated with it. We need to hand this back to Java to be
+ // executed.
+ doDefaultKey((KeyEvent*)event);
+ needFinished = false;
+ }
+
+ const size_t N = mDeliveringKeys.size();
+ for (size_t i=0; i<N; i++) {
+ if (mDeliveringKeys[i] == event) {
+ delete event;
+ mDeliveringKeys.removeAt(i);
+ needFinished = false;
+ break;
+ }
+ }
+
+ if (needFinished) {
+ int32_t res = mConsumer.sendFinishedSignal();
+ if (res != android::OK) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ mConsumer.getChannel()->getName().string(), res);
+ }
+ }
+}
+
+void AInputQueue::dispatchEvent(android::KeyEvent* event) {
+ mLock.lock();
+ LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
+ mDispatchKeyWrite);
+ mDispatchingKeys.add(event);
+ mLock.unlock();
+
+restart:
+ char dummy = 0;
+ int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
+ if (res < 0 && errno == EINTR) {
+ goto restart;
+ }
+
+ if (res == sizeof(dummy)) return;
+
+ if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
+ else LOGW("Truncated writing to dispatch fd: %d", res);
+}
+
+KeyEvent* AInputQueue::consumeUnhandledEvent() {
+ KeyEvent* event = NULL;
+
+ mLock.lock();
+ if (mPendingKeys.size() > 0) {
+ event = mPendingKeys[0];
+ mPendingKeys.removeAt(0);
+ }
+ mLock.unlock();
+
+ LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
+
+ return event;
+}
+
+void AInputQueue::doDefaultKey(KeyEvent* keyEvent) {
+ mLock.lock();
+ LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
+ if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
+ write_work(mWorkWrite, CMD_DEF_KEY);
+ }
+ mPendingKeys.add(keyEvent);
+ mLock.unlock();
+}
+
+namespace android {
+
// ------------------------------------------------------------------------
/*
* Native state for interacting with the NativeActivity class.
*/
-struct NativeCode {
+struct NativeCode : public ANativeActivity {
NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
- memset(&activity, sizeof(activity), 0);
- memset(&callbacks, sizeof(callbacks), 0);
+ memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
+ memset(&callbacks, 0, sizeof(callbacks));
dlhandle = _dlhandle;
createActivityFunc = _createFunc;
nativeWindow = NULL;
@@ -103,8 +288,11 @@
}
~NativeCode() {
- if (activity.env != NULL && activity.clazz != NULL) {
- activity.env->DeleteGlobalRef(activity.clazz);
+ if (callbacks.onDestroy != NULL) {
+ callbacks.onDestroy(this);
+ }
+ if (env != NULL && clazz != NULL) {
+ env->DeleteGlobalRef(clazz);
}
if (pollLoop != NULL && mainWorkRead >= 0) {
pollLoop->removeCallback(mainWorkRead);
@@ -114,9 +302,6 @@
}
setSurface(NULL);
setInputChannel(NULL);
- if (callbacks.onDestroy != NULL) {
- callbacks.onDestroy(&activity);
- }
if (mainWorkRead >= 0) close(mainWorkRead);
if (mainWorkWrite >= 0) close(mainWorkWrite);
if (dlhandle != NULL) {
@@ -129,7 +314,7 @@
void setSurface(jobject _surface) {
if (_surface != NULL) {
- nativeWindow = android_Surface_getNativeWindow(activity.env, _surface);
+ nativeWindow = android_Surface_getNativeWindow(env, _surface);
} else {
nativeWindow = NULL;
}
@@ -138,16 +323,16 @@
status_t setInputChannel(jobject _channel) {
if (inputChannel != NULL) {
delete nativeInputQueue;
- activity.env->DeleteGlobalRef(inputChannel);
+ env->DeleteGlobalRef(inputChannel);
}
inputChannel = NULL;
nativeInputQueue = NULL;
if (_channel != NULL) {
- inputChannel = activity.env->NewGlobalRef(_channel);
+ inputChannel = env->NewGlobalRef(_channel);
sp<InputChannel> ic =
- android_view_InputChannel_getInputChannel(activity.env, _channel);
+ android_view_InputChannel_getInputChannel(env, _channel);
if (ic != NULL) {
- nativeInputQueue = new MyInputQueue(ic, mainWorkWrite);
+ nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
if (nativeInputQueue->getConsumer().initialize() != android::OK) {
delete nativeInputQueue;
nativeInputQueue = NULL;
@@ -160,7 +345,6 @@
return OK;
}
- ANativeActivity activity;
ANativeActivityCallbacks callbacks;
void* dlhandle;
@@ -170,8 +354,11 @@
String8 externalDataPath;
sp<ANativeWindow> nativeWindow;
+ int32_t lastWindowWidth;
+ int32_t lastWindowHeight;
+
jobject inputChannel;
- struct MyInputQueue* nativeInputQueue;
+ struct AInputQueue* nativeInputQueue;
// These are used to wake up the main thread to process work.
int mainWorkRead;
@@ -179,6 +366,30 @@
sp<PollLoop> pollLoop;
};
+void android_NativeActivity_setWindowFormat(
+ ANativeActivity* activity, int32_t format) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
+}
+
+void android_NativeActivity_setWindowFlags(
+ ANativeActivity* activity, int32_t values, int32_t mask) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
+}
+
+void android_NativeActivity_showSoftInput(
+ ANativeActivity* activity, int32_t flags) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
+}
+
+void android_NativeActivity_hideSoftInput(
+ ANativeActivity* activity, int32_t flags) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
+}
+
// ------------------------------------------------------------------------
/*
@@ -186,19 +397,51 @@
*/
static bool mainWorkCallback(int fd, int events, void* data) {
NativeCode* code = (NativeCode*)data;
- if ((events & POLLIN) != 0) {
- KeyEvent* keyEvent;
- while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
- jobject inputEventObj = android_view_KeyEvent_fromNative(
- code->activity.env, keyEvent);
- code->activity.env->CallVoidMethod(code->activity.clazz,
- gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
- int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
- if (res != OK) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+ if ((events & POLLIN) == 0) {
+ return true;
+ }
+
+ ActivityWork work;
+ if (!read_work(code->mainWorkRead, &work)) {
+ return true;
+ }
+
+ LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
+
+ switch (work.cmd) {
+ case CMD_DEF_KEY: {
+ KeyEvent* keyEvent;
+ while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
+ jobject inputEventObj = android_view_KeyEvent_fromNative(
+ code->env, keyEvent);
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+ int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
+ if (res != OK) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+ }
}
- }
+ } break;
+ case CMD_SET_WINDOW_FORMAT: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.setWindowFormat, work.arg1);
+ } break;
+ case CMD_SET_WINDOW_FLAGS: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
+ } break;
+ case CMD_SHOW_SOFT_INPUT: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.showIme, work.arg1);
+ } break;
+ case CMD_HIDE_SOFT_INPUT: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.hideIme, work.arg1);
+ } break;
+ default:
+ LOGW("Unknown work command: %d", work.cmd);
+ break;
}
return true;
@@ -210,6 +453,8 @@
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
jstring internalDataDir, jstring externalDataDir, int sdkVersion)
{
+ LOG_TRACE("loadNativeCode_native");
+
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
@@ -241,30 +486,36 @@
}
code->mainWorkRead = msgpipe[0];
code->mainWorkWrite = msgpipe[1];
+ int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make main work read pipe "
+ "non-blocking: %s", strerror(errno));
+ result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
+ SLOGW_IF(result != 0, "Could not make main work write pipe "
+ "non-blocking: %s", strerror(errno));
code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code);
- code->activity.callbacks = &code->callbacks;
- if (env->GetJavaVM(&code->activity.vm) < 0) {
+ code->ANativeActivity::callbacks = &code->callbacks;
+ if (env->GetJavaVM(&code->vm) < 0) {
LOGW("NativeActivity GetJavaVM failed");
delete code;
return 0;
}
- code->activity.env = env;
- code->activity.clazz = env->NewGlobalRef(clazz);
+ code->env = env;
+ code->clazz = env->NewGlobalRef(clazz);
const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
code->internalDataPath = dirStr;
- code->activity.internalDataPath = code->internalDataPath.string();
+ code->internalDataPath = code->internalDataPath.string();
env->ReleaseStringUTFChars(path, dirStr);
dirStr = env->GetStringUTFChars(externalDataDir, NULL);
code->externalDataPath = dirStr;
- code->activity.externalDataPath = code->externalDataPath.string();
+ code->externalDataPath = code->externalDataPath.string();
env->ReleaseStringUTFChars(path, dirStr);
- code->activity.sdkVersion = sdkVersion;
+ code->sdkVersion = sdkVersion;
- code->createActivityFunc(&code->activity, NULL, 0);
+ code->createActivityFunc(code, NULL, 0);
}
return (jint)code;
@@ -273,6 +524,7 @@
static void
unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("unloadNativeCode_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
delete code;
@@ -282,10 +534,11 @@
static void
onStart_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onStart_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onStart != NULL) {
- code->callbacks.onStart(&code->activity);
+ code->callbacks.onStart(code);
}
}
}
@@ -293,10 +546,11 @@
static void
onResume_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onResume_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onResume != NULL) {
- code->callbacks.onResume(&code->activity);
+ code->callbacks.onResume(code);
}
}
}
@@ -304,11 +558,12 @@
static void
onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onSaveInstanceState_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onSaveInstanceState != NULL) {
size_t len = 0;
- code->callbacks.onSaveInstanceState(&code->activity, &len);
+ code->callbacks.onSaveInstanceState(code, &len);
}
}
}
@@ -316,10 +571,11 @@
static void
onPause_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onPause_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onPause != NULL) {
- code->callbacks.onPause(&code->activity);
+ code->callbacks.onPause(code);
}
}
}
@@ -327,10 +583,11 @@
static void
onStop_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onStop_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onStop != NULL) {
- code->callbacks.onStop(&code->activity);
+ code->callbacks.onStop(code);
}
}
}
@@ -338,10 +595,11 @@
static void
onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
{
+ LOG_TRACE("onLowMemory_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onLowMemory != NULL) {
- code->callbacks.onLowMemory(&code->activity);
+ code->callbacks.onLowMemory(code);
}
}
}
@@ -349,10 +607,11 @@
static void
onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
{
+ LOG_TRACE("onWindowFocusChanged_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onWindowFocusChanged != NULL) {
- code->callbacks.onWindowFocusChanged(&code->activity, focused ? 1 : 0);
+ code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
}
}
}
@@ -360,33 +619,72 @@
static void
onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
{
+ LOG_TRACE("onSurfaceCreated_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
code->setSurface(surface);
if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
- code->callbacks.onNativeWindowCreated(&code->activity,
+ code->callbacks.onNativeWindowCreated(code,
code->nativeWindow.get());
}
}
}
+static int32_t getWindowProp(ANativeWindow* window, int what) {
+ int value;
+ int res = window->query(window, what, &value);
+ return res < 0 ? res : value;
+}
+
static void
onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
jint format, jint width, jint height)
{
+ LOG_TRACE("onSurfaceChanged_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
code->setSurface(surface);
if (oldNativeWindow != code->nativeWindow) {
if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
- code->callbacks.onNativeWindowDestroyed(&code->activity,
+ code->callbacks.onNativeWindowDestroyed(code,
oldNativeWindow.get());
}
- if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
- code->callbacks.onNativeWindowCreated(&code->activity,
- code->nativeWindow.get());
+ if (code->nativeWindow != NULL) {
+ if (code->callbacks.onNativeWindowCreated != NULL) {
+ code->callbacks.onNativeWindowCreated(code,
+ code->nativeWindow.get());
+ }
+ code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
+ NATIVE_WINDOW_WIDTH);
+ code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
+ NATIVE_WINDOW_HEIGHT);
}
+ } else {
+ // Maybe it resized?
+ int32_t newWidth = getWindowProp(code->nativeWindow.get(),
+ NATIVE_WINDOW_WIDTH);
+ int32_t newHeight = getWindowProp(code->nativeWindow.get(),
+ NATIVE_WINDOW_HEIGHT);
+ if (newWidth != code->lastWindowWidth
+ || newHeight != code->lastWindowHeight) {
+ if (code->callbacks.onNativeWindowResized != NULL) {
+ code->callbacks.onNativeWindowResized(code,
+ code->nativeWindow.get());
+ }
+ }
+ }
+ }
+}
+
+static void
+onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
+{
+ LOG_TRACE("onSurfaceRedrawNeeded_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
+ code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
}
}
}
@@ -394,10 +692,11 @@
static void
onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
{
+ LOG_TRACE("onSurfaceDestroyed_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
- code->callbacks.onNativeWindowDestroyed(&code->activity,
+ code->callbacks.onNativeWindowDestroyed(code,
code->nativeWindow.get());
}
code->setSurface(NULL);
@@ -407,6 +706,7 @@
static void
onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
{
+ LOG_TRACE("onInputChannelCreated_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
status_t err = code->setInputChannel(channel);
@@ -416,7 +716,7 @@
return;
}
if (code->callbacks.onInputQueueCreated != NULL) {
- code->callbacks.onInputQueueCreated(&code->activity,
+ code->callbacks.onInputQueueCreated(code,
code->nativeInputQueue);
}
}
@@ -425,17 +725,50 @@
static void
onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
{
+ LOG_TRACE("onInputChannelDestroyed_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL
&& code->callbacks.onInputQueueDestroyed != NULL) {
- code->callbacks.onInputQueueDestroyed(&code->activity,
+ code->callbacks.onInputQueueDestroyed(code,
code->nativeInputQueue);
}
code->setInputChannel(NULL);
}
}
+static void
+onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
+ jint x, jint y, jint w, jint h)
+{
+ LOG_TRACE("onContentRectChanged_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->callbacks.onContentRectChanged != NULL) {
+ ARect rect;
+ rect.left = x;
+ rect.top = y;
+ rect.right = x+w;
+ rect.bottom = y+h;
+ code->callbacks.onContentRectChanged(code, &rect);
+ }
+ }
+}
+
+static void
+dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
+{
+ LOG_TRACE("dispatchKeyEvent_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->nativeInputQueue != NULL) {
+ KeyEvent* event = new KeyEvent();
+ android_view_KeyEvent_toNative(env, eventObj, INPUT_EVENT_NATURE_KEY, event);
+ code->nativeInputQueue->dispatchEvent(event);
+ }
+ }
+}
+
static const JNINativeMethod g_methods[] = {
{ "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;I)I",
(void*)loadNativeCode_native },
@@ -449,9 +782,12 @@
{ "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
{ "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
{ "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
+ { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
{ "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
{ "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
+ { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
+ { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
};
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
@@ -481,6 +817,12 @@
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
gNativeActivityClassInfo.clazz,
"setWindowFormat", "(I)V");
+ GET_METHOD_ID(gNativeActivityClassInfo.showIme,
+ gNativeActivityClassInfo.clazz,
+ "showIme", "(I)V");
+ GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
+ gNativeActivityClassInfo.clazz,
+ "hideIme", "(I)V");
return AndroidRuntime::registerNativeMethods(
env, kNativeActivityPathName,
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 5a92193..290b532 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -62,6 +62,7 @@
};
static jfieldID offset_db_handle;
+static jmethodID method_custom_function_callback;
static char *createStr(const char *path, short extra) {
int len = strlen(path) + extra;
@@ -458,6 +459,62 @@
}
}
+static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (!env) {
+ LOGE("custom_function_callback cannot call into Java on this thread");
+ return;
+ }
+
+ // pack up the arguments into a string array
+ jobjectArray strArray = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL);
+ if (!strArray) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return;
+ }
+ for (int i = 0; i < argc; i++) {
+ char* arg = (char *)sqlite3_value_text(argv[i]);
+ jobject obj = env->NewStringUTF(arg);
+ if (!obj) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return;
+ }
+ env->SetObjectArrayElement(strArray, i, obj);
+ env->DeleteLocalRef(obj);
+ }
+
+ // get global ref to CustomFunction object from our user data
+ jobject function = (jobject)sqlite3_user_data(context);
+ env->CallVoidMethod(function, method_custom_function_callback, strArray);
+}
+
+static jint native_addCustomFunction(JNIEnv* env, jobject object,
+ jstring name, jint numArgs, jobject function)
+{
+ sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
+ char const *nameStr = env->GetStringUTFChars(name, NULL);
+ jobject ref = env->NewGlobalRef(function);
+ LOGD("native_addCustomFunction %s ref: %d", nameStr, ref);
+ int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8,
+ (void *)ref, custom_function_callback, NULL, NULL);
+ env->ReleaseStringUTFChars(name, nameStr);
+
+ if (err == SQLITE_OK)
+ return (int)ref;
+ else {
+ LOGE("sqlite3_create_function returned %d", err);
+ env->DeleteGlobalRef(ref);
+ throw_sqlite3_exception(env, handle);
+ return 0;
+ }
+}
+
+static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
+{
+ LOGD("native_releaseCustomFunction %d", ref);
+ env->DeleteGlobalRef((jobject)ref);
+}
+
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
@@ -472,6 +529,10 @@
{"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
{"releaseMemory", "()I", (void *)native_releaseMemory},
{"native_finalize", "(I)V", (void *)native_finalize},
+ {"native_addCustomFunction",
+ "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
+ (void *)native_addCustomFunction},
+ {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
};
int register_android_database_SQLiteDatabase(JNIEnv *env)
@@ -490,6 +551,17 @@
return -1;
}
+ clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction");
+ if (clazz == NULL) {
+ LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n");
+ return -1;
+ }
+ method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V");
+ if (method_custom_function_callback == NULL) {
+ LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n");
+ return -1;
+ }
+
return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
sMethods, NELEM(sMethods));
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index feb0dad..3cde9d6 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -222,10 +222,10 @@
{ "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
- { "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute },
+ { "addHostRouteNative", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute },
{ "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes },
- { "setDefaultRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_setDefaultRoute },
- { "getDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_getDefaultRoute },
+ { "setDefaultRouteNative", "(Ljava/lang/String;I)I", (void *)android_net_utils_setDefaultRoute },
+ { "getDefaultRouteNative", "(Ljava/lang/String;)I", (void *)android_net_utils_getDefaultRoute },
{ "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute },
{ "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3ee404a..4a877d2 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define LOG_TAG "android.os.Debug"
#include "JNIHelp.h"
#include "jni.h"
#include "utils/misc.h"
@@ -24,6 +25,8 @@
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
+#include <errno.h>
+#include <assert.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
@@ -274,6 +277,176 @@
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
+
+#ifdef HAVE_ANDROID_OS
+/* pulled out of bionic */
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+ size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
+extern "C" void free_malloc_leak_info(uint8_t* info);
+#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
+#define BACKTRACE_SIZE 32
+
+/*
+ * This is a qsort() callback.
+ *
+ * See dumpNativeHeap() for comments about the data format and sort order.
+ */
+static int compareHeapRecords(const void* vrec1, const void* vrec2)
+{
+ const size_t* rec1 = (const size_t*) vrec1;
+ const size_t* rec2 = (const size_t*) vrec2;
+ size_t size1 = *rec1;
+ size_t size2 = *rec2;
+
+ if (size1 < size2) {
+ return 1;
+ } else if (size1 > size2) {
+ return -1;
+ }
+
+ intptr_t* bt1 = (intptr_t*)(rec1 + 2);
+ intptr_t* bt2 = (intptr_t*)(rec2 + 2);
+ for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
+ intptr_t addr1 = bt1[idx];
+ intptr_t addr2 = bt2[idx];
+ if (addr1 == addr2) {
+ if (addr1 == 0)
+ break;
+ continue;
+ }
+ if (addr1 < addr2) {
+ return -1;
+ } else if (addr1 > addr2) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The get_malloc_leak_info() call returns an array of structs that
+ * look like this:
+ *
+ * size_t size
+ * size_t allocations
+ * intptr_t backtrace[32]
+ *
+ * "size" is the size of the allocation, "backtrace" is a fixed-size
+ * array of function pointers, and "allocations" is the number of
+ * allocations with the exact same size and backtrace.
+ *
+ * The entries are sorted by descending total size (i.e. size*allocations)
+ * then allocation count. For best results with "diff" we'd like to sort
+ * primarily by individual size then stack trace. Since the entries are
+ * fixed-size, and we're allowed (by the current implementation) to mangle
+ * them, we can do this in place.
+ */
+static void dumpNativeHeap(FILE* fp)
+{
+ uint8_t* info = NULL;
+ size_t overallSize, infoSize, totalMemory, backtraceSize;
+
+ get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
+ &backtraceSize);
+ if (info == NULL) {
+ fprintf(fp, "Native heap dump not available. To enable, run these"
+ " commands (requires root):\n");
+ fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
+ fprintf(fp, "$ adb shell stop\n");
+ fprintf(fp, "$ adb shell start\n");
+ return;
+ }
+ assert(infoSize != 0);
+ assert(overallSize % infoSize == 0);
+
+ fprintf(fp, "Android Native Heap Dump v1.0\n\n");
+
+ size_t recordCount = overallSize / infoSize;
+ fprintf(fp, "Total memory: %zu\n", totalMemory);
+ fprintf(fp, "Allocation records: %zd\n", recordCount);
+ if (backtraceSize != BACKTRACE_SIZE) {
+ fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
+ backtraceSize, BACKTRACE_SIZE);
+ }
+ fprintf(fp, "\n");
+
+ /* re-sort the entries */
+ qsort(info, recordCount, infoSize, compareHeapRecords);
+
+ /* dump the entries to the file */
+ const uint8_t* ptr = info;
+ for (size_t idx = 0; idx < recordCount; idx++) {
+ size_t size = *(size_t*) ptr;
+ size_t allocations = *(size_t*) (ptr + sizeof(size_t));
+ intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
+
+ fprintf(fp, "z %d sz %8zu num %4zu bt",
+ (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
+ size & ~SIZE_FLAG_ZYGOTE_CHILD,
+ allocations);
+ for (size_t bt = 0; bt < backtraceSize; bt++) {
+ if (backtrace[bt] == 0) {
+ break;
+ } else {
+ fprintf(fp, " %08x", backtrace[bt]);
+ }
+ }
+ fprintf(fp, "\n");
+
+ ptr += infoSize;
+ }
+
+ fprintf(fp, "END\n");
+ free_malloc_leak_info(info);
+}
+#endif /*HAVE_ANDROID_OS*/
+
+/*
+ * Dump the native heap, writing human-readable output to the specified
+ * file descriptor.
+ */
+static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor)
+{
+ if (fileDescriptor == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+ int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ if (origFd < 0) {
+ jniThrowRuntimeException(env, "Invalid file descriptor");
+ return;
+ }
+
+ /* dup() the descriptor so we don't close the original with fclose() */
+ int fd = dup(origFd);
+ if (fd < 0) {
+ LOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
+ jniThrowRuntimeException(env, "dup() failed");
+ return;
+ }
+
+ FILE* fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
+ close(fd);
+ jniThrowRuntimeException(env, "fdopen() failed");
+ return;
+ }
+
+#ifdef HAVE_ANDROID_OS
+ LOGD("Native heap dump starting...\n");
+ dumpNativeHeap(fp);
+ LOGD("Native heap dump complete.\n");
+#else
+ fprintf(fp, "Native heap dump not available on this platform\n");
+#endif
+
+ fclose(fp);
+}
+
+
/*
* JNI registration.
*/
@@ -289,6 +462,8 @@
(void*) android_os_Debug_getDirtyPages },
{ "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
(void*) android_os_Debug_getDirtyPagesPid },
+ { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_os_Debug_dumpNativeHeap },
{ "getBinderSentTransactions", "()I",
(void*) android_os_Debug_getBinderSentTransactions },
{ "getBinderReceivedTransactions", "()I",
@@ -320,4 +495,4 @@
return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
}
-};
+}; // namespace android
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 961f806..847b5a5 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -53,7 +53,7 @@
NativeMessageQueue::NativeMessageQueue() {
mPollLoop = PollLoop::getForThread();
if (mPollLoop == NULL) {
- mPollLoop = new PollLoop();
+ mPollLoop = new PollLoop(false);
PollLoop::setForThread(mPollLoop);
}
}
@@ -62,7 +62,7 @@
}
bool NativeMessageQueue::pollOnce(int timeoutMillis) {
- return mPollLoop->pollOnce(timeoutMillis);
+ return mPollLoop->pollOnce(timeoutMillis) != PollLoop::POLL_TIMEOUT;
}
void NativeMessageQueue::wake() {
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 3c88158..efaff12 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -231,6 +231,20 @@
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
+ "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
+ &err);
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ return JNI_FALSE;
+ }
+ dbus_bus_add_match(nat->conn,
+ "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
+ &err);
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ return JNI_FALSE;
+ }
+ dbus_bus_add_match(nat->conn,
"type='signal',interface='org.bluez.AudioSink'",
&err);
if (dbus_error_is_set(&err)) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 47f5bfc..dbf482e 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -178,65 +178,32 @@
// ----------------------------------------------------------------------------
static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas,
- OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top,
- SkPaint* paint, jint bitmapDensity, jint canvasDensity,jint screenDensity) {
- if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
- renderer->drawBitmap(bitmap, left, top, paint);
- } else {
- renderer->save(0);
- const float scale = canvasDensity / float(bitmapDensity);
- renderer->translate(left, top);
- renderer->scale(scale, scale);
- renderer->drawBitmap(bitmap, left, top, paint);
- renderer->restore();
- }
+ OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+ renderer->drawBitmap(bitmap, left, top, paint);
}
static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, SkBitmap* bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- SkPaint* paint, jint bitmapDensity, jint canvasDensity, jint screenDensity) {
- if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
- renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, paint);
- } else {
- // TODO: implement
- }
+ float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) {
+ renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint);
}
static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas,
- OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint,
- jint bitmapDensity, jint canvasDensity,jint screenDensity) {
- if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
- renderer->drawBitmap(bitmap, matrix, paint);
- } else {
- // TODO: implement
- }
+ OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+ renderer->drawBitmap(bitmap, matrix, paint);
}
static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks,
- float left, float top, float right, float bottom, SkPaint* paint,
- jint bitmapDensity, jint canvasDensity,jint screenDensity) {
+ float left, float top, float right, float bottom, SkPaint* paint) {
jbyte* storage = env->GetByteArrayElements(chunks, NULL);
Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage);
Res_png_9patch::deserialize(patch);
- if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
- renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
- } else {
- renderer->save(0);
- const float scale = canvasDensity / float(bitmapDensity);
- renderer->translate(left, top);
- renderer->scale(scale, scale);
- left = top = 0.0f;
- right = (right - left) / scale;
- bottom = (bottom - top) / scale;
- renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
- renderer->restore();
- }
+ renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
// TODO: make sure that 0 is correct for the flags
env->ReleaseByteArrayElements(chunks, storage, 0);
@@ -254,43 +221,62 @@
}
// ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_resetShader(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer) {
+ renderer->resetShader();
+}
+
+static void android_view_GLES20Canvas_setupBitmapShader(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, SkShader* shader, SkBitmap* bitmap,
+ SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix) {
+ renderer->setupBitmapShader(bitmap, tileX, tileY, matrix,
+ (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+}
+
+// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
const char* const kClassPathName = "android/view/GLES20Canvas";
static JNINativeMethod gMethods[] = {
- { "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", "(IFFFF)Z", (void*) android_view_GLES20Canvas_clipRectF },
- { "nClipRect", "(IIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
+ { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
+ { "nClipRect", "(IFFFF)Z", (void*) android_view_GLES20Canvas_clipRectF },
+ { "nClipRect", "(IIIII)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", "(IIFFIIII)V", (void*) android_view_GLES20Canvas_drawBitmap },
- { "nDrawBitmap", "(IIFFFFFFFFIIII)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
- { "nDrawBitmap", "(IIIIIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
- { "nDrawPatch", "(II[BFFFFIIII)V", (void*) android_view_GLES20Canvas_drawPatch },
- { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
- { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
+ { "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 },
+
+ { "nResetShader", "(I)V", (void*) android_view_GLES20Canvas_resetShader },
+ { "nSetupBitmapShader", "(IIIIII)V", (void*) android_view_GLES20Canvas_setupBitmapShader },
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a82abc93..7305032 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -33,7 +33,7 @@
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
-#include "android_view_Surface.h"
+#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 866c038..941ed63 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -16,6 +16,7 @@
*/
#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
#include <EGL/egl.h>
@@ -25,8 +26,6 @@
#include <SkBitmap.h>
#include <SkPixelRef.h>
-#include "android_view_Surface.h"
-
namespace android {
static jclass gDisplay_class;
diff --git a/core/res/res/drawable-hdpi/cursor_controller.png b/core/res/res/drawable-hdpi/cursor_controller.png
new file mode 100644
index 0000000..720aded
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cursor_controller.png
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
new file mode 100644
index 0000000..624ab58
--- /dev/null
+++ b/core/res/res/drawable-hdpi/selection_end_handle.png
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
new file mode 100644
index 0000000..7d6f24c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/selection_start_handle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
deleted file mode 100644
index 2d13237..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
deleted file mode 100644
index 950713b..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
deleted file mode 100644
index 2d13237..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cursor_controller.png b/core/res/res/drawable-mdpi/cursor_controller.png
new file mode 100644
index 0000000..1a8a459
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cursor_controller.png
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
new file mode 100644
index 0000000..7e075eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/selection_end_handle.png
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
new file mode 100644
index 0000000..d8022f7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/selection_start_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
deleted file mode 100644
index 7abfd19..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
deleted file mode 100644
index c44d062..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
deleted file mode 100644
index 7abfd19..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge/default_wallpaper.jpg b/core/res/res/drawable-xlarge/default_wallpaper.jpg
new file mode 100644
index 0000000..0302f00
--- /dev/null
+++ b/core/res/res/drawable-xlarge/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml
index 6f9f1e0..1414032 100644
--- a/core/res/res/layout/list_content.xml
+++ b/core/res/res/layout/list_content.xml
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/assets/res/layout/list_content.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.
@@ -17,8 +15,42 @@
** limitations under the License.
*/
-->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:drawSelectorOnTop="false"
- />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:id="@+id/progressContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:gravity="center">
+
+ <ProgressBar style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/loading"
+ android:paddingTop="4dip"
+ android:singleLine="true" />
+
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/listContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false" />
+ <TextView android:id="@+android:id/internalEmpty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/core/res/res/layout/list_content_rich.xml b/core/res/res/layout/list_content_rich.xml
deleted file mode 100644
index 1414032..0000000
--- a/core/res/res/layout/list_content_rich.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?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.
-*/
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout android:id="@+id/progressContainer"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- android:gravity="center">
-
- <ProgressBar style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/loading"
- android:paddingTop="4dip"
- android:singleLine="true" />
-
- </LinearLayout>
-
- <FrameLayout android:id="@+id/listContainer"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:drawSelectorOnTop="false" />
- <TextView android:id="@+android:id/internalEmpty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceLarge" />
- </FrameLayout>
-
-</FrameLayout>
diff --git a/core/res/res/layout/list_content_simple.xml b/core/res/res/layout/list_content_simple.xml
new file mode 100644
index 0000000..6f9f1e0
--- /dev/null
+++ b/core/res/res/layout/list_content_simple.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false"
+ />
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
new file mode 100644
index 0000000..2640662
--- /dev/null
+++ b/core/res/res/values-xlarge/config.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Component to be used as the status bar service. Must implement the IStatusBar
+ 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>
+</resources>
+
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b3f280b..67072b6 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -942,6 +942,21 @@
<enum name="KEYCODE_PAGE_DOWN" value="93" />
<enum name="KEYCODE_PICTSYMBOLS" value="94" />
<enum name="KEYCODE_SWITCH_CHARSET" value="95" />
+ <enum name="KEYCODE_BUTTON_A" value="96" />
+ <enum name="KEYCODE_BUTTON_B" value="97" />
+ <enum name="KEYCODE_BUTTON_C" value="98" />
+ <enum name="KEYCODE_BUTTON_X" value="99" />
+ <enum name="KEYCODE_BUTTON_Y" value="100" />
+ <enum name="KEYCODE_BUTTON_Z" value="101" />
+ <enum name="KEYCODE_BUTTON_L1" value="102" />
+ <enum name="KEYCODE_BUTTON_R1" value="103" />
+ <enum name="KEYCODE_BUTTON_L2" value="104" />
+ <enum name="KEYCODE_BUTTON_R2" value="105" />
+ <enum name="KEYCODE_BUTTON_THUMBL" value="106" />
+ <enum name="KEYCODE_BUTTON_THUMBR" value="107" />
+ <enum name="KEYCODE_BUTTON_START" value="108" />
+ <enum name="KEYCODE_BUTTON_SELECT" value="109" />
+ <enum name="KEYCODE_BUTTON_MODE" value="110" />
</attr>
<!-- ***************************************************************** -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 689f73a..ec0d83c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -22,7 +22,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Component to be used as the status bar service. Must implement the IStatusBar
interface. This name is in the ComponentName flattened format (package/class) -->
- <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.StatusBarService</string>
+ <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string>
<!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
icons in the status bar that are not notifications. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 61e7b88..679e642 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,9 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">84dp</dimen>
+ <dimen name="thumbnail_width">0dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">63dp</dimen>
+ <dimen name="thumbnail_height">0dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
@@ -45,4 +45,6 @@
<dimen name="password_keyboard_key_height">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 b9e5e84..d0be554 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -869,8 +869,11 @@
<public type="drawable" name="stat_sys_download" id="0x01080081" />
<public type="drawable" name="stat_sys_download_done" id="0x01080082" />
<public type="drawable" name="stat_sys_headset" id="0x01080083" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call" id="0x01080084" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call_forward" id="0x01080085" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call_on_hold" id="0x01080086" />
<public type="drawable" name="stat_sys_speakerphone" id="0x01080087" />
<public type="drawable" name="stat_sys_upload" id="0x01080088" />
@@ -1132,7 +1135,9 @@
<public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
<public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_vp_phone_call" id="0x010800a7" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_vp_phone_call_on_hold" id="0x010800a8" />
<public type="anim" name="anticipate_interpolator" id="0x010a0007" />
@@ -1246,6 +1251,7 @@
<public type="attr" name="logo" id="0x010102be" />
<public type="attr" name="xlargeScreens" id="0x010102bf" />
<public type="attr" name="heavyWeight" id="0x010102c0" />
+ <public type="attr" name="immersive" id="0x010102c1" />
<public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
<public-padding type="id" name="kraken_resource_pad" end="0x01020040" />
@@ -1309,4 +1315,11 @@
<public type="style" name="Widget.Spinner.DropDown" />
<public type="style" name="Widget.ActionButton" />
+ <!-- Standard content view for a {@link android.app.ListFragment}.
+ If you are implementing a subclass of ListFragment with your
+ own customized content, you can include this layout in that
+ content to still retain all of the standard functionality of
+ the base class. -->
+ <public type="layout" name="list_content" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 289ec32..7a80884 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1605,7 +1605,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.2 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.2</string>
+ AppleWebKit/534.3 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.3</string>
<!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
<string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 449b56c..3c09a89 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -626,7 +626,7 @@
<style name="TextAppearance">
<item name="android:textColor">?textColorPrimary</item>
- <item name="android:textColorHighlight">#FFFF9200</item>
+ <item name="android:textColorHighlight">#D077A14B</item>
<item name="android:textColorHint">?textColorHint</item>
<item name="android:textColorLink">#5C5CFF</item>
<item name="android:textSize">16sp</item>
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 8006b73..91ef0b7 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -199,7 +199,7 @@
private boolean inXact = false;
private int numXacts;
- private static final int TIME_TO_RUN_WAL_TEST_FOR = 15; // num sec this test shoudl run
+ private static final int TIME_TO_RUN_WAL_TEST_FOR = 15; // num sec this test should run
private int[][] counts = new int[2][2];
private synchronized boolean inXact() {
@@ -279,11 +279,24 @@
}
}
+ private static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram {
+ private ClassToTestSqlCompilationAndCaching(SQLiteDatabase db, String sql) {
+ super(db, sql);
+ }
+ private static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) {
+ db.lock();
+ try {
+ return new ClassToTestSqlCompilationAndCaching(db, sql);
+ } finally {
+ db.unlock();
+ }
+ }
+ }
+
@SmallTest
public void testLruCachingOfSqliteCompiledSqlObjs() {
mDatabase.disableWriteAheadLogging();
mDatabase.execSQL("CREATE TABLE test (i int, j int);");
- mDatabase.execSQL("insert into test values(1,1);");
// set cache size
int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
mDatabase.setMaxSqlCacheSize(N);
@@ -292,22 +305,24 @@
// insertion of (N+1)th entry, make sure 0th entry is closed
ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
ArrayList<String> sqlStrings = new ArrayList<String>();
- SQLiteStatement stmt0 = null;
+ int stmt0 = 0;
for (int i = 0; i < N+1; i++) {
- String s = "select * from test where i = " + i + " and j = ?";
+ String s = "insert into test values(" + i + ",?);";
sqlStrings.add(s);
- SQLiteStatement c = mDatabase.compileStatement(s);
- c.bindLong(1, 1);
- stmtObjs.add(i, c.getSqlStatementId());
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
+ int n = c.getSqlStatementId();
+ stmtObjs.add(i, n);
if (i == 0) {
- // save thie SQLiteStatement obj. we want to make sure it is thrown out of
- // the cache and its handle is 0'ed.
- stmt0 = c;
+ // save the statementId of this obj. we want to make sure it is thrown out of
+ // the cache at the end of this test.
+ stmt0 = n;
}
c.close();
}
- // is 0'th entry out of the cache?
- assertEquals(0, stmt0.getSqlStatementId());
+ // is 0'th entry out of the cache? it should be in the list of statementIds
+ // corresponding to the pre-compiled sql statements to be finalized.
+ assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
for (int i = 1; i < N+1; i++) {
SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i));
assertNotNull(compSql);
@@ -321,11 +336,7 @@
"num1 INTEGER, num2 INTEGER, image BLOB);");
final String statement = "DELETE FROM test WHERE _id=?;";
SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement);
- // SQl statement is compiled only at find bind or execute call
- assertTrue(statementDoNotClose.getSqlStatementId() == 0);
statementDoNotClose.bindLong(1, 1);
- assertTrue(statementDoNotClose.getSqlStatementId() > 0);
- int nStatement = statementDoNotClose.getSqlStatementId();
/* do not close statementDoNotClose object.
* That should leave it in SQLiteDatabase.mPrograms.
* mDatabase.close() in tearDown() should release it.
@@ -334,30 +345,31 @@
/**
* test to make sure the statement finalizations are not done right away but
- * piggybacked onto the next sql statement execution on the same database.
+ * piggy-backed onto the next sql statement execution on the same database.
*/
@SmallTest
public void testStatementClose() {
mDatabase.execSQL("CREATE TABLE test (i int, j int);");
// fill up statement cache in mDatabase\
- int N = 26;
+ int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
mDatabase.setMaxSqlCacheSize(N);
SQLiteStatement stmt;
int stmt0Id = 0;
for (int i = 0; i < N; i ++) {
- stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);");
- stmt.bindLong(1, 1);
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(" + i + ", ?);");
// keep track of 0th entry
if (i == 0) {
- stmt0Id = stmt.getSqlStatementId();
+ stmt0Id = c.getSqlStatementId();
}
- stmt.executeInsert();
- stmt.close();
+ c.close();
}
// add one more to the cache - and the above 'stmt0Id' should fall out of cache
- SQLiteStatement stmt1 = mDatabase.compileStatement("select * from test where i = ?;");
- stmt1.bindLong(1, 1);
+ ClassToTestSqlCompilationAndCaching stmt1 =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(100, ?);");
stmt1.close();
// the above close() should have queuedUp the statement for finalization
@@ -372,7 +384,7 @@
/**
* same as above - except that the statement to be finalized is from Thread # 1.
- * and it is eventually finalized in Thread # 2 when it executes a sql statement.
+ * and it is eventually finalized in Thread # 2 when it executes a SQL statement.
* @throws InterruptedException
*/
@LargeTest
@@ -381,18 +393,18 @@
// fill up statement cache in mDatabase in a thread
Thread t1 = new Thread() {
@Override public void run() {
- int N = 26;
+ int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
mDatabase.setMaxSqlCacheSize(N);
SQLiteStatement stmt;
for (int i = 0; i < N; i ++) {
- stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);");
- stmt.bindLong(1,1);
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(" + i + ", ?);");
// keep track of 0th entry
if (i == 0) {
- setStmt0Id(stmt.getSqlStatementId());
+ stmt0Id = c.getSqlStatementId();
}
- stmt.executeInsert();
- stmt.close();
+ c.close();
}
}
};
@@ -404,9 +416,9 @@
// just for the heck of it, do it in a separate thread
Thread t2 = new Thread() {
@Override public void run() {
- SQLiteStatement stmt1 = mDatabase.compileStatement(
- "select * from test where i = ?;");
- stmt1.bindLong(1, 1);
+ ClassToTestSqlCompilationAndCaching stmt1 =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(100, ?);");
stmt1.close();
}
};
@@ -452,18 +464,18 @@
// fill up statement cache in mDatabase in a thread
Thread t1 = new Thread() {
@Override public void run() {
- int N = 26;
+ int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
mDatabase.setMaxSqlCacheSize(N);
SQLiteStatement stmt;
for (int i = 0; i < N; i ++) {
- stmt = mDatabase.compileStatement("insert into test values(" + i + ", ?);");
- stmt.bindLong(1, 1);
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(" + i + ", ?);");
// keep track of 0th entry
if (i == 0) {
- setStmt0Id(stmt.getSqlStatementId());
+ stmt0Id = c.getSqlStatementId();
}
- stmt.executeInsert();
- stmt.close();
+ c.close();
}
}
};
@@ -475,8 +487,9 @@
// just for the heck of it, do it in a separate thread
Thread t2 = new Thread() {
@Override public void run() {
- SQLiteStatement stmt1 = mDatabase.compileStatement(
- "select * from test where i = ?;");
+ ClassToTestSqlCompilationAndCaching stmt1 =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase,
+ "insert into test values(100, ?);");
stmt1.bindLong(1, 1);
stmt1.close();
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
index eb27551..217545f 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
@@ -158,14 +158,16 @@
assertEquals(0, stmt.getSqlStatementId());
int colValue = new Random().nextInt();
stmt.bindLong(1, colValue);
- // verify that the sql statement is now compiled
- int n = stmt.nStatement;
- assertTrue(n > 0);
- assertEquals(n, stmt.getSqlStatementId());
+ // verify that the sql statement is still not compiled
+ assertEquals(0, stmt.getSqlStatementId());
// should still be using the mDatabase connection - verify
assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+ assertEquals(mDatabase, stmt.mDatabase);
stmt.bindString(2, "blah" + colValue);
- assertEquals(n, stmt.nStatement);
+ // verify that the sql statement is still not compiled
+ assertEquals(0, stmt.getSqlStatementId());
+ assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+ assertEquals(mDatabase, stmt.mDatabase);
stmt.executeInsert();
// now that the statement is executed, pre-compiled statement should be released
assertEquals(0, stmt.nStatement);
@@ -187,97 +189,25 @@
assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
assertEquals(mDatabase, stmt.mDatabase);
stmt.bindString(1, "blah" + colValue);
- // verify that the sql statement is now compiled
- n = stmt.nStatement;
- assertTrue(n > 0);
- assertEquals(n, stmt.getSqlStatementId());
- SQLiteDatabase dbUsed = mDatabase;
- if (wal) {
- // if wal is set, should be using a pooled connection handle
- dbUsed = mDatabase.mConnectionPool.getConnectionList().get(0);
- assertTrue(mDatabase.mNativeHandle != dbUsed.mNativeHandle);
- }
- assertEquals(dbUsed.mNativeHandle, stmt.nHandle);
- assertEquals(dbUsed, stmt.mDatabase);
+ // verify that the sql statement is still not compiled
+ assertEquals(0, stmt.nStatement);
+ assertEquals(0, stmt.getSqlStatementId());
+ assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+ assertEquals(mDatabase, stmt.mDatabase);
// execute the statement
Long l = stmt.simpleQueryForLong();
assertEquals(colValue, l.intValue());
// now that the statement is executed, pre-compiled statement should be released
assertEquals(0, stmt.nStatement);
assertEquals(0, stmt.getSqlStatementId());
- // but the database handle should still remain attached to the statement
- assertEquals(dbUsed.mNativeHandle, stmt.nHandle);
- assertEquals(dbUsed, stmt.mDatabase);
+ assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+ assertEquals(mDatabase, stmt.mDatabase);
stmt.close();
// pre-compiled SQL statement should still remain released from this object
assertEquals(0, stmt.nStatement);
assertEquals(0, stmt.getSqlStatementId());
// but the database handle should still remain attached to the statement
- assertEquals(dbUsed, stmt.mDatabase);
- }
-
- /**
- * test to make sure SqliteStatement.nStatement is populated only during bind and execute calls.
- */
- public void testGetSqlStatementId() {
- mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
- "num1 INTEGER, num2 INTEGER, image BLOB);");
- final String statement = "DELETE FROM test WHERE _id=?;";
- SQLiteStatement statementOne = mDatabase.compileStatement(statement);
- // sql statement is NOT compiled until the bind or execute call.
- statementOne.bindLong(1, 1);
- SQLiteStatement statementTwo = mDatabase.compileStatement(statement);
- statementTwo.bindLong(1, 1);
- // since the same compiled statement is being accessed at the same time by 2 different
- // objects, they each get their own statement id
- assertTrue(statementOne.getSqlStatementId() != statementTwo.getSqlStatementId());
- statementOne.close();
- statementTwo.close();
-
- // two SQLiteStatements referring to the same SQL statement should refer to the same
- // pre-compiled SQl statement id if the SQLiteStatement objects are NOT in use at the same
- // time
- statementOne = mDatabase.compileStatement(statement);
- statementOne.bindLong(1, 1);
- // now that the SQL statement is compiled, get its pre-compiled SQL statement id
- int n = statementOne.getSqlStatementId();
- statementOne.close();
- statementTwo = mDatabase.compileStatement(statement);
- statementTwo.bindLong(1, 2); // use different value for bindarg, just for the heck of it
- assertEquals(n, statementTwo.getSqlStatementId());
- statementTwo.close();
-
- // now try to compile 2 different statements and they should have different uniquerIds.
- SQLiteStatement statement1 = mDatabase.compileStatement("DELETE FROM test WHERE _id > ?;");
- statement1.bindLong(1, 1);
- SQLiteStatement statement2 = mDatabase.compileStatement("DELETE FROM test WHERE _id < ?;");
- statement2.bindLong(1, 11);
- assertTrue(statement1.getSqlStatementId() != statement2.getSqlStatementId());
- statement1.close();
- statement2.close();
- }
-
- public void testOnAllReferencesReleased() {
- mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
- "num1 INTEGER, num2 INTEGER, image BLOB);");
- final String statement = "DELETE FROM test WHERE _id=?;";
- SQLiteStatement statementOne = mDatabase.compileStatement(statement);
- statementOne.bindLong(1, 1);
- assertTrue(statementOne.getSqlStatementId() > 0);
- statementOne.releaseReference();
- assertEquals(0, statementOne.getSqlStatementId());
- statementOne.close();
- }
-
- public void testOnAllReferencesReleasedFromContainer() {
- mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
- "num1 INTEGER, num2 INTEGER, image BLOB);");
- final String statement = "DELETE FROM test WHERE _id=?;";
- SQLiteStatement statementOne = mDatabase.compileStatement(statement);
- statementOne.bindLong(1, 1);
- assertTrue(statementOne.getSqlStatementId() > 0);
- statementOne.releaseReferenceFromContainer();
- assertEquals(0, statementOne.getSqlStatementId());
- statementOne.close();
+ assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+ assertEquals(mDatabase, stmt.mDatabase);
}
}
diff --git a/docs/html/community/index.jd b/docs/html/community/index.jd
index 3e69de4..23203c1 100644
--- a/docs/html/community/index.jd
+++ b/docs/html/community/index.jd
@@ -4,10 +4,10 @@
<div id="mainBodyFluid">
<h1>Community</h1>
-<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/discuss/android-discussion-groups-charter">Groups Charter</a> that covers the community guidelines.</p>
+<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/community/groups-charter.html">Groups Charter</a> that covers the community guidelines.</p>
<p class="note"><strong>Note:</strong> If you are seeking discussion about Android source code (not application development),
-then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+then please refer to the <a href="http://source.android.com/community">Open Source Project Mailing lists</a>.</p>
<p style="margin-bottom:.5em"><strong>Contents</strong></p>
<ol class="toc">
@@ -31,7 +31,7 @@
As you write your post, please do the following:
<ol>
<li><b>Read
-the <a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">mailing list charter</a></b> that covers the community guidelines.
+the <a href="http://source.android.com/community/groups-charter.html">mailing list charter</a></b> that covers the community guidelines.
</li>
<li><b>Select the most appropriate mailing list for your question</b>. There are several different lists for
developers, described below.</li>
diff --git a/docs/html/guide/topics/resources/available-resources.jd b/docs/html/guide/topics/resources/available-resources.jd
index 09c55a5..19babee 100644
--- a/docs/html/guide/topics/resources/available-resources.jd
+++ b/docs/html/guide/topics/resources/available-resources.jd
@@ -18,23 +18,6 @@
<p>Here's a brief summary of each resource type:</p>
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h2>{@code R.id} Is Not a Resource</h2>
-
-<p>You will often use an {@code R.id} integer to handle {@link android.view.View} objects in
-your UI. Although the {@code id} is a subclass of the {@code R} class, it is not considered a
-"resource" because it is not a reference to an externalized application resource. The {@code id}
-is simply a unique identifier that allows you to handle elements in your UI by instantiating
-objects with {@link android.app.Activity#findViewById(int) findViewById()}.</p>
-
-<p>For information about using {@code R.id} with your UI, see <a
-href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Declaring Layout</a>.</p>
-
-</div>
-</div>
-
-
<dl>
<dt><a href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a></dt>
<dd>Define pre-determined animations.<br/>
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index d8de16a..1e4cca7 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -18,32 +18,50 @@
<dl>
<dt><a href="#Bitmap">Bitmap File</a><dt>
<dd>A bitmap graphic file ({@code .png}, {@code .jpg}, or {@code .gif}).
- A {@link android.graphics.drawable.BitmapDrawable}.</dd>
+ Creates a {@link android.graphics.drawable.BitmapDrawable}.</dd>
<dt><a href="#NinePatch">Nine-Patch File</a></dt>
<dd>A PNG file with stretchable regions to allow image resizing based on content ({@code
-.9.png}). A {@link android.graphics.drawable.NinePatchDrawable}.</dd>
-<!-- <dt><a href="#BitmapAlias">Bitmap Alias</a><dt>
- <dd>An alias for a drawable.</dd> -->
+.9.png}). Creates a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
+ <dt><a href="#LayerList">Layer List</a></dt>
+ <dd>A Drawable that manages an array of other Drawables. These are drawn in array order, so the
+element with the largest index is be drawn on top. Creates a {@link
+android.graphics.drawable.LayerDrawable}.</dd>
<dt><a href="#StateList">State List</a></dt>
<dd>An XML file that references different bitmap graphics
for different states (for example, to use a different image when a button is pressed).
- A {@link android.graphics.drawable.StateListDrawable}.</dd>
- <dt><a href="#Color">Color</a></dt>
- <dd>A resource defined in XML that specifies a rectangle of color, with
- optionally rounded corners. A {@link android.graphics.drawable.PaintDrawable}.</dd>
- <dt><a href="#Shape">Shape</a></dt>
+ Creates a {@link android.graphics.drawable.StateListDrawable}.</dd>
+ <dt><a href="#LevelList">Level List</a></dt>
+ <dd>An XML file that defines a Drawable that manages a number of alternate Drawables, each
+assigned a maximum numerical value. Creates a {@link
+android.graphics.drawable.LevelListDrawable}.</dd>
+ <dt><a href="#Transition">Transition Drawable</a></dt>
+ <dd>An XML file that defines a Drawable that can cross-fade between two drawable resources.
+Creates a {@link android.graphics.drawable.TransitionDrawable}.</dd>
+ <dt><a href="#Clip">Clip Drawable</a></dt>
+ <dd>An XML file that defines a drawable that clips another Drawable based on this Drawable's
+current level value. Creates a {@link android.graphics.drawable.ClipDrawable}.</dd>
+ <dt><a href="#Scale">Scale Drawable</a></dt>
+ <dd>An XML file that defines a drawable that changes the size of another Drawable based on its
+current level value. Creates a {@link android.graphics.drawable.ScaleDrawable}</dd>
+ <dt><a href="#Shape">Shape Drawable</a></dt>
<dd>An XML file that defines a geometric shape, including colors and gradients.
- A {@link android.graphics.drawable.ShapeDrawable}.</dd>
+ Creates a {@link android.graphics.drawable.ShapeDrawable}.</dd>
</dl>
-<p>Documentation for the {@link android.graphics.drawable.AnimationDrawable} resource
-is in the <a href="animation-resource.html">Animation Resource</a> document.</p>
+<p>Also see the <a href="animation-resource.html">Animation Resource</a> document for how to
+create an {@link android.graphics.drawable.AnimationDrawable}.</p>
-<h2 id="Bitmap">Bitmap File</h2>
-<p>A basic bitmap image. Android supports basic bitmap files in a few different formats:
+
+
+<h2 id="Bitmap">Bitmap</h2>
+
+<p>A bitmap image. Android supports bitmap files in a three formats:
{@code .png} (preferred), {@code .jpg} (acceptable), {@code .gif} (discouraged).</p>
+<p>You can reference a bitmap file directly, using the filename as the resource ID, or create an
+alias resource ID in XML.</p>
+
<p class="note"><strong>Note:</strong> Bitmap files may be automatically optimized with lossless
image compression by the <a href="{@docRoot}guide/developing/tools/aapt.html">aapt</a> tool. For
example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit
@@ -52,11 +70,18 @@
you plan on reading an image as a bit stream in order to convert it to a bitmap, put your images in
the <code>res/raw/</code> folder instead, where they will not be optimized.</p>
+
+<h3 id="BitmapFile">Bitmap File</h3>
+
+<p>A bitmap file is a {@code .png}, {@code .jpg}, or {@code .gif} file. Android creates a {@link
+android.graphics.drawable.Drawable}
+resource for any of these files when you save them in the {@code res/drawable/} directory.</p>
+
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.png</code> ({@code .png}, {@code .jpg}, or {@code .gif})<br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.BitmapDrawable}.</dd>
@@ -68,15 +93,16 @@
</dd>
<dt>example:</dt>
-<dd>With an image saved at <code>res/drawable/myimage.png</code>, this layout XML will apply
+
+<dd>With an image saved at <code>res/drawable/myimage.png</code>, this layout XML applies
the image to a View:
<pre>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:src="@drawable/myimage"</strong> />
+ android:src="@drawable/myimage" />
</pre>
-<p>This application code will retrieve the image as a {@link
+<p>The following application code retrieves the image as a {@link
android.graphics.drawable.Drawable}:</p>
<pre>
Resources res = {@link android.content.Context#getResources()};
@@ -97,50 +123,218 @@
+<h3 id="XmlBitmap">XML Bitmap</h3>
+
+<p>An XML bitmap is a resource defined in XML that points to a bitmap file. The effect is an alias for a
+raw bitmap file. The XML can specify additional properties for the bitmap such as dithering and tiling.</p>
+
+<p class="note"><strong>Note:</strong> You can use a {@code <bitmap>} element as a child of
+an {@code <item>} element. For
+example, when creating a <a href="#StateList">state list</a> or <a href="#LayerList">layer list</a>,
+you can exclude the {@code android:drawable}
+attribute from an {@code <item>} element and nest a {@code <bitmap>} inside it
+that defines the drawable item.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.BitmapDrawable}.</dd>
+
+<dt>resource reference:</dt>
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code></li><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#bitmap-element">bitmap</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@[package:]drawable/<em>drawable_resource</em>"
+ android:antialias=["true" | "false"]
+ android:dither=["true" | "false"]
+ android:filter=["true" | "false"]
+ android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"]
+ android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] />
+</pre>
+</dd>
+
+
+<dt>elements:</dt>
+<dd>
+<dl class="tag-list">
+
+ <dt id="bitmap-element"><code><bitmap></code></dt>
+ <dd>Defines the bitmap source and its properties.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>. This is required only if the
+<code><bitmap></code> is the root element—it is not needed when the
+<code><bitmap></code> is nested inside an <code><item></code>.</dd>
+ <dt><code>android:src</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:antialias</code></dt>
+ <dd><em>Boolean</em>. Enables or disables antialiasing.</dd>
+ <dt><code>android:dither</code></dt>
+ <dd><em>Boolean</em>. Enables or disables dithering of the bitmap if the bitmap does not
+have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with an RGB 565
+screen).</dd>
+ <dt><code>android:filter</code></dt>
+ <dd><em>Boolean</em>. Enables or disables bitmap filtering. Filtering is used when the
+bitmap is shrunk or stretched to smooth its apperance.</dd>
+ <dt><code>android:gravity</code></dt>
+ <dd><em>Keyword</em>. Defines the gravity for the bitmap. The gravity indicates where to
+position the drawable in its container if the bitmap is smaller than the container.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. </td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. </td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. </td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. </td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container.
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. </td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. This is the default.</td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table>
+ </dd>
+ <dt><code>android:tileMode</code></dt>
+ <dd><em>Keyword</em>. Defines the tile mode. When the tile mode is enabled, the bitmap is
+repeated. Gravity is ignored when the tile mode is enabled.
+ <p>Must be one of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>disabled</code></td>
+<td>Do not tile the bitmap. This is the default value.</td></tr>
+<tr><td><code>clamp</code></td>
+<td>Replicates the edge color if the shader draws outside of its original bounds</td></tr>
+<tr><td><code>repeat</code></td>
+<td>Repeats the shader's image horizontally and vertically.</td></tr>
+<tr><td><code>mirror</code></td>
+<td>Repeats the shader's image horizontally and vertically, alternating mirror images so that
+adjacent images always seam.</td></tr>
+</table>
+
+ </dd>
+ </dl>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+<dd>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/icon"
+ android:tileMode="repeat" />
+</pre>
+
+</dd>
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.BitmapDrawable}</li>
+ <li><a href="{@docRoot}guide/topics/resources/providing-resources.html#AliasResources">Creating
+alias resources</a>
+</ul>
+</dd>
+
+</dl>
-<h2 id="NinePatch">Nine-Patch File</h2>
+
+
+
+<h2 id="NinePatch">Nine-Patch</h2>
<p>A {@link android.graphics.NinePatch} is a PNG image in which you can define stretchable regions
-that Android will scale when content within the View exceeds the normal image bounds. You will
+that Android scales when content within the View exceeds the normal image bounds. You
typically assign this type of image as the background of a View that has at least one dimension set
to {@code "wrap_content"}, and when the View grows to accomodate the content, the Nine-Patch image
-will also be scaled to match the size of the View. An example use of a Nine-Patch image is the
+is also scaled to match the size of the View. An example use of a Nine-Patch image is the
background used by Android's standard {@link android.widget.Button} widget, which must stretch to
accommodate the text (or image) inside the button.</p>
-<p>For a complete discussion about how to define a Nine-Patch file with stretchable regions,
+<p>Same as with a normal <a href="#Bitmap">bitmap</a>, you can reference a Nine-Patch file directly
+or from a resource defined by XML.</p>
+
+<p>For a complete discussion about how to create a Nine-Patch file with stretchable regions,
see the <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a>
document.</p>
+
+<h3 id="NinePatchFile">Nine-Patch File</h3>
+
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.9.png</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>example:</dt>
-<dd>With an image saved at <code>res/drawable/myninepatch.9.png</code>, this layout XML will
-apply the Nine-Patch to a View:
+
+<dd>With an image saved at <code>res/drawable/myninepatch.9.png</code>, this layout XML
+applies the Nine-Patch to a View:
<pre>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:background="@drawable/myninepatch"</strong> />
+ android:background="@drawable/myninepatch" />
</pre>
</dd>
<dt>see also:</dt>
+
<dd>
<ul>
<li><a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a></li>
@@ -153,6 +347,238 @@
+<h3 id="NinePatchXml">XML Nine-Patch</h3>
+
+<p>An XML Nine-Patch is a resource defined in XML that points to a Nine-Patch file. The XML can
+specify dithering for the image.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#bitmap-element">nine-patch</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@[package:]drawable/<em>drawable_resource</em>"
+ android:dither=["true" | "false"] />
+</pre>
+</dd>
+
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="layerlist-element"><code><bitmap></code></dt>
+ <dd>Defines the bitmap source and its properties.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:src</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a Nine-Patch
+file.</dd>
+ <dt><code>android:dither</code></dt>
+ <dd><em>Boolean</em>. Enables or disables dithering of the bitmap if the bitmap does not
+have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with an RGB 565
+screen).</dd>
+ </dl>
+ </dd>
+</dl>
+</dd>
+
+
+<dt>example:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/myninepatch"
+ android:dither="false" />
+</pre>
+</dd>
+</dl>
+
+
+
+
+
+
+<h2 id="LayerList">Layer List</h2>
+
+<p>A {@link android.graphics.drawable.LayerDrawable} is a drawable object
+that manages an array of other drawables. Each drawable in the list is drawn in the order of the
+list—the last drawable in the list is drawn on top.</p>
+
+<p>Each drawable is represented by an {@code <item>} element inside a single {@code
+<layer-list>} element.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.LayerDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#layerlist-element">layer-list</a>
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <<a href="#layerlist-item-element">item</a>
+ android:drawable="@[package:]drawable/<em>drawable_resource</em>"
+ android:id="@[+][<em>package</em>:]id/<i>resource_name</i>"
+ android:top="<em>dimension</em>"
+ android:right="<em>dimension</em>"
+ android:bottom="<em>dimension</em>"
+ android:left="<em>dimension</em>" />
+</selector>
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="layerlist-element"><code><layer-list></code></dt>
+ <dd><strong>Required.</strong> This must be the root element. Contains one or more {@code
+<item>} elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ </dl>
+ </dd>
+ <dt id="layerlist-item-element"><code><item></code></dt>
+ <dd>Defines a drawable to place in the layer drawable, in a position defined by its attributes.
+Must be a child of a <code><selector></code> element. Accepts child {@code <bitmap>}
+elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. A unique resource ID for this drawable. To create a new resource
+ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new
+ID. You can use this identifier to
+retrieve and modify the drawable with {@link android.view.View#findViewById(int)
+View.findViewById()} or {@link android.app.Activity#findViewById(int) Activity.findViewById()}.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Integer</em>. The top offset in pixels.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Integer</em>. The right offset in pixels.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Integer</em>. The bottom offset in pixels.</dd>
+ <dt><code>android:left</code></dt>
+ <dd><em>Integer</em>. The left offset in pixels.</dd>
+ </dl>
+ <p>All drawable items are scaled to fit the size of the containing View, by default. Thus,
+placing your images in a layer list at different positions might increase the size of the View and
+some images scale as appropriate. To avoid
+scaling items in the list, use a {@code <bitmap>} element inside the {@code
+<item>} element to specify the drawable and define the gravity to something that does not
+scale, such as {@code "center"}. For example, the following {@code <item>} defines an item
+that scales to fit its container View:</p>
+<pre>
+<item android:drawable="@drawable/image" />
+</pre>
+
+<p>To avoid scaling, the following example uses a {@code <bitmap>} element with centered
+gravity:</p>
+<pre>
+<item>
+ <bitmap android:src="<b>@drawable/image</b>"
+ android:gravity="center" />
+</item>
+</pre>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+
+<dd>XML file saved at <code>res/drawable/layers.xml</code>:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <bitmap android:src="@drawable/android_red"
+ android:gravity="center" />
+ </item>
+ <item android:top="10dp" android:left="10dp">
+ <bitmap android:src="@drawable/android_green"
+ android:gravity="center" />
+ </item>
+ <item android:top="20dp" android:left="20dp">
+ <bitmap android:src="@drawable/android_blue"
+ android:gravity="center" />
+ </item>
+</layer-list>
+</pre>
+<p>Notice that this example uses a nested {@code <bitmap>} element to define the drawable
+resource for each item with a "center" gravity. This ensures that none of the images are scaled to
+fit the size of the container, due to resizing caused by the offset images.</p>
+
+<p>This layout XML applies the drawable to a View:</p>
+<pre>
+<ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/layers" />
+</pre>
+
+<p>The result is a stack of increasingly offset images:</p>
+<img src="{@docRoot}images/resources/layers.png" alt="" />
+</dd> <!-- end example -->
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.LayerDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
@@ -163,33 +589,36 @@
that uses a several different images to represent the same graphic, depending on the state of
the object. For example, a {@link
android.widget.Button} widget can exist in one of several different states (pressed, focused,
-or niether) and, using a state list drawable, you can provide a different button image for each
+or niether) and, using a state list drawable, you can provide a different background image for each
state.</p>
<p>You can describe the state list in an XML file. Each graphic is represented by an {@code
<item>} element inside a single {@code <selector>} element. Each {@code <item>}
uses various attributes to describe the state in which it should be used as the graphic for the
drawable.</p>
+
<p>During each state change, the state list is traversed top to bottom and the first item that
-matches the current state will be used—the selection is <em>not</em> based on the "best
+matches the current state is used—the selection is <em>not</em> based on the "best
match," but simply the first item that meets the minimum criteria of the state.</p>
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.StateListDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
<?xml version="1.0" encoding="utf-8"?>
@@ -212,6 +641,7 @@
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
@@ -224,8 +654,8 @@
<dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
<code>"http://schemas.android.com/apk/res/android"</code>.
<dt><code>android:constantSize</code></dt>
- <dd><em>Boolean</em>. "true" if the drawable's reported internal size will remain constant as the state
-changes (the size will be the maximum of all of the states); "false" if the size will vary based on
+ <dd><em>Boolean</em>. "true" if the drawable's reported internal size remains constant as the state
+changes (the size is the maximum of all of the states); "false" if the size varies based on
the current state. Default is false.</dd>
<dt><code>android:dither</code></dt>
<dd><em>Boolean</em>. "true" to enable dithering of the bitmap if the bitmap does not have the same pixel
@@ -270,9 +700,9 @@
application is in the foreground), "false" if this item should be used when the application
window does not have focus (for example, if the notification shade is pulled down or a dialog appears).</dd>
</dl>
- <p class="note"><strong>Note:</strong>Remember that the first item in the state list that
-matches the current state of the object will be applied. So if the first item in the list contains
-none of the state attributes above, then it will be applied every time, which is why your
+ <p class="note"><strong>Note:</strong> Remember that Android applies the first item in the state list that
+matches the current state of the object. So, if the first item in the list contains
+none of the state attributes above, then it is applied every time, which is why your
default value should always be last (as demonstrated in the following example).</p>
</dd>
@@ -280,6 +710,7 @@
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
+
<dd>XML file saved at <code>res/drawable/button.xml</code>:
<pre>
<?xml version="1.0" encoding="utf-8"?>
@@ -292,12 +723,12 @@
</selector>
</pre>
-<p>This layout XML will apply the drawable to a View:</p>
+<p>This layout XML applies the drawable to a View:</p>
<pre>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:src="@drawable/button"</strong> />
+ android:src="@drawable/button" />
</pre>
</dd> <!-- end example -->
@@ -317,106 +748,513 @@
+<h2 id="LevelList">Level List</h2>
-
-
-
-
-
-<h2 id="Color">Color</h2>
-
-<p>This is a color defined in XML that's used as a drawable to fill a rectangular space,
-with optionally rounded corners. This kind of drawable behaves like a color fill.</p>
-
-<p class="note"><strong>Note:</strong> A color drawable is a simple resource that is referenced
-using the value provided in the {@code name} attribute (not the name of the XML file). As
-such, you can combine a color drawable resources with other simple resources in the one XML file,
-under one {@code <resources>} element.</p>
-
+<p>A Drawable that manages a number of alternate Drawables, each assigned a maximum numerical
+value. Setting the level value of the drawable with {@link
+android.graphics.drawable.Drawable#setLevel(int) setLevel()} loads the drawable resource in the
+level list that has a {@code android:maxLevel} value greater than or equal to the value
+passed to the method.</p>
<dl class="xml">
<dt>file location:</dt>
-<dd><code>res/drawable/<em>filename</em>.png</code><br/>
-The filename is arbitrary. The element's {@code name} will be used as the resource ID.</dd>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
-<dd>Resource pointer to a {@link android.graphics.drawable.PaintDrawable}.</dd>
+<dd>Resource pointer to a {@link android.graphics.drawable.LevelListDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
-In Java: <code>R.drawable.<em>color_name</em></code><br/>
-In XML: <code>@[<em>package</em>:]drawable/<em>color_name</em></code>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
-<?xml version="1.0" encoding="utf-8"?>
-<<a href="#color-resources-element">resources</a>>
- <<a href="#drawable-element">drawable</a>
- name="<em>color_name</em>"
- ><em>color</em></drawable>
-</resources>
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#levellist-element">level-list</a>
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <<a href="#levellist-item-element">item</a>
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:maxLevel="<i>integer</i>"
+ android:minLevel="<i>integer</i>" />
+</level-list>
</pre>
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
- <dt id="color-resources-element"><code><resources></code></dt>
- <dd><strong>Required.</strong> This must be the root node.
- <p>No attributes.</p>
- </dd>
- <dt id="drawable-element"><code><drawable></code></dt>
- <dd>A color to use as a drawable rectangle. The value can be
- any valid hexadecimal color value or a <a href="more-resources.html#Color">color
- resource</a>. A color value always begins with a pound (#) character, followed
- by the Alpha-Red-Green-Blue information in one of the following formats:
- #<em>RGB</em>, #<em>RRGGBB</em>, #<em>ARGB</em>, or #<em>AARRGGBB</em>.
+ <dt id="levellist-element"><code><level-list></code></dt>
+ <dd>This must be the root element. Contains one or more {@code <item>} elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ </dl>
+ </dd>
+
+ <dt id="levellist-item-element"><code><item></code></dt>
+ <dd>Defines a drawable to use at a certain level.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be inset.</dd>
+ <dt><code>android:maxLevel</code></dt>
+ <dd><em>Integer</em>. The maximum level allowed for this item.</dd>
+ <dt><code>android:minLevel</code></dt>
+ <dd><em>Integer</em>. The minimum level allowed for this item.</dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item
+ android:drawable="@drawable/status_off"
+ android:maxLevel="0" />
+ <item
+ android:drawable="@drawable/status_on"
+ android:maxLevel="1" />
+</level-list>
+</pre>
+<p>Once this is applied to a {@link android.view.View}, the level can be changed with {@link
+android.graphics.drawable.Drawable#setLevel(int) setLevel()} or {@link
+android.widget.ImageView#setImageLevel(int) setImageLevel()}.</p>
+
+</dd>
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.LevelListDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+<h2 id="Transition">Transition Drawable</h2>
+
+<p>A {@link android.graphics.drawable.TransitionDrawable} is a drawable object
+that can cross-fade between the two drawable resources.</p>
+
+<p>Each drawable is represented by an {@code <item>} element inside a single {@code
+<transition>} element. No more than two items are supported. To transition forward, call
+{@link android.graphics.drawable.TransitionDrawable#startTransition(int) startTransition()}. To
+transition backward, call {@link android.graphics.drawable.TransitionDrawable#reverseTransition(int)
+reverseTransition()}.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.TransitionDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#transition-element">layer-list</a>
+xmlns:android="http://schemas.android.com/apk/res/android" >
+ <<a href="#transition-item-element">item</a>
+ android:drawable="@[package:]drawable/<em>drawable_resource</em>"
+ android:id="@[+][<em>package</em>:]id/<i>resource_name</i>"
+ android:top="<em>dimension</em>"
+ android:right="<em>dimension</em>"
+ android:bottom="<em>dimension</em>"
+ android:left="<em>dimension</em>" />
+</selector>
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="transition-element"><code><transition></code></dt>
+ <dd><strong>Required.</strong> This must be the root element. Contains one or more {@code
+<item>} elements.
<p class="caps">attributes:</p>
<dl class="atn-list">
- <dt><code>name</code></dt>
- <dd><em>String</em>. <strong>Required</strong>.
- A name for the color. This name will be used as the resource ID.</dd>
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
</dl>
-
+ </dd>
+ <dt id="transition-item-element"><code><item></code></dt>
+ <dd>Defines a drawable to place in the layer drawable, in a position defined by its attributes.
+Must be a child of a <code><selector></code> element. Accepts child {@code <bitmap>}
+elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. A unique resource ID for this drawable. To create a new resource
+ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new
+ID. You can use this identifier to
+retrieve and modify the drawable with {@link android.view.View#findViewById(int)
+View.findViewById()} or {@link android.app.Activity#findViewById(int) Activity.findViewById()}.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Integer</em>. The top offset in pixels.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Integer</em>. The right offset in pixels.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Integer</em>. The bottom offset in pixels.</dd>
+ <dt><code>android:left</code></dt>
+ <dd><em>Integer</em>. The left offset in pixels.</dd>
+ </dl>
</dd>
</dl>
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
-<dd>XML file saved at <code>res/drawable/colors.xml</code>:
-<pre>
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <drawable name="solid_red">#f00</drawable>
- <drawable name="solid_blue">#0000ff</drawable>
-</resources>
-</pre>
- <p>This layout XML will apply a color drawable to a View:</p>
-<pre>
-<TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- <strong>android:background="@drawable/solid_blue"</strong> />
-</pre>
- <p>This application code will get a color drawable and apply it to a View:</p>
-<pre>
-Resources res = {@link android.content.Context#getResources()};
-Drawable redDrawable = res.{@link android.content.res.Resources#getDrawable(int) getDrawable}(R.drawable.solid_red);
-TextView tv = (TextView) findViewByID(R.id.text);
-tv.setBackground(redDrawable);
+<dd>XML file saved at <code>res/drawable/transition.xml</code>:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/on" />
+ <item android:drawable="@drawable/off" />
+</layer-list>
</pre>
+
+<p>This layout XML applies the drawable to a View:</p>
+<pre>
+<ImageButton
+ android:id="@+id/button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/transition" />
+</pre>
+
+<p>And the following code performs a 500ms transition from the first item to the second:</p>
+<pre>
+ImageButton button = (ImageButton) findViewById(R.id.button);
+TransitionDrawable drawable = (TransitionDrawable) button.getDrawable();
+drawable.startTransition(500);
+</pre>
+
</dd> <!-- end example -->
<dt>see also:</dt>
+
<dd>
<ul>
- <li>{@link android.graphics.drawable.PaintDrawable}</li>
+ <li>{@link android.graphics.drawable.TransitionDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+
+
+<h2 id="Inset">Inset Drawable</h2>
+
+<p>A drawable defined in XML that insets another drawable by a specified distance. This is used when
+a View needs a background that is smaller than the View's actual bounds.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.InsetDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#inset-element">inset</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:insetTop="<i>dimension</i>"
+ android:insetRight="<i>dimension</i>"
+ android:insetBottom="<i>dimension</i>"
+ android:insetLeft="<i>dimension</i>" />
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="inset-element"><code><inset></code></dt>
+ <dd>Defines the inset drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be inset.</dd>
+ <dt><code>android:insetTop</code></dt>
+ <dd><em>Dimension</em>. The top inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetRight</code></dt>
+ <dd><em>Dimension</em>. The right inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetBottom</code></dt>
+ <dd><em>Dimension</em>. The bottom inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetLeft</code></dt>
+ <dd><em>Dimension</em>. The left inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/background"
+ android:insetTop="10dp"
+ android:insetLeft="10dp" />
+</pre>
+</dd>
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.InsetDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+
+
+<h2 id="Clip">Clip Drawable</h2>
+
+<p>A drawable defined in XML that clips another drawable based on this Drawable's current level. You
+can control how much the child drawable gets clipped in width and height based on the level, as well
+as a gravity to control where it is placed in its overall container. Most often used to implement
+things like progress bars.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.ClipDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#clip-element">clip</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:clipOrientation=["horizontal" | "vertical"]
+ android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"] />
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="clip-element"><code><clip></code></dt>
+ <dd>Defines the clip drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be clipped.</dd>
+ <dt><code>android:clipOrientation</code></dt>
+ <dd><em>Keyword</em>. The orientation for the clip.
+ <p>Must be one of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>horizontal</code></td>
+<td>Clip the drawable horizontally.</td></tr>
+<tr><td><code>vertical</code></td>
+<td>Clip the drawable vertically.</td></tr>
+</table>
+ </dd>
+ <dt><code>android:gravity</code></dt>
+ <dd><em>Keyword</em>. Specifies where to clip within the drawable.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs at the bottom of the drawable.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs at the top of the drawable.</td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. This is the
+default. When {@code clipOrientation} is {@code "horizontal"}, clipping occurs at the right side of
+the drawable. This is the default.</td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. When {@code
+clipOrientation} is {@code "horizontal"}, clipping occurs at the left side of
+the drawable.</td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. Clipping behaves
+the same as when gravity is {@code "center"}.</td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. When {@code
+clipOrientation} is {@code "vertical"}, no clipping occurs because the drawable fills the
+vertical space (unless the drawable level is 0, in which case it's not visible).</td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size.
+Clipping behaves the same as when gravity is {@code "center"}.</td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container. When
+{@code clipOrientation} is {@code "horizontal"}, no clipping occurs because the drawable fills the
+horizontal space (unless the drawable level is 0, in which case it's not visible).
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. When {@code
+clipOrientation} is {@code "horizontal"}, clipping occurs on the left and right. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs on the top and bottom.</td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. No clipping occurs because the drawable fills the
+horizontal and vertical space (unless the drawable level is 0, in which case it's not
+visible).</td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table></dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+
+<dd>XML file saved at <code>res/drawable/clip.xml</code>:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/android"
+ android:clipOrientation="horizontal"
+ android:gravity="left" />
+</shape>
+</pre>
+ <p>The following layout XML applies the clip drawable to a View:</p>
+<pre>
+<ImageView
+ android:id="@+id/image"
+ android:background="@drawable/clip"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+</pre>
+
+ <p>The following code gets the drawable and increases the amount of clipping in order to
+progressively reveal the image:</p>
+<pre>
+ImageView imageview = (ImageView) findViewById(R.id.image);
+ClipDrawable drawable = (ClipDrawable) imageview.getDrawable();
+drawable.setLevel(drawable.getLevel() + 1000);
+</pre>
+
+<p>Increasing the level reduces the amount of clipping and slowly reveals the image. Here it is
+at a level of 7000:</p>
+<img src="{@docRoot}images/resources/clip.png" alt="" />
+
+<p class="note"><strong>Note:</strong> The default level is 0, which is fully clipped so the image
+is not visible. When the level is 10,000, the image is not clipped and completely visible.</p>
+</dd> <!-- end example -->
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.ClipDrawable}</li>
</ul>
</dd>
@@ -430,10 +1268,139 @@
+<h2 id="Scale">Scale Drawable</h2>
+
+<p>A drawable defined in XML that changes the size of another drawable based on its current
+level.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.ScaleDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#scale-element">scale</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:scaleGravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"]
+ android:scaleHeight="<i>percentage</i>"
+ android:scaleWidth="<i>percentage</i>" />
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="scale-element"><code><scale></code></dt>
+ <dd>Defines the scale drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:scaleGravity</code></dt>
+ <dd><em>Keyword</em>. Specifies the gravity position after scaling.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. </td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. This is the
+default.</td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. </td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. </td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container.
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. </td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. </td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table></dd>
+ <dt><code>android:scaleHeight</code></dt>
+ <dd><em>Percentage</em>. The scale height, expressed as a percentage of the drawable's
+bound. The value's format is XX%. For instance: 100%, 12.5%, etc.</dd>
+ <dt><code>android:scaleWidth</code></dt>
+ <dd><em>Percentage</em>. The scale width, expressed as a percentage of the drawable's
+bound. The value's format is XX%. For instance: 100%, 12.5%, etc.</dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<scale xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/logo"
+ android:scaleGravity="center_vertical|center_horizontal"
+ android:scaleHeight="80%"
+ android:scaleWidth="80%" />
+</pre>
+</dd>
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.ScaleDrawable}</li>
+</ul>
+</dd>
+
+</dl>
-<h2 id="Shape">Shape</h2>
+
+
+
+
+<h2 id="Shape">Shape Drawable</h2>
<p>This is a generic shape defined in XML.</p>
@@ -441,23 +1408,32 @@
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.ShapeDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
<?xml version="1.0" encoding="utf-8"?>
-<<a href="#shape-element">shape</a> xmlns:android="http://schemas.android.com/apk/res/android"
+<<a href="#shape-element">shape</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] >
+ <<a href="#corners-element">corners</a>
+ android:radius="<em>integer</em>"
+ android:topLeftRadius="<em>integer</em>"
+ android:topRightRadius="<em>integer</em>"
+ android:bottomLeftRadius="<em>integer</em>"
+ android:bottomRightRadius="<em>integer</em>" />
<<a href="#gradient-element">gradient</a>
android:angle="<em>integer</em>"
android:centerX="<em>integer</em>"
@@ -467,37 +1443,40 @@
android:gradientRadius="<em>integer</em>"
android:startColor="<em>color</em>"
android:type=["linear" | "radial" | "sweep"]
- android:usesLevel=["true" | "false"] />
- <<a href="#solid-element">solid</a>
- android:color="<em>color</em>" />
- <<a href="#stroke-element">stroke</a>
- android:width="<em>integer</em>"
- android:color="<em>color</em>"
- android:dashWidth="<em>integer</em>"
- android:dashGap="<em>integer</em>" />
+ android:usesLevel=["true" | "false"] />
<<a href="#padding-element">padding</a>
android:left="<em>integer</em>"
android:top="<em>integer</em>"
android:right="<em>integer</em>"
- android:bottom="<em>integer</em>" />
- <<a href="#corners-element">corners</a>
- android:radius="<em>integer</em>"
- android:topLeftRadius="<em>integer</em>"
- android:topRightRadius="<em>integer</em>"
- android:bottomLeftRadius="<em>integer</em>"
- android:bottomRightRadius="<em>integer</em>" />
+ android:bottom="<em>integer</em>" />
+ <<a href="#size-element">size</a>
+ android:width="<em>integer</em>"
+ android:color="<em>color</em>"
+ android:dashWidth="<em>integer</em>"
+ android:dashGap="<em>integer</em>" />
+ <<a href="#solid-element">solid</a>
+ android:color="<em>color</em>" />
+ <<a href="#stroke-element">stroke</a>
+ android:width="<em>integer</em>"
+ android:color="<em>color</em>"
+ android:dashWidth="<em>integer</em>"
+ android:dashGap="<em>integer</em>" />
</shape>
</pre>
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
<dt id="shape-element"><code><shape></code></dt>
- <dd><strong>Required.</strong> This must be the root element.
+ <dd>The shape drawable. This must be the root element.
<p class="caps">attributes:</p>
<dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
<dt><code>android:shape</code></dt>
<dd><em>Keyword</em>. Defines the type of shape. Valid values are:
<table>
@@ -525,7 +1504,7 @@
<dd><em>Float</em>. The radius for the inner
part of the ring, expressed as a ratio of the ring's width. For instance, if {@code
android:innerRadiusRatio="5"}, then the inner radius equals the ring's width divided by 5. This
-value will be overridden by {@code android:innerRadius}. Default value is 9.</dd>
+value is overridden by {@code android:innerRadius}. Default value is 9.</dd>
<dt><code>android:thickness</code></dt>
<dd><em>Dimension</em>. The thickness of the
ring, as a dimension value or <a
@@ -533,13 +1512,40 @@
<dt><code>android:thicknessRatio</code></dt>
<dd><em>Float</em>. The thickness of the ring,
expressed as a ratio of the ring's width. For instance, if {@code android:thicknessRatio="2"}, then
-the thickness equals the ring's width divided by 2. This value will be overridden by {@code
+the thickness equals the ring's width divided by 2. This value is overridden by {@code
android:innerRadius}. Default value is 3.</dd>
<dt><code>android:useLevel</code></dt>
<dd><em>Boolean</em>. "true" if this is used as
a {@link android.graphics.drawable.LevelListDrawable}. This should normally be "false"
or your shape may not appear.</dd>
</dl>
+ <dt id="corners-element"><code><corners></code></dt>
+ <dd>Creates rounded corners for the shape. Applies only when the shape is a rectangle.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:radius</code></dt>
+ <dd><em>Dimension</em>. The radius for all corners, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>. This is overridden for each
+corner by the following attributes.</dd>
+ <dt><code>android:topLeftRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the top-left corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:topRightRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the top-right corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottomLeftRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the bottom-left corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottomRightRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the bottom-right corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ <p class="note"><strong>Note:</strong> Every corner must (initially) be provided a corner
+radius greater than 1, or else no corners are rounded. If you want specific corners
+to <em>not</em> be rounded, a work-around is to use {@code android:radius} to set a default corner
+radius greater than 1, but then override each and every corner with the values you really
+want, providing zero ("0dp") where you don't want rounded corners.</p>
+ </dd>
<dt id="gradient-element"><code><gradient></code></dt>
<dd>Specifies a gradient color for the shape.
<p class="caps">attributes:</p>
@@ -582,6 +1588,42 @@
android.graphics.drawable.LevelListDrawable}.</dd>
</dl>
</dd>
+ <dt id="padding-element"><code><padding></code></dt>
+ <dd>Padding to apply to the containing View element (this pads the position of the View
+content, not the shape).
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:left</code></dt>
+ <dd><em>Dimension</em>. Left padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Dimension</em>. Top padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Dimension</em>. Right padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Dimension</em>. Bottom padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ </dd>
+ <dt id="solid-element"><code><size></code></dt>
+ <dd>The size of the shape.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:height</code></dt>
+ <dd><em>Dimension</em>. The height of the shape, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:width</code></dt>
+ <dd><em>Dimension</em>. The width of the shape, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ <p class="note"><strong>Note:</strong> The shape scales to the size of the container
+View proportionate to the dimensions defined here, by default. When you use the shape in an {@link
+android.widget.ImageView}, you can restrict scaling by setting the <a
+href="{@docRoot}reference/android/widget/ImageView.html#attr_android:scaleType">{@code
+android:scaleType}</a> to {@code "center"}.</p>
+ </dd>
<dt id="solid-element"><code><solid></code></dt>
<dd>A solid color to fill the shape.
<p class="caps">attributes:</p>
@@ -611,57 +1653,12 @@
android:dashGap} is set.</dd>
</dl>
</dd>
- <dt id="padding-element"><code><padding></code></dt>
- <dd>Padding to apply to the containing View element (this pads the position of the View
-content, not the shape).
- <p class="caps">attributes:</p>
- <dl class="atn-list">
- <dt><code>android:left</code></dt>
- <dd><em>Dimension</em>. Left padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:top</code></dt>
- <dd><em>Dimension</em>. Top padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:right</code></dt>
- <dd><em>Dimension</em>. Right padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottom</code></dt>
- <dd><em>Dimension</em>. Bottom padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- </dl>
- </dd>
- <dt id="corners-element"><code><corners></code></dt>
- <dd>Creates rounded corners for the shape. Applies only when the shape is a rectangle.
- <p class="caps">attributes:</p>
- <dl class="atn-list">
- <dt><code>android:radius</code></dt>
- <dd><em>Dimension</em>. The radius for all corners, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>. This will be overridden for each
-corner by the following attributes.</dd>
- <dt><code>android:topLeftRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the top-left corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:topRightRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the top-right corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottomLeftRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the bottom-left corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottomRightRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the bottom-right corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- </dl>
- <p class="note"><strong>Note:</strong> Every corner must (initially) be provided a corner
-radius greater than zero, or else no corners will be rounded. If you want specific corners
-to <em>not</em> be rounded, a work-around is to use {@code android:radius} to set a default corner
-radius greater than zero, but then override each and every corner with the values you really
-want, providing zero ("0dp") where you don't want rounded corners.</p>
- </dd>
</dl>
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
+
<dd>XML file saved at <code>res/drawable/gradient_box.xml</code>:
<pre>
<?xml version="1.0" encoding="utf-8"?>
@@ -678,14 +1675,16 @@
<corners android:radius="8dp" />
</shape>
</pre>
- <p>This layout XML will apply the shape drawable to a View:</p>
+
+ <p>This layout XML applies the shape drawable to a View:</p>
<pre>
<TextView
android:background="@drawable/gradient_box"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</pre>
- <p>This application code will get the shape drawable and apply it to a View:</p>
+
+ <p>This application code gets the shape drawable and applies it to a View:</p>
<pre>
Resources res = {@link android.content.Context#getResources()};
Drawable shape = res. {@link android.content.res.Resources#getDrawable(int) getDrawable}(R.drawable.gradient_box);
@@ -695,6 +1694,14 @@
</pre>
</dd> <!-- end example -->
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.ShapeDrawable}</li>
+</ul>
+</dd>
+
</dl>
diff --git a/docs/html/guide/topics/resources/layout-resource.jd b/docs/html/guide/topics/resources/layout-resource.jd
index 0688a18..111851c 100644
--- a/docs/html/guide/topics/resources/layout-resource.jd
+++ b/docs/html/guide/topics/resources/layout-resource.jd
@@ -35,12 +35,12 @@
<pre class="stx">
<?xml version="1.0" encoding="utf-8"?>
<<a href="#viewgroup-element"><em>ViewGroup</em></a> xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/<em>name</em>"
+ android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:layout_height=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
android:layout_width=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
[<em>ViewGroup-specific attributes</em>] >
<<a href="#view-element"><em>View</em></a>
- android:id="@+id/<em>name</em>"
+ android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:layout_height=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
android:layout_width=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
[<em>View-specific attributes</em>] >
@@ -49,10 +49,12 @@
<<a href="#viewgroup-element"><em>ViewGroup</em></a> >
<<a href="#view-element"><em>View</em></a> />
</<em>ViewGroup</em>>
+ <<a href="#include-element">include</a> layout="@layout/<i>layout_resource</i>"/>
</<em>ViewGroup</em>>
</pre>
<p class="note"><strong>Note:</strong> The root element can be either a
-{@link android.view.ViewGroup} or a {@link android.view.View}, but there must be only
+{@link android.view.ViewGroup}, a {@link android.view.View}, or a <a
+href="#merge-element">{@code <merge>}</a> element, but there must be only
one root element and it must contain the {@code xmlns:android} attribute with the {@code android}
namespace as shown.</p>
</dd>
@@ -74,10 +76,9 @@
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name for the element, which you can
-use to obtain a reference to the {@link android.view.ViewGroup} from your application.
- The value takes the form: <code>"@+id/<em>name</em>"</code>. See more about the
- <a href="#idvalue">value for {@code android:id}</a> below.
+ <dd><em>Resource ID</em>. A unique resource name for the element, which you can
+use to obtain a reference to the {@link android.view.ViewGroup} from your application. See more
+about the <a href="#idvalue">value for {@code android:id}</a> below.
</dd>
<dt><code>android:layout_height</code></dt>
<dd><em>Dimension or keyword</em>. <strong>Required</strong>. The height for the group, as a
@@ -107,10 +108,9 @@
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name for the element, which you can use to
- obtain a reference to the {@link android.view.View} from your application.
- The value takes the form: <code>"@+id/<em>name</em>"</code>. See more about the
- <a href="#idvalue">value for {@code android:id}</a> below.
+ <dd><em>Resource ID</em>. A unique resource name for the element, which you can use to
+ obtain a reference to the {@link android.view.View} from your application. See more about
+the <a href="#idvalue">value for {@code android:id}</a> below.
</dd>
<dt><code>android:layout_height</code></dt>
<dd><em>Dimension or keyword</em>. <strong>Required</strong>. The height for the element, as
@@ -137,20 +137,71 @@
which gives it's parent initial focus on the screen. You can have only one of these
elements per file.</dd>
+ <dt id="include-element"><code><include></code></dt>
+ <dd>Includes a layout file into this layout.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>layout</code></dt>
+ <dd><em>Layout resource</em>. <strong>Required</strong>. Reference to a layout
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. Overrides the ID given to the root view in the included layout.
+ </dd>
+ <dt><code>android:layout_height</code></dt>
+ <dd><em>Dimension or keyword</em>. Overrides the height given to the root view in the
+included layout.
+ </dd>
+ <dt><code>android:layout_width</code></dt>
+ <dd><em>Dimension or keyword</em>. Overrides the width given to the root view in the
+included layout.
+ </dd>
+ </dl>
+ <p>You can include any other layout attributes in the <code><include></code> that are
+supported by the root element in the included layout and they will override those defined in the
+root element.</p>
+
+ <p>Another way to include a layout is to use {@link android.view.ViewStub}. It is a lightweight
+View that consumes no layout space until you explicitly inflate it, at which point, it includes a
+layout file defined by its {@code android:layout} attribute. For more information about using {@link
+android.view.ViewStub}, read <a href="{@docRoot}resources/articles/layout-tricks-stubs.html">Layout
+Tricks: ViewStubs</a>.</p>
+ </dd>
+
+ <dt id="merge-element"><code><merge></code></dt>
+ <dd>An alternative root element that is not drawn in the layout hierarchy. Using this as the
+root element is useful when you know that this layout will be placed into a layout
+that already contains the appropriate parent View to contain the children of the
+<code><merge></code> element. This is particularly useful when you plan to include this layout
+in another layout file using <a href="#include-element"><code><include></code></a> and
+this layout doesn't require a different {@link android.view.ViewGroup} container. For more
+information about merging layouts, read <a
+href="{@docRoot}resources/articles/layout-tricks-merging.html">Layout
+Tricks: Merging</a>.</dd>
+
</dl>
+
+
<h4 id="idvalue">Value for <code>android:id</code></h4>
-<p>For the ID value, you should use this syntax form: <code>"@+id/<em>name</em>"</code>. The plus symbol,
-{@code +}, indicates that this is a new resource ID and the aapt tool will create
-a new resource number to the {@code R.java} class, if it doesn't already exist. For example:</p>
+<p>For the ID value, you should usually use this syntax form: <code>"@+id/<em>name</em>"</code>. The
+plus symbol, {@code +}, indicates that this is a new resource ID and the <code>aapt</code> tool will
+create a new resource integer in the {@code R.java} class, if it doesn't already exist. For
+example:</p>
<pre>
<TextView android:id="@+id/nameTextbox"/>
</pre>
-<p>You can then refer to it this way in Java:</p>
+<p>The <code>nameTextbox</code> name is now a resource ID attached to this element. You can then
+refer to the {@link android.widget.TextView} to which the ID is associated in Java:</p>
<pre>
findViewById(R.id.nameTextbox);
</pre>
+<p>This code returns the {@link android.widget.TextView} object.</p>
+
+<p>However, if you have already defined an <a
+href="{@docRoot}guide/topics/resources/drawable-resource.html#Id">ID resource</a> (and it is not
+already used), then you can apply that ID to a {@link android.view.View} element by excluding the
+plus symbol in the <code>android:id</code> value.</p>
<h4 id="layoutvalues">Value for <code>android:layout_height</code> and
<code>android:layout_width</code>:</h4>
diff --git a/docs/html/guide/topics/resources/menu-resource.jd b/docs/html/guide/topics/resources/menu-resource.jd
index badc403..cde72bd 100644
--- a/docs/html/guide/topics/resources/menu-resource.jd
+++ b/docs/html/guide/topics/resources/menu-resource.jd
@@ -35,7 +35,7 @@
<pre>
<?xml version="1.0" encoding="utf-8"?>
<<a href="#menu-element">menu</a> xmlns:android="http://schemas.android.com/apk/res/android">
- <<a href="#item-element">item</a> android:id="@+id/<em>id_name</em>"
+ <<a href="#item-element">item</a> android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="<em>integer</em>"
android:title="<em>string</em>"
@@ -46,7 +46,7 @@
android:checkable=["true" | "false"]
android:visible=["visible" | "invisible" | "gone"]
android:enabled=["enabled" | "disabled"] />
- <<a href="#group-element">group</a> android:id="<em>resource ID</em>"
+ <<a href="#group-element">group</a> android:id="@[+][<em>package</em>:]id/<em>resource name</em>"
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="<em>integer</em>"
android:checkableBehavior=["none" | "all" | "single"]
@@ -84,8 +84,8 @@
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name. The value takes the form:
-<code>"@+id/<em>name</em>"</code>.</dd>
+ <dd><em>Resource ID</em>. A unique resource ID. To create a new resource ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new ID.</dd>
<dt><code>android:menuCategory</code></dt>
<dd><em>Keyword</em>. Value corresponding to {@link android.view.Menu} {@code CATEGORY_*}
constants, which define the group's priority. Valid values:
@@ -124,8 +124,8 @@
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name. The value takes the form:
-<code>"@+id/<em>name</em>"</code>.</dd>
+ <dd><em>Resource ID</em>. A unique resource ID. To create a new resource ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new ID.</dd>
<dt><code>android:menuCategory</code></dt>
<dd><em>Keyword</em>. Value corresponding to {@link android.view.Menu} {@code CATEGORY_*}
constants, which define the item's priority. Valid values:
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index 0e2b30be..22abbb2 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -12,6 +12,9 @@
<dd>XML resource that carries a color value (a hexadecimal color).</dd>
<dt><a href="#Dimension">Dimension</a></dt>
<dd>XML resource that carries a dimension value (with a unit of measure).</dd>
+ <dt><a href="#Id">ID</a></dt>
+ <dd>XML resource that provides a unique identifier for application resources and
+components.</dd>
<dt><a href="#Integer">Integer</a></dt>
<dd>XML resource that carries an integer value.</dd>
<dt><a href="#IntegerArray">Integer Array</a></dt>
@@ -111,8 +114,8 @@
<h2 id="Color">Color</h2>
<p>A color value defined in XML.
-The color is specified with an RGB value and alpha channel. A color resource can be used
-any place that expects a hexadecimal color value.</p>
+The color is specified with an RGB value and alpha channel. You can use color resource
+any place that accepts a hexadecimal color value.</p>
<p>The value always begins with a pound (#) character and then followed by the
Alpha-Red-Green-Blue information in one of the following formats:</p>
@@ -318,6 +321,118 @@
+<h2 id="Id">ID</h2>
+
+<p>A unique resource ID defined in XML. Using the name you provide in the {@code <item>}
+element, the Android developer tools create a unique integer in your project's {@code
+R.java} class, which you can use as an
+identifier for an application resources (for example, a {@link android.view.View} in your UI layout)
+or a unique integer for use in your application code (for example, as an ID for a dialog or a
+result code).</p>
+
+<p class="note"><strong>Note:</strong> An ID is a simple resource that is referenced
+using the value provided in the {@code name} attribute (not the name of the XML file). As
+such, you can combine ID resources with other simple resources in the one XML file,
+under one {@code <resources>} element. Also, remember that an ID resources does not reference
+an actual resource item; it is simply a unique ID that you can attach to other resources or use
+as a unique integer in your application.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/values/<em>filename.xml</em></code><br/>
+The filename is arbitrary.</dd>
+
+<dt>resource reference:</dt>
+<dd>
+In Java: <code>R.id.<em>name</em></code><br/>
+In XML: <code>@[<em>package</em>:]id/<em>name</em></code>
+</dd>
+
+<dt>syntax:</dt>
+<dd>
+<pre class="stx">
+<?xml version="1.0" encoding="utf-8"?>
+<<a href="#id-resources-element">resources</a>>
+ <<a href="#id-item-element">item</a>
+ type="id"
+ name="<em>id_name</em>" />
+</resources>
+</pre>
+</dd>
+
+<dt>elements:</dt>
+<dd>
+<dl class="tag-list">
+
+ <dt id="integer-resources-element"><code><resources></code></dt>
+ <dd><strong>Required.</strong> This must be the root node.
+ <p>No attributes.</p>
+ </dd>
+ <dt id="integer-element"><code><integer></code></dt>
+ <dd>Defines a unique ID. Takes no value, only attributes.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>type</code></dt>
+ <dd>Must be "id".</dd>
+ <dt><code>name</code></dt>
+ <dd><em>String</em>. A unique name for the ID.</dd>
+ </dl>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+<dd>
+ <p>XML file saved at <code>res/values/ids.xml</code>:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item type="id" name="button_ok" />
+ <item type="id" name="dialog_exit" />
+</resources>
+</pre>
+
+ <p>Then, this layout snippet uses the "button_ok" ID for a Button widget:</p>
+<pre>
+<Button android:id="<b>@id/button_ok</b>"
+ style="@style/button_style" />
+</pre>
+
+ <p>Notice that the {@code android:id} value does not include the plus sign in the ID reference,
+because the ID already exists, as defined in the {@code ids.xml} example above. (When you specify an
+ID to an XML resource using the plus sign—in the format {@code
+android:id="@+id/name"}—it means that the "name" ID does not exist and should be created.)</p>
+
+ <p>As another example, the following code snippet uses the "dialog_exit" ID as a unique identifier
+for a dialog:</p>
+<pre>
+{@link android.app.Activity#showDialog(int) showDialog}(<b>R.id.dialog_exit</b>);
+</pre>
+ <p>In the same application, the "dialog_exit" ID is compared when creating a dialog:</p>
+<pre>
+protected Dialog {@link android.app.Activity#onCreateDialog(int)}(int id) {
+ Dialog dialog;
+ switch(id) {
+ case <b>R.id.dialog_exit</b>:
+ ...
+ break;
+ default:
+ dialog = null;
+ }
+ return dialog;
+}
+</pre>
+</dd> <!-- end example -->
+
+
+</dl>
+
+
+
+
+
<h2 id="Integer">Integer</h2>
<p>An integer defined in XML.</p>
@@ -347,7 +462,7 @@
<<a href="#integer-resources-element">resources</a>>
<<a href="#integer-element">integer</a>
name="<em>integer_name</em>"
- ><em>integer</em></dimen>
+ ><em>integer</em></integer>
</resources>
</pre>
</dd>
@@ -379,8 +494,8 @@
<pre>
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <integer name="max_speed">75</dimen>
- <integer name="min_speed">5</dimen>
+ <integer name="max_speed">75</integer>
+ <integer name="min_speed">5</integer>
</resources>
</pre>
<p>This application code retrieves an integer:</p>
diff --git a/docs/html/images/resources/clip.png b/docs/html/images/resources/clip.png
new file mode 100644
index 0000000..9196b3f
--- /dev/null
+++ b/docs/html/images/resources/clip.png
Binary files differ
diff --git a/docs/html/images/resources/layers.png b/docs/html/images/resources/layers.png
new file mode 100644
index 0000000..f7e6929
--- /dev/null
+++ b/docs/html/images/resources/layers.png
Binary files differ
diff --git a/docs/html/intl/ja/community/index.jd b/docs/html/intl/ja/community/index.jd
index 659aee7..490b23f 100644
--- a/docs/html/intl/ja/community/index.jd
+++ b/docs/html/intl/ja/community/index.jd
@@ -4,9 +4,9 @@
<div id="mainBodyFluid">
<h1>コミュニティ</h1>
- <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/discuss/android-discussion-groups-charter">グループの趣意</a>をお読みください。</p>
+ <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/community/groups-charter.html">グループの趣意</a>をお読みください。</p>
-<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/discuss">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
+<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/community">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
<p style="margin-bottom:.5em"><strong>目次</strong></p>
<ol class="toc">
@@ -28,7 +28,7 @@
<p>質問への答えが見つからない場合、コミュニティで質問することをおすすめします。投稿する際は、次の手順に従ってください。
<ol>
-<li>コミュニティ ガイドラインが記載されている<b><a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">Android メーリングリストの趣意</a></b>をお読みください。
+<li>コミュニティ ガイドラインが記載されている<b><a href="http://source.android.com/community/groups-charter.html">Android メーリングリストの趣意</a></b>をお読みください。
</li>
<li><b>質問に最適なメーリング リストを選択してください</b>。後述するように、デベロッパー向けのメーリング リストは何種類かに分かれています。</li>
<li>
diff --git a/docs/html/intl/ja/resources/community-groups.jd b/docs/html/intl/ja/resources/community-groups.jd
index c99b1f8..ecedde1 100644
--- a/docs/html/intl/ja/resources/community-groups.jd
+++ b/docs/html/intl/ja/resources/community-groups.jd
@@ -4,9 +4,9 @@
<div id="mainBodyFluid">
<h1>コミュニティ</h1>
- <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/discuss/android-discussion-groups-charter">グループの趣意</a>をお読みください。</p>
+ <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/community/groups-charter.html">グループの趣意</a>をお読みください。</p>
-<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/discuss">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
+<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/community">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
<p style="margin-bottom:.5em"><strong>目次</strong></p>
<ol class="toc">
@@ -28,7 +28,7 @@
<p>質問への答えが見つからない場合、コミュニティで質問することをおすすめします。投稿する際は、次の手順に従ってください。
<ol>
-<li>コミュニティ ガイドラインが記載されている<b><a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">Android メーリングリストの趣意</a></b>をお読みください。
+<li>コミュニティ ガイドラインが記載されている<b><a href="http://source.android.com/community/groups-charter.html">Android メーリングリストの趣意</a></b>をお読みください。
</li>
<li><b>質問に最適なメーリング リストを選択してください</b>。後述するように、デベロッパー向けのメーリング リストは何種類かに分かれています。</li>
<li>
diff --git a/docs/html/resources/articles/painless-threading.jd b/docs/html/resources/articles/painless-threading.jd
index 921f4df..17cec35 100644
--- a/docs/html/resources/articles/painless-threading.jd
+++ b/docs/html/resources/articles/painless-threading.jd
@@ -108,7 +108,7 @@
new DownloadImageTask().execute("http://example.com/image.png");
}
-private class DownloadImageTask extends AsyncTask<string, void,="" bitmap=""> {
+private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
index 72bdf7a..6d59648 100644
--- a/docs/html/resources/community-groups.jd
+++ b/docs/html/resources/community-groups.jd
@@ -22,7 +22,7 @@
<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in discussions with other Android application developers on topics that interest you.</p>
-<p>The lists on this page are primarily for discussion about Android application development. If you are seeking discussion about Android source code (not application development), then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+<p>The lists on this page are primarily for discussion about Android application development. If you are seeking discussion about Android source code (not application development), then please refer to the <a href="http://source.android.com/community">Open Source Project Mailing lists</a>.</p>
<h2 id="StackOverflow">Stack Overflow</h2>
@@ -56,7 +56,7 @@
As you write your post, please do the following:
<ol>
<li><strong>Read
-the <a href="http://source.android.com/discuss/android-discussion-groups-charter">mailing list charter</a></strong> that covers the community guidelines.
+the <a href="http://source.android.com/community/groups-charter.html">mailing list charter</a></strong> that covers the community guidelines.
</li>
<li><strong>Select the most appropriate mailing list for your question</strong>. There are several different lists for
developers, described below.</li>
diff --git a/docs/html/sdk/android-1.5.jd b/docs/html/sdk/android-1.5.jd
index 1d6e0ad..0c16b60 100644
--- a/docs/html/sdk/android-1.5.jd
+++ b/docs/html/sdk/android-1.5.jd
@@ -139,7 +139,7 @@
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.5, Revision 3</a> <em>(July 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-1.6.jd b/docs/html/sdk/android-1.6.jd
index c2651b6..c4e08ff 100644
--- a/docs/html/sdk/android-1.6.jd
+++ b/docs/html/sdk/android-1.6.jd
@@ -138,7 +138,7 @@
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.6, Revision 2</a> <em>(December 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index 7490bae..cd48a72 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -139,7 +139,7 @@
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 2.1, Revision 1</a> <em>(January 2010)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index f82edf9..495fd80 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -2,7 +2,6 @@
sdk.platform.version=2.2
sdk.platform.apiLevel=8
sdk.platform.majorMinor=minor
-sdk.platform.deployableDate=May 2010
@jd:body
@@ -118,6 +117,30 @@
<div class="toggleable opened">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ Android {@sdkPlatformVersion}, Revision 2</a> <em>(July 2010)</em></a>
+ <div class="toggleme">
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+<p>Requires SDK Tools r6 or higher.</p>
+</dd>
+
+<dt>System Image:</dt>
+<dd>
+<ul>
+<li>Adds default Search Widget.</li>
+<li>Includes proper provisioning for the platform's Backup Manager. For more information about how to use the Backup Manager, see <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>.</li>
+<li>Updates the Android 2.2 system image to FRF91.</li>
+</ul>
+</dd>
+
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android {@sdkPlatformVersion}, Revision 1</a> <em>(May 2010)</em></a>
<div class="toggleme">
<dl>
@@ -135,7 +158,6 @@
</div>
</div>
-
<h2 id="api-level">API Level</h2>
<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
@@ -444,4 +466,4 @@
<p>For more information about how to develop an application that displays
and functions properly on all Android-powered devices, see <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
-Screens</a>.</p>
\ No newline at end of file
+Screens</a>.</p>
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
new file mode 100644
index 0000000..81b4ff6
--- /dev/null
+++ b/docs/html/sdk/download.jd
@@ -0,0 +1,4 @@
+sdk.redirect=true
+
+@jd:body
+
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index f5558ab..bd7eeed 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -48,6 +48,9 @@
how to update ADT to the latest version or how to uninstall it, if necessary.
</p>
+<p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin running with
+Eclipse 3.6. Please stay on 3.5 until further notice.</p>
+
<h2 id="notes">Revisions</h2>
<p>The sections below provide notes about successive releases of
@@ -295,7 +298,7 @@
<p>Additionally, before you can configure or use ADT, you must install the
Android SDK starter package, as described in <a
-href="installing.html#Installing">Downloading the SDK Starter Pacskage</a>.
+href="installing.html#Installing">Downloading the SDK Starter Package</a>.
Specifically, you need to install a compatible version of the Android SDK Tools
and at least one development platform. To simplify ADT setup, we recommend
installing the Android SDK prior to installing ADT. </p>
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 69cc73d..9e88d94 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
ndk=true
-ndk.win_download=android-ndk-r4-windows.zip
-ndk.win_bytes=45778965
-ndk.win_checksum=1eded98a7f5cd5e71f8ac74565f73f11
+ndk.win_download=android-ndk-r4b-windows.zip
+ndk.win_bytes=45792835
+ndk.win_checksum=e397145e155a639be53ee4b6db8ad511
-ndk.mac_download=android-ndk-r4-darwin-x86.zip
-ndk.mac_bytes=50572163
-ndk.mac_checksum=b7d5f149fecf951c05a79b045f00419f
+ndk.mac_download=android-ndk-r4b-darwin-x86.zip
+ndk.mac_bytes=50586041
+ndk.mac_checksum=41dbd54335fb828ee408eab17103a1b0
-ndk.linux_download=android-ndk-r4-linux-x86.zip
-ndk.linux_bytes=49450682
-ndk.linux_checksum=0892b0637d45d145e045cc68e163dee3
+ndk.linux_download=android-ndk-r4b-linux-x86.zip
+ndk.linux_bytes=49464776
+ndk.linux_checksum=2deabcb125c219b34140975b710f00ec
page.title=Android NDK
@jd:body
@@ -62,8 +62,15 @@
<div class="toggleable open">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-Android NDK, Revision 4</a> <em>(May 2010)</em>
+Android NDK, Revision 4b</a> <em>(June 2010)</em>
<div class="toggleme">
+<dl>
+<dt>NDK r4b notes:</dt>
+<dd><p>Includes fixes for several issues in the NDK build and debugging scripts
+— if you are using NDK r4, we recommend downloading the NDK r4b build. For
+detailed information the changes in this release, read the CHANGES.TXT document
+included in the downloaded NDK package.</p></dd>
+</dl>
<dl>
<dt>General notes:</dt>
@@ -114,7 +121,7 @@
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android NDK, Revision 3</a> <em>(March 2010)</em>
<div class="toggleme">
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index cb9cdf3..d710b8e 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -23,6 +23,9 @@
<h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4>
<ul>
<li>Eclipse 3.4 (Ganymede) or 3.5 (Galileo)
+ <p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin
+running with Eclipse 3.6. Please stay on 3.5 until further notice.</p>
+ </li>
<li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included
in most Eclipse IDE packages) </li>
<li>If you need to install or update Eclipse, you can download it from <a
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index a80981ce..404e938 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -75,7 +75,8 @@
</li>
</ul>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a>
+ </li>
<li><a href="<?cs var:toroot ?>sdk/win-usb.html">USB Driver for
Windows, r3</a>
</li>
@@ -101,7 +102,6 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span></a>
- <span class="new">new!</span>
</li>
</ul>
</li>
@@ -116,7 +116,7 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4</a>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4b</a>
<span class="new">new!</span></li>
</ul>
</li>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 5394530..320fc4d 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -69,8 +69,11 @@
* the decoder will try to pick the best matching config based on the
* system's screen depth, and characteristics of the original image such
* as if it has per-pixel alpha (requiring a config that also does).
+ *
+ * The configuration is set to {@link android.graphics.Bitmap.Config#ARGB_8888}
+ * by default.
*/
- public Bitmap.Config inPreferredConfig;
+ public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
/**
* If dither is true, the decoder will attempt to dither the decoded
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 612b0ab..37b40e7 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,10 +16,25 @@
package android.graphics;
+/**
+ * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
+ * mirrored by setting the tiling mode.
+ */
public class BitmapShader extends Shader {
-
- // we hold on just for the GC, since our native counterpart is using it
- private Bitmap mBitmap;
+ /**
+ * We hold on just for the GC, since our native counterpart is using it.
+ *
+ * @hide
+ */
+ public Bitmap mBitmap;
+ /**
+ * @hide
+ */
+ public int mTileX;
+ /**
+ * @hide
+ */
+ public int mTileY;
/**
* Call this to create a new shader that will draw with a bitmap.
@@ -30,12 +45,11 @@
*/
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
mBitmap = bitmap;
- native_instance = nativeCreate(bitmap.ni(),
- tileX.nativeInt, tileY.nativeInt);
+ mTileX = tileX.nativeInt;
+ mTileY = tileY.nativeInt;
+ native_instance = nativeCreate(bitmap.ni(), mTileX, mTileY);
}
- private static native int nativeCreate(int native_bitmap,
- int shaderTileModeX,
- int shaderTileModeY);
+ private static native int nativeCreate(int native_bitmap, int shaderTileModeX,
+ int shaderTileModeY);
}
-
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a4df80c..77a1930 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -126,6 +126,7 @@
*
* @deprecated This constructor is not supported and should not be invoked.
*/
+ @Deprecated
public Canvas(GL gl) {
mNativeCanvas = initGL();
mGL = gl;
@@ -151,6 +152,7 @@
*
* @deprecated This method is not supported and should not be invoked.
*/
+ @Deprecated
public GL getGL() {
return mGL;
}
@@ -162,6 +164,7 @@
*
* @deprecated This method is not supported and should not be invoked.
*/
+ @Deprecated
public static void freeGlCaches() {
freeCaches();
}
@@ -198,6 +201,7 @@
*
* @deprecated This method is not supported and should not be invoked.
*/
+ @Deprecated
public void setViewport(int width, int height) {
if (mGL != null) {
nativeSetViewport(mNativeCanvas, width, height);
@@ -415,8 +419,8 @@
*
* @param sx The amount to scale in X
* @param sy The amount to scale in Y
- * @param px The x-coord for the pivot point (unchanged by the rotation)
- * @param py The y-coord for the pivot point (unchanged by the rotation)
+ * @param px The x-coord for the pivot point (unchanged by the scale)
+ * @param py The y-coord for the pivot point (unchanged by the scale)
*/
public final void scale(float sx, float sy, float px, float py) {
translate(px, py);
@@ -1585,6 +1589,7 @@
restore();
}
+ @Override
protected void finalize() throws Throwable {
super.finalize();
// If the constructor threw an exception before setting mNativeCanvas, the native finalizer
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index f126374..3f9f961 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,61 +16,84 @@
package android.graphics;
-public class ImageFormat
-{
- /* these constants are chosen to be binary compatible with
- * their previous location in PixelFormat.java */
-
- public static final int UNKNOWN = 0;
+public class ImageFormat {
+ /*
+ * these constants are chosen to be binary compatible with their previous
+ * location in PixelFormat.java
+ */
- /** RGB format used for pictures encoded as RGB_565
- * see {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
- */
- public static final int RGB_565 = 4;
+ public static final int UNKNOWN = 0;
- /**
- * YCbCr formats, used for video. These are not necessarily supported
- * by the hardware.
- */
- public static final int NV16 = 0x10;
+ /**
+ * RGB format used for pictures encoded as RGB_565 see
+ * {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
+ */
+ public static final int RGB_565 = 4;
-
- /** YCrCb format used for images, which uses the NV21 encoding format.
- * This is the default format for camera preview images, when not
- * otherwise set with
- * {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
- */
- public static final int NV21 = 0x11;
+ /**
+ * Planar 4:2:0 YCrCb format. This format assumes an horizontal stride of 16
+ * pixels for all planes and an implicit vertical stride of the image
+ * height's next multiple of two.
+ * y_size = stride * ALIGN(height, 2)
+ * c_size = ALIGN(stride/2, 16) * height
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ * Whether this format is supported by the camera hardware can be determined
+ * by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int YV12 = 0x32315659;
+ /**
+ * YCbCr format, used for video. Whether this format is supported by the
+ * camera hardware can be determined by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int NV16 = 0x10;
- /** YCbCr format used for images, which uses YUYV (YUY2) encoding format.
- * This is an alternative format for camera preview images. Whether this
- * format is supported by the camera hardware can be determined by
- * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
- */
- public static final int YUY2 = 0x14;
+ /**
+ * YCrCb format used for images, which uses the NV21 encoding format. This
+ * is the default format for camera preview images, when not otherwise set
+ * with {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
+ */
+ public static final int NV21 = 0x11;
-
- /**
- * Encoded formats. These are not necessarily supported by the hardware.
- */
- public static final int JPEG = 0x100;
+ /**
+ * YCbCr format used for images, which uses YUYV (YUY2) encoding format.
+ * This is an alternative format for camera preview images. Whether this
+ * format is supported by the camera hardware can be determined by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int YUY2 = 0x14;
+ /**
+ * Encoded formats. These are not necessarily supported by the hardware.
+ */
+ public static final int JPEG = 0x100;
- /**
- * Use this function to retrieve the number of bits per pixel of
- * an ImageFormat.
- * @param format
- * @return the number of bits per pixel of the given format or -1 if the
- * format doesn't exist or is not supported.
- */
- public static int getBitsPerPixel(int format) {
- switch (format) {
- case RGB_565: return 16;
- case NV16: return 16;
- case NV21: return 12;
- case YUY2: return 16;
- }
- return -1;
- }
+ /**
+ * Use this function to retrieve the number of bits per pixel of an
+ * ImageFormat.
+ *
+ * @param format
+ * @return the number of bits per pixel of the given format or -1 if the
+ * format doesn't exist or is not supported.
+ */
+ public static int getBitsPerPixel(int format) {
+ switch (format) {
+ case RGB_565:
+ return 16;
+ case NV16:
+ return 16;
+ case YUY2:
+ return 16;
+ case YV12:
+ return 12;
+ case NV21:
+ return 12;
+ }
+ return -1;
+ }
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 88ff0e6..9b4d3a8 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1718,7 +1718,8 @@
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
+ native_getTextPath(mNativePaint, mBidiFlags, text, index, count, x, y,
+ path.ni());
}
/**
@@ -1739,7 +1740,8 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, text, start, end, x, y, path.ni());
+ native_getTextPath(mNativePaint, mBidiFlags, text, start, end, x, y,
+ path.ni());
}
/**
@@ -1836,9 +1838,9 @@
private native int native_getTextRunCursor(int native_object, String text,
int contextStart, int contextEnd, int flags, int offset, int cursorOpt);
- private static native void native_getTextPath(int native_object,
+ private static native void native_getTextPath(int native_object, int bidiFlags,
char[] text, int index, int count, float x, float y, int path);
- private static native void native_getTextPath(int native_object,
+ private static native void native_getTextPath(int native_object, int bidiFlags,
String text, int start, int end, float x, float y, int path);
private static native void nativeGetStringBounds(int nativePaint,
String text, int start, int end, Rect bounds);
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 98ffb8b..7830224 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -75,6 +75,7 @@
bottom = r.bottom;
}
+ @Override
public boolean equals(Object obj) {
Rect r = (Rect) obj;
if (r != null) {
@@ -84,6 +85,7 @@
return false;
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(32);
sb.append("Rect("); sb.append(left); sb.append(", ");
@@ -351,7 +353,7 @@
* rectangle, return true and set this rectangle to that intersection,
* otherwise return false and do not change this rectangle. No check is
* performed to see if either rectangle is empty. Note: To just test for
- * intersection, use intersects()
+ * intersection, use {@link #intersects(Rect, Rect)}.
*
* @param left The left side of the rectangle being intersected with this
* rectangle
@@ -445,7 +447,7 @@
/**
* Returns true iff the two specified rectangles intersect. In no event are
* either of the rectangles modified. To record the intersection,
- * use intersect() or setIntersect().
+ * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}.
*
* @param a The first rectangle being tested for intersection
* @param b The second rectangle being tested for intersection
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index ae0304e..03eb55b 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -23,9 +23,19 @@
* drawn with that paint will get its color(s) from the shader.
*/
public class Shader {
+ /**
+ * Local matrix native instance.
+ *
+ * @hide
+ */
+ public int mLocalMatrix;
- // this is set by subclasses, but don't make it public
- /* package */ int native_instance;
+ /**
+ * This is set by subclasses, but don't make it public.
+ *
+ * @hide
+ */
+ public int native_instance;
public enum TileMode {
/**
@@ -64,11 +74,12 @@
* @param localM The shader's new local matrix, or null to specify identity
*/
public void setLocalMatrix(Matrix localM) {
- nativeSetLocalMatrix(native_instance,
- localM != null ? localM.native_instance : 0);
+ mLocalMatrix = localM != null ? localM.native_instance : 0;
+ nativeSetLocalMatrix(native_instance, mLocalMatrix);
}
protected void finalize() throws Throwable {
+ super.finalize();
nativeDestructor(native_instance);
}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index fdc4c92..e275ba8 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -64,6 +64,8 @@
* // Start the animation (looped playback by default).
* frameAnimation.start()
* </pre>
+ * <p>For more information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
*
* @attr ref android.R.styleable#AnimationDrawable_visible
* @attr ref android.R.styleable#AnimationDrawable_variablePadding
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 29e14d2..32111e8 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -40,7 +40,9 @@
* A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
* BitmapDrawable from a file path, an input stream, through XML inflation, or from
* a {@link android.graphics.Bitmap} object.
- * <p>It can be defined in an XML file with the <code><bitmap></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><bitmap></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
* <p>
* Also see the {@link android.graphics.Bitmap} class, which handles the management and
* transformation of raw bitmap graphics, and should be used when drawing to a
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index c387a9b..a772871 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -32,9 +32,14 @@
* level value. You can control how much the child Drawable gets clipped in width
* and height based on the level, as well as a gravity to control where it is
* placed in its overall container. Most often used to implement things like
- * progress bars.
+ * progress bars, by increasing the drawable's level with {@link
+ * android.graphics.drawable.Drawable#setLevel(int) setLevel()}.
+ * <p class="note"><strong>Note:</strong> The drawable is clipped completely and not visible when
+ * the level is 0 and fully revealed when the level is 10,000.</p>
*
- * <p>It can be defined in an XML file with the <code><clip></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><clip></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#ClipDrawable_clipOrientation
* @attr ref android.R.styleable#ClipDrawable_gravity
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 4c1d243..7b2d9d7 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -102,8 +102,8 @@
* whose overall size is modified based on the current level.
* </ul>
* <p>For information and examples of creating drawable resources (XML or bitmap files that
- * can be loaded in code), see <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources
- * and Internationalization</a>.
+ * can be loaded in code), see <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public abstract class Drawable {
private static final Rect ZERO_BOUNDS_RECT = new Rect();
@@ -720,8 +720,7 @@
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
- * <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources and
- * Internationalization</a>.
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 63d1446..33ecbea 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -42,7 +42,9 @@
/**
* A Drawable with a color gradient for buttons, backgrounds, etc.
*
- * <p>It can be defined in an XML file with the <code><shape></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><shape></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#GradientDrawable_visible
* @attr ref android.R.styleable#GradientDrawable_shape
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 4fa9d44..a9c983e 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -32,7 +32,9 @@
* This is used when a View needs a background that is smaller than
* the View's actual bounds.
*
- * <p>It can be defined in an XML file with the <code><inset></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><inset></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#InsetDrawable_visible
* @attr ref android.R.styleable#InsetDrawable_drawable
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index b727d47..501cca9 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -32,8 +32,9 @@
* order, so the element with the largest index will be drawn on top.
* <p>
* It can be defined in an XML file with the <code><layer-list></code> element.
- * Each Drawable in the layer is defined in a nested <code><item></code>.
- * </p>
+ * Each Drawable in the layer is defined in a nested <code><item></code>. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#LayerDrawableItem_left
* @attr ref android.R.styleable#LayerDrawableItem_top
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index ae8f224..21be983 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -47,7 +47,10 @@
* <p>With this XML saved into the res/drawable/ folder of the project, it can be referenced as
* the drawable for an {@link android.widget.ImageView}. The default image is the first in the list.
* It can then be changed to one of the other levels with
- * {@link android.widget.ImageView#setImageLevel(int)}.</p>
+ * {@link android.widget.ImageView#setImageLevel(int)}. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#LevelListDrawableItem_minLevel
* @attr ref android.R.styleable#LevelListDrawableItem_maxLevel
* @attr ref android.R.styleable#LevelListDrawableItem_drawable
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 2083e05..9c47dab 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -35,7 +35,9 @@
* value. The start and end angles of rotation can be controlled to map any
* circular arc to the level values range.</p>
*
- * <p>It can be defined in an XML file with the <code><rotate></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><rotate></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
*
* @attr ref android.R.styleable#RotateDrawable_visible
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 275e36f..b623d80 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -34,7 +34,9 @@
* placed in its overall container. Most often used to implement things like
* progress bars.
*
- * <p>It can be defined in an XML file with the <code><scale></code> element.</p>
+ * <p>It can be defined in an XML file with the <code><scale></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#ScaleDrawable_scaleWidth
* @attr ref android.R.styleable#ScaleDrawable_scaleHeight
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index c699a82..be1892e 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -34,6 +34,10 @@
* the ShapeDrawable will default to a
* {@link android.graphics.drawable.shapes.RectShape}.
*
+ * <p>It can be defined in an XML file with the <code><shape></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#ShapeDrawablePadding_left
* @attr ref android.R.styleable#ShapeDrawablePadding_top
* @attr ref android.R.styleable#ShapeDrawablePadding_right
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index b94df84..239be40 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -31,7 +31,9 @@
* ID value.
* <p/>
* <p>It can be defined in an XML file with the <code><selector></code> element.
- * Each state Drawable is defined in a nested <code><item></code> element.</p>
+ * Each state Drawable is defined in a nested <code><item></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#StateListDrawable_visible
* @attr ref android.R.styleable#StateListDrawable_variablePadding
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 97b45d8..4470356 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -26,8 +26,10 @@
* display just the first layer, call {@link #resetTransition()}.
* <p>
* It can be defined in an XML file with the <code><transition></code> element.
- * Each Drawable in the transition is defined in a nested <code><item></code>.
- * </p>
+ * Each Drawable in the transition is defined in a nested <code><item></code>. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#LayerDrawableItem_left
* @attr ref android.R.styleable#LayerDrawableItem_top
* @attr ref android.R.styleable#LayerDrawableItem_right
@@ -212,7 +214,7 @@
* Enables or disables the cross fade of the drawables. When cross fade
* is disabled, the first drawable is always drawn opaque. With cross
* fade enabled, the first drawable is drawn with the opposite alpha of
- * the second drawable.
+ * the second drawable. Cross fade is disabled by default.
*
* @param enabled True to enable cross fading, false otherwise.
*/
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index ddb2abf..87735b5 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -40,6 +40,21 @@
mType = t;
}
+ Allocation(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ @Override
+ void updateFromNative() {
+ mRS.validate();
+ int typeID = mRS.nAllocationGetType(mID);
+ if(typeID != 0) {
+ mType = new Type(typeID, mRS);
+ mType.updateFromNative();
+ }
+ }
+
public Type getType() {
return mType;
}
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 002fc78..28675dc 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -81,5 +81,10 @@
mRS.nObjDestroy(mID);
}
+ // If an object came from an a3d file, java fields need to be
+ // created with objects from the native layer
+ void updateFromNative() {
+ }
+
}
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 308d663..5d2a059 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -17,6 +17,7 @@
package android.renderscript;
import java.lang.reflect.Field;
+import android.util.Log;
/**
* @hide
@@ -308,6 +309,45 @@
mID = rs.nElementCreate(dt.mID, dk.mID, norm, size);
}
+ Element(RenderScript rs, int id) {
+ super(rs);
+ mID = id;
+ }
+
+ @Override
+ void updateFromNative() {
+
+ // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+ int[] dataBuffer = new int[5];
+ mRS.nElementGetNativeData(mID, dataBuffer);
+ for (DataType dt: DataType.values()) {
+ if(dt.mID == dataBuffer[0]){
+ mType = dt;
+ }
+ }
+ for (DataKind dk: DataKind.values()) {
+ if(dk.mID == dataBuffer[1]){
+ mKind = dk;
+ }
+ }
+
+ mNormalized = dataBuffer[2] == 1 ? true : false;
+ mVectorSize = dataBuffer[3];
+ int numSubElements = dataBuffer[4];
+ if(numSubElements > 0) {
+ mElements = new Element[numSubElements];
+ mElementNames = new String[numSubElements];
+
+ 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].updateFromNative();
+ }
+ }
+
+ }
+
public void destroy() throws IllegalStateException {
super.destroy();
}
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index 3b3711b..302a5f4 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -56,7 +56,7 @@
}
// Read only class with index entries
- public class IndexEntry {
+ public static class IndexEntry {
RenderScript mRS;
int mIndex;
int mID;
@@ -73,34 +73,40 @@
}
public BaseObj getObject() {
- if(mLoadedObj != null) {
- return mLoadedObj;
+ mRS.validate();
+ BaseObj obj = internalCreate(mRS, this);
+ return obj;
+ }
+
+ static synchronized BaseObj internalCreate(RenderScript rs, IndexEntry entry) {
+ if(entry.mLoadedObj != null) {
+ return entry.mLoadedObj;
}
- if(mClassID == ClassID.UNKNOWN) {
+ if(entry.mClassID == ClassID.UNKNOWN) {
return null;
}
- int objectID = mRS.nFileA3DGetEntryByIndex(mID, mIndex);
+ int objectID = rs.nFileA3DGetEntryByIndex(entry.mID, entry.mIndex);
if(objectID == 0) {
return null;
}
- switch (mClassID) {
+ switch (entry.mClassID) {
case MESH:
- mLoadedObj = new Mesh(objectID, mRS);
+ entry.mLoadedObj = new Mesh(objectID, rs);
break;
case TYPE:
- mLoadedObj = new Type(objectID, mRS);
+ entry.mLoadedObj = new Type(objectID, rs);
break;
case ELEMENT:
- mLoadedObj = null;
+ entry.mLoadedObj = null;
break;
case ALLOCATION:
- mLoadedObj = null;
+ entry.mLoadedObj = null;
break;
case PROGRAM_VERTEX:
- mLoadedObj = new ProgramVertex(objectID, mRS);
+ entry.mLoadedObj = new ProgramVertex(objectID, rs);
break;
case PROGRAM_RASTER:
break;
@@ -122,7 +128,9 @@
break;
}
- return mLoadedObj;
+ entry.mLoadedObj.updateFromNative();
+
+ return entry.mLoadedObj;
}
IndexEntry(RenderScript rs, int index, int id, String name, ClassID classID) {
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index 5a53878..4bee97a 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -59,6 +59,38 @@
return mPrimitives[slot];
}
+ @Override
+ void updateFromNative() {
+ int vtxCount = mRS.nMeshGetVertexBufferCount(mID);
+ int idxCount = mRS.nMeshGetIndexCount(mID);
+
+ int[] vtxIDs = new int[vtxCount];
+ int[] idxIDs = new int[idxCount];
+ int[] primitives = new int[idxCount];
+
+ mRS.nMeshGetVertices(mID, vtxIDs, vtxCount);
+ mRS.nMeshGetIndices(mID, idxIDs, primitives, vtxCount);
+
+ mVertexBuffers = new Allocation[vtxCount];
+ mIndexBuffers = new Allocation[idxCount];
+ mPrimitives = new Primitive[idxCount];
+
+ for(int i = 0; i < vtxCount; i ++) {
+ if(vtxIDs[i] != 0) {
+ mVertexBuffers[i] = new Allocation(vtxIDs[i], mRS);
+ mVertexBuffers[i].updateFromNative();
+ }
+ }
+
+ for(int i = 0; i < idxCount; i ++) {
+ if(idxIDs[i] != 0) {
+ mIndexBuffers[i] = new Allocation(idxIDs[i], mRS);
+ mIndexBuffers[i].updateFromNative();
+ }
+ mPrimitives[i] = Primitive.values()[primitives[i]];
+ }
+ }
+
public static class Builder {
RenderScript mRS;
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 1614ec5..b16dac1 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -91,8 +91,9 @@
mTextureCount = 0;
}
- public void setShader(String s) {
+ public BaseProgramBuilder setShader(String s) {
mShader = s;
+ return this;
}
public void addInput(Element e) throws IllegalStateException {
@@ -120,12 +121,13 @@
return mConstantCount++;
}
- public void setTextureCount(int count) throws IllegalArgumentException {
+ public BaseProgramBuilder setTextureCount(int count) throws IllegalArgumentException {
// Should check for consistant and non-conflicting names...
if(count >= MAX_CONSTANT) {
throw new IllegalArgumentException("Max texture count exceeded.");
}
mTextureCount = count;
+ return this;
}
protected void initProgram(Program p) {
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 5e04f0c..d06d768 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -106,16 +106,18 @@
mPointSpriteEnable = false;
}
- public void setTexture(EnvMode env, Format fmt, int slot)
+ public Builder setTexture(EnvMode env, Format fmt, int slot)
throws IllegalArgumentException {
if((slot < 0) || (slot >= MAX_TEXTURE)) {
throw new IllegalArgumentException("MAX_TEXTURE exceeded.");
}
mSlots[slot] = new Slot(env, fmt);
+ return this;
}
- public void setPointSpriteTexCoordinateReplacement(boolean enable) {
+ public Builder setPointSpriteTexCoordinateReplacement(boolean enable) {
mPointSpriteEnable = enable;
+ return this;
}
public ProgramFragment create() {
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index 56f9bf4..6fc9fff 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -26,23 +26,34 @@
*
**/
public class ProgramRaster extends BaseObj {
+
+ public enum CullMode {
+ BACK (0),
+ FRONT (1),
+ NONE (2);
+
+ int mID;
+ CullMode(int id) {
+ mID = id;
+ }
+ }
+
boolean mPointSmooth;
boolean mLineSmooth;
boolean mPointSprite;
- float mPointSize;
float mLineWidth;
- Element mIn;
- Element mOut;
+ CullMode mCullMode;
ProgramRaster(int id, RenderScript rs) {
super(rs);
mID = id;
- mPointSize = 1.0f;
mLineWidth = 1.0f;
mPointSmooth = false;
mLineSmooth = false;
mPointSprite = false;
+
+ mCullMode = CullMode.BACK;
}
public void setLineWidth(float w) {
@@ -51,51 +62,43 @@
mRS.nProgramRasterSetLineWidth(mID, w);
}
- public void setPointSize(float s) {
+ public void setCullMode(CullMode m) {
mRS.validate();
- mPointSize = s;
- mRS.nProgramRasterSetPointSize(mID, s);
+ mCullMode = m;
+ mRS.nProgramRasterSetCullMode(mID, m.mID);
}
- void internalInit() {
- int inID = 0;
- int outID = 0;
- if (mIn != null) {
- inID = mIn.mID;
- }
- if (mOut != null) {
- outID = mOut.mID;
- }
- mID = mRS.nProgramRasterCreate(inID, outID, mPointSmooth, mLineSmooth, mPointSprite);
- }
-
-
public static class Builder {
RenderScript mRS;
- ProgramRaster mPR;
+ boolean mPointSprite;
+ boolean mPointSmooth;
+ boolean mLineSmooth;
- public Builder(RenderScript rs, Element in, Element out) {
+ public Builder(RenderScript rs) {
mRS = rs;
- mPR = new ProgramRaster(0, rs);
+ mPointSmooth = false;
+ mLineSmooth = false;
+ mPointSprite = false;
}
- public void setPointSpriteEnable(boolean enable) {
- mPR.mPointSprite = enable;
+ public Builder setPointSpriteEnable(boolean enable) {
+ mPointSprite = enable;
+ return this;
}
- public void setPointSmoothEnable(boolean enable) {
- mPR.mPointSmooth = enable;
+ public Builder setPointSmoothEnable(boolean enable) {
+ mPointSmooth = enable;
+ return this;
}
- public void setLineSmoothEnable(boolean enable) {
- mPR.mLineSmooth = enable;
+ public Builder setLineSmoothEnable(boolean enable) {
+ mLineSmooth = enable;
+ return this;
}
-
static synchronized ProgramRaster internalCreate(RenderScript rs, Builder b) {
- b.mPR.internalInit();
- ProgramRaster pr = b.mPR;
- b.mPR = new ProgramRaster(0, b.mRS);
+ int id = rs.nProgramRasterCreate(b.mPointSmooth, b.mLineSmooth, b.mPointSprite);
+ ProgramRaster pr = new ProgramRaster(id, rs);
return pr;
}
@@ -111,3 +114,4 @@
+
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index f558117..a92cbb6 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -114,28 +114,33 @@
}
- public void setDepthFunc(DepthFunc func) {
+ public Builder setDepthFunc(DepthFunc func) {
mDepthFunc = func;
+ return this;
}
- public void setDepthMask(boolean enable) {
+ public Builder setDepthMask(boolean enable) {
mDepthMask = enable;
+ return this;
}
- public void setColorMask(boolean r, boolean g, boolean b, boolean a) {
+ public Builder setColorMask(boolean r, boolean g, boolean b, boolean a) {
mColorMaskR = r;
mColorMaskG = g;
mColorMaskB = b;
mColorMaskA = a;
+ return this;
}
- public void setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
+ public Builder setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
mBlendSrc = src;
mBlendDst = dst;
+ return this;
}
- public void setDitherEnable(boolean enable) {
+ public Builder setDitherEnable(boolean enable) {
mDither = enable;
+ return this;
}
static synchronized ProgramStore internalCreate(RenderScript rs, Builder b) {
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 1b155d7..ec377e2 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -47,8 +47,9 @@
mRS = rs;
}
- public void setTextureMatrixEnable(boolean enable) {
+ public Builder setTextureMatrixEnable(boolean enable) {
mTextureMatrixEnable = enable;
+ return this;
}
public ProgramVertex create() {
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index fcc6cbb..1135a75 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -90,12 +90,15 @@
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);
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 int nAllocationCreateTyped(int type);
native int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
@@ -117,6 +120,7 @@
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 nFileA3DCreateFromAssetStream(int assetStream);
native int nFileA3DGetNumIndexEntries(int fileA3D);
@@ -165,9 +169,9 @@
native void nProgramStoreDither(boolean enable);
native int nProgramStoreCreate();
- native int nProgramRasterCreate(int in, int out, boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
+ native int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
native void nProgramRasterSetLineWidth(int pr, float v);
- native void nProgramRasterSetPointSize(int pr, float v);
+ native void nProgramRasterSetCullMode(int pr, int mode);
native void nProgramBindConstants(int pv, int slot, int mID);
native void nProgramBindTexture(int vpf, int slot, int a);
@@ -189,6 +193,10 @@
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 void nAnimationBegin(int attribCount, int keyframeCount);
native void nAnimationAdd(float time, float[] attribs);
@@ -358,3 +366,4 @@
}
+
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 14422b2..8e45f2b 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -16,7 +16,9 @@
package android.renderscript;
+
import java.lang.reflect.Field;
+import android.util.Log;
/**
* @hide
@@ -108,6 +110,27 @@
super.finalize();
}
+ @Override
+ void updateFromNative() {
+ // We have 6 integer to obtain mDimX; mDimY; mDimZ;
+ // mDimLOD; mDimFaces; mElement;
+ int[] dataBuffer = new int[6];
+ mRS.nTypeGetNativeData(mID, dataBuffer);
+
+ mDimX = dataBuffer[0];
+ mDimY = dataBuffer[1];
+ mDimZ = dataBuffer[2];
+ mDimLOD = dataBuffer[3] == 1 ? true : false;
+ mDimFaces = dataBuffer[4] == 1 ? true : false;
+
+ int elementID = dataBuffer[5];
+ if(elementID != 0) {
+ mElement = new Element(mRS, elementID);
+ mElement.updateFromNative();
+ }
+ calcElementCount();
+ }
+
public static Type createFromClass(RenderScript rs, Class c, int size, String scriptName) {
android.util.Log.e("RenderScript", "Calling depricated createFromClass");
return null;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index cbc24c4..888c76a 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -297,6 +297,46 @@
return (jint)id;
}
+static void
+nElementGetNativeData(JNIEnv *_env, jobject _this, 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
+ assert(dataSize == 5);
+
+ uint32_t elementData[5];
+ rsElementGetNativeData(con, (RsElement)id, elementData, dataSize);
+
+ for(jint i = 0; i < dataSize; i ++) {
+ _env->SetIntArrayRegion(_elementData, i, 1, (const jint*)&elementData[i]);
+ }
+}
+
+
+static void
+nElementGetSubElements(JNIEnv *_env, jobject _this, 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));
+ const char **names = (const char **)malloc((uint32_t)dataSize * sizeof(const char *));
+
+ rsElementGetSubElements(con, (RsElement)id, ids, names, (uint32_t)dataSize);
+
+ for(jint i = 0; i < dataSize; i ++) {
+ _env->SetObjectArrayElement(_names, i, _env->NewStringUTF(names[i]));
+ _env->SetIntArrayRegion(_IDs, i, 1, (const jint*)&ids[i]);
+ }
+
+ free(ids);
+ free(names);
+}
+
// -----------------------------------
static void
@@ -323,6 +363,26 @@
return (jint)rsTypeCreate(con);
}
+static void
+nTypeGetNativeData(JNIEnv *_env, jobject _this, 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];
+ rsTypeGetNativeData(con, (RsType)id, typeData, 6);
+
+ for(jint i = 0; i < elementCount; i ++) {
+ _env->SetIntArrayRegion(_typeData, i, 1, (const jint*)&typeData[i]);
+ }
+}
+
static void * SF_LoadInt(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
{
((int32_t *)buffer)[0] = _env->GetIntField(_obj, _field);
@@ -708,6 +768,14 @@
free(bufAlloc);
}
+static jint
+nAllocationGetType(JNIEnv *_env, jobject _this, 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);
+}
+
// -----------------------------------
static int
@@ -725,12 +793,10 @@
static int
nFileA3DGetNumIndexEntries(JNIEnv *_env, jobject _this, jint fileA3D)
{
- LOGV("______nFileA3D %u", (uint32_t) fileA3D);
RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
int32_t numEntries = 0;
rsFileA3DGetNumIndexEntries(con, &numEntries, (RsFile)fileA3D);
- LOGV("______nFileA3D NumEntries %u", (uint32_t) numEntries);
return numEntries;
}
@@ -1203,21 +1269,12 @@
// ---------------------------------------------------------------------------
static jint
-nProgramRasterCreate(JNIEnv *_env, jobject _this, jint in, jint out,
- jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
+nProgramRasterCreate(JNIEnv *_env, jobject _this, jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
{
RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nProgramRasterCreate, con(%p), in(%p), out(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
- con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite);
- return (jint)rsProgramRasterCreate(con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite);
-}
-
-static void
-nProgramRasterSetPointSize(JNIEnv *_env, jobject _this, jint vpr, jfloat v)
-{
- RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
- LOG_API("nProgramRasterSetPointSize, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v);
- rsProgramRasterSetPointSize(con, (RsProgramFragment)vpr, v);
+ LOG_API("nProgramRasterCreate, con(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
+ con, pointSmooth, lineSmooth, pointSprite);
+ return (jint)rsProgramRasterCreate(con, pointSmooth, lineSmooth, pointSprite);
}
static void
@@ -1225,7 +1282,15 @@
{
RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
LOG_API("nProgramRasterSetLineWidth, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v);
- rsProgramRasterSetLineWidth(con, (RsProgramFragment)vpr, v);
+ rsProgramRasterSetLineWidth(con, (RsProgramRaster)vpr, v);
+}
+
+static void
+nProgramRasterSetCullMode(JNIEnv *_env, jobject _this, 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);
}
@@ -1360,19 +1425,75 @@
}
static void
-nMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot)
+nMeshBindVertex(JNIEnv *_env, jobject _this, 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)s, (RsAllocation)alloc, slot);
- rsMeshBindVertex(con, (RsMesh)s, (RsAllocation)alloc, slot);
+ 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 s, jint alloc, jint primID, jint slot)
+nMeshBindIndex(JNIEnv *_env, jobject _this, 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)s, (RsAllocation)alloc);
- rsMeshBindIndex(con, (RsMesh)s, (RsAllocation)alloc, primID, slot);
+ 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)
+{
+ 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);
+ return vtxCount;
+}
+
+static jint
+nMeshGetIndexCount(JNIEnv *_env, jobject _this, 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);
+ return idxCount;
+}
+
+static void
+nMeshGetVertices(JNIEnv *_env, jobject _this, 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));
+ rsMeshGetVertices(con, (RsMesh)mesh, allocs, (uint32_t)numVtxIDs);
+
+ for(jint i = 0; i < numVtxIDs; i ++) {
+ _env->SetIntArrayRegion(_ids, i, 1, (const jint*)&allocs[i]);
+ }
+
+ free(allocs);
+}
+
+static void
+nMeshGetIndices(JNIEnv *_env, jobject _this, 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));
+ uint32_t *prims= (uint32_t*)malloc((uint32_t)numIndices * sizeof(uint32_t));
+
+ rsMeshGetIndices(con, (RsMesh)mesh, allocs, prims, (uint32_t)numIndices);
+
+ for(jint i = 0; i < numIndices; i ++) {
+ _env->SetIntArrayRegion(_idxIds, i, 1, (const jint*)&allocs[i]);
+ _env->SetIntArrayRegion(_primitives, i, 1, (const jint*)&prims[i]);
+ }
+
+ free(allocs);
+ free(prims);
}
// ---------------------------------------------------------------------------
@@ -1413,12 +1534,15 @@
{"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 },
{"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 },
{"nAllocationCreateTyped", "(I)I", (void*)nAllocationCreateTyped },
{"nAllocationCreateFromBitmap", "(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap },
@@ -1437,6 +1561,7 @@
{"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},
{"nAdapter1DBindAllocation", "(II)V", (void*)nAdapter1DBindAllocation },
{"nAdapter1DSetConstraint", "(III)V", (void*)nAdapter1DSetConstraint },
@@ -1481,9 +1606,9 @@
{"nProgramFragmentCreate", "([I)I", (void*)nProgramFragmentCreate },
{"nProgramFragmentCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramFragmentCreate2 },
-{"nProgramRasterCreate", "(IIZZZ)I", (void*)nProgramRasterCreate },
-{"nProgramRasterSetPointSize", "(IF)V", (void*)nProgramRasterSetPointSize },
+{"nProgramRasterCreate", "(ZZZ)I", (void*)nProgramRasterCreate },
{"nProgramRasterSetLineWidth", "(IF)V", (void*)nProgramRasterSetLineWidth },
+{"nProgramRasterSetCullMode", "(II)V", (void*)nProgramRasterSetCullMode },
{"nProgramVertexCreate", "(Z)I", (void*)nProgramVertexCreate },
{"nProgramVertexCreate2", "(Ljava/lang/String;[I)I", (void*)nProgramVertexCreate2 },
@@ -1509,6 +1634,11 @@
{"nMeshBindVertex", "(III)V", (void*)nMeshBindVertex },
{"nMeshBindIndex", "(IIII)V", (void*)nMeshBindIndex },
+{"nMeshGetVertexBufferCount", "(I)I", (void*)nMeshGetVertexBufferCount },
+{"nMeshGetIndexCount", "(I)I", (void*)nMeshGetIndexCount },
+{"nMeshGetVertices", "(I[II)V", (void*)nMeshGetVertices },
+{"nMeshGetIndices", "(I[I[II)V", (void*)nMeshGetIndices },
+
};
static int registerFuncs(JNIEnv *_env)
@@ -1541,3 +1671,4 @@
bail:
return result;
}
+
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
new file mode 100644
index 0000000..d7a9a2c
--- /dev/null
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -0,0 +1,95 @@
+/*
+ * 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_APP_NATIVEACTIVITY_H
+#define _ANDROID_APP_NATIVEACTIVITY_H
+
+#include <ui/InputTransport.h>
+
+#include <android/native_activity.h>
+
+#include "jni.h"
+
+namespace android {
+
+extern void android_NativeActivity_setWindowFormat(
+ ANativeActivity* activity, int32_t format);
+
+extern void android_NativeActivity_setWindowFlags(
+ ANativeActivity* activity, int32_t values, int32_t mask);
+
+extern void android_NativeActivity_showSoftInput(
+ ANativeActivity* activity, int32_t flags);
+
+extern void android_NativeActivity_hideSoftInput(
+ ANativeActivity* activity, int32_t flags);
+
+} // namespace android
+
+
+/*
+ * NDK input queue API.
+ */
+struct AInputQueue {
+public:
+ /* Creates a consumer associated with an input channel. */
+ explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
+
+ /* Destroys the consumer and releases its input channel. */
+ ~AInputQueue();
+
+ void attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data);
+
+ void detachLooper();
+
+ int32_t hasEvents();
+
+ int32_t getEvent(AInputEvent** outEvent);
+
+ void finishEvent(AInputEvent* event, bool handled);
+
+
+ // ----------------------------------------------------------
+
+ inline android::InputConsumer& getConsumer() { return mConsumer; }
+
+ void dispatchEvent(android::KeyEvent* event);
+
+ android::KeyEvent* consumeUnhandledEvent();
+
+ int mWorkWrite;
+
+private:
+ void doDefaultKey(android::KeyEvent* keyEvent);
+
+ android::InputConsumer mConsumer;
+ android::PreallocatedInputEventFactory mInputEventFactory;
+ android::sp<android::PollLoop> mPollLoop;
+
+ int mDispatchKeyRead;
+ int mDispatchKeyWrite;
+
+ // This is only touched by the event reader thread. It is the current
+ // key events that came out of the mDispatchingKeys list and are now
+ //Êdelivered to the app.
+ android::Vector<android::KeyEvent*> mDeliveringKeys;
+
+ android::Mutex mLock;
+ android::Vector<android::KeyEvent*> mPendingKeys;
+ android::Vector<android::KeyEvent*> mDispatchingKeys;
+};
+
+#endif // _ANDROID_APP_NATIVEACTIVITY_H
diff --git a/core/jni/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
similarity index 100%
rename from core/jni/android_view_Surface.h
rename to include/android_runtime/android_view_Surface.h
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 100644
index 0000000..2316fef
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1,60 @@
+/*
+ * 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_BINDER_SERVICE_H
+#define ANDROID_BINDER_SERVICE_H
+
+#include <stdint.h>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template<typename SERVICE>
+class BinderService
+{
+public:
+ static status_t publish() {
+ sp<IServiceManager> sm(defaultServiceManager());
+ return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+ }
+
+ static void publishAndJoinThreadPool() {
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm(defaultServiceManager());
+ sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ }
+
+ static void instantiate() { publish(); }
+
+ static status_t shutdown() {
+ return NO_ERROR;
+ }
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_BINDER_SERVICE_H
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 2cc4db9..3aba5f6 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -103,6 +103,11 @@
status_t writeObject(const flat_binder_object& val, bool nullMetaData);
+ // Like Parcel.java's writeNoException(). Just writes a zero int32.
+ // Currently the native implementation doesn't do any of the StrictMode
+ // stack gathering and serialization that the Java implementation does.
+ status_t writeNoException();
+
void remove(size_t start, size_t amt);
status_t read(void* outData, size_t len) const;
@@ -125,7 +130,14 @@
sp<IBinder> readStrongBinder() const;
wp<IBinder> readWeakBinder() const;
status_t read(Flattenable& val) const;
-
+
+ // Like Parcel.java's readExceptionCode(). Reads the first int32
+ // off of a Parcel's header, returning 0 or the negative error
+ // code on exceptions, but also deals with skipping over rich
+ // response headers. Callers should use this to read & parse the
+ // response headers rather than doing it by hand.
+ int32_t readExceptionCode() const;
+
// Retrieve native_handle from the parcel. This returns a copy of the
// parcel's native_handle (the caller takes ownership). The caller
// must free the native_handle with native_handle_close() and
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
new file mode 100644
index 0000000..ed4e4cc
--- /dev/null
+++ b/include/gui/ISensorEventConnection.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 ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
+#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class SensorChannel;
+
+class ISensorEventConnection : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorEventConnection);
+
+ virtual sp<SensorChannel> getSensorChannel() const = 0;
+ virtual status_t enableDisable(int handle, bool enabled) = 0;
+ virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
new file mode 100644
index 0000000..3e05076
--- /dev/null
+++ b/include/gui/ISensorServer.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 ANDROID_GUI_ISENSORSERVER_H
+#define ANDROID_GUI_ISENSORSERVER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Sensor;
+class ISensorEventConnection;
+
+class ISensorServer : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorServer);
+
+ virtual Vector<Sensor> getSensorList()= 0;
+ virtual sp<ISensorEventConnection> createSensorEventConnection() = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorServer : public BnInterface<ISensorServer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_ISENSORSERVER_H
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
new file mode 100644
index 0000000..86a16f1
--- /dev/null
+++ b/include/gui/Sensor.h
@@ -0,0 +1,88 @@
+/*
+ * 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_GUI_SENSOR_H
+#define ANDROID_GUI_SENSOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Flattenable.h>
+
+#include <hardware/sensors.h>
+
+#include <android/sensor.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensor { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Parcel;
+
+// ----------------------------------------------------------------------------
+
+class Sensor : public ASensor, public Flattenable
+{
+public:
+ enum {
+ TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
+ TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+ TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
+ TYPE_LIGHT = ASENSOR_TYPE_LIGHT,
+ TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY
+ };
+
+ Sensor();
+ virtual ~Sensor();
+
+ const String8& getName() const;
+ const String8& getVendor() const;
+ int32_t getHandle() const;
+ int32_t getType() const;
+ float getMinValue() const;
+ float getMaxValue() const;
+ float getResolution() const;
+ float getPowerUsage() const;
+
+ // Flattenable interface
+ virtual size_t getFlattenedSize() const;
+ virtual size_t getFdCount() const;
+ virtual status_t flatten(void* buffer, size_t size,
+ int fds[], size_t count) const;
+ virtual status_t unflatten(void const* buffer, size_t size,
+ int fds[], size_t count);
+
+private:
+ String8 mName;
+ String8 mVendor;
+ int32_t mHandle;
+ int32_t mType;
+ float mMinValue;
+ float mMaxValue;
+ float mResolution;
+ float mPower;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_H
diff --git a/include/gui/SensorChannel.h b/include/gui/SensorChannel.h
new file mode 100644
index 0000000..bb54618
--- /dev/null
+++ b/include/gui/SensorChannel.h
@@ -0,0 +1,53 @@
+/*
+ * 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_GUI_SENSOR_CHANNEL_H
+#define ANDROID_GUI_SENSOR_CHANNEL_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+
+namespace android {
+// ----------------------------------------------------------------------------
+class Parcel;
+
+class SensorChannel : public RefBase
+{
+public:
+
+ SensorChannel();
+ SensorChannel(const Parcel& data);
+ virtual ~SensorChannel();
+
+ int getFd() const;
+ ssize_t write(void const* vaddr, size_t size);
+ ssize_t read(void* vaddr, size_t size);
+
+ status_t writeToParcel(Parcel* reply) const;
+
+private:
+ int mSendFd;
+ mutable int mReceiveFd;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_CHANNEL_H
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
new file mode 100644
index 0000000..d8d8128
--- /dev/null
+++ b/include/gui/SensorEventQueue.h
@@ -0,0 +1,71 @@
+/*
+ * 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_SENSOR_EVENT_QUEUE_H
+#define ANDROID_SENSOR_EVENT_QUEUE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <gui/SensorChannel.h>
+
+// ----------------------------------------------------------------------------
+
+struct ALooper;
+struct ASensorEvent;
+
+// Concrete types for the NDK
+struct ASensorEventQueue {
+ ALooper* looper;
+};
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Sensor;
+
+// ----------------------------------------------------------------------------
+
+class SensorEventQueue : public ASensorEventQueue, public RefBase
+{
+public:
+ SensorEventQueue(const sp<ISensorEventConnection>& connection);
+ virtual ~SensorEventQueue();
+ virtual void onFirstRef();
+
+ int getFd() const;
+ ssize_t write(ASensorEvent const* events, size_t numEvents);
+ ssize_t read(ASensorEvent* events, size_t numEvents);
+
+ status_t enableSensor(Sensor const* sensor) const;
+ status_t disableSensor(Sensor const* sensor) const;
+ status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+
+private:
+ sp<ISensorEventConnection> mSensorEventConnection;
+ sp<SensorChannel> mSensorChannel;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_EVENT_QUEUE_H
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
new file mode 100644
index 0000000..0d65334
--- /dev/null
+++ b/include/gui/SensorManager.h
@@ -0,0 +1,63 @@
+/*
+ * 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_GUI_SENSOR_MANAGER_H
+#define ANDROID_GUI_SENSOR_MANAGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <utils/Vector.h>
+
+#include <gui/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensorManager { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorServer;
+class Sensor;
+class SensorEventQueue;
+
+// ----------------------------------------------------------------------------
+
+class SensorManager : public ASensorManager, public Singleton<SensorManager>
+{
+public:
+ SensorManager();
+ ~SensorManager();
+
+ ssize_t getSensorList(Sensor**) const;
+ Sensor* getDefaultSensor(int type);
+ sp<SensorEventQueue> createEventQueue();
+
+private:
+ sp<ISensorServer> mSensorServer;
+ Sensor* mSensorList;
+ Vector<Sensor> mSensors;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index b4d738c..9f3d0b6 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -223,6 +223,11 @@
// samples as specified in output buffer descriptor. If the buffer descriptor
// is not specified the function must use either the buffer or the
// buffer provider function installed by the EFFECT_CMD_CONFIGURE command.
+// The effect framework will call the process() function after the EFFECT_CMD_ENABLE
+// command is received and until the EFFECT_CMD_DISABLE is received. When the engine
+// receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
+// and when done indicate that it is OK to stop calling the process() function by
+// returning the -ENODATA status.
//
// NOTE: the process() function implementation should be "real-time safe" that is
// it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
@@ -239,6 +244,8 @@
//
// Output:
// returned value: 0 successful operation
+// -ENODATA the engine has finished the disable phase and the framework
+// can stop calling process()
// -EINVAL invalid interface handle or
// invalid input/output buffer description
////////////////////////////////////////////////////////////////////////////////
diff --git a/include/media/EffectBassBoostApi.h b/include/media/EffectBassBoostApi.h
new file mode 100644
index 0000000..b24a5f4
--- /dev/null
+++ b/include/media/EffectBassBoostApi.h
@@ -0,0 +1,42 @@
+/*
+ * 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_EFFECTBASSBOOSTAPI_H_
+#define ANDROID_EFFECTBASSBOOSTAPI_H_
+
+#include <media/EffectApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// TODO: include OpenSLES_IID.h instead
+static const effect_uuid_t SL_IID_BASSBOOST_ = { 0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_BASSBOOST = &SL_IID_BASSBOOST_;
+
+/* enumerated parameter settings for BassBoost effect */
+typedef enum
+{
+ BASSBOOST_PARAM_STRENGTH_SUPPORTED,
+ BASSBOOST_PARAM_STRENGTH
+} t_bassboost_params;
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTBASSBOOSTAPI_H_*/
diff --git a/include/media/EffectReverbApi.h b/include/media/EffectEnvironmentalReverbApi.h
similarity index 79%
rename from include/media/EffectReverbApi.h
rename to include/media/EffectEnvironmentalReverbApi.h
index 6371adb..d490f71 100644
--- a/include/media/EffectReverbApi.h
+++ b/include/media/EffectEnvironmentalReverbApi.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_EFFECTREVERBAPI_H_
-#define ANDROID_EFFECTREVERBAPI_H_
+#ifndef ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_
+#define ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_
#include <media/EffectApi.h>
@@ -27,14 +27,9 @@
static const effect_uuid_t SL_IID_ENVIRONMENTALREVERB_ = { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x6, 0x83, 0x9e } };
const effect_uuid_t * const SL_IID_ENVIRONMENTALREVERB = &SL_IID_ENVIRONMENTALREVERB_;
-static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_;
-
-/* enumerated parameter settings for Reverb effect */
+/* enumerated parameter settings for environmental reverb effect */
typedef enum
{
- REVERB_PARAM_BYPASS,
- REVERB_PARAM_PRESET,
// Parameters below are as defined in OpenSL ES specification for environmental reverb interface
REVERB_PARAM_ROOM_LEVEL, // in millibels, range -6000 to 0
REVERB_PARAM_ROOM_HF_LEVEL, // in millibels, range -4000 to 0
@@ -46,17 +41,9 @@
REVERB_PARAM_REVERB_DELAY, // in milliseconds, range 0 to 65
REVERB_PARAM_DIFFUSION, // in permilles, range 0 to 1000
REVERB_PARAM_DENSITY, // in permilles, range 0 to 1000
- REVERB_PARAM_PROPERTIES
-} t_reverb_params;
-
-
-typedef enum
-{
- REVERB_PRESET_LARGE_HALL,
- REVERB_PRESET_HALL,
- REVERB_PRESET_CHAMBER,
- REVERB_PRESET_ROOM,
-} t_reverb_presets;
+ REVERB_PARAM_PROPERTIES,
+ REVERB_PARAM_BYPASS
+} t_env_reverb_params;
//t_reverb_properties is equal to SLEnvironmentalReverbSettings defined in OpenSL ES specification.
typedef struct s_reverb_properties {
@@ -79,4 +66,4 @@
#endif
-#endif /*ANDROID_EFFECTREVERBAPI_H_*/
+#endif /*ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_*/
diff --git a/include/media/EffectEqualizerApi.h b/include/media/EffectEqualizerApi.h
index e3069d5..cb05b32 100644
--- a/include/media/EffectEqualizerApi.h
+++ b/include/media/EffectEqualizerApi.h
@@ -19,14 +19,13 @@
#include <media/EffectApi.h>
+// for the definition of SL_IID_EQUALIZER
+#include "OpenSLES.h"
+
#if __cplusplus
extern "C" {
#endif
-//TODO replace by openSL ES include when available
-static const effect_uuid_t SL_IID_EQUALIZER_ = { 0x0bed4300, 0xddd6, 0x11db, 0x8f34, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_EQUALIZER = &SL_IID_EQUALIZER_;
-
/* enumerated parameters for Equalizer effect */
typedef enum
{
diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h
new file mode 100644
index 0000000..34ffffe
--- /dev/null
+++ b/include/media/EffectPresetReverbApi.h
@@ -0,0 +1,54 @@
+/*
+ * 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_EFFECTPRESETREVERBAPI_H_
+#define ANDROID_EFFECTPRESETREVERBAPI_H_
+
+#include <media/EffectApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// TODO: include OpenSLES_IID.h instead
+
+static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_;
+
+/* enumerated parameter settings for preset reverb effect */
+typedef enum
+{
+ REVERB_PARAM_PRESET
+} t_preset_reverb_params;
+
+
+typedef enum
+{
+ REVERB_PRESET_NONE,
+ REVERB_PRESET_SMALLROOM,
+ REVERB_PRESET_MEDIUMROOM,
+ REVERB_PRESET_LARGEROOM,
+ REVERB_PRESET_MEDIUMHALL,
+ REVERB_PRESET_LARGEHALL,
+ REVERB_PRESET_PLATE
+} t_reverb_presets;
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTPRESETREVERBAPI_H_*/
diff --git a/include/media/EffectVirtualizerApi.h b/include/media/EffectVirtualizerApi.h
new file mode 100644
index 0000000..601c384
--- /dev/null
+++ b/include/media/EffectVirtualizerApi.h
@@ -0,0 +1,42 @@
+/*
+ * 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_EFFECTVIRTUALIZERAPI_H_
+#define ANDROID_EFFECTVIRTUALIZERAPI_H_
+
+#include <media/EffectApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// TODO: include OpenSLES_IID.h instead
+static const effect_uuid_t SL_IID_VIRTUALIZER_ = { 0x37cc2c00, 0xdddd, 0x11db, 0x8577, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_VIRTUALIZER = &SL_IID_VIRTUALIZER_;
+
+/* enumerated parameter settings for virtualizer effect */
+typedef enum
+{
+ VIRTUALIZER_PARAM_STRENGTH_SUPPORTED,
+ VIRTUALIZER_PARAM_STRENGTH
+} t_virtualizer_params;
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTVIRTUALIZERAPI_H_*/
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 497965c..5e9e368 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -48,6 +48,7 @@
virtual status_t close() = 0;
virtual status_t reset() = 0;
virtual status_t getMaxAmplitude(int *max) = 0;
+ virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
private:
MediaRecorderBase(const MediaRecorderBase &);
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index c04105e..f75d80d 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -52,6 +52,7 @@
virtual status_t close();
virtual status_t reset();
virtual status_t getMaxAmplitude(int *max);
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
private:
status_t doStop();
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 962b38b..232583a 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -52,6 +52,7 @@
void endBox();
uint32_t interleaveDuration() const { return mInterleaveDurationUs; }
status_t setInterleaveDuration(uint32_t duration);
+ int32_t getTimeScale() const { return mTimeScale; }
protected:
virtual ~MPEG4Writer();
@@ -72,6 +73,7 @@
bool mStreamableFile;
off_t mEstimatedMoovBoxSize;
uint32_t mInterleaveDurationUs;
+ int32_t mTimeScale;
int64_t mStartTimestampUs;
Mutex mLock;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 73f5547..cdbf483 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -68,6 +68,7 @@
kKeyDiscNumber = 'dnum', // cstring
kKeyDate = 'date', // cstring
kKeyWriter = 'writ', // cstring
+ kKeyTimeScale = 'tmsl', // int32_t
// video profile and level
kKeyVideoProfile = 'vprf', // int32_t
diff --git a/include/ui/Input.h b/include/ui/Input.h
index a2e0ba06..a7d23d4 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -43,7 +43,9 @@
/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
-struct AInputEvent { };
+struct AInputEvent {
+ virtual ~AInputEvent() { }
+};
namespace android {
diff --git a/include/ui/InputDevice.h b/include/ui/InputDevice.h
new file mode 100644
index 0000000..4420600
--- /dev/null
+++ b/include/ui/InputDevice.h
@@ -0,0 +1,338 @@
+/*
+ * 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 _UI_INPUT_DEVICE_H
+#define _UI_INPUT_DEVICE_H
+
+#include <ui/EventHub.h>
+#include <ui/Input.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/BitSet.h>
+
+#include <stddef.h>
+#include <unistd.h>
+
+/* Maximum pointer id value supported.
+ * (This is limited by our use of BitSet32 to track pointer assignments.) */
+#define MAX_POINTER_ID 31
+
+/* Maximum number of historical samples to average. */
+#define AVERAGING_HISTORY_SIZE 5
+
+
+namespace android {
+
+extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
+extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation);
+
+/*
+ * An input device structure tracks the state of a single input device.
+ *
+ * This structure is only used by ReaderThread and is not intended to be shared with
+ * DispatcherThread (because that would require locking). This works out fine because
+ * DispatcherThread is only interested in cooked event data anyways and does not need
+ * any of the low-level data from InputDevice.
+ */
+struct InputDevice {
+ struct AbsoluteAxisInfo {
+ bool valid; // set to true if axis parameters are known, false otherwise
+
+ int32_t minValue; // minimum value
+ int32_t maxValue; // maximum value
+ int32_t range; // range of values, equal to maxValue - minValue
+ int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
+ int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+ };
+
+ struct VirtualKey {
+ int32_t keyCode;
+ int32_t scanCode;
+ uint32_t flags;
+
+ // computed hit box, specified in touch screen coords based on known display size
+ int32_t hitLeft;
+ int32_t hitTop;
+ int32_t hitRight;
+ int32_t hitBottom;
+
+ inline bool isHit(int32_t x, int32_t y) const {
+ return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+ }
+ };
+
+ struct KeyboardState {
+ struct Current {
+ int32_t metaState;
+ nsecs_t downTime; // time of most recent key down
+ } current;
+
+ void reset();
+ };
+
+ struct TrackballState {
+ struct Accumulator {
+ enum {
+ FIELD_BTN_MOUSE = 1,
+ FIELD_REL_X = 2,
+ FIELD_REL_Y = 4
+ };
+
+ uint32_t fields;
+
+ bool btnMouse;
+ int32_t relX;
+ int32_t relY;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } accumulator;
+
+ struct Current {
+ bool down;
+ nsecs_t downTime;
+ } current;
+
+ struct Precalculated {
+ float xScale;
+ float yScale;
+ float xPrecision;
+ float yPrecision;
+ } precalculated;
+
+ void reset();
+ };
+
+ struct SingleTouchScreenState {
+ struct Accumulator {
+ enum {
+ FIELD_BTN_TOUCH = 1,
+ FIELD_ABS_X = 2,
+ FIELD_ABS_Y = 4,
+ FIELD_ABS_PRESSURE = 8,
+ FIELD_ABS_TOOL_WIDTH = 16
+ };
+
+ uint32_t fields;
+
+ bool btnTouch;
+ int32_t absX;
+ int32_t absY;
+ int32_t absPressure;
+ int32_t absToolWidth;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } accumulator;
+
+ struct Current {
+ bool down;
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ int32_t size;
+ } current;
+
+ void reset();
+ };
+
+ struct MultiTouchScreenState {
+ struct Accumulator {
+ enum {
+ FIELD_ABS_MT_POSITION_X = 1,
+ FIELD_ABS_MT_POSITION_Y = 2,
+ FIELD_ABS_MT_TOUCH_MAJOR = 4,
+ FIELD_ABS_MT_WIDTH_MAJOR = 8,
+ FIELD_ABS_MT_TRACKING_ID = 16
+ };
+
+ uint32_t pointerCount;
+ struct Pointer {
+ uint32_t fields;
+
+ int32_t absMTPositionX;
+ int32_t absMTPositionY;
+ int32_t absMTTouchMajor;
+ int32_t absMTWidthMajor;
+ int32_t absMTTrackingId;
+
+ inline void clear() {
+ fields = 0;
+ }
+ } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+
+ inline void clear() {
+ pointerCount = 0;
+ pointers[0].clear();
+ }
+
+ inline bool isDirty() {
+ return pointerCount != 0;
+ }
+ } accumulator;
+
+ void reset();
+ };
+
+ struct PointerData {
+ uint32_t id;
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ int32_t size;
+ };
+
+ struct TouchData {
+ uint32_t pointerCount;
+ PointerData pointers[MAX_POINTERS];
+ BitSet32 idBits;
+ uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+ void copyFrom(const TouchData& other);
+
+ inline void clear() {
+ pointerCount = 0;
+ idBits.clear();
+ }
+ };
+
+ // common state used for both single-touch and multi-touch screens after the initial
+ // touch decoding has been performed
+ struct TouchScreenState {
+ Vector<VirtualKey> virtualKeys;
+
+ struct Parameters {
+ bool useBadTouchFilter;
+ bool useJumpyTouchFilter;
+ bool useAveragingTouchFilter;
+
+ AbsoluteAxisInfo xAxis;
+ AbsoluteAxisInfo yAxis;
+ AbsoluteAxisInfo pressureAxis;
+ AbsoluteAxisInfo sizeAxis;
+ } parameters;
+
+ // The touch data of the current sample being processed.
+ TouchData currentTouch;
+
+ // The touch data of the previous sample that was processed. This is updated
+ // incrementally while the current sample is being processed.
+ TouchData lastTouch;
+
+ // The time the primary pointer last went down.
+ nsecs_t downTime;
+
+ struct CurrentVirtualKeyState {
+ enum Status {
+ STATUS_UP,
+ STATUS_DOWN,
+ STATUS_CANCELED
+ };
+
+ Status status;
+ nsecs_t downTime;
+ int32_t keyCode;
+ int32_t scanCode;
+ } currentVirtualKey;
+
+ struct AveragingTouchFilterState {
+ // Individual history tracks are stored by pointer id
+ uint32_t historyStart[MAX_POINTERS];
+ uint32_t historyEnd[MAX_POINTERS];
+ struct {
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ } pointers[MAX_POINTERS];
+ } historyData[AVERAGING_HISTORY_SIZE];
+ } averagingTouchFilter;
+
+ struct JumpTouchFilterState {
+ int32_t jumpyPointsDropped;
+ } jumpyTouchFilter;
+
+ struct Precalculated {
+ int32_t xOrigin;
+ float xScale;
+
+ int32_t yOrigin;
+ float yScale;
+
+ int32_t pressureOrigin;
+ float pressureScale;
+
+ int32_t sizeOrigin;
+ float sizeScale;
+ } precalculated;
+
+ void reset();
+
+ bool applyBadTouchFilter();
+ bool applyJumpyTouchFilter();
+ void applyAveragingTouchFilter();
+ void calculatePointerIds();
+
+ bool isPointInsideDisplay(int32_t x, int32_t y) const;
+ const InputDevice::VirtualKey* findVirtualKeyHit() const;
+ };
+
+ InputDevice(int32_t id, uint32_t classes, String8 name);
+
+ int32_t id;
+ uint32_t classes;
+ String8 name;
+ bool ignored;
+
+ KeyboardState keyboard;
+ TrackballState trackball;
+ TouchScreenState touchScreen;
+ union {
+ SingleTouchScreenState singleTouchScreen;
+ MultiTouchScreenState multiTouchScreen;
+ };
+
+ void reset();
+
+ inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; }
+ inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; }
+ inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; }
+ inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; }
+ inline bool isSingleTouchScreen() const { return (classes
+ & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT))
+ == INPUT_DEVICE_CLASS_TOUCHSCREEN; }
+ inline bool isMultiTouchScreen() const { return classes
+ & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; }
+ inline bool isTouchScreen() const { return classes
+ & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); }
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_DEVICE_H
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 781da35..85a0084 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -19,6 +19,7 @@
#include <ui/EventHub.h>
#include <ui/Input.h>
+#include <ui/InputDevice.h>
#include <ui/InputDispatcher.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
@@ -30,304 +31,8 @@
#include <stddef.h>
#include <unistd.h>
-/* Maximum pointer id value supported.
- * (This is limited by our use of BitSet32 to track pointer assignments.) */
-#define MAX_POINTER_ID 32
-
-/* Maximum number of historical samples to average. */
-#define AVERAGING_HISTORY_SIZE 5
-
-
namespace android {
-extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
-extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation);
-
-/*
- * An input device structure tracks the state of a single input device.
- *
- * This structure is only used by ReaderThread and is not intended to be shared with
- * DispatcherThread (because that would require locking). This works out fine because
- * DispatcherThread is only interested in cooked event data anyways and does not need
- * any of the low-level data from InputDevice.
- */
-struct InputDevice {
- struct AbsoluteAxisInfo {
- bool valid; // set to true if axis parameters are known, false otherwise
-
- int32_t minValue; // minimum value
- int32_t maxValue; // maximum value
- int32_t range; // range of values, equal to maxValue - minValue
- int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
- int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
- };
-
- struct VirtualKey {
- int32_t keyCode;
- int32_t scanCode;
- uint32_t flags;
-
- // computed hit box, specified in touch screen coords based on known display size
- int32_t hitLeft;
- int32_t hitTop;
- int32_t hitRight;
- int32_t hitBottom;
-
- inline bool isHit(int32_t x, int32_t y) const {
- return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
- }
- };
-
- struct KeyboardState {
- struct Current {
- int32_t metaState;
- nsecs_t downTime; // time of most recent key down
- } current;
-
- void reset();
- };
-
- struct TrackballState {
- struct Accumulator {
- enum {
- FIELD_BTN_MOUSE = 1,
- FIELD_REL_X = 2,
- FIELD_REL_Y = 4
- };
-
- uint32_t fields;
-
- bool btnMouse;
- int32_t relX;
- int32_t relY;
-
- inline void clear() {
- fields = 0;
- }
-
- inline bool isDirty() {
- return fields != 0;
- }
- } accumulator;
-
- struct Current {
- bool down;
- nsecs_t downTime;
- } current;
-
- struct Precalculated {
- float xScale;
- float yScale;
- float xPrecision;
- float yPrecision;
- } precalculated;
-
- void reset();
- };
-
- struct SingleTouchScreenState {
- struct Accumulator {
- enum {
- FIELD_BTN_TOUCH = 1,
- FIELD_ABS_X = 2,
- FIELD_ABS_Y = 4,
- FIELD_ABS_PRESSURE = 8,
- FIELD_ABS_TOOL_WIDTH = 16
- };
-
- uint32_t fields;
-
- bool btnTouch;
- int32_t absX;
- int32_t absY;
- int32_t absPressure;
- int32_t absToolWidth;
-
- inline void clear() {
- fields = 0;
- }
-
- inline bool isDirty() {
- return fields != 0;
- }
- } accumulator;
-
- struct Current {
- bool down;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t size;
- } current;
-
- void reset();
- };
-
- struct MultiTouchScreenState {
- struct Accumulator {
- enum {
- FIELD_ABS_MT_POSITION_X = 1,
- FIELD_ABS_MT_POSITION_Y = 2,
- FIELD_ABS_MT_TOUCH_MAJOR = 4,
- FIELD_ABS_MT_WIDTH_MAJOR = 8,
- FIELD_ABS_MT_TRACKING_ID = 16
- };
-
- uint32_t pointerCount;
- struct Pointer {
- uint32_t fields;
-
- int32_t absMTPositionX;
- int32_t absMTPositionY;
- int32_t absMTTouchMajor;
- int32_t absMTWidthMajor;
- int32_t absMTTrackingId;
-
- inline void clear() {
- fields = 0;
- }
- } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
-
- inline void clear() {
- pointerCount = 0;
- pointers[0].clear();
- }
-
- inline bool isDirty() {
- return pointerCount != 0;
- }
- } accumulator;
-
- void reset();
- };
-
- struct PointerData {
- uint32_t id;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t size;
- };
-
- struct TouchData {
- uint32_t pointerCount;
- PointerData pointers[MAX_POINTERS];
- BitSet32 idBits;
- uint32_t idToIndex[MAX_POINTER_ID];
-
- void copyFrom(const TouchData& other);
-
- inline void clear() {
- pointerCount = 0;
- idBits.clear();
- }
- };
-
- // common state used for both single-touch and multi-touch screens after the initial
- // touch decoding has been performed
- struct TouchScreenState {
- Vector<VirtualKey> virtualKeys;
-
- struct Parameters {
- bool useBadTouchFilter;
- bool useJumpyTouchFilter;
- bool useAveragingTouchFilter;
-
- AbsoluteAxisInfo xAxis;
- AbsoluteAxisInfo yAxis;
- AbsoluteAxisInfo pressureAxis;
- AbsoluteAxisInfo sizeAxis;
- } parameters;
-
- // The touch data of the current sample being processed.
- TouchData currentTouch;
-
- // The touch data of the previous sample that was processed. This is updated
- // incrementally while the current sample is being processed.
- TouchData lastTouch;
-
- // The time the primary pointer last went down.
- nsecs_t downTime;
-
- struct CurrentVirtualKeyState {
- bool down;
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
- } currentVirtualKey;
-
- struct AveragingTouchFilterState {
- // Individual history tracks are stored by pointer id
- uint32_t historyStart[MAX_POINTERS];
- uint32_t historyEnd[MAX_POINTERS];
- struct {
- struct {
- int32_t x;
- int32_t y;
- int32_t pressure;
- } pointers[MAX_POINTERS];
- } historyData[AVERAGING_HISTORY_SIZE];
- } averagingTouchFilter;
-
- struct JumpTouchFilterState {
- int32_t jumpyPointsDropped;
- } jumpyTouchFilter;
-
- struct Precalculated {
- int32_t xOrigin;
- float xScale;
-
- int32_t yOrigin;
- float yScale;
-
- int32_t pressureOrigin;
- float pressureScale;
-
- int32_t sizeOrigin;
- float sizeScale;
- } precalculated;
-
- void reset();
-
- bool applyBadTouchFilter();
- bool applyJumpyTouchFilter();
- void applyAveragingTouchFilter();
- void calculatePointerIds();
-
- bool isPointInsideDisplay(int32_t x, int32_t y) const;
- };
-
- InputDevice(int32_t id, uint32_t classes, String8 name);
-
- int32_t id;
- uint32_t classes;
- String8 name;
- bool ignored;
-
- KeyboardState keyboard;
- TrackballState trackball;
- TouchScreenState touchScreen;
- union {
- SingleTouchScreenState singleTouchScreen;
- MultiTouchScreenState multiTouchScreen;
- };
-
- void reset();
-
- inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; }
- inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; }
- inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; }
- inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; }
- inline bool isSingleTouchScreen() const { return (classes
- & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT))
- == INPUT_DEVICE_CLASS_TOUCHSCREEN; }
- inline bool isMultiTouchScreen() const { return classes
- & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; }
- inline bool isTouchScreen() const { return classes
- & (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); }
-};
-
-
/*
* Input reader policy interface.
*
@@ -390,11 +95,9 @@
virtual bool getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation) = 0;
- /* Provides feedback for a virtual key.
+ /* Provides feedback for a virtual key down.
*/
- virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t flags, int32_t keyCode,
- int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
+ virtual void virtualKeyDownFeedback() = 0;
/* Intercepts a key event.
* The policy can use this method as an opportunity to perform power management functions
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 11714d5..226d1d5 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -331,30 +331,4 @@
} // namespace android
-/*
- * NDK input queue API.
- */
-struct AInputQueue {
-public:
- /* Creates a consumer associated with an input channel. */
- explicit AInputQueue(const android::sp<android::InputChannel>& channel);
-
- /* Destroys the consumer and releases its input channel. */
- virtual ~AInputQueue();
-
- inline android::InputConsumer& getConsumer() { return mConsumer; }
-
- android::status_t consume(android::InputEvent** event);
-
- void setPollLoop(const android::sp<android::PollLoop>& pollLoop) { mPollLoop = pollLoop; }
- const android::sp<android::PollLoop> getPollLoop() const { return mPollLoop; }
-
- virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
-
-private:
- android::InputConsumer mConsumer;
- android::PreallocatedInputEventFactory mInputEventFactory;
- android::sp<android::PollLoop> mPollLoop;
-};
-
#endif // _UI_INPUT_TRANSPORT_H
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index e81d0f9..c8d6ffc 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -17,6 +17,8 @@
#ifndef _UI_KEYCODE_LABELS_H
#define _UI_KEYCODE_LABELS_H
+#include <android/keycodes.h>
+
struct KeycodeLabel {
const char *literal;
int value;
@@ -118,117 +120,28 @@
{ "PAGE_DOWN", 93 },
{ "PICTSYMBOLS", 94 },
{ "SWITCH_CHARSET", 95 },
+ { "BUTTON_A", 96 },
+ { "BUTTON_B", 97 },
+ { "BUTTON_C", 98 },
+ { "BUTTON_X", 99 },
+ { "BUTTON_Y", 100 },
+ { "BUTTON_Z", 101 },
+ { "BUTTON_L1", 102 },
+ { "BUTTON_R1", 103 },
+ { "BUTTON_L2", 104 },
+ { "BUTTON_R2", 105 },
+ { "BUTTON_THUMBL", 106 },
+ { "BUTTON_THUMBR", 107 },
+ { "BUTTON_START", 108 },
+ { "BUTTON_SELECT", 109 },
+ { "BUTTON_MODE", 110 },
- // NOTE: If you add a new keycode here you must also add it to:
- // (enum KeyCode, in this file)
- // frameworks/base/core/java/android/view/KeyEvent.java
- // tools/puppet_master/PuppetMaster.nav_keys.py
- // frameworks/base/core/res/res/values/attrs.xml
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
{ NULL, 0 }
};
-// These constants need to match the above mappings.
-typedef enum KeyCode {
- kKeyCodeUnknown = 0,
-
- kKeyCodeSoftLeft = 1,
- kKeyCodeSoftRight = 2,
- kKeyCodeHome = 3,
- kKeyCodeBack = 4,
- kKeyCodeCall = 5,
- kKeyCodeEndCall = 6,
- kKeyCode0 = 7,
- kKeyCode1 = 8,
- kKeyCode2 = 9,
- kKeyCode3 = 10,
- kKeyCode4 = 11,
- kKeyCode5 = 12,
- kKeyCode6 = 13,
- kKeyCode7 = 14,
- kKeyCode8 = 15,
- kKeyCode9 = 16,
- kKeyCodeStar = 17,
- kKeyCodePound = 18,
- kKeyCodeDpadUp = 19,
- kKeyCodeDpadDown = 20,
- kKeyCodeDpadLeft = 21,
- kKeyCodeDpadRight = 22,
- kKeyCodeDpadCenter = 23,
- kKeyCodeVolumeUp = 24,
- kKeyCodeVolumeDown = 25,
- kKeyCodePower = 26,
- kKeyCodeCamera = 27,
- kKeyCodeClear = 28,
- kKeyCodeA = 29,
- kKeyCodeB = 30,
- kKeyCodeC = 31,
- kKeyCodeD = 32,
- kKeyCodeE = 33,
- kKeyCodeF = 34,
- kKeyCodeG = 35,
- kKeyCodeH = 36,
- kKeyCodeI = 37,
- kKeyCodeJ = 38,
- kKeyCodeK = 39,
- kKeyCodeL = 40,
- kKeyCodeM = 41,
- kKeyCodeN = 42,
- kKeyCodeO = 43,
- kKeyCodeP = 44,
- kKeyCodeQ = 45,
- kKeyCodeR = 46,
- kKeyCodeS = 47,
- kKeyCodeT = 48,
- kKeyCodeU = 49,
- kKeyCodeV = 50,
- kKeyCodeW = 51,
- kKeyCodeX = 52,
- kKeyCodeY = 53,
- kKeyCodeZ = 54,
- kKeyCodeComma = 55,
- kKeyCodePeriod = 56,
- kKeyCodeAltLeft = 57,
- kKeyCodeAltRight = 58,
- kKeyCodeShiftLeft = 59,
- kKeyCodeShiftRight = 60,
- kKeyCodeTab = 61,
- kKeyCodeSpace = 62,
- kKeyCodeSym = 63,
- kKeyCodeExplorer = 64,
- kKeyCodeEnvelope = 65,
- kKeyCodeNewline = 66,
- kKeyCodeDel = 67,
- kKeyCodeGrave = 68,
- kKeyCodeMinus = 69,
- kKeyCodeEquals = 70,
- kKeyCodeLeftBracket = 71,
- kKeyCodeRightBracket = 72,
- kKeyCodeBackslash = 73,
- kKeyCodeSemicolon = 74,
- kKeyCodeApostrophe = 75,
- kKeyCodeSlash = 76,
- kKeyCodeAt = 77,
- kKeyCodeNum = 78,
- kKeyCodeHeadSetHook = 79,
- kKeyCodeFocus = 80,
- kKeyCodePlus = 81,
- kKeyCodeMenu = 82,
- kKeyCodeNotification = 83,
- kKeyCodeSearch = 84,
- kKeyCodePlayPause = 85,
- kKeyCodeStop = 86,
- kKeyCodeNextSong = 87,
- kKeyCodePreviousSong = 88,
- kKeyCodeRewind = 89,
- kKeyCodeForward = 90,
- kKeyCodeMute = 91,
- kKeyCodePageUp = 92,
- kKeyCodePageDown = 93,
- kKeyCodePictSymbols = 94,
- kKeyCodeSwitchCharset = 95
-} KeyCode;
-
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a213c09..4e65a2d 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,31 +20,28 @@
#include <utils/TypeHelpers.h>
#include <ui/Point.h>
+#include <android/rect.h>
+
namespace android {
-class Rect
+class Rect : public ARect
{
public:
- int left;
- int top;
- int right;
- int bottom;
-
- typedef int value_type;
+ typedef int32_t value_type;
// we don't provide copy-ctor and operator= on purpose
// because we want the compiler generated versions
inline Rect() {
}
- inline Rect(int w, int h)
- : left(0), top(0), right(w), bottom(h) {
+ inline Rect(int32_t w, int32_t h) {
+ left = top = 0; right = w; bottom = h;
}
- inline Rect(int l, int t, int r, int b)
- : left(l), top(t), right(r), bottom(b) {
+ inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) {
+ left = l; top = t; right = r; bottom = b;
}
- inline Rect(const Point& lt, const Point& rb)
- : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) {
+ inline Rect(const Point& lt, const Point& rb) {
+ left = lt.x; top = lt.y; right = rb.x; bottom = rb.y;
}
void makeInvalid();
@@ -68,12 +65,12 @@
}
// rectangle's width
- inline int width() const {
+ inline int32_t width() const {
return right-left;
}
// rectangle's height
- inline int height() const {
+ inline int32_t height() const {
return bottom-top;
}
@@ -136,12 +133,12 @@
const Rect operator + (const Point& rhs) const;
const Rect operator - (const Point& rhs) const;
- void translate(int dx, int dy) { // legacy, don't use.
+ void translate(int32_t dx, int32_t dy) { // legacy, don't use.
offsetBy(dx, dy);
}
- Rect& offsetTo(int x, int y);
- Rect& offsetBy(int x, int y);
+ Rect& offsetTo(int32_t x, int32_t y);
+ Rect& offsetBy(int32_t x, int32_t y);
bool intersect(const Rect& with, Rect* result) const;
};
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
new file mode 100644
index 0000000..075927c
--- /dev/null
+++ b/include/utils/ObbFile.h
@@ -0,0 +1,87 @@
+/*
+ * 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 OBBFILE_H_
+#define OBBFILE_H_
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class ObbFile : public RefBase {
+protected:
+ virtual ~ObbFile();
+
+public:
+ ObbFile();
+
+ bool readFrom(const char* filename);
+ bool readFrom(int fd);
+ bool writeTo(const char* filename);
+ bool writeTo(int fd);
+
+ const char* getFileName() const {
+ return mFileName;
+ }
+
+ const String8 getPackageName() const {
+ return mPackageName;
+ }
+
+ int32_t getVersion() const {
+ return mVersion;
+ }
+
+ void setPackageName(String8 packageName) {
+ mPackageName = packageName;
+ }
+
+ void setVersion(int32_t version) {
+ mVersion = version;
+ }
+
+ static inline uint32_t get4LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ }
+
+ static inline void put4LE(unsigned char* buf, uint32_t val) {
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+ }
+
+private:
+ /* Package name this ObbFile is associated with */
+ String8 mPackageName;
+
+ /* Package version this ObbFile is associated with */
+ int32_t mVersion;
+
+ const char* mFileName;
+
+ size_t mFileSize;
+
+ unsigned char* mReadBuf;
+
+ bool parseObbFile(int fd);
+};
+
+}
+#endif /* OBBFILE_H_ */
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index b3651ca..81230e8 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -42,7 +42,7 @@
virtual ~PollLoop();
public:
- PollLoop();
+ PollLoop(bool allowNonCallbacks);
/**
* A callback that it to be invoked when an event occurs on a file descriptor.
@@ -54,6 +54,12 @@
*/
typedef bool (*Callback)(int fd, int events, void* data);
+ enum {
+ POLL_CALLBACK = ALOOPER_POLL_CALLBACK,
+ POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT,
+ POLL_ERROR = ALOOPER_POLL_ERROR,
+ };
+
/**
* Performs a single call to poll() with optional timeout in milliseconds.
* Invokes callbacks for all file descriptors on which an event occurred.
@@ -61,16 +67,25 @@
* If the timeout is zero, returns immediately without blocking.
* If the timeout is negative, waits indefinitely until awoken.
*
- * Returns true if a callback was invoked or if the loop was awoken by wake().
- * Returns false if a timeout or error occurred.
+ * Returns ALOOPER_POLL_CALLBACK if a callback was invoked.
*
- * This method must only be called on the main thread.
+ * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns ALOPER_POLL_ERROR if an error occurred.
+ *
+ * Returns a value >= 0 containing a file descriptor if it has data
+ * and it has no callback function (requiring the caller here to handle it).
+ * In this (and only this) case outEvents and outData will contain the poll
+ * events and data associated with the fd.
+ *
+ * This method must only be called on the thread owning the PollLoop.
* This method blocks until either a file descriptor is signalled, a timeout occurs,
* or wake() is called.
* This method does not return until it has finished invoking the appropriate callbacks
* for all file descriptors that were signalled.
*/
- bool pollOnce(int timeoutMillis);
+ int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL);
/**
* Wakes the loop asynchronously.
@@ -81,6 +96,12 @@
void wake();
/**
+ * Control whether this PollLoop instance allows using IDs instead
+ * of callbacks.
+ */
+ bool getAllowNonCallbacks() const;
+
+ /**
* Sets the callback for a file descriptor, replacing the existing one, if any.
* It is an error to call this method with events == 0 or callback == NULL.
*
@@ -95,7 +116,8 @@
/**
* Like setCallback(), but for the NDK callback function.
*/
- void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data);
+ void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+ void* data);
/**
* Removes the callback for a file descriptor, if one exists.
@@ -141,7 +163,9 @@
ALooper_callbackFunc* looperCallback;
void* data;
};
-
+
+ const bool mAllowNonCallbacks;
+
Mutex mLock;
bool mPolling;
uint32_t mWaiters;
@@ -155,7 +179,9 @@
Vector<RequestedCallback> mRequestedCallbacks;
Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
-
+ Vector<PendingCallback> mPendingFds; // used privately by pollOnce
+ size_t mPendingFdsPos;
+
void openWakePipe();
void closeWakePipe();
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index bff4c9b..e13036f 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -36,7 +36,7 @@
: BpInterface<IPermissionController>(impl)
{
}
-
+
virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid)
{
Parcel data, reply;
@@ -46,7 +46,7 @@
data.writeInt32(uid);
remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
// fail on exception
- if (reply.readInt32() != 0) return 0;
+ if (reply.readExceptionCode() != 0) return 0;
return reply.readInt32() != 0;
}
};
@@ -66,8 +66,7 @@
int32_t pid = data.readInt32();
int32_t uid = data.readInt32();
bool res = checkPermission(permission, pid, uid);
- // write exception
- reply->writeInt32(0);
+ reply->writeNoException();
reply->writeInt32(res ? 1 : 0);
return NO_ERROR;
} break;
@@ -77,4 +76,3 @@
}
}; // namespace android
-
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a3a3f0e..1fa4c35 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -158,7 +158,7 @@
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
- return err == NO_ERROR ? reply.readInt32() : err;
+ return err == NO_ERROR ? reply.readExceptionCode() : err;
}
virtual Vector<String16> listServices()
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c2574bd..47be1bf 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -754,6 +754,11 @@
goto restart_write;
}
+status_t Parcel::writeNoException()
+{
+ return writeInt32(0);
+}
+
void Parcel::remove(size_t start, size_t amt)
{
LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -942,6 +947,12 @@
return val;
}
+int32_t Parcel::readExceptionCode() const
+{
+ int32_t exception_code = readAligned<int32_t>();
+ // TODO: skip over the response header here, once that's in.
+ return exception_code;
+}
native_handle* Parcel::readNativeHandle() const
{
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
new file mode 100644
index 0000000..249558a
--- /dev/null
+++ b/libs/gui/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ ISensorEventConnection.cpp \
+ ISensorServer.cpp \
+ Sensor.cpp \
+ SensorChannel.cpp \
+ SensorEventQueue.cpp \
+ SensorManager.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libhardware \
+ libhardware_legacy
+
+LOCAL_MODULE:= libgui
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
new file mode 100644
index 0000000..3e9d456
--- /dev/null
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/ISensorEventConnection.h>
+#include <gui/SensorChannel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+ GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+ ENABLE_DISABLE,
+ SET_EVENT_RATE
+};
+
+class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
+{
+public:
+ BpSensorEventConnection(const sp<IBinder>& impl)
+ : BpInterface<ISensorEventConnection>(impl)
+ {
+ }
+
+ virtual sp<SensorChannel> getSensorChannel() const
+ {
+ Parcel data, reply;
+ remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
+ return new SensorChannel(reply);
+ }
+
+ virtual status_t enableDisable(int handle, bool enabled)
+ {
+ Parcel data, reply;
+ data.writeInt32(handle);
+ data.writeInt32(enabled);
+ remote()->transact(ENABLE_DISABLE, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t setEventRate(int handle, nsecs_t ns)
+ {
+ Parcel data, reply;
+ data.writeInt32(handle);
+ data.writeInt64(ns);
+ remote()->transact(SET_EVENT_RATE, data, &reply);
+ return reply.readInt32();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
+
+// ----------------------------------------------------------------------------
+
+status_t BnSensorEventConnection::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_SENSOR_CHANNEL: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ sp<SensorChannel> channel(getSensorChannel());
+ channel->writeToParcel(reply);
+ return NO_ERROR;
+ } break;
+ case ENABLE_DISABLE: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int enabled = data.readInt32();
+ status_t result = enableDisable(handle, enabled);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_EVENT_RATE: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int ns = data.readInt64();
+ status_t result = setEventRate(handle, ns);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
new file mode 100644
index 0000000..c6177bc
--- /dev/null
+++ b/libs/gui/ISensorServer.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/Sensor.h>
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+ GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
+ CREATE_SENSOR_EVENT_CONNECTION,
+};
+
+class BpSensorServer : public BpInterface<ISensorServer>
+{
+public:
+ BpSensorServer(const sp<IBinder>& impl)
+ : BpInterface<ISensorServer>(impl)
+ {
+ }
+
+ virtual Vector<Sensor> getSensorList()
+ {
+ Parcel data, reply;
+ remote()->transact(GET_SENSOR_LIST, data, &reply);
+ Sensor s;
+ Vector<Sensor> v;
+ int32_t n = reply.readInt32();
+ v.setCapacity(n);
+ while (n--) {
+ reply.read(static_cast<Flattenable&>(s));
+ v.add(s);
+ }
+ return v;
+ }
+
+ virtual sp<ISensorEventConnection> createSensorEventConnection()
+ {
+ Parcel data, reply;
+ remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
+ return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+ }
+};
+
+IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
+
+// ----------------------------------------------------------------------
+
+status_t BnSensorServer::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_SENSOR_LIST: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ Vector<Sensor> v(getSensorList());
+ size_t n = v.size();
+ reply->writeInt32(n);
+ for (size_t i=0 ; i<n ; i++) {
+ reply->write(static_cast<const Flattenable&>(v[i]));
+ }
+ return NO_ERROR;
+ } break;
+ case CREATE_SENSOR_EVENT_CONNECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ sp<ISensorEventConnection> connection(createSensorEventConnection());
+ reply->writeStrongBinder(connection->asBinder());
+ return NO_ERROR;
+ } break;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
new file mode 100644
index 0000000..1fdd285
--- /dev/null
+++ b/libs/gui/Sensor.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Flattenable.h>
+
+#include <hardware/sensors.h>
+
+#include <gui/Sensor.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+Sensor::Sensor()
+ : mHandle(0), mType(0),
+ mMinValue(0), mMaxValue(0), mResolution(0),
+ mPower(0)
+{
+}
+
+Sensor::~Sensor()
+{
+}
+
+const String8& Sensor::getName() const {
+ return mName;
+}
+
+const String8& Sensor::getVendor() const {
+ return mVendor;
+}
+
+int32_t Sensor::getHandle() const {
+ return mHandle;
+}
+
+int32_t Sensor::getType() const {
+ return mType;
+}
+
+float Sensor::getMinValue() const {
+ return mMinValue;
+}
+
+float Sensor::getMaxValue() const {
+ return mMaxValue;
+}
+
+float Sensor::getResolution() const {
+ return mResolution;
+}
+
+float Sensor::getPowerUsage() const {
+ return mPower;
+}
+
+size_t Sensor::getFlattenedSize() const
+{
+ return sizeof(int32_t) + ((mName.length() + 3) & ~3) +
+ sizeof(int32_t) + ((mVendor.length() + 3) & ~3) +
+ sizeof(int32_t) * 2 +
+ sizeof(float) * 3;
+}
+
+size_t Sensor::getFdCount() const
+{
+ return 0;
+}
+
+static inline
+size_t write(void* buffer, size_t offset, const String8& value) {
+ memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length());
+ return (value.length() + 3) & ~3;
+}
+
+static inline
+size_t write(void* buffer, size_t offset, float value) {
+ *reinterpret_cast<float*>(static_cast<char*>(buffer) + offset) = value;
+ return sizeof(float);
+}
+
+static inline
+size_t write(void* buffer, size_t offset, int32_t value) {
+ *reinterpret_cast<int32_t*>(static_cast<char*>(buffer) + offset) = value;
+ return sizeof(int32_t);
+}
+
+status_t Sensor::flatten(void* buffer, size_t size,
+ int fds[], size_t count) const
+{
+ if (size < Sensor::getFlattenedSize())
+ return -ENOMEM;
+
+ size_t offset = 0;
+ offset += write(buffer, offset, int32_t(mName.length()));
+ offset += write(buffer, offset, mName);
+ offset += write(buffer, offset, int32_t(mVendor.length()));
+ offset += write(buffer, offset, mVendor);
+ offset += write(buffer, offset, mHandle);
+ offset += write(buffer, offset, mType);
+ offset += write(buffer, offset, mMinValue);
+ offset += write(buffer, offset, mMaxValue);
+ offset += write(buffer, offset, mResolution);
+ offset += write(buffer, offset, mPower);
+
+ return NO_ERROR;
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, String8* value, int32_t len) {
+ value->setTo(static_cast<char const*>(buffer) + offset, len);
+ return (len + 3) & ~3;
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, float* value) {
+ *value = *reinterpret_cast<float const*>(static_cast<char const*>(buffer) + offset);
+ return sizeof(float);
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, int32_t* value) {
+ *value = *reinterpret_cast<int32_t const*>(static_cast<char const*>(buffer) + offset);
+ return sizeof(int32_t);
+}
+
+status_t Sensor::unflatten(void const* buffer, size_t size,
+ int fds[], size_t count)
+{
+ int32_t len;
+ size_t offset = 0;
+ offset += read(buffer, offset, &len);
+ offset += read(buffer, offset, &mName, len);
+ offset += read(buffer, offset, &len);
+ offset += read(buffer, offset, &mVendor, len);
+ offset += read(buffer, offset, &mHandle);
+ offset += read(buffer, offset, &mType);
+ offset += read(buffer, offset, &mMinValue);
+ offset += read(buffer, offset, &mMaxValue);
+ offset += read(buffer, offset, &mResolution);
+ offset += read(buffer, offset, &mPower);
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/SensorChannel.cpp b/libs/gui/SensorChannel.cpp
new file mode 100644
index 0000000..147e1c2
--- /dev/null
+++ b/libs/gui/SensorChannel.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 <sys/types.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <utils/Errors.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/SensorChannel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorChannel::SensorChannel()
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ int fds[2];
+ if (pipe(fds) == 0) {
+ mReceiveFd = fds[0];
+ mSendFd = fds[1];
+ fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+ fcntl(mSendFd, F_SETFL, O_NONBLOCK);
+ }
+}
+
+SensorChannel::SensorChannel(const Parcel& data)
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ mReceiveFd = dup(data.readFileDescriptor());
+ fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+}
+
+SensorChannel::~SensorChannel()
+{
+ if (mSendFd >= 0)
+ close(mSendFd);
+
+ if (mReceiveFd >= 0)
+ close(mReceiveFd);
+}
+
+int SensorChannel::getFd() const
+{
+ return mReceiveFd;
+}
+
+ssize_t SensorChannel::write(void const* vaddr, size_t size)
+{
+ ssize_t len = ::write(mSendFd, vaddr, size);
+ if (len < 0)
+ return -errno;
+ return len;
+}
+
+ssize_t SensorChannel::read(void* vaddr, size_t size)
+{
+ ssize_t len = ::read(mReceiveFd, vaddr, size);
+ if (len < 0)
+ return -errno;
+ return len;
+}
+
+status_t SensorChannel::writeToParcel(Parcel* reply) const
+{
+ if (mReceiveFd < 0)
+ return -EINVAL;
+
+ status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+ close(mReceiveFd);
+ mReceiveFd = -1;
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
new file mode 100644
index 0000000..f922ac4
--- /dev/null
+++ b/libs/gui/SensorEventQueue.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <gui/Sensor.h>
+#include <gui/SensorChannel.h>
+#include <gui/SensorEventQueue.h>
+#include <gui/ISensorEventConnection.h>
+
+#include <android/sensor.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
+ : mSensorEventConnection(connection)
+{
+}
+
+SensorEventQueue::~SensorEventQueue()
+{
+}
+
+void SensorEventQueue::onFirstRef()
+{
+ mSensorChannel = mSensorEventConnection->getSensorChannel();
+}
+
+int SensorEventQueue::getFd() const
+{
+ return mSensorChannel->getFd();
+}
+
+ssize_t SensorEventQueue::write(ASensorEvent const* events, size_t numEvents)
+{
+ ssize_t size = mSensorChannel->write(events, numEvents * sizeof(events[0]));
+ if (size >= 0) {
+ if (size % sizeof(events[0])) {
+ // partial write!!! should never happen.
+ return -EINVAL;
+ }
+ // returns number of events written
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
+ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents)
+{
+ ssize_t size = mSensorChannel->read(events, numEvents*sizeof(events[0]));
+ if (size >= 0) {
+ if (size % sizeof(events[0])) {
+ // partial write!!! should never happen.
+ return -EINVAL;
+ }
+ // returns number of events read
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor) const
+{
+ return mSensorEventConnection->enableDisable(sensor->getHandle(), true);
+}
+
+status_t SensorEventQueue::disableSensor(Sensor const* sensor) const
+{
+ return mSensorEventConnection->enableDisable(sensor->getHandle(), false);
+}
+
+status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const
+{
+ return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
new file mode 100644
index 0000000..cd89285
--- /dev/null
+++ b/libs/gui/SensorManager.cpp
@@ -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.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorManager)
+
+SensorManager::SensorManager()
+ : mSensorList(0)
+{
+ mSensors = mSensorServer->getSensorList();
+ // TODO: needs implementation
+}
+
+SensorManager::~SensorManager()
+{
+ // TODO: needs implementation
+}
+
+ssize_t SensorManager::getSensorList(Sensor** list) const
+{
+ *list = mSensorList;
+ return mSensors.size();
+}
+
+Sensor* SensorManager::getDefaultSensor(int type)
+{
+ // TODO: needs implementation
+ return mSensorList;
+}
+
+sp<SensorEventQueue> SensorManager::createEventQueue()
+{
+ sp<SensorEventQueue> result = new SensorEventQueue(
+ mSensorServer->createSensorEventConnection());
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 9146ba6..809f74d6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -5,6 +5,8 @@
LayerCache.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
+ Patch.cpp \
+ PatchCache.cpp \
Program.cpp \
TextureCache.cpp
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 586a05e..d4db782 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_LAYER_H
#define ANDROID_UI_LAYER_H
+#include <sys/types.h>
+
#include <GLES2/gl2.h>
#include <SkXfermode.h>
@@ -54,7 +56,7 @@
bool operator==(const LayerSize& rhs) const {
return width == rhs.width && height == rhs.height;
}
-};
+}; // struct LayerSize
/**
* A layer has dimensions and is backed by an OpenGL texture.
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 882ad83..bcda45e7 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -18,6 +18,8 @@
#include <GLES2/gl2.h>
+#include <utils/Log.h>
+
#include "LayerCache.h"
namespace android {
@@ -84,11 +86,55 @@
mCache.setOnEntryRemovedListener(NULL);
}
-Layer* LayerCache::get(LayerSize& size) {
+Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) {
Layer* layer = mCache.remove(size);
if (layer) {
+ LAYER_LOGD("Reusing layer");
+
mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+ } else {
+ LAYER_LOGD("Creating new layer");
+
+ layer = new Layer;
+ layer->blend = true;
+
+ // Generate the FBO and attach the texture
+ glGenFramebuffers(1, &layer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
+ // Generate the texture in which the FBO will draw
+ glGenTextures(1, &layer->texture);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+ // The FBO will not be scaled, so we can use lower quality filtering
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Bind texture to FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ layer->texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+
+ return NULL;
+ }
}
+
return layer;
}
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 519552d..2580551 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -23,6 +23,24 @@
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_LAYERS 0
+
+// Debug
+#if DEBUG_LAYERS
+ #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define LAYER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
public:
LayerCache(uint32_t maxByteSize);
@@ -32,16 +50,28 @@
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(LayerSize& bitmap, Layer*& texture);
+ void operator()(LayerSize& size, Layer*& layer);
/**
- * Returns the layer of specified dimensions, NULL if cannot be found.
+ * Returns the layer of specified dimensions. If not suitable layer
+ * can be found, a new one is created and returned. If creating a new
+ * layer fails, NULL is returned.
+ *
+ * When a layer is obtained from the cache, it is removed and the total
+ * size of the cache goes down.
+ *
+ * @param size The dimensions of the desired layer
+ * @param previousFbo The name of the FBO to bind to if creating a new
+ * layer fails
*/
- Layer* get(LayerSize& size);
+ Layer* get(LayerSize& size, GLuint previousFbo);
/**
* Adds the layer to the cache. The layer will not be added if there is
* not enough space available.
*
+ * @param size The dimensions of the layer
+ * @param layer The layer to add to the cache
+ *
* @return True if the layer was added, false otherwise.
*/
bool put(LayerSize& size, Layer* layer);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 40c80fa..ba5be03 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -88,6 +88,9 @@
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
+ /**
+ * Does not apply rotations!
+ */
void mapRect(Rect& r) const;
float getTranslateX();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ae7719c..decfecf 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -34,15 +34,13 @@
// Defines
///////////////////////////////////////////////////////////////////////////////
-// Debug
-#define DEBUG_LAYERS 0
-
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
#define DEFAULT_TEXTURE_CACHE_SIZE 20
#define DEFAULT_LAYER_CACHE_SIZE 10
+#define DEFAULT_PATCH_CACHE_SIZE 100
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
@@ -51,13 +49,6 @@
#define SV(x, y) { { x, y } }
#define FV(x, y, u, v) { { x, y }, { u, v } }
-// Debug
-#if DEBUG_LAYERS
- #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
-#else
- #define LAYER_LOGD(...)
-#endif
-
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
@@ -99,13 +90,21 @@
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
};
+static const GLint gTileModes[] = {
+ GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
+ GL_REPEAT, // == SkShader::kRepeat_Mode
+ GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
+};
+
///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
OpenGLRenderer::OpenGLRenderer():
+ mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
- mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)) {
+ mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
+ mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
LOGD("Create OpenGLRenderer");
char property[PROPERTY_VALUE_MAX];
@@ -123,8 +122,15 @@
LOGD(" Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE);
}
- mDrawColorShader = new DrawColorProgram;
- mDrawTextureShader = new DrawTextureProgram;
+ mDrawColorProgram = new DrawColorProgram;
+ mDrawTextureProgram = new DrawTextureProgram;
+ mCurrentProgram = mDrawTextureProgram;
+
+ mShader = kShaderNone;
+ mShaderTileX = SkShader::kClamp_TileMode;
+ mShaderTileY = SkShader::kClamp_TileMode;
+ mShaderMatrix = NULL;
+ mShaderBitmap = NULL;
memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
}
@@ -134,6 +140,7 @@
mTextureCache.clear();
mLayerCache.clear();
+ mPatchCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -143,9 +150,7 @@
void OpenGLRenderer::setViewport(int width, int height) {
glViewport(0, 0, width, height);
- mat4 ortho;
- ortho.loadOrtho(0, width, height, 0, -1, 1);
- ortho.copyTo(mOrthoMatrix);
+ mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
mWidth = width;
mHeight = height;
@@ -215,7 +220,7 @@
sp<Snapshot> previous = mSnapshot->previous;
if (restoreOrtho) {
- memcpy(mOrthoMatrix, current->orthoMatrix, sizeof(mOrthoMatrix));
+ mOrthoMatrix.load(current->orthoMatrix);
}
if (restoreLayer) {
@@ -250,9 +255,11 @@
const Rect& rect = layer->layer;
drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->texture, layer->alpha, layer->mode, layer->blend, true);
+ 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");
@@ -300,63 +307,19 @@
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
- LayerSize size(right - left, bottom - top);
- Layer* layer = mLayerCache.get(size);
-
- LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
+ LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
+ GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
+ LayerSize size(right - left, bottom - top);
+
+ Layer* layer = mLayerCache.get(size, previousFbo);
if (!layer) {
- LAYER_LOGD("Creating new layer");
-
- layer = new Layer;
- layer->blend = true;
-
- // Generate the FBO and attach the texture
- glGenFramebuffers(1, &layer->fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
- // Generate the texture in which the FBO will draw
- glGenTextures(1, &layer->texture);
- glBindTexture(GL_TEXTURE_2D, layer->texture);
-
- // The FBO will not be scaled, so we can use lower quality filtering
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
-
- const GLsizei width = right - left;
- const GLsizei height = bottom - top;
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // Bind texture to FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->texture, 0);
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
-
- GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
- glDeleteFramebuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
- delete layer;
-
- return false;
- }
- } else {
- LAYER_LOGD("Reusing layer");
- glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+ return false;
}
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
// Clear the FBO
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -376,18 +339,16 @@
saveSnapshot();
// TODO: This doesn't preserve other transformations (check Skia first)
mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
- mSnapshot->clipRect.set(left, top, right, bottom);
+ mSnapshot->setClip(left, top, right, bottom);
mSnapshot->height = bottom - top;
setScissorFromClip();
mSnapshot->flags = Snapshot::kFlagDirtyTransform | Snapshot::kFlagDirtyOrtho |
Snapshot::kFlagClipSet;
- memcpy(mSnapshot->orthoMatrix, mOrthoMatrix, sizeof(mOrthoMatrix));
+ mSnapshot->orthoMatrix.load(mOrthoMatrix);
// Change the ortho projection
- mat4 ortho;
- ortho.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
- ortho.copyTo(mOrthoMatrix);
+ mOrthoMatrix.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
return true;
}
@@ -457,7 +418,7 @@
}
bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom) {
- bool clipped = mSnapshot->clipRect.intersect(left, top, right, bottom);
+ bool clipped = mSnapshot->clip(left, top, right, bottom);
if (clipped) {
mSnapshot->flags |= Snapshot::kFlagClipSet;
setScissorFromClip();
@@ -470,14 +431,15 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
+ const float right = left + bitmap->width();
+ const float bottom = top + bitmap->height();
+
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
-
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- drawTextureRect(left, top, left + texture->width, top + texture->height, texture->id,
- alpha / 255.0f, mode, texture->blend, true);
+ drawTextureRect(left, top, right, bottom, texture, paint);
}
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
@@ -485,25 +447,23 @@
const mat4 transform(*matrix);
transform.mapRect(r);
+ if (quickReject(r.left, r.top, r.right, r.bottom)) {
+ return;
+ }
+
const Texture* texture = mTextureCache.get(bitmap);
-
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- drawTextureRect(r.left, r.top, r.right, r.bottom, texture->id,
- alpha / 255.0f, mode, texture->blend, true);
+ drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
}
void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
- const Texture* texture = mTextureCache.get(bitmap);
+ if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+ return;
+ }
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ const Texture* texture = mTextureCache.get(bitmap);
const float width = texture->width;
const float height = texture->height;
@@ -515,24 +475,44 @@
resetDrawTextureTexCoords(u1, v1, u2, v2);
- drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture->id,
- alpha / 255.0f, mode, texture->blend, true);
+ drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, const SkPaint* paint) {
- // TODO: Implement
- LOGD("Draw 9patch, paddingLeft=%d", patch->paddingLeft);
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
+ const Texture* texture = mTextureCache.get(bitmap);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ Patch* mesh = mPatchCache.get(patch);
+ mesh->updateVertices(bitmap, left, top, right, bottom,
+ &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
+
+ // Specify right and bottom as +1.0f from left/top to prevent scaling since the
+ // patch mesh already defines the final size
+ drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f,
+ mode, texture->blend, &mesh->vertices[0].position[0],
+ &mesh->vertices[0].texture[0], mesh->indices, mesh->indicesCount);
}
void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
- const Rect& clip = mSnapshot->clipRect;
- drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode);
+ const Rect& clip = mSnapshot->getMappedClip();
+ drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
}
void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
SkXfermode::Mode mode;
const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
@@ -544,104 +524,216 @@
// Skia draws using the color's alpha channel if < 255
// Otherwise, it uses the paint's alpha
int color = p->getColor();
- if (((color >> 24) & 0xFF) == 255) {
+ if (((color >> 24) & 0xff) == 255) {
color |= p->getAlpha() << 24;
}
drawColorRect(left, top, right, bottom, color, mode);
}
+///////////////////////////////////////////////////////////////////////////////
+// Shaders
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShader() {
+ mShader = OpenGLRenderer::kShaderNone;
+ mShaderBlend = false;
+ mShaderTileX = SkShader::kClamp_TileMode;
+ mShaderTileY = SkShader::kClamp_TileMode;
+}
+
+void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
+ SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
+ mShader = kShaderBitmap;
+ mShaderBlend = hasAlpha;
+ mShaderBitmap = bitmap;
+ mShaderTileX = tileX;
+ mShaderTileY = tileY;
+ mShaderMatrix = matrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drawing implementation
+///////////////////////////////////////////////////////////////////////////////
+
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode) {
- const int alpha = (color >> 24) & 0xFF;
- const bool blend = alpha < 255 || mode != SkXfermode::kSrcOver_Mode;
-
- const GLfloat a = alpha / 255.0f;
- const GLfloat r = ((color >> 16) & 0xFF) / 255.0f;
- const GLfloat g = ((color >> 8) & 0xFF) / 255.0f;
- const GLfloat b = ((color ) & 0xFF) / 255.0f;
-
- if (blend) {
- glEnable(GL_BLEND);
- glBlendFunc(gBlends[mode].src, gBlends[mode].dst);
+ int color, SkXfermode::Mode mode, bool ignoreTransform) {
+ // If a shader is set, preserve only the alpha
+ if (mShader != kShaderNone) {
+ color |= 0x00ffffff;
}
+ // Render using pre-multiplied alpha
+ const int alpha = (color >> 24) & 0xFF;
+ 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;
+
+ switch (mShader) {
+ case kShaderBitmap:
+ drawBitmapShader(left, top, right, bottom, a, mode);
+ return;
+ default:
+ break;
+ }
+
+ // Pre-multiplication happens when setting the shader color
+ chooseBlending(alpha < 255 || mShaderBlend, mode);
+
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
- mDrawColorShader->use(&mOrthoMatrix[0], &mModelView.data[0], &mSnapshot->transform.data[0]);
+ // TODO: Pick the program matching the current shader
+ sp<DrawColorProgram> program = mDrawColorProgram;
+ if (!useProgram(program)) {
+ const GLvoid* p = &gDrawColorVertices[0].position[0];
+ glVertexAttribPointer(program->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawColorVertexStride, p);
+ }
- const GLvoid* p = &gDrawColorVertices[0].position[0];
+ if (!ignoreTransform) {
+ program->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+ } else {
+ mat4 identity;
+ program->set(mOrthoMatrix, mModelView, identity);
+ }
- glEnableVertexAttribArray(mDrawColorShader->position);
- glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
- gDrawColorVertexStride, p);
- glVertexAttrib4f(mDrawColorShader->color, r, g, b, a);
+ glUniform4f(program->color, r, g, b, a);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
+}
- glDisableVertexAttribArray(mDrawColorShader->position);
+void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
+ float alpha, SkXfermode::Mode mode) {
+ const Texture* texture = mTextureCache.get(mShaderBitmap);
- if (blend) {
- glDisable(GL_BLEND);
+ const float width = texture->width;
+ const float height = texture->height;
+
+ // This could be done in the vertex shader but we have only 4 vertices
+ float u1 = 0.0f;
+ float v1 = 0.0f;
+ float u2 = right - left;
+ float v2 = bottom - top;
+
+ if (mShaderMatrix) {
+ SkMatrix inverse;
+ mShaderMatrix->invert(&inverse);
+ mat4 m(inverse);
+ Rect r(u1, v1, u2, v2);
+ m.mapRect(r);
+
+ u1 = r.left;
+ u2 = r.right;
+ v1 = r.top;
+ v2 = r.bottom;
}
+
+ u1 /= width;
+ u2 /= width;
+ v1 /= height;
+ v2 /= height;
+
+ resetDrawTextureTexCoords(u1, v1, u2, v2);
+
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
- GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied) {
+ const Texture* texture, const SkPaint* paint) {
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+}
+
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
+ drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+}
+
+void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
- mDrawTextureShader->use(&mOrthoMatrix[0], &mModelView.data[0], &mSnapshot->transform.data[0]);
+ useProgram(mDrawTextureProgram);
+ mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
- if (blend || alpha < 1.0f || mode != SkXfermode::kSrcOver_Mode) {
+ chooseBlending(blend || alpha < 1.0f, mode);
+
+ // TODO: Only bind/set parameters when needed
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]);
+
+ // Always premultiplied
+ glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
+
+ glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawTextureVertexStride, vertices);
+ glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
+ gDrawTextureVertexStride, texCoords);
+
+ if (!indices) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+ } else {
+ glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
+ // In theory we should not blend if the mode is Src, but it's rare enough
+ // that it's not worth it
+ blend = blend || mode != SkXfermode::kSrcOver_Mode;
+ if (blend) {
+ if (!mBlend) {
+ glEnable(GL_BLEND);
+ }
+
GLenum sourceMode = gBlends[mode].src;
+ GLenum destMode = gBlends[mode].dst;
if (!isPremultiplied && sourceMode == GL_ONE) {
sourceMode = GL_SRC_ALPHA;
}
- glEnable(GL_BLEND);
- glBlendFunc(sourceMode, gBlends[mode].dst);
+ if (sourceMode != mLastSrcMode || destMode != mLastDstMode) {
+ glBlendFunc(sourceMode, destMode);
+ mLastSrcMode = sourceMode;
+ mLastDstMode = destMode;
+ }
+ } else if (mBlend) {
+ glDisable(GL_BLEND);
}
+ mBlend = blend;
+}
- glBindTexture(GL_TEXTURE_2D, texture);
-
- // TODO handle tiling and filtering here
-
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mDrawTextureShader->sampler, 0);
-
- const GLvoid* p = &mDrawTextureVertices[0].position[0];
- const GLvoid* t = &mDrawTextureVertices[0].texture[0];
-
- glEnableVertexAttribArray(mDrawTextureShader->position);
- glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
- gDrawTextureVertexStride, p);
-
- glEnableVertexAttribArray(mDrawTextureShader->texCoords);
- glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
- gDrawTextureVertexStride, t);
-
- glVertexAttrib4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
-
- glDisableVertexAttribArray(mDrawTextureShader->position);
- glDisableVertexAttribArray(mDrawTextureShader->texCoords);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glDisable(GL_BLEND);
+bool OpenGLRenderer::useProgram(const sp<Program>& program) {
+ if (!program->isInUse()) {
+ mCurrentProgram->remove();
+ program->use();
+ mCurrentProgram = program;
+ return false;
+ }
+ return true;
}
void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
- mDrawTextureVertices[0].texture[0] = u1;
- mDrawTextureVertices[0].texture[1] = v1;
- mDrawTextureVertices[1].texture[0] = u2;
- mDrawTextureVertices[1].texture[1] = v1;
- mDrawTextureVertices[2].texture[0] = u1;
- mDrawTextureVertices[2].texture[1] = v2;
- mDrawTextureVertices[3].texture[0] = u2;
- mDrawTextureVertices[3].texture[1] = v2;
+ TextureVertex* v = &mDrawTextureVertices[0];
+ TextureVertex::setUV(v++, u1, v1);
+ TextureVertex::setUV(v++, u2, v1);
+ TextureVertex::setUV(v++, u1, v2);
+ TextureVertex::setUV(v++, u2, v2);
}
void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 9da4b8e..2a96432 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -23,6 +23,7 @@
#include <SkBitmap.h>
#include <SkMatrix.h>
#include <SkPaint.h>
+#include <SkShader.h>
#include <SkXfermode.h>
#include <utils/RefBase.h>
@@ -32,10 +33,10 @@
#include "Program.h"
#include "Rect.h"
#include "Snapshot.h"
-#include "Texture.h"
-#include "Layer.h"
#include "TextureCache.h"
#include "LayerCache.h"
+#include "PatchCache.h"
+#include "Vertex.h"
namespace android {
namespace uirenderer {
@@ -45,22 +46,6 @@
///////////////////////////////////////////////////////////////////////////////
/**
- * Simple structure to describe a vertex with a position.
- * This is used to draw filled rectangles without a texture.
- */
-struct SimpleVertex {
- float position[2];
-}; // struct SimpleVertex
-
-/**
- * Simple structure to describe a vertex with a position and a texture.
- */
-struct TextureVertex {
- float position[2];
- float texture[2];
-}; // struct TextureVertex
-
-/**
* Structure mapping Skia xfermodes to OpenGL blending factors.
*/
struct Blender {
@@ -114,8 +99,23 @@
void drawColor(int color, SkXfermode::Mode mode);
void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+ void resetShader();
+ void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY,
+ SkMatrix* matrix, bool hasAlpha);
+
private:
/**
+ * Type of Skia shader in use.
+ */
+ enum ShaderType {
+ kShaderNone,
+ kShaderBitmap,
+ kShaderLinearGradient,
+ kShaderCircularGradient,
+ kShaderSweepGradient
+ };
+
+ /**
* Saves the current state of the renderer as a new snapshot.
* The new snapshot is saved in mSnapshot and the previous snapshot
* is linked from mSnapshot->previous.
@@ -176,9 +176,10 @@
* @param bottom The bottom coordinate of the rectangle
* @param color The rectangle's ARGB color, defined as a packed 32 bits word
* @param mode The Skia xfermode to use
+ * @param ignoreTransform True if the current transform should be ignored
*/
void drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode);
+ int color, SkXfermode::Mode mode, bool ignoreTransform = false);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
@@ -192,10 +193,57 @@
* @param alpha An additional translucency parameter, between 0.0f and 1.0f
* @param mode The blending mode
* @param blend True if the texture contains an alpha channel
- * @param isPremultiplied Indicates whether the texture has premultiplied alpha
*/
void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
- float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = false);
+ float alpha, SkXfermode::Mode mode, bool blend);
+
+ /**
+ * Draws a textured rectangle with the specified texture. The specified coordinates
+ * are transformed by the current snapshot's transform matrix.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param texture The texture to use
+ * @param paint The paint containing the alpha, blending mode, etc.
+ */
+ void drawTextureRect(float left, float top, float right, float bottom,
+ const Texture* texture, const SkPaint* paint);
+
+ /**
+ * Draws a textured mesh with the specified texture. If the indices are omitted, the
+ * mesh is drawn as a simple quad.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param texture The texture name to map onto the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ * @param blend True if the texture contains an alpha channel
+ * @param vertices The vertices that define the mesh
+ * @param texCoords The texture coordinates of each vertex
+ * @param indices The indices of the vertices, can be NULL
+ * @param elementsCount The number of elements in the mesh, required by indices
+ */
+ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
+ float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
+
+ /**
+ * Fills the specified rectangle with the currently set bitmap shader.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ */
+ void drawBitmapShader(float left, float top, float right, float bottom, float alpha,
+ SkXfermode::Mode mode);
/**
* Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
@@ -220,11 +268,29 @@
*/
inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+ /**
+ * Enable or disable blending as necessary. This function sets the appropriate
+ * blend function based on the specified xfermode.
+ */
+ inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);
+
+ /**
+ * Use the specified program with the current GL context. If the program is already
+ * in use, it will not be bound again. If it is not in use, the current program is
+ * marked unused and the specified program becomes used and becomes the new
+ * current program.
+ *
+ * @param program The program to use
+ *
+ * @return true If the specified program was already in use, false otherwise.
+ */
+ inline bool useProgram(const sp<Program>& program);
+
// Dimensions of the drawing surface
int mWidth, mHeight;
// Matrix used for ortho projection in shaders
- float mOrthoMatrix[16];
+ mat4 mOrthoMatrix;
// Model-view matrix used to position/size objects
mat4 mModelView;
@@ -237,15 +303,30 @@
sp<Snapshot> mSnapshot;
// Shaders
- sp<DrawColorProgram> mDrawColorShader;
- sp<DrawTextureProgram> mDrawTextureShader;
+ sp<Program> mCurrentProgram;
+ sp<DrawColorProgram> mDrawColorProgram;
+ sp<DrawTextureProgram> mDrawTextureProgram;
// Used to draw textured quads
TextureVertex mDrawTextureVertices[4];
- // Used to cache all drawBitmap textures
+ // Last known blend state
+ bool mBlend;
+ GLenum mLastSrcMode;
+ GLenum mLastDstMode;
+
+ // Skia shader
+ ShaderType mShader;
+ bool mShaderBlend;
+ SkBitmap* mShaderBitmap;
+ SkShader::TileMode mShaderTileX;
+ SkShader::TileMode mShaderTileY;
+ SkMatrix* mShaderMatrix;
+
+ // Various caches
TextureCache mTextureCache;
LayerCache mLayerCache;
+ PatchCache mPatchCache;
}; // class OpenGLRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
new file mode 100644
index 0000000..4b6bb37
--- /dev/null
+++ b/libs/hwui/Patch.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 <cstring>
+
+#include <utils/Log.h>
+
+#include "Patch.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+Patch::Patch(const uint32_t xCount, const uint32_t yCount):
+ xCount(xCount + 2), yCount(yCount + 2) {
+ verticesCount = (xCount + 2) * (yCount + 2);
+ vertices = new TextureVertex[verticesCount];
+
+ // 2 triangles per patch, 3 vertices per triangle
+ indicesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
+ indices = new uint16_t[indicesCount];
+
+ const uint32_t xNum = xCount + 1;
+ const uint32_t yNum = yCount + 1;
+
+ uint16_t* startIndices = indices;
+ uint32_t n = 0;
+ for (uint32_t y = 0; y < yNum; y++) {
+ for (uint32_t x = 0; x < xNum; x++) {
+ *startIndices++ = n;
+ *startIndices++ = n + 1;
+ *startIndices++ = n + xNum + 2;
+
+ *startIndices++ = n;
+ *startIndices++ = n + xNum + 2;
+ *startIndices++ = n + xNum + 1;
+
+ n += 1;
+ }
+ n += 1;
+ }
+}
+
+Patch::~Patch() {
+ delete indices;
+ delete vertices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertices management
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::updateVertices(const SkBitmap* bitmap, float left, float top, float right,
+ float bottom, const int32_t* xDivs, const int32_t* yDivs, const uint32_t width,
+ const uint32_t height) {
+ const uint32_t xStretchCount = (width + 1) >> 1;
+ const uint32_t yStretchCount = (height + 1) >> 1;
+
+ float xStretch = 0;
+ float yStretch = 0;
+ float xStretchTex = 0;
+ float yStretchTex = 0;
+
+ const float meshWidth = right - left;
+
+ const float bitmapWidth = float(bitmap->width());
+ const float bitmapHeight = float(bitmap->height());
+
+ if (xStretchCount > 0) {
+ uint32_t stretchSize = 0;
+ for (uint32_t i = 1; i < width; i += 2) {
+ stretchSize += xDivs[i] - xDivs[i - 1];
+ }
+ xStretchTex = (stretchSize / bitmapWidth) / xStretchCount;
+ const float fixed = bitmapWidth - stretchSize;
+ xStretch = (right - left - fixed) / xStretchCount;
+ }
+
+ if (yStretchCount > 0) {
+ uint32_t stretchSize = 0;
+ for (uint32_t i = 1; i < height; i += 2) {
+ stretchSize += yDivs[i] - yDivs[i - 1];
+ }
+ yStretchTex = (stretchSize / bitmapHeight) / yStretchCount;
+ const float fixed = bitmapHeight - stretchSize;
+ yStretch = (bottom - top - fixed) / yStretchCount;
+ }
+
+ float vy = 0.0f;
+ float ty = 0.0f;
+ TextureVertex* vertex = vertices;
+
+ generateVertices(vertex, 0.0f, 0.0f, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+ vertex += width + 2;
+
+ for (uint32_t y = 0; y < height; y++) {
+ if (y & 1) {
+ vy += yStretch;
+ ty += yStretchTex;
+ } else {
+ const float step = float(yDivs[y]);
+ vy += step;
+ ty += step / bitmapHeight;
+ }
+ generateVertices(vertex, vy, ty, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+ vertex += width + 2;
+ }
+
+ generateVertices(vertex, bottom - top, 1.0f, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+}
+
+inline void Patch::generateVertices(TextureVertex* vertex, float y, float v,
+ const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
+ float width, float widthTex) {
+ float vx = 0.0f;
+ float tx = 0.0f;
+
+ TextureVertex::set(vertex, vx, y, tx, v);
+ vertex++;
+
+ for (uint32_t x = 0; x < xCount; x++) {
+ if (x & 1) {
+ vx += xStretch;
+ tx += xStretchTex;
+ } else {
+ const float step = float(xDivs[x]);
+ vx += step;
+ tx += step / widthTex;
+ }
+
+ TextureVertex::set(vertex, vx, y, tx, v);
+ vertex++;
+ }
+
+ TextureVertex::set(vertex, width, y, 1.0f, v);
+ vertex++;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Debug tools
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::dump() {
+ LOGD("Vertices [");
+ for (uint32_t y = 0; y < yCount; y++) {
+ char buffer[512];
+ buffer[0] = '\0';
+ uint32_t offset = 0;
+ for (uint32_t x = 0; x < xCount; x++) {
+ TextureVertex* v = &vertices[y * xCount + x];
+ offset += sprintf(&buffer[offset], " (%.4f,%.4f)-(%.4f,%.4f)",
+ v->position[0], v->position[1], v->texture[0], v->texture[1]);
+ }
+ LOGD(" [%s ]", buffer);
+ }
+ LOGD("]\nIndices [ ");
+ char buffer[4096];
+ buffer[0] = '\0';
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < indicesCount; i++) {
+ offset += sprintf(&buffer[offset], "%d ", indices[i]);
+ }
+ LOGD(" %s\n]", buffer);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
new file mode 100644
index 0000000..5d3ad03
--- /dev/null
+++ b/libs/hwui/Patch.h
@@ -0,0 +1,85 @@
+/*
+ * 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_PATCH_H
+#define ANDROID_UI_PATCH_H
+
+#include <sys/types.h>
+
+#include <SkBitmap.h>
+
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Description of a patch.
+ */
+struct PatchDescription {
+ PatchDescription(): xCount(0), yCount(0) { }
+ PatchDescription(const uint32_t xCount, const uint32_t yCount):
+ xCount(xCount), yCount(yCount) { }
+ PatchDescription(const PatchDescription& description):
+ xCount(description.xCount), yCount(description.yCount) { }
+
+ uint32_t xCount;
+ uint32_t yCount;
+
+ bool operator<(const PatchDescription& rhs) const {
+ if (xCount == rhs.xCount) {
+ return yCount < rhs.yCount;
+ }
+ return xCount < rhs.xCount;
+ }
+
+ bool operator==(const PatchDescription& rhs) const {
+ return xCount == rhs.xCount && yCount == rhs.yCount;
+ }
+}; // struct PatchDescription
+
+/**
+ * An OpenGL patch. This contains an array of vertices and an array of
+ * indices to render the vertices.
+ */
+struct Patch {
+ Patch(const uint32_t xCount, const uint32_t yCount);
+ ~Patch();
+
+ void updateVertices(const SkBitmap* bitmap, float left, float top, float right, float bottom,
+ const int32_t* xDivs, const int32_t* yDivs,
+ const uint32_t width, const uint32_t height);
+ void dump();
+
+ uint32_t xCount;
+ uint32_t yCount;
+
+ uint16_t* indices;
+ uint32_t indicesCount;
+
+ TextureVertex* vertices;
+ uint32_t verticesCount;
+
+private:
+ static inline void generateVertices(TextureVertex* vertex, float y, float v,
+ const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
+ float width, float widthTex);
+}; // struct Patch
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PATCH_H
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
new file mode 100644
index 0000000..694e3fd
--- /dev/null
+++ b/libs/hwui/PatchCache.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <utils/Log.h>
+#include <utils/ResourceTypes.h>
+
+#include "PatchCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) {
+}
+
+PatchCache::~PatchCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchCache::operator()(PatchDescription& description, Patch*& mesh) {
+ if (mesh) delete mesh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchCache::clear() {
+ mCache.setOnEntryRemovedListener(this);
+ mCache.clear();
+ mCache.setOnEntryRemovedListener(NULL);
+}
+
+Patch* PatchCache::get(const Res_png_9patch* patch) {
+ const uint32_t width = patch->numXDivs;
+ const uint32_t height = patch->numYDivs;
+ const PatchDescription description(width, height);
+
+ Patch* mesh = mCache.get(description);
+ if (!mesh) {
+ PATCH_LOGD("Creating new patch mesh, w=%d h=%d", width, height);
+ mesh = new Patch(width, height);
+ mCache.put(description, mesh);
+ }
+
+ return mesh;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
new file mode 100644
index 0000000..de95087
--- /dev/null
+++ b/libs/hwui/PatchCache.h
@@ -0,0 +1,65 @@
+/*
+ * 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_PATCH_CACHE_H
+#define ANDROID_UI_PATCH_CACHE_H
+
+#include "Patch.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PATCHES 0
+
+// Debug
+#if DEBUG_PATCHES
+ #define PATCH_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define PATCH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> {
+public:
+ PatchCache(uint32_t maxCapacity);
+ ~PatchCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(PatchDescription& description, Patch*& mesh);
+
+ Patch* get(const Res_png_9patch* patch);
+ void clear();
+
+private:
+ GenerationCache<PatchDescription, Patch*> mCache;
+}; // class PatchCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PATCH_CACHE_H
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 2acddfc..609b28a 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -59,6 +59,8 @@
}
glDeleteProgram(id);
}
+
+ mUse = false;
}
Program::~Program() {
@@ -69,6 +71,11 @@
void Program::use() {
glUseProgram(id);
+ mUse = true;
+}
+
+void Program::remove() {
+ mUse = false;
}
int Program::addAttrib(const char* name) {
@@ -126,18 +133,27 @@
void DrawColorProgram::getAttribsAndUniforms() {
position = addAttrib("position");
- color = addAttrib("color");
- projection = addUniform("projection");
- modelView = addUniform("modelView");
+ color = addUniform("color");
transform = addUniform("transform");
}
-void DrawColorProgram::use(const GLfloat* projectionMatrix, const GLfloat* modelViewMatrix,
- const GLfloat* transformMatrix) {
+void DrawColorProgram::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+ const mat4& transformMatrix) {
+ mat4 t(projectionMatrix);
+ t.multiply(transformMatrix);
+ t.multiply(modelViewMatrix);
+
+ glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
+}
+
+void DrawColorProgram::use() {
Program::use();
- glUniformMatrix4fv(projection, 1, GL_FALSE, projectionMatrix);
- glUniformMatrix4fv(modelView, 1, GL_FALSE, modelViewMatrix);
- glUniformMatrix4fv(transform, 1, GL_FALSE, transformMatrix);
+ glEnableVertexAttribArray(position);
+}
+
+void DrawColorProgram::remove() {
+ Program::remove();
+ glDisableVertexAttribArray(position);
}
///////////////////////////////////////////////////////////////////////////////
@@ -150,5 +166,17 @@
sampler = addUniform("sampler");
}
+void DrawTextureProgram::use() {
+ DrawColorProgram::use();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(sampler, 0);
+ glEnableVertexAttribArray(texCoords);
+}
+
+void DrawTextureProgram::remove() {
+ DrawColorProgram::remove();
+ glDisableVertexAttribArray(texCoords);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index ee16a92..d90bcaf 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -23,6 +23,8 @@
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
+#include "Matrix.h"
+
namespace android {
namespace uirenderer {
@@ -37,12 +39,26 @@
* shaders sources.
*/
Program(const char* vertex, const char* fragment);
- ~Program();
+ virtual ~Program();
/**
* Binds this program to the GL context.
*/
- void use();
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Indicates whether this program is currently in use with
+ * the GL context.
+ */
+ inline bool isInUse() const {
+ return mUse;
+ }
protected:
/**
@@ -85,6 +101,8 @@
// Keeps track of attributes and uniforms slots
KeyedVector<const char*, int> attributes;
KeyedVector<const char*, int> uniforms;
+
+ bool mUse;
}; // class Program
/**
@@ -107,26 +125,29 @@
* Binds the program with the specified projection, modelView and
* transform matrices.
*/
- void use(const GLfloat* projectionMatrix, const GLfloat* modelViewMatrix,
- const GLfloat* transformMatrix);
+ void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+ const mat4& transformMatrix);
+
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
/**
* Name of the position attribute.
*/
int position;
- /**
- * Name of the color attribute.
- */
- int color;
/**
- * Name of the projection uniform.
+ * Name of the color uniform.
*/
- int projection;
- /**
- * Name of the modelView uniform.
- */
- int modelView;
+ int color;
/**
* Name of the transform uniform.
*/
@@ -146,7 +167,25 @@
public:
DrawTextureProgram();
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Name of the texture sampler uniform.
+ */
int sampler;
+
+ /**
+ * Name of the texture coordinates attribute.
+ */
int texCoords;
};
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 17ca440..32fee32 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -56,6 +56,8 @@
previous(s),
layer(NULL),
fbo(s->fbo) {
+ mappedClip.set(s->clipRect);
+ transform.mapRect(mappedClip);
}
/**
@@ -87,12 +89,32 @@
* Returns the current clip region mapped by the current transform.
*/
const Rect& getMappedClip() {
+ return mappedClip;
+ }
+
+ /**
+ * Intersects the current clip with the new clip rectangle.
+ */
+ bool clip(float left, float top, float right, float bottom) {
+ bool clipped = clipRect.intersect(left, top, right, bottom);
if (flags & kFlagDirtyTransform) {
flags &= ~kFlagDirtyTransform;
mappedClip.set(clipRect);
transform.mapRect(mappedClip);
}
- return mappedClip;
+ return clipped;
+ }
+
+ /**
+ * Sets the current clip.
+ */
+ void setClip(float left, float top, float right, float bottom) {
+ clipRect.set(left, top, right, bottom);
+ if (flags & kFlagDirtyTransform) {
+ flags &= ~kFlagDirtyTransform;
+ mappedClip.set(clipRect);
+ transform.mapRect(mappedClip);
+ }
}
/**
@@ -130,7 +152,7 @@
/**
* Contains the previous ortho matrix.
*/
- float orthoMatrix[16];
+ mat4 orthoMatrix;
private:
// Clipping rectangle mapped with the transform
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 612f04e..4977f46 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -133,7 +133,7 @@
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
break;
case SkBitmap::kARGB_8888_Config:
- texture->blend = true;
+ texture->blend = !bitmap->isOpaque();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
break;
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
new file mode 100644
index 0000000..ffd0633
--- /dev/null
+++ b/libs/hwui/Vertex.h
@@ -0,0 +1,54 @@
+/*
+ * 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_VERTEX_H
+#define ANDROID_UI_VERTEX_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Simple structure to describe a vertex with a position.
+ * This is used to draw filled rectangles without a texture.
+ */
+struct SimpleVertex {
+ float position[2];
+}; // struct SimpleVertex
+
+/**
+ * Simple structure to describe a vertex with a position and a texture.
+ */
+struct TextureVertex {
+ float position[2];
+ float texture[2];
+
+ static inline void set(TextureVertex* vertex, float x, float y, float u, float v) {
+ vertex[0].position[0] = x;
+ vertex[0].position[1] = y;
+ vertex[0].texture[0] = u;
+ vertex[0].texture[1] = v;
+ }
+
+ static inline void setUV(TextureVertex* vertex, float u, float v) {
+ vertex[0].texture[0] = u;
+ vertex[0].texture[1] = v;
+ }
+}; // struct TextureVertex
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_VERTEX_H
diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag
index e84c47b..1d6cb8b 100644
--- a/libs/hwui/shaders/drawColor.frag
+++ b/libs/hwui/shaders/drawColor.frag
@@ -1,9 +1,11 @@
SHADER_SOURCE(gDrawColorFragmentShader,
-varying lowp vec4 outColor;
+precision mediump float;
+
+uniform vec4 color;
void main(void) {
- gl_FragColor = outColor;
+ gl_FragColor = color;
}
);
diff --git a/libs/hwui/shaders/drawColor.vert b/libs/hwui/shaders/drawColor.vert
index cef6e49..20e2636 100644
--- a/libs/hwui/shaders/drawColor.vert
+++ b/libs/hwui/shaders/drawColor.vert
@@ -1,17 +1,11 @@
SHADER_SOURCE(gDrawColorVertexShader,
attribute vec4 position;
-attribute vec4 color;
-uniform mat4 projection;
-uniform mat4 modelView;
uniform mat4 transform;
-varying vec4 outColor;
-
void main(void) {
- outColor = color;
- gl_Position = projection * transform * modelView * position;
+ gl_Position = transform * position;
}
);
diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag
index 5bd420e..8390d8e 100644
--- a/libs/hwui/shaders/drawTexture.frag
+++ b/libs/hwui/shaders/drawTexture.frag
@@ -1,12 +1,14 @@
SHADER_SOURCE(gDrawTextureFragmentShader,
-varying lowp vec4 outColor;
-varying mediump vec2 outTexCoords;
+precision mediump float;
+varying vec2 outTexCoords;
+
+uniform vec4 color;
uniform sampler2D sampler;
void main(void) {
- gl_FragColor = texture2D(sampler, outTexCoords) * outColor;
+ gl_FragColor = texture2D(sampler, outTexCoords) * color;
}
);
diff --git a/libs/hwui/shaders/drawTexture.vert b/libs/hwui/shaders/drawTexture.vert
index 310a812..240aebf 100644
--- a/libs/hwui/shaders/drawTexture.vert
+++ b/libs/hwui/shaders/drawTexture.vert
@@ -2,19 +2,14 @@
attribute vec4 position;
attribute vec2 texCoords;
-attribute vec4 color;
-uniform mat4 projection;
-uniform mat4 modelView;
uniform mat4 transform;
-varying vec4 outColor;
varying vec2 outTexCoords;
void main(void) {
- outColor = color;
outTexCoords = texCoords;
- gl_Position = projection * transform * modelView * position;
+ gl_Position = transform * position;
}
);
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 8e6b5c6..6636fef 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -244,11 +244,30 @@
RS_A3D_CLASS_ID_SCRIPT_C
};
+enum RsCullMode {
+ RS_CULL_BACK,
+ RS_CULL_FRONT,
+ RS_CULL_NONE
+};
+
typedef struct {
RsA3DClassID classID;
const char* objectName;
} RsFileIndexEntry;
+// Script to Script
+typedef struct {
+ uint32_t xStart;
+ uint32_t xEnd;
+ uint32_t yStart;
+ uint32_t yEnd;
+ uint32_t zStart;
+ uint32_t zEnd;
+ uint32_t arrayStart;
+ uint32_t arrayEnd;
+
+} RsScriptCall;
+
#ifndef NO_RS_FUNCS
#include "rsgApiFuncDecl.h"
#endif
diff --git a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs
new file mode 100644
index 0000000..7b0e6bc
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs
@@ -0,0 +1,43 @@
+#pragma version(1)
+
+#include "../../../../scriptc/rs_types.rsh"
+#include "../../../../scriptc/rs_math.rsh"
+
+#include "ip.rsh"
+
+uchar4 * ScratchPixel;
+
+#pragma rs export_var(ScratchPixel)
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+ uchar4 *output = (uchar4 *)v_out;
+ const uchar4 *input = (uchar4 *)v_in;
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+
+ float4 blurredPixel = 0;
+ float4 currentPixel = 0;
+ 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);
+
+ float weight = fs->gaussian[r + fs->radius];
+ currentPixel.x = (float)(input[validW].x);
+ currentPixel.y = (float)(input[validW].y);
+ currentPixel.z = (float)(input[validW].z);
+ //currentPixel.w = (float)(input->a);
+
+ blurredPixel += currentPixel * weight;
+ }
+
+ output->x = (uint8_t)blurredPixel.x;
+ output->y = (uint8_t)blurredPixel.y;
+ output->z = (uint8_t)blurredPixel.z;
+}
diff --git a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc
new file mode 100644
index 0000000..c9ba5d9
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/raw/ip.rsh b/libs/rs/java/ImageProcessing/res/raw/ip.rsh
new file mode 100644
index 0000000..4073304
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/ip.rsh
@@ -0,0 +1,15 @@
+#pragma rs java_package_name(com.android.rs.image)
+
+#define MAX_RADIUS 25
+
+typedef struct {
+ float *gaussian; //[MAX_RADIUS * 2 + 1];
+ rs_matrix3x3 colorMat;
+
+ int height;
+ int width;
+ int radius;
+
+} FilterStruct;
+
+
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold.rs b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
index 9585e92..ecbfac4 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold.rs
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
@@ -2,12 +2,8 @@
#include "../../../../scriptc/rs_types.rsh"
#include "../../../../scriptc/rs_math.rsh"
-#include "../../../../scriptc/rs_graphics.rsh"
-#pragma rs java_package_name(com.android.rs.image)
-
-
-#define MAX_RADIUS 25
+#include "ip.rsh"
int height;
int width;
@@ -28,11 +24,15 @@
static float inWMinInB;
static float outWMinOutB;
static float overInWMinInB;
-//static float3 gammaV;
+static FilterStruct filterStruct;
-#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel, inBlack, outBlack, inWhite, outWhite, gamma, saturation, InPixel, OutPixel, ScratchPixel)
+#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(filter, filterBenchmark);
+rs_script vBlurScript;
+rs_script hBlurScript;
+
+
// Store our coefficients here
static float gaussian[MAX_RADIUS * 2 + 1];
static rs_matrix3x3 colorMat;
@@ -145,48 +145,6 @@
rsSendToClient(&count, 1, 4, 0);
}
-static void horizontalBlur() {
- float4 blurredPixel = 0;
- float4 currentPixel = 0;
- // Horizontal blur
- int w, h, r;
- for(h = 0; h < height; h ++) {
- uchar4 *input = InPixel + h*width;
- uchar4 *output = ScratchPixel + h*width;
-
- for(w = 0; w < width; w ++) {
- blurredPixel = 0;
-
- for(r = -radius; r <= radius; r ++) {
- // Stepping left and right away from the pixel
- int validW = w + r;
- // Clamp to zero and width max() isn't exposed for ints yet
- if(validW < 0) {
- validW = 0;
- }
- if(validW > width - 1) {
- validW = width - 1;
- }
- //int validW = rsClamp(w + r, 0, width - 1);
-
- float weight = gaussian[r + radius];
- currentPixel.x = (float)(input[validW].x);
- currentPixel.y = (float)(input[validW].y);
- currentPixel.z = (float)(input[validW].z);
- //currentPixel.w = (float)(input->a);
-
- blurredPixel += currentPixel*weight;
- }
-
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
- //output->a = (uint8_t)blurredPixel.w;
- output++;
- }
- }
-}
-
static void horizontalBlurLevels() {
float4 blurredPixel = 0;
float4 currentPixel = 0;
@@ -232,52 +190,11 @@
}
}
-static void verticalBlur() {
- float4 blurredPixel = 0;
- float4 currentPixel = 0;
- // Vertical blur
- int w, h, r;
- for(h = 0; h < height; h ++) {
- uchar4 *output = OutPixel + h*width;
-
- for(w = 0; w < width; w ++) {
-
- blurredPixel = 0;
- for(r = -radius; r <= radius; r ++) {
-#if 1
- int validH = h + r;
- // Clamp to zero and width
- if(validH < 0) {
- validH = 0;
- }
- if(validH > height - 1) {
- validH = height - 1;
- }
-
- uchar4 *input = ScratchPixel + validH*width + w;
-
- float weight = gaussian[r + radius];
-
- currentPixel.x = (float)(input->x);
- currentPixel.y = (float)(input->y);
- currentPixel.z = (float)(input->z);
-
- blurredPixel.xyz += currentPixel.xyz * weight;
-#else
- int validH = rsClamp(h + r, 0, height - 1);
- uchar4 *input = ScratchPixel + validH*width + w;
- blurredPixel.xyz += convert_float3(input->xyz) * gaussian[r + radius];
-#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;
- //output->a = (uint8_t)blurredPixel.w;
- output++;
- }
- }
+static void initStructs() {
+ filterStruct.gaussian = gaussian;
+ filterStruct.width = width;
+ filterStruct.height = height;
+ filterStruct.radius = radius;
}
void filter() {
@@ -285,6 +202,8 @@
RS_DEBUG(width);
RS_DEBUG(radius);
+ initStructs();
+
computeColorMatrix();
if(radius == 0) {
@@ -295,18 +214,30 @@
computeGaussianWeights();
horizontalBlurLevels();
- verticalBlur();
+
+ rsForEach(vBlurScript,
+ rsGetAllocation(InPixel),
+ rsGetAllocation(OutPixel),
+ &filterStruct);
int count = 0;
rsSendToClient(&count, 1, 4, 0);
}
void filterBenchmark() {
+ initStructs();
computeGaussianWeights();
- horizontalBlur();
- verticalBlur();
+ rsForEach(hBlurScript,
+ rsGetAllocation(InPixel),
+ rsGetAllocation(OutPixel),
+ &filterStruct);
+
+ rsForEach(vBlurScript,
+ rsGetAllocation(InPixel),
+ rsGetAllocation(OutPixel),
+ &filterStruct);
int count = 0;
rsSendToClient(&count, 1, 4, 0);
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
index 95dcd8d..8f37fdc 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs b/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs
new file mode 100644
index 0000000..846f515
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs
@@ -0,0 +1,51 @@
+#pragma version(1)
+
+#include "../../../../scriptc/rs_types.rsh"
+#include "../../../../scriptc/rs_math.rsh"
+
+#include "ip.rsh"
+
+uchar4 * ScratchPixel;
+
+#pragma rs export_var(ScratchPixel)
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+ uchar4 *output = (uchar4 *)v_out;
+ const uchar4 *input = (uchar4 *)v_in;
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+
+ float4 blurredPixel = 0;
+ float4 currentPixel = 0;
+ 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;
+ }
+
+ uchar4 *input = ScratchPixel + validH * fs->width + x;
+
+ float weight = fs->gaussian[r + fs->radius];
+
+ currentPixel.x = (float)(input->x);
+ currentPixel.y = (float)(input->y);
+ currentPixel.z = (float)(input->z);
+
+ blurredPixel.xyz += currentPixel.xyz * weight;
+#else
+ int validH = rsClamp(y + r, 0, height - 1);
+ uchar4 *input = ScratchPixel + validH * width + x;
+ blurredPixel.xyz += convert_float3(input->xyz) * gaussian[r + fs->radius];
+#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;
+}
+
diff --git a/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc b/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc
new file mode 100644
index 0000000..af1cd8e
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc
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 7bf6596..21c3d74 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
@@ -42,6 +42,8 @@
private Bitmap mBitmapOut;
private Bitmap mBitmapScratch;
private ScriptC_Threshold mScript;
+ private ScriptC_Vertical_blur mScriptVBlur;
+ private ScriptC_Horizontal_blur mScriptHBlur;
private int mRadius = 0;
private SeekBar mRadiusSeekBar;
@@ -373,6 +375,12 @@
mOutPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapOut);
mScratchPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapScratch);
+ mScriptVBlur = new ScriptC_Vertical_blur(mRS, getResources(), R.raw.vertical_blur_bc, false);
+ mScriptVBlur.bind_ScratchPixel(mScratchPixelsAllocation);
+
+ mScriptHBlur = new ScriptC_Horizontal_blur(mRS, getResources(), R.raw.horizontal_blur_bc, false);
+ mScriptHBlur.bind_ScratchPixel(mScratchPixelsAllocation);
+
mScript = new ScriptC_Threshold(mRS, getResources(), R.raw.threshold_bc, false);
mScript.set_width(mBitmapIn.getWidth());
mScript.set_height(mBitmapIn.getHeight());
@@ -388,6 +396,9 @@
mScript.bind_InPixel(mInPixelsAllocation);
mScript.bind_OutPixel(mOutPixelsAllocation);
mScript.bind_ScratchPixel(mScratchPixelsAllocation);
+
+ mScript.set_vBlurScript(mScriptVBlur);
+ mScript.set_hBlurScript(mScriptHBlur);
}
private Bitmap loadBitmap(int resource) {
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java
new file mode 100644
index 0000000..8ee50a8
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+import android.util.Log;
+
+public class ScriptC_Horizontal_blur extends ScriptC {
+ // Constructor
+ public ScriptC_Horizontal_blur(RenderScript rs, Resources resources, int id, boolean isRoot) {
+ super(rs, resources, id, isRoot);
+ }
+
+ private final static int mExportVarIdx_ScratchPixel = 0;
+ private Allocation mExportVar_ScratchPixel;
+ public void bind_ScratchPixel(Allocation v) {
+ mExportVar_ScratchPixel = v;
+ if(v == null) bindAllocation(null, mExportVarIdx_ScratchPixel);
+ else bindAllocation(v, mExportVarIdx_ScratchPixel);
+ }
+
+ public Allocation get_ScratchPixel() {
+ return mExportVar_ScratchPixel;
+ }
+
+}
+
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
index ea363d3..c23dca1 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Threshold.java
@@ -161,6 +161,28 @@
return mExportVar_saturation;
}
+ private final static int mExportVarIdx_vBlurScript = 12;
+ private Script mExportVar_vBlurScript;
+ public void set_vBlurScript(Script v) {
+ mExportVar_vBlurScript = v;
+ setVar(mExportVarIdx_vBlurScript, (v == null) ? 0 : v.getID());
+ }
+
+ public Script get_vBlurScript() {
+ return mExportVar_vBlurScript;
+ }
+
+ private final static int mExportVarIdx_hBlurScript = 13;
+ private Script mExportVar_hBlurScript;
+ public void set_hBlurScript(Script v) {
+ mExportVar_hBlurScript = v;
+ setVar(mExportVarIdx_hBlurScript, (v == null) ? 0 : v.getID());
+ }
+
+ public Script get_hBlurScript() {
+ return mExportVar_hBlurScript;
+ }
+
private final static int mExportFuncIdx_filter = 0;
public void invoke_filter() {
invoke(mExportFuncIdx_filter);
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java
new file mode 100644
index 0000000..0215f60
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+import android.util.Log;
+
+public class ScriptC_Vertical_blur extends ScriptC {
+ // Constructor
+ public ScriptC_Vertical_blur(RenderScript rs, Resources resources, int id, boolean isRoot) {
+ super(rs, resources, id, isRoot);
+ }
+
+ private final static int mExportVarIdx_ScratchPixel = 0;
+ private Allocation mExportVar_ScratchPixel;
+ public void bind_ScratchPixel(Allocation v) {
+ mExportVar_ScratchPixel = v;
+ if(v == null) bindAllocation(null, mExportVarIdx_ScratchPixel);
+ else bindAllocation(v, mExportVarIdx_ScratchPixel);
+ }
+
+ public Allocation get_ScratchPixel() {
+ return mExportVar_ScratchPixel;
+ }
+
+}
+
diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d
index d220c81..2d7d32b 100644
--- a/libs/rs/java/ModelViewer/res/raw/robot.a3d
+++ b/libs/rs/java/ModelViewer/res/raw/robot.a3d
Binary files differ
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 136c05d..1b81591 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -78,6 +78,19 @@
ret RsElement
}
+ElementGetNativeData {
+ param RsElement elem
+ param uint32_t *elemData
+ param uint32_t elemDataSize
+ }
+
+ElementGetSubElements {
+ param RsElement elem
+ param uint32_t *ids
+ param const char **names
+ param uint32_t dataSize
+ }
+
TypeBegin {
param RsElement type
}
@@ -91,6 +104,12 @@
ret RsType
}
+TypeGetNativeData {
+ param RsType type
+ param uint32_t * typeData
+ param uint32_t typeDataSize
+ }
+
AllocationCreateTyped {
param RsType type
ret RsAllocation
@@ -231,6 +250,11 @@
param const void *data
}
+AllocationGetType {
+ param RsAllocation va
+ ret const void*
+ }
+
SamplerBegin {
}
@@ -347,8 +371,6 @@
}
ProgramRasterCreate {
- param RsElement in
- param RsElement out
param bool pointSmooth
param bool lineSmooth
param bool pointSprite
@@ -360,12 +382,11 @@
param float lw
}
-ProgramRasterSetPointSize{
+ProgramRasterSetCullMode {
param RsProgramRaster pr
- param float ps
+ param RsCullMode mode
}
-
ProgramBindConstants {
param RsProgram vp
param uint32_t slot
@@ -497,6 +518,29 @@
param uint32_t slot
}
+MeshGetVertexBufferCount {
+ param RsMesh mesh
+ param int32_t *numVtx
+ }
+
+MeshGetIndexCount {
+ param RsMesh mesh
+ param int32_t *numIdx
+ }
+
+MeshGetVertices {
+ param RsMesh mv
+ param RsAllocation *vtxData
+ param uint32_t vtxDataCount
+ }
+
+MeshGetIndices {
+ param RsMesh mv
+ param RsAllocation *va
+ param uint32_t *primType
+ param uint32_t idxDataCount
+ }
+
AnimationCreate {
param const float *inValues
param const float *outValues
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index d9d0bc5..6560101 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -683,6 +683,14 @@
a->read(data);
}
+const void* rsi_AllocationGetType(Context *rsc, RsAllocation va)
+{
+ Allocation *a = static_cast<Allocation *>(va);
+ a->getType()->incUserRef();
+
+ return a->getType();
+}
+
#endif //ANDROID_RS_BUILD_FOR_HOST
}
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index aa20275..37b8bd6 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -293,6 +293,32 @@
return (RsElement)e;
}
+void rsi_ElementGetNativeData(Context *rsc, RsElement elem, uint32_t *elemData, uint32_t elemDataSize)
+{
+ rsAssert(elemDataSize == 5);
+ // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+ Element *e = static_cast<Element *>(elem);
+
+ (*elemData++) = (uint32_t)e->getType();
+ (*elemData++) = (uint32_t)e->getKind();
+ (*elemData++) = e->getComponent().getIsNormalized() ? 1 : 0;
+ (*elemData++) = e->getComponent().getVectorSize();
+ (*elemData++) = e->getFieldCount();
+
+}
+
+void rsi_ElementGetSubElements(Context *rsc, RsElement elem, uint32_t *ids, const char **names, uint32_t dataSize)
+{
+ Element *e = static_cast<Element *>(elem);
+ rsAssert(e->getFieldCount() == dataSize);
+
+ for(uint32_t i = 0; i < dataSize; i ++) {
+ ids[i] = (uint32_t)e->getField(i);
+ names[i] = e->getFieldName(i);
+ }
+
+}
+
}
}
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 2a47ca4..d1346fc 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -511,6 +511,9 @@
ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
mRSC->setVertex(mRSC->getDefaultProgramVertex());
+ ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
+ mRSC->setRaster(mRSC->getDefaultProgramRaster());
+
ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
mRSC->setFragment(mFontShaderF.get());
@@ -519,6 +522,7 @@
if (!mRSC->setupCheck()) {
mRSC->setVertex((ProgramVertex *)tmpV.get());
+ mRSC->setRaster((ProgramRaster *)tmpR.get());
mRSC->setFragment((ProgramFragment *)tmpF.get());
mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
return;
@@ -538,6 +542,7 @@
// Reset the state
mRSC->setVertex((ProgramVertex *)tmpV.get());
+ mRSC->setRaster((ProgramRaster *)tmpR.get());
mRSC->setFragment((ProgramFragment *)tmpF.get());
mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
}
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index d1b5581..9026578 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -271,4 +271,45 @@
sm->updateGLPrimitives();
}
+void rsi_MeshGetVertexBufferCount(Context *rsc, RsMesh mv, int32_t *numVtx)
+{
+ Mesh *sm = static_cast<Mesh *>(mv);
+ *numVtx = sm->mVertexBufferCount;
+}
+
+void rsi_MeshGetIndexCount(Context *rsc, RsMesh mv, int32_t *numIdx)
+{
+ Mesh *sm = static_cast<Mesh *>(mv);
+ *numIdx = sm->mPrimitivesCount;
+}
+
+void rsi_MeshGetVertices(Context *rsc, RsMesh mv, RsAllocation *vtxData, uint32_t vtxDataCount)
+{
+ Mesh *sm = static_cast<Mesh *>(mv);
+ rsAssert(vtxDataCount == sm->mVertexBufferCount);
+
+ for(uint32_t ct = 0; ct < vtxDataCount; ct ++) {
+ vtxData[ct] = sm->mVertexBuffers[ct].get();
+ sm->mVertexBuffers[ct]->incUserRef();
+ }
+}
+
+void rsi_MeshGetIndices(Context *rsc, RsMesh mv, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount)
+{
+ Mesh *sm = static_cast<Mesh *>(mv);
+ rsAssert(idxDataCount == sm->mPrimitivesCount);
+
+ for(uint32_t ct = 0; ct < idxDataCount; ct ++) {
+ va[ct] = sm->mPrimitives[ct]->mIndexBuffer.get();
+ primType[ct] = sm->mPrimitives[ct]->mPrimitive;
+ if(sm->mPrimitives[ct]->mIndexBuffer.get()) {
+ sm->mPrimitives[ct]->mIndexBuffer->incUserRef();
+ }
+ }
+
+}
+
+
+
+
}}
diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp
index 66f6ef8..5b69370 100644
--- a/libs/rs/rsProgramRaster.cpp
+++ b/libs/rs/rsProgramRaster.cpp
@@ -41,9 +41,8 @@
mPointSmooth = pointSmooth;
mLineSmooth = lineSmooth;
mPointSprite = pointSprite;
-
- mPointSize = 1.0f;
mLineWidth = 1.0f;
+ mCull = RS_CULL_BACK;
}
ProgramRaster::~ProgramRaster()
@@ -53,21 +52,23 @@
void ProgramRaster::setLineWidth(float s)
{
mLineWidth = s;
+ mDirty = true;
}
-void ProgramRaster::setPointSize(float s)
+void ProgramRaster::setCullMode(RsCullMode mode)
{
- mPointSize = s;
+ mCull = mode;
+ mDirty = true;
}
void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state)
{
- if (state->mLast.get() == this) {
+ if (state->mLast.get() == this && !mDirty) {
return;
}
state->mLast.set(this);
+ mDirty = false;
- glPointSize(mPointSize);
if (mPointSmooth) {
glEnable(GL_POINT_SMOOTH);
} else {
@@ -90,19 +91,48 @@
}
#endif //ANDROID_RS_BUILD_FOR_HOST
}
+
+ switch(mCull) {
+ case RS_CULL_BACK:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ break;
+ case RS_CULL_FRONT:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+ break;
+ case RS_CULL_NONE:
+ glDisable(GL_CULL_FACE);
+ break;
+ }
}
void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state)
{
- if (state->mLast.get() == this) {
+ if (state->mLast.get() == this && !mDirty) {
return;
}
state->mLast.set(this);
+ mDirty = false;
+
+ switch(mCull) {
+ case RS_CULL_BACK:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ break;
+ case RS_CULL_FRONT:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+ break;
+ case RS_CULL_NONE:
+ glDisable(GL_CULL_FACE);
+ break;
+ }
}
void ProgramRaster::serialize(OStream *stream) const
{
-
+
}
ProgramRaster *ProgramRaster::createFromStream(Context *rsc, IStream *stream)
@@ -134,7 +164,7 @@
namespace android {
namespace renderscript {
-RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, RsElement in, RsElement out,
+RsProgramRaster rsi_ProgramRasterCreate(Context * rsc,
bool pointSmooth,
bool lineSmooth,
bool pointSprite)
@@ -147,18 +177,18 @@
return pr;
}
-void rsi_ProgramRasterSetPointSize(Context * rsc, RsProgramRaster vpr, float s)
-{
- ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
- pr->setPointSize(s);
-}
-
void rsi_ProgramRasterSetLineWidth(Context * rsc, RsProgramRaster vpr, float s)
{
ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
pr->setLineWidth(s);
}
+void rsi_ProgramRasterSetCullMode(Context * rsc, RsProgramRaster vpr, RsCullMode mode)
+{
+ ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
+ pr->setCullMode(mode);
+}
+
}
}
diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h
index 79b14753..801ab2a 100644
--- a/libs/rs/rsProgramRaster.h
+++ b/libs/rs/rsProgramRaster.h
@@ -41,17 +41,14 @@
static ProgramRaster *createFromStream(Context *rsc, IStream *stream);
void setLineWidth(float w);
- void setPointSize(float s);
+ void setCullMode(RsCullMode mode);
protected:
bool mPointSmooth;
bool mLineSmooth;
bool mPointSprite;
-
- float mPointSize;
float mLineWidth;
-
-
+ RsCullMode mCull;
};
class ProgramRasterState
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 85d90c7..5558007 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -191,7 +191,6 @@
mShader.append("attribute vec4 ATTRIB_position;\n");
mShader.append("attribute vec4 ATTRIB_color;\n");
mShader.append("attribute vec3 ATTRIB_normal;\n");
- mShader.append("attribute float ATTRIB_pointSize;\n");
mShader.append("attribute vec4 ATTRIB_texture0;\n");
for (uint32_t ct=0; ct < mUniformCount; ct++) {
@@ -202,7 +201,7 @@
mShader.append("void main() {\n");
mShader.append(" gl_Position = UNI_MVP * ATTRIB_position;\n");
- mShader.append(" gl_PointSize = ATTRIB_pointSize;\n");
+ mShader.append(" gl_PointSize = 1.0;\n");
mShader.append(" varColor = ATTRIB_color;\n");
if (mTextureMatrixEnable) {
@@ -210,9 +209,6 @@
} else {
mShader.append(" varTex0 = ATTRIB_texture0;\n");
}
- //mShader.append(" pos.x = pos.x / 480.0;\n");
- //mShader.append(" pos.y = pos.y / 800.0;\n");
- //mShader.append(" gl_Position = pos;\n");
mShader.append("}\n");
}
}
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index ea6aec5..0717059 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -64,9 +64,11 @@
void setVar(uint32_t slot, const void *val, uint32_t len);
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout) = 0;
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout, uint32_t xStart, uint32_t xEnd) = 0;
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout, uint32_t xStart, uint32_t yStart, uint32_t xEnd, uint32_t yEnd) = 0;
+ virtual void runForEach(Context *rsc,
+ const Allocation * ain,
+ Allocation * aout,
+ const void * usr,
+ const RsScriptCall *sc = NULL) = 0;
virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) = 0;
virtual void setupScript(Context *rsc) = 0;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 975b704..b87ac28 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -136,55 +136,95 @@
return ret;
}
-void ScriptC::runForEach(Context *rsc, const Allocation *ain, Allocation *aout,
- uint32_t xStart, uint32_t yStart, uint32_t xEnd, uint32_t yEnd)
-{
- LOGE("ScriptC::runForEach not implemented");
-}
-void ScriptC::runForEach(Context *rsc, const Allocation *ain, Allocation *aout, uint32_t xStart, uint32_t xEnd)
+void ScriptC::runForEach(Context *rsc,
+ const Allocation * ain,
+ Allocation * aout,
+ const void * usr,
+ const RsScriptCall *sc)
{
uint32_t dimX = ain->getType()->getDimX();
- rsAssert(xStart < dimX);
- rsAssert(xEnd <= dimX);
- rsAssert(ain->getType()->getDimY() == 0);
- rsAssert(ain->getType()->getDimZ() == 0);
+ uint32_t dimY = ain->getType()->getDimY();
+ uint32_t dimZ = ain->getType()->getDimZ();
+ uint32_t dimA = 0;//ain->getType()->getDimArray();
- if (xStart >= dimX) xStart = dimX - 1;
- if (xEnd >= dimX) xEnd = dimX - 1;
- if (xStart > xEnd) return;
+ uint32_t xStart = 0;
+ uint32_t xEnd = 0;
+ uint32_t yStart = 0;
+ uint32_t yEnd = 0;
+ uint32_t zStart = 0;
+ uint32_t zEnd = 0;
+ uint32_t arrayStart = 0;
+ uint32_t arrayEnd = 0;
+
+ if (!sc || (sc->xEnd == 0)) {
+ xStart = 0;
+ xEnd = ain->getType()->getDimX();
+ } else {
+ rsAssert(xStart < dimX);
+ rsAssert(xEnd <= dimX);
+ rsAssert(sc->xStart < sc->xEnd);
+ xStart = rsMin(dimX, sc->xStart);
+ xEnd = rsMin(dimX, sc->xEnd);
+ if (xStart >= xEnd) return;
+ }
+
+ if (!sc || (sc->yEnd == 0)) {
+ yStart = 0;
+ yEnd = ain->getType()->getDimY();
+ } else {
+ rsAssert(yStart < dimY);
+ rsAssert(yEnd <= dimY);
+ rsAssert(sc->yStart < sc->yEnd);
+ yStart = rsMin(dimY, sc->yStart);
+ yEnd = rsMin(dimY, sc->yEnd);
+ if (yStart >= yEnd) return;
+ }
+
+ xEnd = rsMax((uint32_t)1, xEnd);
+ yEnd = rsMax((uint32_t)1, yEnd);
+ zEnd = rsMax((uint32_t)1, zEnd);
+ arrayEnd = rsMax((uint32_t)1, arrayEnd);
+
+ rsAssert(ain->getType()->getDimZ() == 0);
setupScript(rsc);
Script * oldTLS = setTLS(this);
- typedef int (*rs_t)(const void *, void *, uint32_t);
+ typedef int (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t);
+
const uint8_t *ptrIn = (const uint8_t *)ain->getPtr();
- uint32_t strideIn = ain->getType()->getElementSizeBytes();
+ uint32_t eStrideIn = ain->getType()->getElementSizeBytes();
uint8_t *ptrOut = NULL;
- uint32_t strideOut = 0;
+ uint32_t eStrideOut = 0;
if (aout) {
ptrOut = (uint8_t *)aout->getPtr();
- strideOut = aout->getType()->getElementSizeBytes();
+ eStrideOut = aout->getType()->getElementSizeBytes();
}
- for (uint32_t ct=xStart; ct < xEnd; ct++) {
- ((rs_t)mProgram.mRoot) (ptrIn + (strideIn * ct), ptrOut + (strideOut * ct), ct);
+ for (uint32_t ar = arrayStart; ar < arrayEnd; ar++) {
+ for (uint32_t z = zStart; z < zEnd; z++) {
+ for (uint32_t y = yStart; y < yEnd; y++) {
+ uint32_t offset = dimX * dimY * dimZ * ar +
+ dimX * dimY * z +
+ dimX * y;
+ uint8_t *xPtrOut = ptrOut + (eStrideOut * offset);
+ const uint8_t *xPtrIn = ptrIn + (eStrideIn * offset);
+
+ for (uint32_t x = xStart; x < xEnd; x++) {
+ ((rs_t)mProgram.mRoot) (xPtrIn, xPtrOut, usr, x, y, z, ar);
+ xPtrIn += eStrideIn;
+ xPtrOut += eStrideOut;
+ }
+ }
+ }
+
}
setTLS(oldTLS);
}
-void ScriptC::runForEach(Context *rsc, const Allocation *ain, Allocation *aout)
-{
- if (ain->getType()->getDimY()) {
- runForEach(rsc, ain, aout, 0, 0, 0xffffffff, 0xffffffff);
- } else {
- runForEach(rsc, ain, aout, 0, 0xffffffff);
- }
-}
-
-
void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len)
{
//LOGE("rsi_ScriptInvoke %i", slot);
@@ -246,7 +286,7 @@
void ScriptCState::runCompiler(Context *rsc, ScriptC *s)
{
- LOGE("ScriptCState::runCompiler ");
+ LOGV("ScriptCState::runCompiler ");
s->mBccScript = bccCreateScript();
bccScriptBitcode(s->mBccScript, s->mEnviroment.mScriptText, s->mEnviroment.mScriptTextLength);
@@ -254,7 +294,7 @@
bccCompileScript(s->mBccScript);
bccGetScriptLabel(s->mBccScript, "root", (BCCvoid**) &s->mProgram.mRoot);
bccGetScriptLabel(s->mBccScript, "init", (BCCvoid**) &s->mProgram.mInit);
- LOGE("root %p, init %p", s->mProgram.mRoot, s->mProgram.mInit);
+ LOGV("root %p, init %p", s->mProgram.mRoot, s->mProgram.mInit);
if (s->mProgram.mInit) {
s->mProgram.mInit();
@@ -268,24 +308,9 @@
bccGetExportFuncs(s->mBccScript, NULL, s->mEnviroment.mInvokeFunctionCount, (BCCvoid **) s->mEnviroment.mInvokeFunctions);
}
-// s->mEnviroment.mInvokeFunctions = (Script::InvokeFunc_t *)calloc(100, sizeof(void *));
-// BCCchar **labels = new char*[100];
-// bccGetFunctions(s->mBccScript, (BCCsizei *)&s->mEnviroment.mInvokeFunctionCount,
-// 100, (BCCchar **)labels);
- //LOGE("func count %i", s->mEnviroment.mInvokeFunctionCount);
-// for (uint32_t i=0; i < s->mEnviroment.mInvokeFunctionCount; i++) {
-// BCCsizei length;
-// bccGetFunctionBinary(s->mBccScript, labels[i], (BCCvoid **)&(s->mEnviroment.mInvokeFunctions[i]), &length);
- //LOGE("func %i %p", i, s->mEnviroment.mInvokeFunctions[i]);
- // }
-
s->mEnviroment.mFieldAddress = (void **)calloc(100, sizeof(void *));
bccGetExportVars(s->mBccScript, (BCCsizei *)&s->mEnviroment.mFieldCount,
100, s->mEnviroment.mFieldAddress);
- //LOGE("var count %i", s->mEnviroment.mFieldCount);
- for (uint32_t i=0; i < s->mEnviroment.mFieldCount; i++) {
- //LOGE("var %i %p", i, s->mEnviroment.mFieldAddress[i]);
- }
s->mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
s->mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index 50e8a4c..9d09b0b 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -57,9 +57,11 @@
virtual uint32_t run(Context *);
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout);
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout, uint32_t xStart, uint32_t xEnd);
- virtual void runForEach(Context *rsc, const Allocation *ain, Allocation *aout, uint32_t xStart, uint32_t yStart, uint32_t xEnd, uint32_t yEnd);
+ virtual void runForEach(Context *rsc,
+ const Allocation * ain,
+ Allocation * aout,
+ const void * usr,
+ const RsScriptCall *sc = NULL);
virtual void serialize(OStream *stream) const { }
virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SCRIPT_C; }
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index ea01134..8d9ca9f 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -376,58 +376,31 @@
}
-void SC_ForEachii(RsScript vs, RsAllocation vin)
+void SC_ForEach(RsScript vs,
+ RsAllocation vin,
+ RsAllocation vout,
+ const void *usr)
{
GET_TLS();
- Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
- s->runForEach(rsc, ain, NULL);
-}
-
-void SC_ForEachiii(RsScript vs, RsAllocation vin, RsAllocation vout)
-{
- GET_TLS();
- Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
+ const Allocation *ain = static_cast<const Allocation *>(vin);
Allocation *aout = static_cast<Allocation *>(vout);
- s->runForEach(rsc, ain, aout);
+ Script *s = static_cast<Script *>(vs);
+ s->runForEach(rsc, ain, aout, usr);
}
-void SC_ForEachiiii(RsScript vs, RsAllocation vin, int xStart, int xEnd)
+void SC_ForEach2(RsScript vs,
+ RsAllocation vin,
+ RsAllocation vout,
+ const void *usr,
+ const RsScriptCall *call)
{
GET_TLS();
- Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
- s->runForEach(rsc, ain, NULL, xStart, xEnd);
-}
-
-void SC_ForEachiiiii(RsScript vs, RsAllocation vin, RsAllocation vout, int xStart, int xEnd)
-{
- GET_TLS();
- Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
+ const Allocation *ain = static_cast<const Allocation *>(vin);
Allocation *aout = static_cast<Allocation *>(vout);
- s->runForEach(rsc, ain, aout, xStart, xEnd);
-}
-
-void SC_ForEachiiiiii(RsScript vs, RsAllocation vin, int xStart, int yStart, int xEnd, int yEnd)
-{
- GET_TLS();
Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
- s->runForEach(rsc, ain, NULL, xStart, yStart, xEnd, yEnd);
+ s->runForEach(rsc, ain, aout, usr, call);
}
-void SC_ForEachiiiiiii(RsScript vs, RsAllocation vin, RsAllocation vout, int xStart, int yStart, int xEnd, int yEnd)
-{
- GET_TLS();
- Script *s = static_cast<Script *>(vs);
- Allocation *ain = static_cast<Allocation *>(vin);
- Allocation *aout = static_cast<Allocation *>(vout);
- s->runForEach(rsc, ain, aout, xStart, yStart, xEnd, yEnd);
-}
-
-
//////////////////////////////////////////////////////////////////////////////
// Class implementation
//////////////////////////////////////////////////////////////////////////////
@@ -502,12 +475,8 @@
{ "rsMatrixScale", (void *)&SC_matrixScale },
{ "rsMatrixTranslate", (void *)&SC_matrixTranslate },
- { "_Z9rsForEachii", (void *)&SC_ForEachii },
- { "_Z9rsForEachiii", (void *)&SC_ForEachiii },
- { "_Z9rsForEachiiii", (void *)&SC_ForEachiiii },
- { "_Z9rsForEachiiiii", (void *)&SC_ForEachiiiii },
- { "_Z9rsForEachiiiiii", (void *)&SC_ForEachiiiiii },
- { "_Z9rsForEachiiiiiii", (void *)&SC_ForEachiiiiiii },
+ { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach },
+ //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2 },
////////////////////////////////////////////////////////////////////
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 504ffba..5c073b3 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -101,8 +101,7 @@
glBindAttribLocation(pgm, 0, "ATTRIB_position");
glBindAttribLocation(pgm, 1, "ATTRIB_color");
glBindAttribLocation(pgm, 2, "ATTRIB_normal");
- glBindAttribLocation(pgm, 3, "ATTRIB_pointSize");
- glBindAttribLocation(pgm, 4, "ATTRIB_texture0");
+ glBindAttribLocation(pgm, 3, "ATTRIB_texture0");
}
//LOGE("e2 %x", glGetError());
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 8dca0fe..52e0d52 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -91,7 +91,7 @@
if (mLODCount != oldLODCount) {
if(mLODs){
delete [] mLODs;
- }
+ }
mLODs = new LOD[mLODCount];
}
@@ -340,6 +340,22 @@
return st;
}
+void rsi_TypeGetNativeData(Context *rsc, RsType type, uint32_t *typeData, uint32_t typeDataSize)
+{
+ rsAssert(typeDataSize == 6);
+ // Pack the data in the follofing way mDimX; mDimY; mDimZ;
+ // mDimLOD; mDimFaces; mElement; into typeData
+ Type *t = static_cast<Type *>(type);
+
+ (*typeData++) = t->getDimX();
+ (*typeData++) = t->getDimY();
+ (*typeData++) = t->getDimZ();
+ (*typeData++) = t->getDimLOD();
+ (*typeData++) = t->getDimFaces() ? 1 : 0;
+ (*typeData++) = (uint32_t)t->getElement();
+
+}
+
}
}
diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp
index 6937a85..001927c 100644
--- a/libs/rs/rsVertexArray.cpp
+++ b/libs/rs/rsVertexArray.cpp
@@ -144,10 +144,8 @@
slot = 1;
} else if (mAttribs[ct].name == "normal") {
slot = 2;
- } else if (mAttribs[ct].name == "pointSize") {
- slot = 3;
} else if (mAttribs[ct].name == "texture0") {
- slot = 4;
+ slot = 3;
} else {
continue;
}
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 3709296..e11c832 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -48,11 +48,27 @@
extern int rsSendToClient(void *data, int cmdID, int len, int waitForSpace);
// Script to Script
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input);
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, rs_allocation output);
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, int xStart, int xEnd);
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, rs_allocation output, int xStart, int xEnd);
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, int xStart, int yStart, int xEnd, int yEnd);
-extern void __attribute__((overloadable))rsForEach(rs_script, rs_allocation input, rs_allocation output, int xStart, int yStart, int xEnd, int yEnd);
+typedef struct rs_script_call {
+ uint32_t xStart;
+ uint32_t xEnd;
+ uint32_t yStart;
+ uint32_t yEnd;
+ uint32_t zStart;
+ uint32_t zEnd;
+ uint32_t arrayStart;
+ uint32_t arrayEnd;
+
+} rs_script_call_t;
+
+extern void __attribute__((overloadable))rsForEach(rs_script script,
+ rs_allocation input,
+ rs_allocation output,
+ const void * usrData);
+
+extern void __attribute__((overloadable))rsForEach(rs_script script,
+ rs_allocation input,
+ rs_allocation output,
+ const void * usrData,
+ const rs_script_call_t *);
#endif
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 24cdc78..4243bbf 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -12,6 +12,7 @@
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
Input.cpp \
+ InputDevice.cpp \
InputDispatcher.cpp \
InputManager.cpp \
InputReader.cpp \
@@ -38,3 +39,13 @@
endif
include $(BUILD_SHARED_LIBRARY)
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 27895f2..768b04e 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -54,6 +54,9 @@
*/
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+/* this macro computes the number of bytes needed to represent a bit array of the specified size */
+#define sizeof_bit_array(bits) ((bits + 7) / 8)
+
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
@@ -182,7 +185,7 @@
}
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
- uint8_t key_bitmask[(KEY_MAX + 7) / 8];
+ 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) {
@@ -218,7 +221,7 @@
Vector<int32_t> scanCodes;
device->layoutMap->findScancodes(keyCode, &scanCodes);
- uint8_t key_bitmask[(KEY_MAX + 7) / 8];
+ 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) {
@@ -264,7 +267,7 @@
}
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
- uint8_t sw_bitmask[(SW_MAX + 7) / 8];
+ 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,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
@@ -409,7 +412,7 @@
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
iev.code, *outKeycode, *outFlags, err);
if (err != 0) {
- *outKeycode = 0;
+ *outKeycode = AKEYCODE_UNKNOWN;
*outFlags = 0;
}
} else {
@@ -509,6 +512,26 @@
// ----------------------------------------------------------------------------
+static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
+ const uint8_t* end = array + endIndex;
+ array += startIndex;
+ while (array != end) {
+ if (*(array++) != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static const int32_t GAMEPAD_KEYCODES[] = {
+ AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
+ AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
+ AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
+ AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
+ AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
+};
+
int EventHub::open_device(const char *deviceName)
{
int version;
@@ -626,27 +649,27 @@
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
- // figure out the kinds of events the device reports
+ // Figure out the kinds of events the device reports.
- // See if this is a keyboard, and classify it. Note that we only
- // consider up through the function keys; we don't want to include
- // ones after that (play cd etc) so we don't mistakenly consider a
- // controller to be a keyboard.
- uint8_t key_bitmask[(KEY_MAX+7)/8];
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
+
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP\n");
- //for (int i=0; i<((KEY_MAX+7)/8); i++) {
+ //for (int i = 0; i < sizeof(key_bitmask); i++) {
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
//}
- for (int i=0; i<((BTN_MISC+7)/8); i++) {
- if (key_bitmask[i] != 0) {
- device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
- break;
- }
- }
- if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
+
+ // See if this is a keyboard. Ignore everything in the button range except for
+ // gamepads which are also considered keyboards.
+ if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
+ sizeof_bit_array(BTN_DIGI))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
+ sizeof_bit_array(KEY_MAX + 1))) {
+ device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
@@ -658,39 +681,39 @@
}
}
- // See if this is a trackball.
+ // See if this is a trackball (or mouse).
if (test_bit(BTN_MOUSE, key_bitmask)) {
- uint8_t rel_bitmask[(REL_MAX+7)/8];
+ uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
- if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
- {
+ if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
}
}
}
-
- uint8_t abs_bitmask[(ABS_MAX+7)/8];
+
+ // See if this is a touch pad.
+ uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
-
- // Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
- && test_bit(ABS_MT_POSITION_X, abs_bitmask)
- && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
-
- // Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, key_bitmask)
- && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
+ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
+ // Is this a new modern multi-touch driver?
+ if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_X, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
+
+ // Is this an old style single-touch driver?
+ } else if (test_bit(BTN_TOUCH, key_bitmask)
+ && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
+ }
}
#ifdef EV_SW
// figure out the switches this device reports
- uint8_t sw_bitmask[(SW_MAX+7)/8];
+ uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
@@ -726,7 +749,10 @@
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
- device->layoutMap->load(keylayoutFilename);
+ status_t status = device->layoutMap->load(keylayoutFilename);
+ if (status) {
+ LOGE("Error %d loading key layout.", status);
+ }
// tell the world about the devname (the descriptive name)
if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
@@ -746,19 +772,27 @@
property_set(propName, name);
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycode(device, kKeyCodeQ)) {
+ if (hasKeycode(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
- // See if this has a DPAD.
- if (hasKeycode(device, kKeyCodeDpadUp) &&
- hasKeycode(device, kKeyCodeDpadDown) &&
- hasKeycode(device, kKeyCodeDpadLeft) &&
- hasKeycode(device, kKeyCodeDpadRight) &&
- hasKeycode(device, kKeyCodeDpadCenter)) {
+ // See if this device has a DPAD.
+ if (hasKeycode(device, AKEYCODE_DPAD_UP) &&
+ hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycode(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
+ // See if this device has a gamepad.
+ for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
+ if (hasKeycode(device, GAMEPAD_KEYCODES[i])) {
+ device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+ break;
+ }
+ }
+
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
device->id, name, propName, keylayoutFilename);
}
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index a64251f..1f19c2c 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -22,26 +22,26 @@
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
switch (keyCode) {
- case KEYCODE_HOME:
- case KEYCODE_BACK:
- case KEYCODE_CALL:
- case KEYCODE_ENDCALL:
- case KEYCODE_VOLUME_UP:
- case KEYCODE_VOLUME_DOWN:
- case KEYCODE_POWER:
- case KEYCODE_CAMERA:
- case KEYCODE_HEADSETHOOK:
- case KEYCODE_MENU:
- case KEYCODE_NOTIFICATION:
- case KEYCODE_FOCUS:
- case KEYCODE_SEARCH:
- case KEYCODE_MEDIA_PLAY_PAUSE:
- case KEYCODE_MEDIA_STOP:
- case KEYCODE_MEDIA_NEXT:
- case KEYCODE_MEDIA_PREVIOUS:
- case KEYCODE_MEDIA_REWIND:
- case KEYCODE_MEDIA_FAST_FORWARD:
- case KEYCODE_MUTE:
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_POWER:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MENU:
+ case AKEYCODE_NOTIFICATION:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MUTE:
return true;
}
@@ -54,26 +54,26 @@
bool KeyEvent::isSystemKey(int32_t keyCode) {
switch (keyCode) {
- case KEYCODE_MENU:
- case KEYCODE_SOFT_RIGHT:
- case KEYCODE_HOME:
- case KEYCODE_BACK:
- case KEYCODE_CALL:
- case KEYCODE_ENDCALL:
- case KEYCODE_VOLUME_UP:
- case KEYCODE_VOLUME_DOWN:
- case KEYCODE_MUTE:
- case KEYCODE_POWER:
- case KEYCODE_HEADSETHOOK:
- case KEYCODE_MEDIA_PLAY_PAUSE:
- case KEYCODE_MEDIA_STOP:
- case KEYCODE_MEDIA_NEXT:
- case KEYCODE_MEDIA_PREVIOUS:
- case KEYCODE_MEDIA_REWIND:
- case KEYCODE_MEDIA_FAST_FORWARD:
- case KEYCODE_CAMERA:
- case KEYCODE_FOCUS:
- case KEYCODE_SEARCH:
+ case AKEYCODE_MENU:
+ case AKEYCODE_SOFT_RIGHT:
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_POWER:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
return true;
}
diff --git a/libs/ui/InputDevice.cpp b/libs/ui/InputDevice.cpp
new file mode 100644
index 0000000..6014017
--- /dev/null
+++ b/libs/ui/InputDevice.cpp
@@ -0,0 +1,729 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input reader.
+//
+#define LOG_TAG "InputDevice"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages for each raw event received from the EventHub.
+#define DEBUG_RAW_EVENTS 0
+
+// Log debug messages about touch screen filtering hacks.
+#define DEBUG_HACKS 0
+
+// Log debug messages about virtual key processing.
+#define DEBUG_VIRTUAL_KEYS 0
+
+// Log debug messages about pointers.
+#define DEBUG_POINTERS 0
+
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
+#include <cutils/log.h>
+#include <ui/InputDevice.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+/* Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value. */
+#define JUMPY_EPSILON_DIVISOR 212
+
+/* Number of jumpy points to drop for touchscreens that need it. */
+#define JUMPY_TRANSITION_DROPS 3
+#define JUMPY_DROP_LIMIT 3
+
+/* Maximum squared distance for averaging.
+ * If moving farther than this, turn of averaging to avoid lag in response. */
+#define AVERAGING_DISTANCE_LIMIT (75 * 75)
+
+
+namespace android {
+
+// --- Static Functions ---
+
+template<typename T>
+inline static T abs(const T& value) {
+ return value < 0 ? - value : value;
+}
+
+template<typename T>
+inline static T min(const T& a, const T& b) {
+ return a < b ? a : b;
+}
+
+template<typename T>
+inline static void swap(T& a, T& b) {
+ T temp = a;
+ a = b;
+ b = temp;
+}
+
+
+// --- InputDevice ---
+
+InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) :
+ id(id), classes(classes), name(name), ignored(false) {
+}
+
+void InputDevice::reset() {
+ if (isKeyboard()) {
+ keyboard.reset();
+ }
+
+ if (isTrackball()) {
+ trackball.reset();
+ }
+
+ if (isMultiTouchScreen()) {
+ multiTouchScreen.reset();
+ } else if (isSingleTouchScreen()) {
+ singleTouchScreen.reset();
+ }
+
+ if (isTouchScreen()) {
+ touchScreen.reset();
+ }
+}
+
+
+// --- InputDevice::TouchData ---
+
+void InputDevice::TouchData::copyFrom(const TouchData& other) {
+ pointerCount = other.pointerCount;
+ idBits = other.idBits;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointers[i] = other.pointers[i];
+ idToIndex[i] = other.idToIndex[i];
+ }
+}
+
+
+// --- InputDevice::KeyboardState ---
+
+void InputDevice::KeyboardState::reset() {
+ current.metaState = META_NONE;
+ current.downTime = 0;
+}
+
+
+// --- InputDevice::TrackballState ---
+
+void InputDevice::TrackballState::reset() {
+ accumulator.clear();
+ current.down = false;
+ current.downTime = 0;
+}
+
+
+// --- InputDevice::TouchScreenState ---
+
+void InputDevice::TouchScreenState::reset() {
+ lastTouch.clear();
+ downTime = 0;
+ currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP;
+
+ for (uint32_t i = 0; i < MAX_POINTERS; i++) {
+ averagingTouchFilter.historyStart[i] = 0;
+ averagingTouchFilter.historyEnd[i] = 0;
+ }
+
+ jumpyTouchFilter.jumpyPointsDropped = 0;
+}
+
+struct PointerDistanceHeapElement {
+ uint32_t currentPointerIndex : 8;
+ uint32_t lastPointerIndex : 8;
+ uint64_t distance : 48; // squared distance
+};
+
+void InputDevice::TouchScreenState::calculatePointerIds() {
+ uint32_t currentPointerCount = currentTouch.pointerCount;
+ uint32_t lastPointerCount = lastTouch.pointerCount;
+
+ if (currentPointerCount == 0) {
+ // No pointers to assign.
+ currentTouch.idBits.clear();
+ } else if (lastPointerCount == 0) {
+ // All pointers are new.
+ currentTouch.idBits.clear();
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ currentTouch.pointers[i].id = i;
+ currentTouch.idToIndex[i] = i;
+ currentTouch.idBits.markBit(i);
+ }
+ } else if (currentPointerCount == 1 && lastPointerCount == 1) {
+ // Only one pointer and no change in count so it must have the same id as before.
+ uint32_t id = lastTouch.pointers[0].id;
+ currentTouch.pointers[0].id = id;
+ currentTouch.idToIndex[id] = 0;
+ currentTouch.idBits.value = BitSet32::valueForBit(id);
+ } else {
+ // General case.
+ // We build a heap of squared euclidean distances between current and last pointers
+ // associated with the current and last pointer indices. Then, we find the best
+ // match (by distance) for each current pointer.
+ PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+ uint32_t heapSize = 0;
+ for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+ currentPointerIndex++) {
+ for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+ lastPointerIndex++) {
+ int64_t deltaX = currentTouch.pointers[currentPointerIndex].x
+ - lastTouch.pointers[lastPointerIndex].x;
+ int64_t deltaY = currentTouch.pointers[currentPointerIndex].y
+ - lastTouch.pointers[lastPointerIndex].y;
+
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+ // Insert new element into the heap (sift up).
+ heap[heapSize].currentPointerIndex = currentPointerIndex;
+ heap[heapSize].lastPointerIndex = lastPointerIndex;
+ heap[heapSize].distance = distance;
+ heapSize += 1;
+ }
+ }
+
+ // Heapify
+ for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+ startIndex -= 1;
+ for (uint32_t parentIndex = startIndex; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+
+ // Pull matches out by increasing order of distance.
+ // To avoid reassigning pointers that have already been matched, the loop keeps track
+ // of which last and current pointers have been matched using the matchedXXXBits variables.
+ // It also tracks the used pointer id bits.
+ BitSet32 matchedLastBits(0);
+ BitSet32 matchedCurrentBits(0);
+ BitSet32 usedIdBits(0);
+ bool first = true;
+ for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
+ for (;;) {
+ if (first) {
+ // The first time through the loop, we just consume the root element of
+ // the heap (the one with smallest distance).
+ first = false;
+ } else {
+ // Previous iterations consumed the root element of the heap.
+ // Pop root element off of the heap (sift down).
+ heapSize -= 1;
+ assert(heapSize > 0);
+
+ // Sift down.
+ heap[0] = heap[heapSize];
+ for (uint32_t parentIndex = 0; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+ }
+
+ uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+ if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+ uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+ if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+ matchedCurrentBits.markBit(currentPointerIndex);
+ matchedLastBits.markBit(lastPointerIndex);
+
+ uint32_t id = lastTouch.pointers[lastPointerIndex].id;
+ currentTouch.pointers[currentPointerIndex].id = id;
+ currentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+ break;
+ }
+ }
+
+ // Assign fresh ids to new pointers.
+ if (currentPointerCount > lastPointerCount) {
+ for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
+ uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
+ uint32_t id = usedIdBits.firstUnmarkedBit();
+
+ currentTouch.pointers[currentPointerIndex].id = id;
+ currentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+ currentPointerIndex, id);
+#endif
+
+ if (--i == 0) break; // done
+ matchedCurrentBits.markBit(currentPointerIndex);
+ }
+ }
+
+ // Fix id bits.
+ currentTouch.idBits = usedIdBits;
+ }
+}
+
+/* Special hack for devices that have bad screen data: if one of the
+ * points has moved more than a screen height from the last position,
+ * then drop it. */
+bool InputDevice::TouchScreenState::applyBadTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! parameters.yAxis.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = currentTouch.pointerCount;
+
+ // Nothing to do if there are no points.
+ if (pointerCount == 0) {
+ return false;
+ }
+
+ // Don't do anything if a finger is going down or up. We run
+ // here before assigning pointer IDs, so there isn't a good
+ // way to do per-finger matching.
+ if (pointerCount != lastTouch.pointerCount) {
+ return false;
+ }
+
+ // We consider a single movement across more than a 7/16 of
+ // the long size of the screen to be bad. This was a magic value
+ // determined by looking at the maximum distance it is feasible
+ // to actually move in one sample.
+ int32_t maxDeltaY = parameters.yAxis.range * 7 / 16;
+
+ // XXX The original code in InputDevice.java included commented out
+ // code for testing the X axis. Note that when we drop a point
+ // we don't actually restore the old X either. Strange.
+ // The old code also tries to track when bad points were previously
+ // detected but it turns out that due to the placement of a "break"
+ // at the end of the loop, we never set mDroppedBadPoint to true
+ // so it is effectively dead code.
+ // Need to figure out if the old code is busted or just overcomplicated
+ // but working as intended.
+
+ // Look through all new points and see if any are farther than
+ // acceptable from all previous points.
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t y = currentTouch.pointers[i].y;
+ int32_t closestY = INT_MAX;
+ int32_t closestDeltaY = 0;
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
+#endif
+
+ for (uint32_t j = pointerCount; j-- > 0; ) {
+ int32_t lastY = lastTouch.pointers[j].y;
+ int32_t deltaY = abs(y - lastY);
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
+ j, lastY, deltaY);
+#endif
+
+ if (deltaY < maxDeltaY) {
+ goto SkipSufficientlyClosePoint;
+ }
+ if (deltaY < closestDeltaY) {
+ closestDeltaY = deltaY;
+ closestY = lastY;
+ }
+ }
+
+ // Must not have found a close enough match.
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
+ i, y, closestY, closestDeltaY, maxDeltaY);
+#endif
+
+ currentTouch.pointers[i].y = closestY;
+ return true; // XXX original code only corrects one point
+
+ SkipSufficientlyClosePoint: ;
+ }
+
+ // No change.
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: drop points where
+ * the coordinate value for one axis has jumped to the other pointer's location.
+ */
+bool InputDevice::TouchScreenState::applyJumpyTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! parameters.yAxis.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = currentTouch.pointerCount;
+ if (lastTouch.pointerCount != pointerCount) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
+ lastTouch.pointerCount, pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d (%d, %d)", i,
+ currentTouch.pointers[i].x, currentTouch.pointers[i].y);
+ }
+#endif
+
+ if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ if (lastTouch.pointerCount == 1 && pointerCount == 2) {
+ // Just drop the first few events going from 1 to 2 pointers.
+ // They're bad often enough that they're not worth considering.
+ currentTouch.pointerCount = 1;
+ jumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Pointer 2 dropped");
+#endif
+ return true;
+ } else if (lastTouch.pointerCount == 2 && pointerCount == 1) {
+ // The event when we go from 2 -> 1 tends to be messed up too
+ currentTouch.pointerCount = 2;
+ currentTouch.pointers[0] = lastTouch.pointers[0];
+ currentTouch.pointers[1] = lastTouch.pointers[1];
+ jumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ for (int32_t i = 0; i < 2; i++) {
+ LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
+ currentTouch.pointers[i].x, currentTouch.pointers[i].y);
+ }
+#endif
+ return true;
+ }
+ }
+ // Reset jumpy points dropped on other transitions or if limit exceeded.
+ jumpyTouchFilter.jumpyPointsDropped = 0;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Transition - drop limit reset");
+#endif
+ return false;
+ }
+
+ // We have the same number of pointers as last time.
+ // A 'jumpy' point is one where the coordinate value for one axis
+ // has jumped to the other pointer's location. No need to do anything
+ // else if we only have one pointer.
+ if (pointerCount < 2) {
+ return false;
+ }
+
+ if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
+ int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR;
+
+ // We only replace the single worst jumpy point as characterized by pointer distance
+ // in a single axis.
+ int32_t badPointerIndex = -1;
+ int32_t badPointerReplacementIndex = -1;
+ int32_t badPointerDistance = INT_MIN; // distance to be corrected
+
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t x = currentTouch.pointers[i].x;
+ int32_t y = currentTouch.pointers[i].y;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
+#endif
+
+ // Check if a touch point is too close to another's coordinates
+ bool dropX = false, dropY = false;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ if (i == j) {
+ continue;
+ }
+
+ if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) {
+ dropX = true;
+ break;
+ }
+
+ if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) {
+ dropY = true;
+ break;
+ }
+ }
+ if (! dropX && ! dropY) {
+ continue; // not jumpy
+ }
+
+ // Find a replacement candidate by comparing with older points on the
+ // complementary (non-jumpy) axis.
+ int32_t distance = INT_MIN; // distance to be corrected
+ int32_t replacementIndex = -1;
+
+ if (dropX) {
+ // X looks too close. Find an older replacement point with a close Y.
+ int32_t smallestDeltaY = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaY = abs(y - lastTouch.pointers[j].y);
+ if (deltaY < smallestDeltaY) {
+ smallestDeltaY = deltaY;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(x - lastTouch.pointers[replacementIndex].x);
+ } else {
+ // Y looks too close. Find an older replacement point with a close X.
+ int32_t smallestDeltaX = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaX = abs(x - lastTouch.pointers[j].x);
+ if (deltaX < smallestDeltaX) {
+ smallestDeltaX = deltaX;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(y - lastTouch.pointers[replacementIndex].y);
+ }
+
+ // If replacing this pointer would correct a worse error than the previous ones
+ // considered, then use this replacement instead.
+ if (distance > badPointerDistance) {
+ badPointerIndex = i;
+ badPointerReplacementIndex = replacementIndex;
+ badPointerDistance = distance;
+ }
+ }
+
+ // Correct the jumpy pointer if one was found.
+ if (badPointerIndex >= 0) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
+ badPointerIndex,
+ lastTouch.pointers[badPointerReplacementIndex].x,
+ lastTouch.pointers[badPointerReplacementIndex].y);
+#endif
+
+ currentTouch.pointers[badPointerIndex].x =
+ lastTouch.pointers[badPointerReplacementIndex].x;
+ currentTouch.pointers[badPointerIndex].y =
+ lastTouch.pointers[badPointerReplacementIndex].y;
+ jumpyTouchFilter.jumpyPointsDropped += 1;
+ return true;
+ }
+ }
+
+ jumpyTouchFilter.jumpyPointsDropped = 0;
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: aggregate and
+ * compute averages of the coordinate data, to reduce the amount of
+ * jitter seen by applications. */
+void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
+ for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) {
+ uint32_t id = currentTouch.pointers[currentIndex].id;
+ int32_t x = currentTouch.pointers[currentIndex].x;
+ int32_t y = currentTouch.pointers[currentIndex].y;
+ int32_t pressure = currentTouch.pointers[currentIndex].pressure;
+
+ if (lastTouch.idBits.hasBit(id)) {
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
+ uint32_t start = averagingTouchFilter.historyStart[id];
+ uint32_t end = averagingTouchFilter.historyEnd[id];
+
+ int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x;
+ int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y;
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
+ id, distance);
+#endif
+
+ if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
+ end += 1;
+ if (end > AVERAGING_HISTORY_SIZE) {
+ end = 0;
+ }
+
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
+ if (end == start) {
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ // Add the raw data to the historical trace.
+ averagingTouchFilter.historyStart[id] = start;
+ averagingTouchFilter.historyEnd[id] = end;
+ averagingTouchFilter.historyData[end].pointers[id].x = x;
+ averagingTouchFilter.historyData[end].pointers[id].y = y;
+ averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+
+ // Average over all historical positions in the trace by total pressure.
+ int32_t averagedX = 0;
+ int32_t averagedY = 0;
+ int32_t totalPressure = 0;
+ for (;;) {
+ int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
+ int32_t historicalPressure = averagingTouchFilter.historyData[start]
+ .pointers[id].pressure;
+
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
+ totalPressure += historicalPressure;
+
+ if (start == end) {
+ break;
+ }
+
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ averagedX /= totalPressure;
+ averagedY /= totalPressure;
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - "
+ "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
+ averagedX, averagedY);
+#endif
+
+ currentTouch.pointers[currentIndex].x = averagedX;
+ currentTouch.pointers[currentIndex].y = averagedY;
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
+#endif
+ }
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
+#endif
+ }
+
+ // Reset pointer history.
+ averagingTouchFilter.historyStart[id] = 0;
+ averagingTouchFilter.historyEnd[id] = 0;
+ averagingTouchFilter.historyData[0].pointers[id].x = x;
+ averagingTouchFilter.historyData[0].pointers[id].y = y;
+ averagingTouchFilter.historyData[0].pointers[id].pressure = pressure;
+ }
+}
+
+bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const {
+ if (! parameters.xAxis.valid || ! parameters.yAxis.valid) {
+ // Assume all points on a touch screen without valid axis parameters are
+ // inside the display.
+ return true;
+ }
+
+ return x >= parameters.xAxis.minValue
+ && x <= parameters.xAxis.maxValue
+ && y >= parameters.yAxis.minValue
+ && y <= parameters.yAxis.maxValue;
+}
+
+const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const {
+ int32_t x = currentTouch.pointers[0].x;
+ int32_t y = currentTouch.pointers[0].y;
+ for (size_t i = 0; i < virtualKeys.size(); i++) {
+ const InputDevice::VirtualKey& virtualKey = virtualKeys[i];
+
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y,
+ virtualKey.keyCode, virtualKey.scanCode,
+ virtualKey.hitLeft, virtualKey.hitTop,
+ virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+ if (virtualKey.isHit(x, y)) {
+ return & virtualKey;
+ }
+ }
+
+ return NULL;
+}
+
+
+// --- InputDevice::SingleTouchScreenState ---
+
+void InputDevice::SingleTouchScreenState::reset() {
+ accumulator.clear();
+ current.down = false;
+ current.x = 0;
+ current.y = 0;
+ current.pressure = 0;
+ current.size = 0;
+}
+
+
+// --- InputDevice::MultiTouchScreenState ---
+
+void InputDevice::MultiTouchScreenState::reset() {
+ accumulator.clear();
+}
+
+} // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 8f6d1fe..c4ffce1 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -40,10 +40,10 @@
// TODO, this needs to be somewhere else, perhaps in the policy
static inline bool isMovementKey(int32_t keyCode) {
- return keyCode == KEYCODE_DPAD_UP
- || keyCode == KEYCODE_DPAD_DOWN
- || keyCode == KEYCODE_DPAD_LEFT
- || keyCode == KEYCODE_DPAD_RIGHT;
+ return keyCode == AKEYCODE_DPAD_UP
+ || keyCode == AKEYCODE_DPAD_DOWN
+ || keyCode == AKEYCODE_DPAD_LEFT
+ || keyCode == AKEYCODE_DPAD_RIGHT;
}
static inline nsecs_t now() {
@@ -54,7 +54,7 @@
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy) {
- mPollLoop = new PollLoop();
+ mPollLoop = new PollLoop(false);
mInboundQueue.head.refCount = -1;
mInboundQueue.head.type = EventEntry::TYPE_SENTINEL;
@@ -299,14 +299,13 @@
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
entry->eventTime = currentTime;
- entry->downTime = currentTime;
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
} else {
KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
entry->deviceId, entry->nature, policyFlags,
entry->action, entry->flags, entry->keyCode, entry->scanCode,
- entry->metaState, entry->repeatCount + 1, currentTime);
+ entry->metaState, entry->repeatCount + 1, entry->downTime);
mKeyRepeatState.lastKeyEntry = newEntry;
mAllocator.releaseKeyEntry(entry);
@@ -314,6 +313,10 @@
entry = newEntry;
}
+ if (entry->repeatCount == 1) {
+ entry->flags |= KEY_EVENT_FLAG_LONG_PRESS;
+ }
+
mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
#if DEBUG_OUTBOUND_EVENT_DETAILS
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 899027c..0a21db7 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -33,18 +33,6 @@
/** Amount that trackball needs to move in order to generate a key event. */
#define TRACKBALL_MOVEMENT_THRESHOLD 6
-/* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
-#define JUMPY_EPSILON_DIVISOR 212
-
-/* Number of jumpy points to drop for touchscreens that need it. */
-#define JUMPY_TRANSITION_DROPS 3
-#define JUMPY_DROP_LIMIT 3
-
-/* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
-#define AVERAGING_DISTANCE_LIMIT (75 * 75)
-
namespace android {
@@ -71,19 +59,19 @@
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
int32_t mask;
switch (keyCode) {
- case KEYCODE_ALT_LEFT:
+ case AKEYCODE_ALT_LEFT:
mask = META_ALT_LEFT_ON;
break;
- case KEYCODE_ALT_RIGHT:
+ case AKEYCODE_ALT_RIGHT:
mask = META_ALT_RIGHT_ON;
break;
- case KEYCODE_SHIFT_LEFT:
+ case AKEYCODE_SHIFT_LEFT:
mask = META_SHIFT_LEFT_ON;
break;
- case KEYCODE_SHIFT_RIGHT:
+ case AKEYCODE_SHIFT_RIGHT:
mask = META_SHIFT_RIGHT_ON;
break;
- case KEYCODE_SYM:
+ case AKEYCODE_SYM:
mask = META_SYM_ON;
break;
default:
@@ -107,10 +95,10 @@
static const int32_t keyCodeRotationMap[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
- { KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT },
- { KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN },
- { KEYCODE_DPAD_UP, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT },
- { KEYCODE_DPAD_LEFT, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_UP },
+ { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
+ { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
+ { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
+ { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
};
static const int keyCodeRotationMapSize =
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
@@ -127,645 +115,6 @@
}
-// --- InputDevice ---
-
-InputDevice::InputDevice(int32_t id, uint32_t classes, String8 name) :
- id(id), classes(classes), name(name), ignored(false) {
-}
-
-void InputDevice::reset() {
- if (isKeyboard()) {
- keyboard.reset();
- }
-
- if (isTrackball()) {
- trackball.reset();
- }
-
- if (isMultiTouchScreen()) {
- multiTouchScreen.reset();
- } else if (isSingleTouchScreen()) {
- singleTouchScreen.reset();
- }
-
- if (isTouchScreen()) {
- touchScreen.reset();
- }
-}
-
-
-// --- InputDevice::TouchData ---
-
-void InputDevice::TouchData::copyFrom(const TouchData& other) {
- pointerCount = other.pointerCount;
- idBits = other.idBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointers[i] = other.pointers[i];
- idToIndex[i] = other.idToIndex[i];
- }
-}
-
-
-// --- InputDevice::KeyboardState ---
-
-void InputDevice::KeyboardState::reset() {
- current.metaState = META_NONE;
- current.downTime = 0;
-}
-
-
-// --- InputDevice::TrackballState ---
-
-void InputDevice::TrackballState::reset() {
- accumulator.clear();
- current.down = false;
- current.downTime = 0;
-}
-
-
-// --- InputDevice::TouchScreenState ---
-
-void InputDevice::TouchScreenState::reset() {
- lastTouch.clear();
- downTime = 0;
- currentVirtualKey.down = false;
-
- for (uint32_t i = 0; i < MAX_POINTERS; i++) {
- averagingTouchFilter.historyStart[i] = 0;
- averagingTouchFilter.historyEnd[i] = 0;
- }
-
- jumpyTouchFilter.jumpyPointsDropped = 0;
-}
-
-struct PointerDistanceHeapElement {
- uint32_t currentPointerIndex : 8;
- uint32_t lastPointerIndex : 8;
- uint64_t distance : 48; // squared distance
-};
-
-void InputDevice::TouchScreenState::calculatePointerIds() {
- uint32_t currentPointerCount = currentTouch.pointerCount;
- uint32_t lastPointerCount = lastTouch.pointerCount;
-
- if (currentPointerCount == 0) {
- // No pointers to assign.
- currentTouch.idBits.clear();
- } else if (lastPointerCount == 0) {
- // All pointers are new.
- currentTouch.idBits.clear();
- for (uint32_t i = 0; i < currentPointerCount; i++) {
- currentTouch.pointers[i].id = i;
- currentTouch.idToIndex[i] = i;
- currentTouch.idBits.markBit(i);
- }
- } else if (currentPointerCount == 1 && lastPointerCount == 1) {
- // Only one pointer and no change in count so it must have the same id as before.
- uint32_t id = lastTouch.pointers[0].id;
- currentTouch.pointers[0].id = id;
- currentTouch.idToIndex[id] = 0;
- currentTouch.idBits.value = BitSet32::valueForBit(id);
- } else {
- // General case.
- // We build a heap of squared euclidean distances between current and last pointers
- // associated with the current and last pointer indices. Then, we find the best
- // match (by distance) for each current pointer.
- PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
-
- uint32_t heapSize = 0;
- for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
- currentPointerIndex++) {
- for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
- lastPointerIndex++) {
- int64_t deltaX = currentTouch.pointers[currentPointerIndex].x
- - lastTouch.pointers[lastPointerIndex].x;
- int64_t deltaY = currentTouch.pointers[currentPointerIndex].y
- - lastTouch.pointers[lastPointerIndex].y;
-
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
- // Insert new element into the heap (sift up).
- heap[heapSize].currentPointerIndex = currentPointerIndex;
- heap[heapSize].lastPointerIndex = lastPointerIndex;
- heap[heapSize].distance = distance;
- heapSize += 1;
- }
- }
-
- // Heapify
- for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
- startIndex -= 1;
- for (uint32_t parentIndex = startIndex; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
-
- // Pull matches out by increasing order of distance.
- // To avoid reassigning pointers that have already been matched, the loop keeps track
- // of which last and current pointers have been matched using the matchedXXXBits variables.
- // It also tracks the used pointer id bits.
- BitSet32 matchedLastBits(0);
- BitSet32 matchedCurrentBits(0);
- BitSet32 usedIdBits(0);
- bool first = true;
- for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
- for (;;) {
- if (first) {
- // The first time through the loop, we just consume the root element of
- // the heap (the one with smallest distance).
- first = false;
- } else {
- // Previous iterations consumed the root element of the heap.
- // Pop root element off of the heap (sift down).
- heapSize -= 1;
- assert(heapSize > 0);
-
- // Sift down.
- heap[0] = heap[heapSize];
- for (uint32_t parentIndex = 0; ;) {
- uint32_t childIndex = parentIndex * 2 + 1;
- if (childIndex >= heapSize) {
- break;
- }
-
- if (childIndex + 1 < heapSize
- && heap[childIndex + 1].distance < heap[childIndex].distance) {
- childIndex += 1;
- }
-
- if (heap[parentIndex].distance <= heap[childIndex].distance) {
- break;
- }
-
- swap(heap[parentIndex], heap[childIndex]);
- parentIndex = childIndex;
- }
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
- i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
- heap[i].distance);
- }
-#endif
- }
-
- uint32_t currentPointerIndex = heap[0].currentPointerIndex;
- if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
- uint32_t lastPointerIndex = heap[0].lastPointerIndex;
- if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
- matchedCurrentBits.markBit(currentPointerIndex);
- matchedLastBits.markBit(lastPointerIndex);
-
- uint32_t id = lastTouch.pointers[lastPointerIndex].id;
- currentTouch.pointers[currentPointerIndex].id = id;
- currentTouch.idToIndex[id] = currentPointerIndex;
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
- break;
- }
- }
-
- // Assign fresh ids to new pointers.
- if (currentPointerCount > lastPointerCount) {
- for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
- uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
- uint32_t id = usedIdBits.firstUnmarkedBit();
-
- currentTouch.pointers[currentPointerIndex].id = id;
- currentTouch.idToIndex[id] = currentPointerIndex;
- usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
- LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
- currentPointerIndex, id);
-#endif
-
- if (--i == 0) break; // done
- matchedCurrentBits.markBit(currentPointerIndex);
- }
- }
-
- // Fix id bits.
- currentTouch.idBits = usedIdBits;
- }
-}
-
-/* Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it. */
-bool InputDevice::TouchScreenState::applyBadTouchFilter() {
- // This hack requires valid axis parameters.
- if (! parameters.yAxis.valid) {
- return false;
- }
-
- uint32_t pointerCount = currentTouch.pointerCount;
-
- // Nothing to do if there are no points.
- if (pointerCount == 0) {
- return false;
- }
-
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (pointerCount != lastTouch.pointerCount) {
- return false;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- int32_t maxDeltaY = parameters.yAxis.range * 7 / 16;
-
- // XXX The original code in InputDevice.java included commented out
- // code for testing the X axis. Note that when we drop a point
- // we don't actually restore the old X either. Strange.
- // The old code also tries to track when bad points were previously
- // detected but it turns out that due to the placement of a "break"
- // at the end of the loop, we never set mDroppedBadPoint to true
- // so it is effectively dead code.
- // Need to figure out if the old code is busted or just overcomplicated
- // but working as intended.
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t y = currentTouch.pointers[i].y;
- int32_t closestY = INT_MAX;
- int32_t closestDeltaY = 0;
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
-#endif
-
- for (uint32_t j = pointerCount; j-- > 0; ) {
- int32_t lastY = lastTouch.pointers[j].y;
- int32_t deltaY = abs(y - lastY);
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
- j, lastY, deltaY);
-#endif
-
- if (deltaY < maxDeltaY) {
- goto SkipSufficientlyClosePoint;
- }
- if (deltaY < closestDeltaY) {
- closestDeltaY = deltaY;
- closestY = lastY;
- }
- }
-
- // Must not have found a close enough match.
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
- i, y, closestY, closestDeltaY, maxDeltaY);
-#endif
-
- currentTouch.pointers[i].y = closestY;
- return true; // XXX original code only corrects one point
-
- SkipSufficientlyClosePoint: ;
- }
-
- // No change.
- return false;
-}
-
-/* Special hack for devices that have bad screen data: drop points where
- * the coordinate value for one axis has jumped to the other pointer's location.
- */
-bool InputDevice::TouchScreenState::applyJumpyTouchFilter() {
- // This hack requires valid axis parameters.
- if (! parameters.yAxis.valid) {
- return false;
- }
-
- uint32_t pointerCount = currentTouch.pointerCount;
- if (lastTouch.pointerCount != pointerCount) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
- lastTouch.pointerCount, pointerCount);
- for (uint32_t i = 0; i < pointerCount; i++) {
- LOGD(" Pointer %d (%d, %d)", i,
- currentTouch.pointers[i].x, currentTouch.pointers[i].y);
- }
-#endif
-
- if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- if (lastTouch.pointerCount == 1 && pointerCount == 2) {
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- currentTouch.pointerCount = 1;
- jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Pointer 2 dropped");
-#endif
- return true;
- } else if (lastTouch.pointerCount == 2 && pointerCount == 1) {
- // The event when we go from 2 -> 1 tends to be messed up too
- currentTouch.pointerCount = 2;
- currentTouch.pointers[0] = lastTouch.pointers[0];
- currentTouch.pointers[1] = lastTouch.pointers[1];
- jumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- for (int32_t i = 0; i < 2; i++) {
- LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
- currentTouch.pointers[i].x, currentTouch.pointers[i].y);
- }
-#endif
- return true;
- }
- }
- // Reset jumpy points dropped on other transitions or if limit exceeded.
- jumpyTouchFilter.jumpyPointsDropped = 0;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Transition - drop limit reset");
-#endif
- return false;
- }
-
- // We have the same number of pointers as last time.
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (pointerCount < 2) {
- return false;
- }
-
- if (jumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
- int jumpyEpsilon = parameters.yAxis.range / JUMPY_EPSILON_DIVISOR;
-
- // We only replace the single worst jumpy point as characterized by pointer distance
- // in a single axis.
- int32_t badPointerIndex = -1;
- int32_t badPointerReplacementIndex = -1;
- int32_t badPointerDistance = INT_MIN; // distance to be corrected
-
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t x = currentTouch.pointers[i].x;
- int32_t y = currentTouch.pointers[i].y;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
-#endif
-
- // Check if a touch point is too close to another's coordinates
- bool dropX = false, dropY = false;
- for (uint32_t j = 0; j < pointerCount; j++) {
- if (i == j) {
- continue;
- }
-
- if (abs(x - currentTouch.pointers[j].x) <= jumpyEpsilon) {
- dropX = true;
- break;
- }
-
- if (abs(y - currentTouch.pointers[j].y) <= jumpyEpsilon) {
- dropY = true;
- break;
- }
- }
- if (! dropX && ! dropY) {
- continue; // not jumpy
- }
-
- // Find a replacement candidate by comparing with older points on the
- // complementary (non-jumpy) axis.
- int32_t distance = INT_MIN; // distance to be corrected
- int32_t replacementIndex = -1;
-
- if (dropX) {
- // X looks too close. Find an older replacement point with a close Y.
- int32_t smallestDeltaY = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaY = abs(y - lastTouch.pointers[j].y);
- if (deltaY < smallestDeltaY) {
- smallestDeltaY = deltaY;
- replacementIndex = j;
- }
- }
- distance = abs(x - lastTouch.pointers[replacementIndex].x);
- } else {
- // Y looks too close. Find an older replacement point with a close X.
- int32_t smallestDeltaX = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaX = abs(x - lastTouch.pointers[j].x);
- if (deltaX < smallestDeltaX) {
- smallestDeltaX = deltaX;
- replacementIndex = j;
- }
- }
- distance = abs(y - lastTouch.pointers[replacementIndex].y);
- }
-
- // If replacing this pointer would correct a worse error than the previous ones
- // considered, then use this replacement instead.
- if (distance > badPointerDistance) {
- badPointerIndex = i;
- badPointerReplacementIndex = replacementIndex;
- badPointerDistance = distance;
- }
- }
-
- // Correct the jumpy pointer if one was found.
- if (badPointerIndex >= 0) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
- badPointerIndex,
- lastTouch.pointers[badPointerReplacementIndex].x,
- lastTouch.pointers[badPointerReplacementIndex].y);
-#endif
-
- currentTouch.pointers[badPointerIndex].x =
- lastTouch.pointers[badPointerReplacementIndex].x;
- currentTouch.pointers[badPointerIndex].y =
- lastTouch.pointers[badPointerReplacementIndex].y;
- jumpyTouchFilter.jumpyPointsDropped += 1;
- return true;
- }
- }
-
- jumpyTouchFilter.jumpyPointsDropped = 0;
- return false;
-}
-
-/* Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications. */
-void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
- for (uint32_t currentIndex = 0; currentIndex < currentTouch.pointerCount; currentIndex++) {
- uint32_t id = currentTouch.pointers[currentIndex].id;
- int32_t x = currentTouch.pointers[currentIndex].x;
- int32_t y = currentTouch.pointers[currentIndex].y;
- int32_t pressure = currentTouch.pointers[currentIndex].pressure;
-
- if (lastTouch.idBits.hasBit(id)) {
- // Pointer was down before and is still down now.
- // Compute average over history trace.
- uint32_t start = averagingTouchFilter.historyStart[id];
- uint32_t end = averagingTouchFilter.historyEnd[id];
-
- int64_t deltaX = x - averagingTouchFilter.historyData[end].pointers[id].x;
- int64_t deltaY = y - averagingTouchFilter.historyData[end].pointers[id].y;
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
- id, distance);
-#endif
-
- if (distance < AVERAGING_DISTANCE_LIMIT) {
- // Increment end index in preparation for recording new historical data.
- end += 1;
- if (end > AVERAGING_HISTORY_SIZE) {
- end = 0;
- }
-
- // If the end index has looped back to the start index then we have filled
- // the historical trace up to the desired size so we drop the historical
- // data at the start of the trace.
- if (end == start) {
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- // Add the raw data to the historical trace.
- averagingTouchFilter.historyStart[id] = start;
- averagingTouchFilter.historyEnd[id] = end;
- averagingTouchFilter.historyData[end].pointers[id].x = x;
- averagingTouchFilter.historyData[end].pointers[id].y = y;
- averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
-
- // Average over all historical positions in the trace by total pressure.
- int32_t averagedX = 0;
- int32_t averagedY = 0;
- int32_t totalPressure = 0;
- for (;;) {
- int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
- int32_t historicalPressure = averagingTouchFilter.historyData[start]
- .pointers[id].pressure;
-
- averagedX += historicalX * historicalPressure;
- averagedY += historicalY * historicalPressure;
- totalPressure += historicalPressure;
-
- if (start == end) {
- break;
- }
-
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- averagedX /= totalPressure;
- averagedY /= totalPressure;
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - "
- "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
- averagedX, averagedY);
-#endif
-
- currentTouch.pointers[currentIndex].x = averagedX;
- currentTouch.pointers[currentIndex].y = averagedY;
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
-#endif
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
-#endif
- }
-
- // Reset pointer history.
- averagingTouchFilter.historyStart[id] = 0;
- averagingTouchFilter.historyEnd[id] = 0;
- averagingTouchFilter.historyData[0].pointers[id].x = x;
- averagingTouchFilter.historyData[0].pointers[id].y = y;
- averagingTouchFilter.historyData[0].pointers[id].pressure = pressure;
- }
-}
-
-bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const {
- if (! parameters.xAxis.valid || ! parameters.yAxis.valid) {
- // Assume all points on a touch screen without valid axis parameters are
- // inside the display.
- return true;
- }
-
- return x >= parameters.xAxis.minValue
- && x <= parameters.xAxis.maxValue
- && y >= parameters.yAxis.minValue
- && y <= parameters.yAxis.maxValue;
-}
-
-
-// --- InputDevice::SingleTouchScreenState ---
-
-void InputDevice::SingleTouchScreenState::reset() {
- accumulator.clear();
- current.down = false;
- current.x = 0;
- current.y = 0;
- current.pressure = 0;
- current.size = 0;
-}
-
-
-// --- InputDevice::MultiTouchScreenState ---
-
-void InputDevice::MultiTouchScreenState::reset() {
- accumulator.clear();
-}
-
-
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
@@ -904,32 +253,30 @@
bool down = rawEvent->value != 0;
int32_t scanCode = rawEvent->scanCode;
- if (device->isKeyboard() && (scanCode < BTN_FIRST || scanCode > BTN_LAST)) {
- int32_t keyCode = rawEvent->keyCode;
- onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
- } else if (device->isSingleTouchScreen()) {
+ if (device->isSingleTouchScreen()) {
switch (rawEvent->scanCode) {
case BTN_TOUCH:
device->singleTouchScreen.accumulator.fields |=
InputDevice::SingleTouchScreenState::Accumulator::FIELD_BTN_TOUCH;
device->singleTouchScreen.accumulator.btnTouch = down;
- break;
+ return;
}
- } else if (device->isTrackball()) {
+ }
+
+ if (device->isTrackball()) {
switch (rawEvent->scanCode) {
case BTN_MOUSE:
device->trackball.accumulator.fields |=
InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
device->trackball.accumulator.btnMouse = down;
-
- // send the down immediately
- // XXX this emulates the old behavior of KeyInputQueue, unclear whether it is
- // necessary or if we can wait until the next sync
- onTrackballStateChanged(rawEvent->when, device);
- device->trackball.accumulator.clear();
- break;
+ return;
}
}
+
+ if (device->isKeyboard()) {
+ int32_t keyCode = rawEvent->keyCode;
+ onKey(rawEvent->when, device, down, keyCode, scanCode, rawEvent->flags);
+ }
}
void InputReader::handleRelativeMotion(const RawEvent* rawEvent) {
@@ -1269,81 +616,76 @@
bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
InputDevice* device, uint32_t policyFlags) {
- if (device->touchScreen.currentVirtualKey.down) {
+ switch (device->touchScreen.currentVirtualKey.status) {
+ case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED:
if (device->touchScreen.currentTouch.pointerCount == 0) {
- // Pointer went up while virtual key was down. Send key up event.
- device->touchScreen.currentVirtualKey.down = false;
+ // Pointer went up after virtual key canceled.
+ device->touchScreen.currentVirtualKey.status =
+ InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
+ }
+ return true; // consumed
+ case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN:
+ if (device->touchScreen.currentTouch.pointerCount == 0) {
+ // Pointer went up while virtual key was down.
+ device->touchScreen.currentVirtualKey.status =
+ InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
device->touchScreen.currentVirtualKey.keyCode,
device->touchScreen.currentVirtualKey.scanCode);
#endif
-
dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP,
KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
return true; // consumed
}
- int32_t x = device->touchScreen.currentTouch.pointers[0].x;
- int32_t y = device->touchScreen.currentTouch.pointers[0].y;
- if (device->touchScreen.isPointInsideDisplay(x, y)
- || device->touchScreen.currentTouch.pointerCount != 1) {
- // Pointer moved inside the display area or another pointer also went down.
- // Send key cancellation.
- device->touchScreen.currentVirtualKey.down = false;
-
-#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
- device->touchScreen.currentVirtualKey.keyCode,
- device->touchScreen.currentVirtualKey.scanCode);
-#endif
-
- dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP,
- KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY
- | KEY_EVENT_FLAG_CANCELED);
-
- // Clear the last touch data so we will consider the pointer as having just been
- // pressed down when generating subsequent motion events.
- device->touchScreen.lastTouch.clear();
- return false; // not consumed
+ if (device->touchScreen.currentTouch.pointerCount == 1) {
+ const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
+ if (virtualKey
+ && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) {
+ // Pointer is still within the space of the virtual key.
+ return true; // consumed
+ }
}
- } else if (device->touchScreen.currentTouch.pointerCount == 1
- && device->touchScreen.lastTouch.pointerCount == 0) {
- int32_t x = device->touchScreen.currentTouch.pointers[0].x;
- int32_t y = device->touchScreen.currentTouch.pointers[0].y;
- for (size_t i = 0; i < device->touchScreen.virtualKeys.size(); i++) {
- const InputDevice::VirtualKey& virtualKey = device->touchScreen.virtualKeys[i];
+ // Pointer left virtual key area or another pointer also went down.
+ // Send key cancellation.
+ device->touchScreen.currentVirtualKey.status =
+ InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED;
#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y,
- virtualKey.keyCode, virtualKey.scanCode,
- virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
+ LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ device->touchScreen.currentVirtualKey.keyCode,
+ device->touchScreen.currentVirtualKey.scanCode);
#endif
+ dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP,
+ KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | KEY_EVENT_FLAG_CANCELED);
+ return true; // consumed
- if (virtualKey.isHit(x, y)) {
- device->touchScreen.currentVirtualKey.down = true;
+ default:
+ if (device->touchScreen.currentTouch.pointerCount == 1
+ && device->touchScreen.lastTouch.pointerCount == 0) {
+ // Pointer just went down. Check for virtual key hit.
+ const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit();
+ if (virtualKey) {
+ device->touchScreen.currentVirtualKey.status =
+ InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN;
device->touchScreen.currentVirtualKey.downTime = when;
- device->touchScreen.currentVirtualKey.keyCode = virtualKey.keyCode;
- device->touchScreen.currentVirtualKey.scanCode = virtualKey.scanCode;
-
+ device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode;
+ device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode;
#if DEBUG_VIRTUAL_KEYS
- LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- device->touchScreen.currentVirtualKey.keyCode,
- device->touchScreen.currentVirtualKey.scanCode);
+ LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ device->touchScreen.currentVirtualKey.keyCode,
+ device->touchScreen.currentVirtualKey.scanCode);
#endif
-
dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_DOWN,
KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
return true; // consumed
}
}
+ return false; // not consumed
}
-
- return false; // not consumed
}
void InputReader::dispatchVirtualKey(nsecs_t when,
@@ -1356,8 +698,9 @@
nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
int32_t metaState = globalMetaState();
- mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
- keyCode, scanCode, metaState, downTime);
+ if (keyEventAction == KEY_EVENT_ACTION_DOWN) {
+ mPolicy->virtualKeyDownFeedback();
+ }
int32_t policyActions = mPolicy->interceptKey(when, device->id,
keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
@@ -1852,7 +1195,7 @@
uint32_t flags;
if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode,
& keyCode, & flags)) {
- LOGI(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
+ LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
device->touchScreen.virtualKeys.pop(); // drop the key
continue;
}
@@ -1933,7 +1276,8 @@
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
if (device->isTouchScreen()) {
- if (device->touchScreen.currentVirtualKey.down) {
+ if (device->touchScreen.currentVirtualKey.status
+ == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) {
keyCode = device->touchScreen.currentVirtualKey.keyCode;
scanCode = device->touchScreen.currentVirtualKey.scanCode;
}
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 25def3c..fc83e31 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -690,22 +690,3 @@
}
} // namespace android
-
-// --- AInputQueue ---
-
-using android::InputEvent;
-using android::InputChannel;
-using android::InputConsumer;
-using android::sp;
-using android::status_t;
-
-AInputQueue::AInputQueue(const sp<InputChannel>& channel) :
- mConsumer(channel) {
-}
-
-AInputQueue::~AInputQueue() {
-}
-
-status_t AInputQueue::consume(InputEvent** event) {
- return mConsumer.consume(&mInputEventFactory, event);
-}
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index b205418..edf1aed 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -60,7 +60,6 @@
switch (format) {
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
case HAL_PIXEL_FORMAT_YCbCr_422_I:
- case HAL_PIXEL_FORMAT_YV16:
info->bitsPerPixel = 16;
goto done;
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 66b9576..5694e00 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -18,11 +18,11 @@
namespace android {
-static inline int min(int a, int b) {
+static inline int32_t min(int32_t a, int32_t b) {
return (a<b) ? a : b;
}
-static inline int max(int a, int b) {
+static inline int32_t max(int32_t a, int32_t b) {
return (a>b) ? a : b;
}
@@ -53,7 +53,7 @@
return false;
}
-Rect& Rect::offsetTo(int x, int y)
+Rect& Rect::offsetTo(int32_t x, int32_t y)
{
right -= left - x;
bottom -= top - y;
@@ -62,7 +62,7 @@
return *this;
}
-Rect& Rect::offsetBy(int x, int y)
+Rect& Rect::offsetBy(int32_t x, int32_t y)
{
left += x;
top += y;
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 46d7493..62f824f 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -2,6 +2,9 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
test_src_files := \
InputChannel_test.cpp \
InputDispatcher_test.cpp \
@@ -43,3 +46,5 @@
# Build the manual test programs.
include $(call all-subdir-makefiles)
+
+endif
\ No newline at end of file
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index 2d6b531..55504f2 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -76,7 +76,7 @@
const int32_t nature = INPUT_EVENT_NATURE_KEY;
const int32_t action = KEY_EVENT_ACTION_DOWN;
const int32_t flags = KEY_EVENT_FLAG_FROM_SYSTEM;
- const int32_t keyCode = KEYCODE_ENTER;
+ const int32_t keyCode = AKEYCODE_ENTER;
const int32_t scanCode = 13;
const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON;
const int32_t repeatCount = 1;
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 7d4524a..8bd5823 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -26,6 +26,7 @@
Debug.cpp \
FileMap.cpp \
Flattenable.cpp \
+ ObbFile.cpp \
Pool.cpp \
RefBase.cpp \
ResourceTypes.cpp \
@@ -65,6 +66,11 @@
endif
endif
+ifeq ($(HOST_OS),darwin)
+# MacOS doesn't have lseek64. However, off_t is 64-bit anyway.
+LOCAL_CFLAGS += -DOFF_T_IS_64_BIT
+endif
+
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -116,3 +122,13 @@
include $(BUILD_STATIC_LIBRARY)
endif
endif
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
\ No newline at end of file
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
new file mode 100644
index 0000000..fe49300
--- /dev/null
+++ b/libs/utils/ObbFile.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "ObbFile"
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+
+//#define DEBUG 1
+
+#define kFooterTagSize 8 /* last two 32-bit integers */
+
+#define kFooterMinSize 21 /* 32-bit signature version
+ * 32-bit package version
+ * 32-bit package name size
+ * 1-character package name
+ * 32-bit footer size
+ * 32-bit footer marker
+ */
+
+#define kMaxBufSize 32768 /* Maximum file read buffer */
+
+#define kSignature 0x01059983U /* ObbFile signature */
+
+#define kSigVersion 1 /* We only know about signature version 1 */
+
+/* offsets in version 1 of the header */
+#define kPackageVersionOffset 4
+#define kPackageNameLenOffset 8
+#define kPackageNameOffset 12
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+/*
+ * Work around situations where off_t is 64-bit and use off64_t in
+ * situations where it's 32-bit.
+ */
+#ifdef OFF_T_IS_64_BIT
+#define my_lseek64 lseek
+typedef off_t my_off64_t;
+#else
+#define my_lseek64 lseek64
+typedef off64_t my_off64_t;
+#endif
+
+namespace android {
+
+ObbFile::ObbFile() :
+ mVersion(-1) {
+}
+
+ObbFile::~ObbFile() {
+}
+
+bool ObbFile::readFrom(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_RDONLY);
+ if (fd < 0) {
+ LOGW("couldn't open file %s: %s", filename, strerror(errno));
+ goto out;
+ }
+ success = readFrom(fd);
+ close(fd);
+
+ if (!success) {
+ LOGW("failed to read from %s (fd=%d)\n", filename, fd);
+ }
+
+out:
+ return success;
+}
+
+bool ObbFile::readFrom(int fd)
+{
+ if (fd < 0) {
+ LOGW("attempt to read from invalid fd\n");
+ return false;
+ }
+
+ return parseObbFile(fd);
+}
+
+bool ObbFile::parseObbFile(int fd)
+{
+ my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END);
+
+ if (fileLength < kFooterMinSize) {
+ if (fileLength < 0) {
+ LOGW("error seeking in ObbFile: %s\n", strerror(errno));
+ } else {
+ LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
+ }
+ return false;
+ }
+
+ ssize_t actual;
+ size_t footerSize;
+
+ {
+ my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET);
+
+ char *footer = new char[kFooterTagSize];
+ actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize));
+ if (actual != kFooterTagSize) {
+ LOGW("couldn't read footer signature: %s\n", strerror(errno));
+ return false;
+ }
+
+ unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t));
+ if (fileSig != kSignature) {
+ LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n",
+ kSignature, fileSig);
+ return false;
+ }
+
+ footerSize = get4LE((unsigned char*)footer);
+ if (footerSize > (size_t)fileLength - kFooterTagSize
+ || footerSize > kMaxBufSize) {
+ LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n",
+ footerSize, fileLength);
+ return false;
+ }
+
+ if (footerSize < kFooterMinSize) {
+ LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize);
+ return false;
+ }
+ }
+
+ my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
+ if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
+ LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
+ return false;
+ }
+
+ char* scanBuf = (char*)malloc(footerSize);
+ if (scanBuf == NULL) {
+ LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
+ return false;
+ }
+
+ actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize));
+ // readAmount is guaranteed to be less than kMaxBufSize
+ if (actual != (ssize_t)footerSize) {
+ LOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < footerSize; ++i) {
+ LOGI("char: 0x%02x", scanBuf[i]);
+ }
+#endif
+
+ uint32_t sigVersion = get4LE((unsigned char*)scanBuf);
+ if (sigVersion != kSigVersion) {
+ LOGW("Unsupported ObbFile version %d\n", sigVersion);
+ free(scanBuf);
+ return false;
+ }
+
+ mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
+
+ uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
+ if (packageNameLen <= 0
+ || packageNameLen > (footerSize - kPackageNameOffset)) {
+ LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n",
+ packageNameLen, footerSize - kPackageNameOffset);
+ free(scanBuf);
+ return false;
+ }
+
+ char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
+ mPackageName = String8(const_cast<char*>(packageName), packageNameLen);
+
+ free(scanBuf);
+
+#ifdef DEBUG
+ LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion);
+#endif
+
+ return true;
+}
+
+bool ObbFile::writeTo(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_WRONLY);
+ if (fd < 0) {
+ goto out;
+ }
+ success = writeTo(fd);
+ close(fd);
+
+out:
+ if (!success) {
+ LOGW("failed to write to %s: %s\n", filename, strerror(errno));
+ }
+ return success;
+}
+
+bool ObbFile::writeTo(int fd)
+{
+ if (fd < 0) {
+ return false;
+ }
+
+ my_lseek64(fd, 0, SEEK_END);
+
+ if (mPackageName.size() == 0 || mVersion == -1) {
+ LOGW("tried to write uninitialized ObbFile data");
+ return false;
+ }
+
+ unsigned char intBuf[sizeof(uint32_t)+1];
+ memset(&intBuf, 0, sizeof(intBuf));
+
+ put4LE(intBuf, kSigVersion);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write signature version: %s", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, mVersion);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package version");
+ return false;
+ }
+
+ size_t packageNameLen = mPackageName.size();
+ put4LE(intBuf, packageNameLen);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package name length: %s", strerror(errno));
+ return false;
+ }
+
+ if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
+ LOGW("couldn't write package name: %s", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write footer size: %s", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, kSignature);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write footer magic signature: %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+}
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index 58fe141..f740fa0 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -25,8 +25,9 @@
static bool gHaveTLS = false;
static pthread_key_t gTLS = 0;
-PollLoop::PollLoop() :
- mPolling(false), mWaiters(0) {
+PollLoop::PollLoop(bool allowNonCallbacks) :
+ mAllowNonCallbacks(allowNonCallbacks), mPolling(false),
+ mWaiters(0), mPendingFdsPos(0) {
openWakePipe();
}
@@ -106,7 +107,18 @@
// method is currently only called by the destructor.
}
-bool PollLoop::pollOnce(int timeoutMillis) {
+int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
+ // If there are still pending fds from the last call, dispatch those
+ // first, to avoid an earlier fd from starving later ones.
+ const size_t pendingFdsCount = mPendingFds.size();
+ if (mPendingFdsPos < pendingFdsCount) {
+ const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos);
+ mPendingFdsPos++;
+ if (outEvents != NULL) *outEvents = pending.events;
+ if (outData != NULL) *outData = pending.data;
+ return pending.fd;
+ }
+
mLock.lock();
while (mWaiters != 0) {
mResume.wait(mLock);
@@ -114,7 +126,7 @@
mPolling = true;
mLock.unlock();
- bool result;
+ int32_t result;
size_t requestedCount = mRequestedFds.size();
#if DEBUG_POLL_AND_WAKE
@@ -131,7 +143,7 @@
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - timeout", this);
#endif
- result = false;
+ result = POLL_TIMEOUT;
goto Done;
}
@@ -143,7 +155,7 @@
if (errno != EINTR) {
LOGW("Poll failed with an unexpected error, errno=%d", errno);
}
- result = false;
+ result = POLL_ERROR;
goto Done;
}
@@ -156,38 +168,44 @@
#endif
mPendingCallbacks.clear();
+ mPendingFds.clear();
+ mPendingFdsPos = 0;
+ if (outEvents != NULL) *outEvents = 0;
+ if (outData != NULL) *outData = NULL;
+
+ result = POLL_CALLBACK;
for (size_t i = 0; i < requestedCount; i++) {
const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
short revents = requestedFd.revents;
if (revents) {
const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
- Callback callback = requestedCallback.callback;
- ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback;
+ PendingCallback pending;
+ pending.fd = requestedFd.fd;
+ pending.events = revents;
+ pending.callback = requestedCallback.callback;
+ pending.looperCallback = requestedCallback.looperCallback;
+ pending.data = requestedCallback.data;
- if (callback || looperCallback) {
- PendingCallback pendingCallback;
- pendingCallback.fd = requestedFd.fd;
- pendingCallback.events = requestedFd.revents;
- pendingCallback.callback = callback;
- pendingCallback.looperCallback = looperCallback;
- pendingCallback.data = requestedCallback.data;
- mPendingCallbacks.push(pendingCallback);
- } else {
- if (requestedFd.fd == mWakeReadPipeFd) {
-#if DEBUG_POLL_AND_WAKE
- LOGD("%p ~ pollOnce - awoken", this);
-#endif
- char buffer[16];
- ssize_t nRead;
- do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while (nRead == sizeof(buffer));
+ if (pending.callback || pending.looperCallback) {
+ mPendingCallbacks.push(pending);
+ } else if (pending.fd != mWakeReadPipeFd) {
+ if (result == POLL_CALLBACK) {
+ result = pending.fd;
+ if (outEvents != NULL) *outEvents = pending.events;
+ if (outData != NULL) *outData = pending.data;
} else {
-#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
- LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd);
-#endif
+ mPendingFds.push(pending);
}
+ } else {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - awoken", this);
+#endif
+ char buffer[16];
+ ssize_t nRead;
+ do {
+ nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+ } while (nRead == sizeof(buffer));
}
respondedCount -= 1;
@@ -196,7 +214,6 @@
}
}
}
- result = true;
Done:
mLock.lock();
@@ -206,7 +223,7 @@
}
mLock.unlock();
- if (result) {
+ if (result == POLL_CALLBACK || result >= 0) {
size_t pendingCount = mPendingCallbacks.size();
for (size_t i = 0; i < pendingCount; i++) {
const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i);
@@ -247,6 +264,10 @@
}
}
+bool PollLoop::getAllowNonCallbacks() const {
+ return mAllowNonCallbacks;
+}
+
void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
setCallbackCommon(fd, events, callback, NULL, data);
}
@@ -263,12 +284,18 @@
LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
#endif
- if (! events || (! callback && ! looperCallback)) {
- LOGE("Invalid attempt to set a callback with no selected poll events or no callback.");
+ if (! events) {
+ LOGE("Invalid attempt to set a callback with no selected poll events.");
removeCallback(fd);
return;
}
+ if (! callback && ! looperCallback && ! mAllowNonCallbacks) {
+ LOGE("Invalid attempt to set NULL callback but not allowed.");
+ removeCallback(fd);
+ return;
+ }
+
wakeAndLock();
struct pollfd requestedFd;
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 92ebfd7c..b9f206a 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -2,7 +2,11 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
test_src_files := \
+ ObbFile_test.cpp \
PollLoop_test.cpp
shared_libraries := \
@@ -36,3 +40,5 @@
$(eval LOCAL_MODULE_TAGS := $(module_tags)) \
$(eval include $(BUILD_EXECUTABLE)) \
)
+
+endif
\ No newline at end of file
diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp
new file mode 100644
index 0000000..29bb70a
--- /dev/null
+++ b/libs/utils/tests/ObbFile_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "ObbFile_test"
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+
+namespace android {
+
+#define TEST_FILENAME "/test.obb"
+
+class ObbFileTest : public testing::Test {
+protected:
+ sp<ObbFile> mObbFile;
+ char* mExternalStorage;
+ char* mFileName;
+
+ virtual void SetUp() {
+ mObbFile = new ObbFile();
+ mExternalStorage = getenv("EXTERNAL_STORAGE");
+
+ const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
+ mFileName = new char[totalLen];
+ snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
+
+ int fd = ::open(mFileName, O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ FAIL() << "Couldn't create " << mFileName << " for tests";
+ }
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(ObbFileTest, ReadFailure) {
+ EXPECT_FALSE(mObbFile->readFrom(-1))
+ << "No failure on invalid file descriptor";
+}
+
+TEST_F(ObbFileTest, WriteThenRead) {
+ const char* packageName = "com.example.obbfile";
+ const int32_t versionNum = 1;
+
+ mObbFile->setPackageName(String8(packageName));
+ mObbFile->setVersion(versionNum);
+
+ EXPECT_TRUE(mObbFile->writeTo(mFileName))
+ << "couldn't write to fake .obb file";
+
+ mObbFile = new ObbFile();
+
+ EXPECT_TRUE(mObbFile->readFrom(mFileName))
+ << "couldn't read from fake .obb file";
+
+ EXPECT_EQ(versionNum, mObbFile->getVersion())
+ << "version didn't come out the same as it went in";
+ const char* currentPackageName = mObbFile->getPackageName().string();
+ EXPECT_STREQ(packageName, currentPackageName)
+ << "package name didn't come out the same as it went in";
+}
+
+}
diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp
index 4848c0f..02f1808 100644
--- a/libs/utils/tests/PollLoop_test.cpp
+++ b/libs/utils/tests/PollLoop_test.cpp
@@ -87,7 +87,7 @@
sp<PollLoop> mPollLoop;
virtual void SetUp() {
- mPollLoop = new PollLoop();
+ mPollLoop = new PollLoop(false);
}
virtual void TearDown() {
@@ -98,26 +98,26 @@
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) {
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(100);
+ int32_t result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) {
mPollLoop->wake();
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(1000);
+ int32_t result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. zero because wake() was called before waiting";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because loop was awoken";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because loop was awoken";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) {
@@ -125,24 +125,24 @@
delayedWake->run();
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(1000);
+ int32_t result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal wake delay";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because loop was awoken";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because loop was awoken";
}
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) {
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(0);
+ int32_t result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
}
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) {
@@ -152,13 +152,13 @@
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(0);
+ int32_t result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not have been invoked because FD was not signalled";
}
@@ -171,13 +171,13 @@
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(0);
+ int32_t result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because FD was signalled";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
@@ -193,13 +193,13 @@
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(100);
+ int32_t result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not have been invoked because FD was not signalled";
}
@@ -212,15 +212,15 @@
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(100);
+ int32_t result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_EQ(OK, pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because FD was signalled";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
@@ -238,15 +238,15 @@
delayedWriteSignal->run();
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(1000);
+ int32_t result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_EQ(OK, pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal signal delay";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because FD was signalled";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
@@ -264,15 +264,15 @@
mPollLoop->removeCallback(pipe.receiveFd);
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(100);
+ int32_t result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_EQ(OK, pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout because FD was no longer registered";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not be invoked";
}
@@ -287,15 +287,15 @@
pipe.writeSignal();
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(0);
+ int32_t result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_EQ(OK, pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal zero because FD was already signalled";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because FD was signalled";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked";
@@ -310,8 +310,8 @@
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal zero because timeout was zero";
- EXPECT_FALSE(result)
- << "pollOnce result should be false because timeout occurred";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should not be invoked this time";
}
@@ -351,15 +351,15 @@
pipe.writeSignal(); // would cause FD to be considered signalled
StopWatch stopWatch("pollOnce");
- bool result = mPollLoop->pollOnce(100);
+ int32_t result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_EQ(OK, pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. zero because FD was already signalled";
- EXPECT_TRUE(result)
- << "pollOnce result should be true because FD was signalled";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because FD was signalled";
EXPECT_EQ(0, handler1.callbackCount)
<< "original handler callback should not be invoked because it was replaced";
EXPECT_EQ(1, handler2.callbackCount)
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b3aae72..403a68e 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -31,9 +31,9 @@
public static final int ENCODING_INVALID = 0;
/** Default audio data format */
public static final int ENCODING_DEFAULT = 1;
- /** Audio data format: PCM 16 bit per sample */
+ /** Audio data format: PCM 16 bit per sample. Guaranteed to be supported by devices. */
public static final int ENCODING_PCM_16BIT = 2; // accessed by native code
- /** Audio data format: PCM 8 bit per sample */
+ /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */
public static final int ENCODING_PCM_8BIT = 3; // accessed by native code
/** Invalid audio channel configuration */
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c48eaad..c567a6e 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -194,11 +194,13 @@
* Class constructor.
* @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for
* recording source definitions.
- * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
- * not limited to) 44100, 22050 and 11025.
+ * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
+ * rate that is guaranteed to work on all devices, but other rates such as 22050,
+ * 16000, and 11025 may work on some devices.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
- * {@link AudioFormat#CHANNEL_IN_STEREO}
+ * {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
+ * to work on all devices.
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -444,6 +446,8 @@
* or {@link #ERROR} if the implementation was unable to query the hardware for its
* output properties,
* or the minimum buffer size expressed in bytes.
+ * @see #AudioRecord(int, int, int, int, int) for more information on valid
+ * configuration values.
*/
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java
new file mode 100644
index 0000000..ef4ce05
--- /dev/null
+++ b/media/java/android/media/BassBoost.java
@@ -0,0 +1,213 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import android.media.AudioEffect;
+
+/**
+ * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
+ * to an simple equalizer but limited to one band amplification in the low frequency range.
+ * <p>An application creates a BassBoost object to instantiate and control a bass boost engine
+ * in the audio framework.
+ * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLBassBoostItf interface. Please refer to this specification for more details.
+ * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the BassBoost. If the audio session ID 0
+ * is specified, the BassBoost applies to the main audio output mix.
+ // TODO when AudioEffect is unhidden
+ // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class BassBoost extends AudioEffect {
+
+ private final static String TAG = "BassBoost";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectBassBoostApi.h
+ /**
+ * Is strength parameter supported by bass boost engine. Parameter ID for getParameter().
+ */
+ public static final int PARAM_STRENGTH_SUPPORTED = 0;
+ /**
+ * Bass boost effect strength. Parameter ID for
+ * {@link android.media.BassBoost.OnParameterChangeListener}
+ */
+ public static final int PARAM_STRENGTH = 1;
+
+ /**
+ * Indicates if strength parameter is supported by the bass boost engine
+ */
+ private boolean mStrengthSupported = false;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the BassBoost
+ * engine. As the same engine can be shared by several applications, this parameter indicates
+ * how much the requesting application needs control of effect parameters. The normal priority
+ * is 0, above normal is a positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the BassBoost will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the BassBoost will apply to the output mix.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public BassBoost(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
+
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
+ mStrengthSupported = (value[0] != 0);
+ }
+
+ /**
+ * Indicates whether setting strength is supported. If this method returns false, only one
+ * strength is supported and the setStrength() method always rounds to that value.
+ * @return true is strength parameter is supported, false otherwise
+ */
+ public boolean getStrengthSupported() {
+ return mStrengthSupported;
+ }
+
+ /**
+ * Sets the strength of the bass boost effect. If the implementation does not support per mille
+ * accuracy for setting the strength, it is allowed to round the given strength to the nearest
+ * supported value. You can use the {@link #getRoundedStrength()} method to query the
+ * (possibly rounded) value that was actually set.
+ * @param strength Strength of the effect. The valid range for strength strength is [0, 1000],
+ * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setStrength(short strength)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, strength));
+ }
+
+ /**
+ * Gets the current strength of the effect.
+ * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per
+ * mille designates the mildest effect and 1000 per mille the strongest
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoundedStrength()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ return value[0];
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the BassBoost when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * BassBoost engine.
+ * @param effect the BassBoost on which the interface is registered.
+ * @param status status of the set parameter operation.
+ // TODO when AudioEffect is unhidden
+ // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}.
+ * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(BassBoost effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(BassBoost.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/EnvironmentalReverb.java
new file mode 100644
index 0000000..88230fc
--- /dev/null
+++ b/media/java/android/media/EnvironmentalReverb.java
@@ -0,0 +1,504 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+
+import android.media.AudioEffect;
+
+/**
+ * A sound generated within a room travels in many directions. The listener first hears the
+ * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
+ * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
+ * undergoing more and more reflections, individual reflections become indistinguishable and
+ * the listener hears continuous reverberation that decays over time.
+ * Reverb is vital for modeling a listener's environment. It can be used in music applications
+ * to simulate music being played back in various environments, or in games to immerse the
+ * listener within the game's environment.
+ * The EnvironmentalReverb class allows an application to control each reverb engine property in a
+ * global reverb environment and is more suitable for games. For basic control, more suitable for
+ * music applications, it is recommended to use the
+ // TODO when PresetReverb is unhidden
+ // {_at_link android.media.PresetReverb} class.
+ * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine
+ * in the audio framework.
+ * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are
+ * directly mapping those defined by the OpenSL ES 1.0.1 Specification
+ * (http://www.khronos.org/opensles/) for the SLEnvironmentalReverbItf interface.
+ * Please refer to this specification for more details.
+ * <p>The EnvironmentalReverb is an output mix auxiliary effect and should be created on
+ * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
+ * they must be explicitely attached to it and a send level must be specified. Use the effect ID
+ * returned by getId() method to designate this particular effect when attaching it to the
+ * MediaPlayer or AudioTrack.
+ // TODO when AudioEffect is unhidden
+ // <p> See {_at_link android.media.AudioEffect} class for more details on controlling
+ * audio effects.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class EnvironmentalReverb extends AudioEffect {
+
+ private final static String TAG = "EnvironmentalReverb";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
+
+ /**
+ * Room level. Parameter ID for
+ * {@link android.media.EnvironmentalReverb.OnParameterChangeListener}
+ */
+ public static final int PARAM_ROOM_LEVEL = 0;
+ /**
+ * Room HF level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_ROOM_HF_LEVEL = 1;
+ /**
+ * Decay time. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DECAY_TIME = 2;
+ /**
+ * Decay HF ratio. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DECAY_HF_RATIO = 3;
+ /**
+ * Early reflections level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REFLECTIONS_LEVEL = 4;
+ /**
+ * Early reflections delay. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REFLECTIONS_DELAY = 5;
+ /**
+ * Reverb level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REVERB_LEVEL = 6;
+ /**
+ * Reverb delay. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REVERB_DELAY = 7;
+ /**
+ * Diffusion. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DIFFUSION = 8;
+ /**
+ * Density. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DENSITY = 9;
+
+ /**
+ * Registered listener for parameter changes
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super
+ * class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the
+ * EnvironmentalReverb engine. As the same engine can be shared by several applications, this
+ * parameter indicates how much the requesting application needs control of effect parameters.
+ * The normal priority is 0, above normal is a positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the EnvironmentalReverb will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the EnvironmentalReverb will apply to the output mix.
+ * As the EnvironmentalReverb is an auxiliary effect it is recommended to instantiate it on
+ * audio session 0 and to attach it to the MediaPLayer auxiliary output.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public EnvironmentalReverb(int priority, int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
+ Log.e(TAG, "contructor");
+ }
+
+ /**
+ * Sets the master volume level of the environmental reverb effect.
+ * @param room Room level in millibels. The valid range is [-9000, 0].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setRoomLevel(short room)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(room);
+ checkStatus(setParameter(PARAM_ROOM_LEVEL, param));
+ }
+
+ /**
+ * Gets the master volume level of the environmental reverb effect.
+ * @return the room level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoomLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_ROOM_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the volume level at 5 kHz relative to the volume level at low frequencies of the
+ * overall reverb effect.
+ * <p>This controls a low-pass filter that will reduce the level of the high-frequency.
+ * @param roomHF High frequency attenuation level in millibels. The valid range is [-9000, 0].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setRoomHFLevel(short roomHF)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(roomHF);
+ checkStatus(setParameter(PARAM_ROOM_HF_LEVEL, param));
+ }
+
+ /**
+ * Gets the room HF level.
+ * @return the room HF level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoomHFLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_ROOM_HF_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the time taken for the level of reverberation to decay by 60 dB.
+ * @param decayTime Decay time in milliseconds. The valid range is [100, 20000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDecayTime(int decayTime)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(decayTime);
+ checkStatus(setParameter(PARAM_DECAY_TIME, param));
+ }
+
+ /**
+ * Gets the decay time.
+ * @return the decay time in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getDecayTime()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_DECAY_TIME, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the ratio of high frequency decay time (at 5 kHz) relative to the decay time at low
+ * frequencies.
+ * @param decayHFRatio High frequency decay ratio using a permille scale. The valid range is
+ * [100, 2000]. A ratio of 1000 indicates that all frequencies decay at the same rate.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDecayHFRatio(short decayHFRatio)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(decayHFRatio);
+ checkStatus(setParameter(PARAM_DECAY_HF_RATIO, param));
+ }
+
+ /**
+ * Gets the ratio of high frequency decay time (at 5 kHz) relative to low frequencies.
+ * @return the decay HF ration. See {@link #setDecayHFRatio(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDecayHFRatio()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DECAY_HF_RATIO, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the volume level of the early reflections.
+ * <p>This level is combined with the overall room level
+ * (set using {@link #setRoomLevel(short)}).
+ * @param reflectionsLevel Reflection level in millibels. The valid range is [-9000, 1000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReflectionsLevel(short reflectionsLevel)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(reflectionsLevel);
+ checkStatus(setParameter(PARAM_REFLECTIONS_LEVEL, param));
+ }
+
+ /**
+ * Gets the volume level of the early reflections.
+ * @return the early reflections level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getReflectionsLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_REFLECTIONS_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the delay time for the early reflections.
+ * <p>This method sets the time between when the direct path is heard and when the first
+ * reflection is heard.
+ * @param reflectionsDelay Reflections delay in milliseconds. The valid range is [0, 300].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReflectionsDelay(int reflectionsDelay)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(reflectionsDelay);
+ checkStatus(setParameter(PARAM_REFLECTIONS_DELAY, param));
+ }
+
+ /**
+ * Gets the reflections delay.
+ * @return the early reflections delay in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getReflectionsDelay()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_REFLECTIONS_DELAY, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the volume level of the late reverberation.
+ * <p>This level is combined with the overall room level (set using {@link #setRoomLevel(short)}).
+ * @param reverbLevel Reverb level in millibels. The valid range is [-9000, 2000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReverbLevel(short reverbLevel)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(reverbLevel);
+ checkStatus(setParameter(PARAM_REVERB_LEVEL, param));
+ }
+
+ /**
+ * Gets the reverb level.
+ * @return the reverb level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getReverbLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_REVERB_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the time between the first reflection and the reverberation.
+ * @param reverbDelay Reverb delay in milliseconds. The valid range is [0, 100].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReverbDelay(int reverbDelay)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(reverbDelay);
+ checkStatus(setParameter(PARAM_REVERB_DELAY, param));
+ }
+
+ /**
+ * Gets the reverb delay.
+ * @return the reverb delay in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getReverbDelay()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_REVERB_DELAY, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the echo density in the late reverberation decay.
+ * <p>The scale should approximately map linearly to the perceived change in reverberation.
+ * @param diffusion Diffusion specified using a permille scale. The diffusion valid range is
+ * [0, 1000]. A value of 1000 o/oo indicates a smooth reverberation decay.
+ * Values below this level give a more <i>grainy</i> character.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDiffusion(short diffusion)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(diffusion);
+ checkStatus(setParameter(PARAM_DIFFUSION, param));
+ }
+
+ /**
+ * Gets diffusion level.
+ * @return the diffusion level. See {@link #setDiffusion(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDiffusion()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DIFFUSION, param));
+ return byteArrayToShort(param);
+ }
+
+
+ /**
+ * Controls the modal density of the late reverberation decay.
+ * <p> The scale should approximately map linearly to the perceived change in reverberation.
+ * A lower density creates a hollow sound that is useful for simulating small reverberation
+ * spaces such as bathrooms.
+ * @param density Density specified using a permille scale. The valid range is [0, 1000].
+ * A value of 1000 o/oo indicates a natural sounding reverberation. Values below this level
+ * produce a more colored effect.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDensity(short density)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(density);
+ checkStatus(setParameter(PARAM_DENSITY, param));
+ }
+
+ /**
+ * Gets the density level.
+ * @return the density level. See {@link #setDiffusion(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDensity()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DENSITY, param));
+ return byteArrayToShort(param);
+ }
+
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the EnvironmentalReverb
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * EnvironmentalReverb engine.
+ * @param effect the EnvironmentalReverb on which the interface is registered.
+ * @param status status of the set parameter operation.
+ // TODO when AudioEffect is unhidden
+ // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}.
+ * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(EnvironmentalReverb effect, int status, int param, int value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ int v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = (int)byteArrayToShort(value, 0);
+ } else if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(EnvironmentalReverb.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/Equalizer.java
new file mode 100644
index 0000000..082f694
--- /dev/null
+++ b/media/java/android/media/Equalizer.java
@@ -0,0 +1,443 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import android.media.AudioEffect;
+
+/**
+ * An Equalizer is used to alter the frequency response of a particular music source or of the main
+ * output mix.
+ * <p>An application creates an Equalizer object to instantiate and control an Equalizer engine
+ * in the audio framework. The application can either simply use predefined presets or have a more
+ * precise control of the gain in each frequency band controlled by the equalizer.
+ * <p>The methods, parameter types and units exposed by the Equalizer implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLEqualizerItf interface. Please refer to this specification for more details.
+ * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. If the audio session ID 0
+ * is specified, the Equalizer applies to the main audio output mix.
+ // TODO when AudioEffect is unhidden
+ // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class Equalizer extends AudioEffect {
+
+ private final static String TAG = "Equalizer";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectEqualizerApi.h
+ /**
+ * Number of bands. Parameter ID for {@link android.media.Equalizer.OnParameterChangeListener}
+ */
+ public static final int PARAM_NUM_BANDS = 0;
+ /**
+ * Band level range. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_LEVEL_RANGE = 1;
+ /**
+ * Band level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_BAND_LEVEL = 2;
+ /**
+ * Band center frequency. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_CENTER_FREQ = 3;
+ /**
+ * Band frequency range. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_BAND_FREQ_RANGE = 4;
+ /**
+ * Band for a given frequency. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_GET_BAND = 5;
+ /**
+ * Current preset. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_CURRENT_PRESET = 6;
+ /**
+ * Request number of presets. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_GET_NUM_OF_PRESETS = 7;
+ /**
+ * Request preset name. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_GET_PRESET_NAME = 8;
+ /**
+ * maximum size for perset name
+ */
+ public static final int PARAM_STRING_SIZE_MAX = 32;
+
+ /**
+ * Number of presets implemented by Equalizer engine
+ */
+ private int mNumPresets;
+ /**
+ * Names of presets implemented by Equalizer engine
+ */
+ private String[] mPresetNames;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the Equalizer
+ * engine. As the same engine can be shared by several applications, this parameter indicates
+ * how much the requesting application needs control of effect parameters. The normal priority
+ * is 0, above normal is a positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the Equalizer will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the Equalizer will apply to the output mix.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public Equalizer(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
+
+ mNumPresets = (int)getNumberOfPresets();
+
+ if (mNumPresets != 0) {
+ mPresetNames = new String[mNumPresets];
+ byte[] value = new byte[PARAM_STRING_SIZE_MAX];
+ int[] param = new int[2];
+ param[0] = PARAM_GET_PRESET_NAME;
+ for (int i = 0; i < mNumPresets; i++) {
+ param[1] = i;
+ checkStatus(getParameter(param, value));
+ int length = 0;
+ while (value[length] != 0) length++;
+ try {
+ mPresetNames[i] = new String(value, 0, length, "ISO-8859-1");
+ Log.e(TAG, "preset #: "+i+" name: "+mPresetNames[i]+" length: "+length);
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.e(TAG, "preset name decode error");
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the number of frequency bands supported by the Equalizer engine.
+ * @return the number of bands
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getNumberOfBands()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[1];
+ param[0] = PARAM_NUM_BANDS;
+ short[] value = new short[1];
+ checkStatus(getParameter(param, value));
+ return value[0];
+ }
+
+ /**
+ * Gets the level range for use by {@link #setBandLevel(int,short)}. The level is expressed in
+ * milliBel.
+ * @return the band level range in an array of short integers. The first element is the lower
+ * limit of the range, the second element the upper limit.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short[] getBandLevelRange()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[1];
+ int[] value = new int[2];
+ param[0] = PARAM_LEVEL_RANGE;
+ checkStatus(getParameter(param, value));
+
+ short[] result = new short[2];
+
+ result[0] = (short)value[0];
+ result[1] = (short)value[1];
+
+ return result;
+ }
+
+ /**
+ * Sets the given equalizer band to the given gain value.
+ * @param band Frequency band that will have the new gain. The numbering of the bands starts
+ * from 0 and ends at (number of bands - 1). See @see #getNumberOfBands().
+ * @param level New gain in millibels that will be set to the given band. getBandLevelRange()
+ * will define the maximum and minimum values.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setBandLevel(int band, short level)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] value = new int[1];
+
+ param[0] = PARAM_BAND_LEVEL;
+ param[1] = band;
+ value[0] = (int)level;
+ checkStatus(setParameter(param, value));
+ }
+
+ /**
+ * Gets the gain set for the given equalizer band.
+ * @param band Frequency band whose gain is requested. The numbering of the bands starts
+ * from 0 and ends at (number of bands - 1).
+ * @return Gain in millibels of the given band.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getBandLevel(int band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[1];
+
+ param[0] = PARAM_BAND_LEVEL;
+ param[1] = band;
+ checkStatus(getParameter(param, result));
+
+ return (short)result[0];
+ }
+
+
+ /**
+ * Gets the center frequency of the given band.
+ * @param band Frequency band whose center frequency is requested. The numbering of the bands
+ * starts from 0 and ends at (number of bands - 1).
+ * @return The center frequency in milliHertz
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getCenterFreq(int band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[1];
+
+ param[0] = PARAM_CENTER_FREQ;
+ param[1] = band;
+ checkStatus(getParameter(param, result));
+
+ return result[0];
+ }
+
+ /**
+ * Gets the frequency range of the given frequency band.
+ * @param band Frequency band whose frequency range is requested. The numbering of the bands
+ * starts from 0 and ends at (number of bands - 1).
+ * @return The frequency range in millHertz in an array of integers. The first element is the
+ * lower limit of the range, the second element the upper limit.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int[] getBandFreqRange(int band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[2];
+ param[0] = PARAM_BAND_FREQ_RANGE;
+ param[1] = band;
+ checkStatus(getParameter(param, result));
+
+ return result;
+ }
+
+ /**
+ * Gets the band that has the most effect on the given frequency.
+ * @param frequency Frequency in milliHertz which is to be equalized via the returned band.
+ * @return Frequency band that has most effect on the given frequency.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getBand(int frequency)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[1];
+
+ param[0] = PARAM_GET_BAND;
+ param[1] = frequency;
+ checkStatus(getParameter(param, result));
+
+ return result[0];
+ }
+
+ /**
+ * Gets current preset.
+ * @return Preset that is set at the moment.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getCurrentPreset()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[1];
+ param[0] = PARAM_CURRENT_PRESET;
+ short[] value = new short[1];
+ checkStatus(getParameter(param, value));
+ return value[0];
+ }
+
+ /**
+ * Sets the equalizer according to the given preset.
+ * @param preset New preset that will be taken into use. The valid range is [0,
+ * number of presets-1]. See {@see #getNumberOfPresets()}.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void usePreset(short preset)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_CURRENT_PRESET, preset));
+ }
+
+ /**
+ * Gets the total number of presets the equalizer supports. The presets will have indices
+ * [0, number of presets-1].
+ * @return The number of presets the equalizer supports.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getNumberOfPresets()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[1];
+ param[0] = PARAM_GET_NUM_OF_PRESETS;
+ short[] value = new short[1];
+ checkStatus(getParameter(param, value));
+ return value[0];
+ }
+
+ /**
+ * Gets the preset name based on the index.
+ * @param preset Index of the preset. The valid range is [0, number of presets-1].
+ * @return A string containing the name of the given preset.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public String getPresetName(short preset)
+ {
+ if (preset >= 0 && preset < mNumPresets) {
+ return mPresetNames[preset];
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the Equalizer when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * Equalizer engine.
+ * @param effect the Equalizer on which the interface is registered.
+ * @param status status of the set parameter operation.
+ // TODO when AudioEffect is unhidden
+ // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}.
+ * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ...
+ * @param param2 additional parameter qualifier (e.g the band for band level parameter).
+ * @param value the new parameter value.
+ */
+ void onParameterChange(Equalizer effect, int status, int param1, int param2, int value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p1 = -1;
+ int p2 = -1;
+ int v = -1;
+
+ if (param.length >= 4) {
+ p1 = byteArrayToInt(param, 0);
+ if (param.length >= 8) {
+ p2 = byteArrayToInt(param, 4);
+ }
+ }
+ if (value.length == 2) {
+ v = (int)byteArrayToShort(value, 0);;
+ } else if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+
+ if (p1 != -1 && v != -1) {
+ l.onParameterChange(Equalizer.this, status, p1, p2, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+}
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 6e527d9..a346ae4 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -20,6 +20,7 @@
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
+import android.provider.Mtp;
import android.media.DecoderCapabilities;
import android.media.DecoderCapabilities.VideoDecoder;
import android.media.DecoderCapabilities.AudioDecoder;
@@ -96,15 +97,32 @@
}
}
- private static HashMap<String, MediaFileType> sFileTypeMap
+ private static HashMap<String, MediaFileType> sFileTypeMap
= new HashMap<String, MediaFileType>();
- private static HashMap<String, Integer> sMimeTypeMap
- = new HashMap<String, Integer>();
+ private static HashMap<String, Integer> sMimeTypeMap
+ = new HashMap<String, Integer>();
+ // maps file extension to MTP format code
+ private static HashMap<String, Integer> sFileTypeToFormatMap
+ = new HashMap<String, Integer>();
+ // maps mime type to MTP format code
+ private static HashMap<String, Integer> sMimeTypeToFormatMap
+ = new HashMap<String, Integer>();
+ // maps MTP format code to mime type
+ private static HashMap<Integer, String> sFormatToMimeTypeMap
+ = new HashMap<Integer, String>();
+
static void addFileType(String extension, int fileType, String mimeType) {
sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));
sMimeTypeMap.put(mimeType, Integer.valueOf(fileType));
}
+ static void addFileType(String extension, int fileType, String mimeType, int mtpFormatCode) {
+ addFileType(extension, fileType, mimeType);
+ sFileTypeToFormatMap.put(extension, Integer.valueOf(mtpFormatCode));
+ sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode));
+ sFormatToMimeTypeMap.put(mtpFormatCode, mimeType);
+ }
+
private static boolean isWMAEnabled() {
List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders();
for (AudioDecoder decoder: decoders) {
@@ -126,17 +144,17 @@
}
static {
- addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
- addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
- addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
+ addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", Mtp.Object.FORMAT_MP3);
+ addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", Mtp.Object.FORMAT_MPEG);
+ addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", Mtp.Object.FORMAT_WAV);
addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
if (isWMAEnabled()) {
- addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
+ addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma", Mtp.Object.FORMAT_WMA);
}
- addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
- addFileType("OGA", FILE_TYPE_OGG, "application/ogg");
- addFileType("AAC", FILE_TYPE_AAC, "audio/aac");
+ addFileType("OGG", FILE_TYPE_OGG, "application/ogg", Mtp.Object.FORMAT_OGG);
+ addFileType("OGA", FILE_TYPE_OGG, "application/ogg", Mtp.Object.FORMAT_OGG);
+ addFileType("AAC", FILE_TYPE_AAC, "audio/aac", Mtp.Object.FORMAT_AAC);
addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska");
addFileType("MID", FILE_TYPE_MID, "audio/midi");
@@ -148,32 +166,32 @@
addFileType("RTX", FILE_TYPE_MID, "audio/midi");
addFileType("OTA", FILE_TYPE_MID, "audio/midi");
- addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg");
- addFileType("MP4", FILE_TYPE_MP4, "video/mp4");
- addFileType("M4V", FILE_TYPE_M4V, "video/mp4");
- addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp");
- addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
- addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
- addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
+ addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", Mtp.Object.FORMAT_MPEG);
+ addFileType("MP4", FILE_TYPE_MP4, "video/mp4", Mtp.Object.FORMAT_MPEG);
+ addFileType("M4V", FILE_TYPE_M4V, "video/mp4", Mtp.Object.FORMAT_MPEG);
+ addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp", Mtp.Object.FORMAT_3GP_CONTAINER);
+ addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", Mtp.Object.FORMAT_3GP_CONTAINER);
+ addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", Mtp.Object.FORMAT_3GP_CONTAINER);
+ addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2", Mtp.Object.FORMAT_3GP_CONTAINER);
addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
if (isWMVEnabled()) {
- addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
+ addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", Mtp.Object.FORMAT_WMV);
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
}
- addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg");
- addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg");
- addFileType("GIF", FILE_TYPE_GIF, "image/gif");
- addFileType("PNG", FILE_TYPE_PNG, "image/png");
- addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp");
+ addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg", Mtp.Object.FORMAT_EXIF_JPEG);
+ addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg", Mtp.Object.FORMAT_EXIF_JPEG);
+ addFileType("GIF", FILE_TYPE_GIF, "image/gif", Mtp.Object.FORMAT_GIF);
+ addFileType("PNG", FILE_TYPE_PNG, "image/png", Mtp.Object.FORMAT_PNG);
+ addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", Mtp.Object.FORMAT_BMP);
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
- addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl");
- addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls");
- addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl");
+ addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", Mtp.Object.FORMAT_M3U_PLAYLIST);
+ addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", Mtp.Object.FORMAT_PLS_PLAYLIST);
+ addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", Mtp.Object.FORMAT_WPL_PLAYLIST);
// compute file extensions list for native Media Scanner
StringBuilder builder = new StringBuilder();
@@ -222,4 +240,25 @@
return (value == null ? 0 : value.intValue());
}
+ public static int getFormatCode(String fileName, String mimeType) {
+ if (mimeType != null) {
+ Integer value = sMimeTypeToFormatMap.get(mimeType);
+ if (value != null) {
+ return value.intValue();
+ }
+ }
+ int lastDot = fileName.lastIndexOf('.');
+ if (lastDot > 0) {
+ String extension = fileName.substring(lastDot + 1);
+ Integer value = sFileTypeToFormatMap.get(extension);
+ if (value != null) {
+ return value.intValue();
+ }
+ }
+ return Mtp.Object.FORMAT_UNDEFINED;
+ }
+
+ public static String getMimeTypeForFormatCode(int formatCode) {
+ return sFormatToMimeTypeMap.get(formatCode);
+ }
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3333268..7cbe409 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -305,6 +305,7 @@
private Uri mGenresUri;
private Uri mPlaylistsUri;
private boolean mProcessPlaylists, mProcessGenres;
+ private int mMtpObjectHandle;
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
@@ -625,6 +626,9 @@
map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
map.put(MediaStore.MediaColumns.SIZE, mFileSize);
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
+ if (mMtpObjectHandle != 0) {
+ map.put(MediaStore.MediaColumns.MTP_OBJECT_HANDLE, mMtpObjectHandle);
+ }
if (MediaFile.isVideoFileType(mFileType)) {
map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING));
@@ -1227,6 +1231,14 @@
}
}
+ public Uri scanMtpFile(String path, String volumeName, int objectHandle, int format) {
+ String mimeType = MediaFile.getMimeTypeForFormatCode(format);
+ mMtpObjectHandle = objectHandle;
+ Uri result = scanSingleFile(path, volumeName, mimeType);
+ mMtpObjectHandle = 0;
+ return result;
+ }
+
// returns the number of matching file/directory names, starting from the right
private int matchPaths(String path1, String path2) {
int result = 0;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
new file mode 100644
index 0000000..2b311f5
--- /dev/null
+++ b/media/java/android/media/MtpDatabase.java
@@ -0,0 +1,313 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.MediaStore.MtpObjects;
+import android.util.Log;
+
+/**
+ * {@hide}
+ */
+public class MtpDatabase {
+
+ private static final String TAG = "MtpDatabase";
+
+ private final IContentProvider mMediaProvider;
+ private final String mVolumeName;
+ private final Uri mObjectsUri;
+
+ // FIXME - this should be passed in via the constructor
+ private final int mStorageID = 0x00010001;
+
+ private static final String[] ID_PROJECTION = new String[] {
+ MtpObjects.ObjectColumns._ID, // 0
+ };
+ private static final String[] PATH_SIZE_PROJECTION = new String[] {
+ MtpObjects.ObjectColumns._ID, // 0
+ MtpObjects.ObjectColumns.DATA, // 1
+ MtpObjects.ObjectColumns.SIZE, // 2
+ };
+ private static final String[] OBJECT_INFO_PROJECTION = new String[] {
+ MtpObjects.ObjectColumns._ID, // 0
+ MtpObjects.ObjectColumns.DATA, // 1
+ MtpObjects.ObjectColumns.FORMAT, // 2
+ MtpObjects.ObjectColumns.PARENT, // 3
+ MtpObjects.ObjectColumns.SIZE, // 4
+ MtpObjects.ObjectColumns.DATE_MODIFIED, // 5
+ };
+ private static final String ID_WHERE = MtpObjects.ObjectColumns._ID + "=?";
+ private static final String PATH_WHERE = MtpObjects.ObjectColumns.DATA + "=?";
+ private static final String PARENT_WHERE = MtpObjects.ObjectColumns.PARENT + "=?";
+ private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+ + MtpObjects.ObjectColumns.FORMAT + "=?";
+
+ private final MediaScanner mMediaScanner;
+
+ // MTP property codes
+ private static final int MTP_PROPERTY_STORAGE_ID = 0xDC01;
+ private static final int MTP_PROPERTY_OBJECT_FORMAT = 0xDC02;
+ private static final int MTP_PROPERTY_OBJECT_SIZE = 0xDC04;
+ private static final int MTP_PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+ private static final int MTP_PROPERTY_DATE_MODIFIED = 0xDC09;
+ private static final int MTP_PROPERTY_PARENT_OBJECT = 0xDC0B;
+
+ // MTP response codes
+ private static final int MTP_RESPONSE_OK = 0x2001;
+ private static final int MTP_RESPONSE_GENERAL_ERROR = 0x2002;
+ private static final int MTP_RESPONSE_INVALID_OBJECT_HANDLE = 0x2009;
+ private static final int MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED = 0xA80A;
+
+ static {
+ System.loadLibrary("media_jni");
+ }
+
+ public MtpDatabase(Context context, String volumeName) {
+ native_setup();
+
+ mMediaProvider = context.getContentResolver().acquireProvider("media");
+ mVolumeName = volumeName;
+ mObjectsUri = MtpObjects.getContentUri(volumeName);
+ mMediaScanner = new MediaScanner(context);
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ private int beginSendObject(String path, int format, int parent,
+ int storage, long size, long modified) {
+ ContentValues values = new ContentValues();
+ values.put(MtpObjects.ObjectColumns.DATA, path);
+ values.put(MtpObjects.ObjectColumns.FORMAT, format);
+ values.put(MtpObjects.ObjectColumns.PARENT, parent);
+ // storage is ignored for now
+ values.put(MtpObjects.ObjectColumns.SIZE, size);
+ values.put(MtpObjects.ObjectColumns.DATE_MODIFIED, modified);
+
+ try {
+ Uri uri = mMediaProvider.insert(mObjectsUri, values);
+ if (uri != null) {
+ return Integer.parseInt(uri.getPathSegments().get(2));
+ } else {
+ return -1;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in beginSendObject", e);
+ return -1;
+ }
+ }
+
+ private void endSendObject(String path, int handle, int format, boolean succeeded) {
+ if (succeeded) {
+ Uri uri = mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
+ } else {
+ deleteFile(handle);
+ }
+ }
+
+ private int[] getObjectList(int storageID, int format, int parent) {
+ // we can ignore storageID until we support multiple storages
+ Log.d(TAG, "getObjectList parent: " + parent);
+ Cursor c = null;
+ try {
+ if (format != 0) {
+ c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+ PARENT_FORMAT_WHERE,
+ new String[] { Integer.toString(parent), Integer.toString(format) },
+ null);
+ } else {
+ c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+ PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
+ }
+ if (c == null) {
+ Log.d(TAG, "null cursor");
+ return null;
+ }
+ int count = c.getCount();
+ if (count > 0) {
+ int[] result = new int[count];
+ for (int i = 0; i < count; i++) {
+ c.moveToNext();
+ result[i] = c.getInt(0);
+ }
+ Log.d(TAG, "returning " + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in getObjectList", e);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return null;
+ }
+
+ private int getObjectProperty(int handle, int property,
+ long[] outIntValue, char[] outStringValue) {
+ Log.d(TAG, "getObjectProperty: " + property);
+ String column = null;
+ boolean isString = false;
+
+ switch (property) {
+ case MTP_PROPERTY_STORAGE_ID:
+ outIntValue[0] = mStorageID;
+ return MTP_RESPONSE_OK;
+ case MTP_PROPERTY_OBJECT_FORMAT:
+ column = MtpObjects.ObjectColumns.FORMAT;
+ break;
+ case MTP_PROPERTY_OBJECT_SIZE:
+ column = MtpObjects.ObjectColumns.SIZE;
+ break;
+ case MTP_PROPERTY_OBJECT_FILE_NAME:
+ column = MtpObjects.ObjectColumns.DATA;
+ isString = true;
+ break;
+ case MTP_PROPERTY_DATE_MODIFIED:
+ column = MtpObjects.ObjectColumns.DATE_MODIFIED;
+ break;
+ case MTP_PROPERTY_PARENT_OBJECT:
+ column = MtpObjects.ObjectColumns.PARENT;
+ break;
+ default:
+ return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+ }
+
+ Cursor c = null;
+ try {
+ // for now we are only reading properties from the "objects" table
+ c = mMediaProvider.query(mObjectsUri,
+ new String [] { MtpObjects.ObjectColumns._ID, column },
+ ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ if (c != null && c.moveToNext()) {
+ if (isString) {
+ String value = c.getString(1);
+ int start = 0;
+
+ if (property == MTP_PROPERTY_OBJECT_FILE_NAME) {
+ // extract name from full path
+ int lastSlash = value.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ start = lastSlash + 1;
+ }
+ }
+ int end = value.length();
+ if (end - start > 255) {
+ end = start + 255;
+ }
+ value.getChars(start, end, outStringValue, 0);
+ outStringValue[end - start] = 0;
+ } else {
+ outIntValue[0] = c.getLong(1);
+ }
+ return MTP_RESPONSE_OK;
+ }
+ } catch (Exception e) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ // query failed if we get here
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+
+ private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
+ char[] outName, long[] outSizeModified) {
+ Log.d(TAG, "getObjectInfo: " + handle);
+ Cursor c = null;
+ try {
+ c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
+ ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ if (c != null && c.moveToNext()) {
+ outStorageFormatParent[0] = mStorageID;
+ outStorageFormatParent[1] = c.getInt(2);
+ outStorageFormatParent[2] = c.getInt(3);
+
+ // extract name from path
+ String path = c.getString(1);
+ int lastSlash = path.lastIndexOf('/');
+ int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
+ int end = path.length();
+ if (end - start > 255) {
+ end = start + 255;
+ }
+ path.getChars(start, end, outName, 0);
+ outName[end - start] = 0;
+
+ outSizeModified[0] = c.getLong(4);
+ outSizeModified[1] = c.getLong(5);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in getObjectProperty", e);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return false;
+ }
+
+ private boolean getObjectFilePath(int handle, char[] outFilePath, long[] outFileLength) {
+ Log.d(TAG, "getObjectFilePath: " + handle);
+ Cursor c = null;
+ try {
+ c = mMediaProvider.query(mObjectsUri, PATH_SIZE_PROJECTION,
+ ID_WHERE, new String[] { Integer.toString(handle) }, null);
+ if (c != null && c.moveToNext()) {
+ String path = c.getString(1);
+ path.getChars(0, path.length(), outFilePath, 0);
+ outFilePath[path.length()] = 0;
+ outFileLength[0] = c.getLong(2);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in getObjectFilePath", e);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return false;
+ }
+
+ private boolean deleteFile(int handle) {
+ Log.d(TAG, "deleteFile: " + handle);
+ Uri uri = MtpObjects.getContentUri(mVolumeName, handle);
+ try {
+ return (mMediaProvider.delete(uri, null, null) == 1);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in deleteFile", e);
+ return false;
+ }
+ }
+
+ // used by the JNI code
+ private int mNativeContext;
+
+ private native final void native_setup();
+ private native final void native_finalize();
+}
diff --git a/media/java/android/media/MtpServer.java b/media/java/android/media/MtpServer.java
index a9a54e7..b0945a5 100644
--- a/media/java/android/media/MtpServer.java
+++ b/media/java/android/media/MtpServer.java
@@ -30,8 +30,8 @@
System.loadLibrary("media_jni");
}
- public MtpServer(String storagePath, String databasePath) {
- native_setup(storagePath, databasePath);
+ public MtpServer(MtpDatabase database, String storagePath) {
+ native_setup(database, storagePath);
}
@Override
@@ -47,11 +47,21 @@
native_stop();
}
+ public void sendObjectAdded(int handle) {
+ native_send_object_added(handle);
+ }
+
+ public void sendObjectRemoved(int handle) {
+ native_send_object_removed(handle);
+ }
+
// used by the JNI code
private int mNativeContext;
- private native final void native_setup(String storagePath, String databasePath);
+ private native final void native_setup(MtpDatabase database, String storagePath);
private native final void native_finalize();
private native final void native_start();
private native final void native_stop();
+ private native final void native_send_object_added(int handle);
+ private native final void native_send_object_removed(int handle);
}
diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/PresetReverb.java
new file mode 100644
index 0000000..83a01a4
--- /dev/null
+++ b/media/java/android/media/PresetReverb.java
@@ -0,0 +1,219 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+
+import android.media.AudioEffect;
+
+/**
+ * A sound generated within a room travels in many directions. The listener first hears the
+ * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
+ * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
+ * undergoing more and more reflections, individual reflections become indistinguishable and
+ * the listener hears continuous reverberation that decays over time.
+ * Reverb is vital for modeling a listener's environment. It can be used in music applications
+ * to simulate music being played back in various environments, or in games to immerse the
+ * listener within the game's environment.
+ * The PresetReverb class allows an application to configure the global reverb using a reverb preset.
+ * This is primarily used for adding some reverb in a music playback context. Applications
+ * requiring control over a more advanced environmental reverb are advised to use the
+ // TODO when EnvironmentalReverb is unhidden
+ // {_at_link android.media.EnvironmentalReverb} class.
+ * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the
+ * audio framework.
+ * <p>The methods, parameter types and units exposed by the PresetReverb implementation are
+ * directly mapping those defined by the OpenSL ES 1.0.1 Specification
+ * (http://www.khronos.org/opensles/) for the SLPresetReverbItf interface.
+ * Please refer to this specification for more details.
+ * <p>The PresetReverb is an output mix auxiliary effect and should be created on
+ * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
+ * they must be explicitely attached to it and a send level must be specified. Use the effect ID
+ * returned by getId() method to designate this particular effect when attaching it to the
+ * MediaPlayer or AudioTrack.
+ // TODO when AudioEffect is unhidden
+ // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class PresetReverb extends AudioEffect {
+
+ private final static String TAG = "PresetReverb";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectPresetReverbApi.h
+
+ /**
+ * Preset. Parameter ID for
+ * {@link android.media.PresetReverb.OnParameterChangeListener}
+ */
+ public static final int PARAM_PRESET = 0;
+
+ /**
+ * Room level. Parameter ID for
+ * {@link android.media.PresetReverb.OnParameterChangeListener}
+ */
+ public static final int PRESET_NONE = 0;
+ public static final int PRESET_SMALLROOM = 1;
+ public static final int PRESET_MEDIUMROOM = 2;
+ public static final int PRESET_LARGEROOM = 3;
+ public static final int PRESET_MEDIUMHALL = 4;
+ public static final int PRESET_LARGEHALL = 5;
+ public static final int PRESET_PLATE = 6;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the
+ * PresetReverb engine. As the same engine can be shared by several applications, this
+ * parameter indicates how much the requesting application needs control of effect parameters.
+ * The normal priority is 0, above normal is a positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the PresetReverb will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the PresetReverb will apply to the output mix.
+ * As the PresetReverb is an auxiliary effect it is recommended to instantiate it on
+ * audio session 0 and to attach it to the MediaPLayer auxiliary output.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public PresetReverb(int priority, int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
+ Log.e(TAG, "contructor");
+ }
+
+ /**
+ * Enables a preset on the reverb.
+ * <p>The reverb PRESET_NONE disables any reverb from the current output but does not free the
+ * resources associated with the reverb. For an application to signal to the implementation
+ * to free the resources, it must call the release() method.
+ * @param preset This must be one of the the preset constants defined in this class.
+ * e.g. {@link #PRESET_SMALLROOM}
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setPreset(short preset)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_PRESET, preset));
+ }
+
+ /**
+ * Gets current reverb preset.
+ * @return Preset that is set at the moment.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getPreset()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[1];
+ param[0] = PARAM_PRESET;
+ short[] value = new short[1];
+ checkStatus(getParameter(param, value));
+ return value[0];
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the PresetReverb
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * PresetReverb engine.
+ * @param effect the PresetReverb on which the interface is registered.
+ * @param status status of the set parameter operation.
+ // TODO when AudioEffect is unhidden
+ // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}.
+ * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(PresetReverb effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(PresetReverb.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java
new file mode 100644
index 0000000..9f71297
--- /dev/null
+++ b/media/java/android/media/Virtualizer.java
@@ -0,0 +1,214 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import android.media.AudioEffect;
+
+/**
+ * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
+ * behavior of this effect is dependent on the number of audio input channels and the types and
+ * number of audio output channels of the device. For example, in the case of a stereo input and
+ * stereo headphone output, a stereo widening effect is used when this effect is turned on.
+ * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine
+ * in the audio framework.
+ * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLVirtualizerItf interface. Please refer to this specification for more details.
+ * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. If the audio session ID 0
+ * is specified, the Virtualizer applies to the main audio output mix.
+ // TODO when AudioEffect is unhidden
+ // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class Virtualizer extends AudioEffect {
+
+ private final static String TAG = "Virtualizer";
+
+ // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h
+ /**
+ * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
+ */
+ public static final int PARAM_STRENGTH_SUPPORTED = 0;
+ /**
+ * Virtualizer effect strength. Parameter ID for
+ * {@link android.media.Virtualizer.OnParameterChangeListener}
+ */
+ public static final int PARAM_STRENGTH = 1;
+
+ /**
+ * Indicates if strength parameter is supported by the virtualizer engine
+ */
+ private boolean mStrengthSupported = false;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the Virtualizer
+ * engine. As the same engine can be shared by several applications, this parameter indicates
+ * how much the requesting application needs control of effect parameters. The normal priority
+ * is 0, above normal is a positive number, below normal a negative number.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the Virtualizer will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the Virtualizer will apply to the output mix.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public Virtualizer(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
+
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
+ mStrengthSupported = (value[0] != 0);
+ }
+
+ /**
+ * Indicates whether setting strength is supported. If this method returns false, only one
+ * strength is supported and the setStrength() method always rounds to that value.
+ * @return true is strength parameter is supported, false otherwise
+ */
+ public boolean getStrengthSupported() {
+ return mStrengthSupported;
+ }
+
+ /**
+ * Sets the strength of the virtualizer effect. If the implementation does not support per mille
+ * accuracy for setting the strength, it is allowed to round the given strength to the nearest
+ * supported value. You can use the {@link #getRoundedStrength()} method to query the
+ * (possibly rounded) value that was actually set.
+ * @param strength Strength of the effect. The valid range for strength strength is [0, 1000],
+ * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setStrength(short strength)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, strength));
+ }
+
+ /**
+ * Gets the current strength of the effect.
+ * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per
+ * mille designates the mildest effect and 1000 per mille the strongest
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoundedStrength()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ return value[0];
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * Virtualizer engine.
+ * @param effect the Virtualizer on which the interface is registered.
+ * @param status status of the set parameter operation.
+ // TODO when AudioEffect is unhidden
+ // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}.
+ * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(Virtualizer effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(Virtualizer.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 3a7291f..653532c 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -15,6 +15,7 @@
android_media_AmrInputStream.cpp \
android_media_MtpClient.cpp \
android_media_MtpCursor.cpp \
+ android_media_MtpDatabase.cpp \
android_media_MtpServer.cpp \
LOCAL_SHARED_LIBRARIES := \
@@ -39,7 +40,9 @@
LOCAL_CFLAGS += -DNO_OPENCORE
endif
+ifneq ($(TARGET_SIMULATOR),true)
LOCAL_STATIC_LIBRARIES := libmtp libusbhost
+endif
LOCAL_C_INCLUDES += \
external/tremor/Tremor \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 47f1974..5c2ec00 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -766,6 +766,7 @@
extern int register_android_media_MediaProfiles(JNIEnv *env);
extern int register_android_media_MtpClient(JNIEnv *env);
extern int register_android_media_MtpCursor(JNIEnv *env);
+extern int register_android_media_MtpDatabase(JNIEnv *env);
extern int register_android_media_MtpServer(JNIEnv *env);
#ifndef NO_OPENCORE
@@ -830,6 +831,11 @@
goto bail;
}
+ if (register_android_media_MtpDatabase(env) < 0) {
+ LOGE("ERROR: MtpDatabase native registration failed");
+ goto bail;
+ }
+
if (register_android_media_MtpServer(env) < 0) {
LOGE("ERROR: MtpServer native registration failed");
goto bail;
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp
index 5c397a6..f69053c 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_MtpClient.cpp
@@ -38,6 +38,8 @@
static jmethodID method_deviceRemoved;
static jfieldID field_context;
+#ifdef HAVE_ANDROID_OS
+
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
@@ -94,52 +96,66 @@
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
+#endif // HAVE_ANDROID_OS
+
// ----------------------------------------------------------------------------
static void
android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("setup\n");
MyClient* client = new MyClient(env, thiz);
client->start();
env->SetIntField(thiz, field_context, (int)client);
+#endif
}
static void
android_media_MtpClient_finalize(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("finalize\n");
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
client->cleanup(env);
delete client;
env->SetIntField(thiz, field_context, 0);
+#endif
}
static jboolean
android_media_MtpClient_start(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("start\n");
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
return client->start();
+#else
+ return false;
+#endif
}
static void
android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("stop\n");
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
client->stop();
+#endif
}
static jboolean
android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
jint device_id, jint object_id)
{
+#ifdef HAVE_ANDROID_OS
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
MtpDevice* device = client->getDevice(device_id);
if (device)
return device->deleteObject(object_id);
else
+ #endif
return NULL;
}
@@ -147,11 +163,13 @@
android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
jint device_id, jint object_id)
{
+#ifdef HAVE_ANDROID_OS
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
MtpDevice* device = client->getDevice(device_id);
if (device)
return device->getParent(object_id);
else
+#endif
return -1;
}
@@ -159,11 +177,13 @@
android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
jint device_id, jint object_id)
{
+ #ifdef HAVE_ANDROID_OS
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
MtpDevice* device = client->getDevice(device_id);
if (device)
return device->getStorageID(object_id);
else
+#endif
return -1;
}
diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_MtpCursor.cpp
index 470fa05..6228b5d 100644
--- a/media/jni/android_media_MtpCursor.cpp
+++ b/media/jni/android_media_MtpCursor.cpp
@@ -51,6 +51,7 @@
android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
jint queryType, jint deviceID, jint storageID, jint objectID, jintArray javaColumns)
{
+#ifdef HAVE_ANDROID_OS
LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %d objectID: %d\n",
queryType, deviceID, storageID, objectID);
@@ -68,19 +69,23 @@
if (columns)
env->ReleaseIntArrayElements(javaColumns, columns, 0);
env->SetIntField(thiz, field_context, (int)cursor);
+#endif
}
static void
android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("finalize\n");
MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
delete cursor;
+#endif
}
static jint
android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
{
+#ifdef HAVE_ANDROID_OS
CursorWindow* window = get_window_from_object(env, javaWindow);
if (!window) {
LOGE("Invalid CursorWindow");
@@ -91,6 +96,9 @@
MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
return cursor->fillWindow(window, startPos);
+#else
+ return 0;
+#endif
}
// ----------------------------------------------------------------------------
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
new file mode 100644
index 0000000..be59362
--- /dev/null
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -0,0 +1,463 @@
+/*
+ * 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 "MtpDatabaseJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jmethodID method_beginSendObject;
+static jmethodID method_endSendObject;
+static jmethodID method_getObjectList;
+static jmethodID method_getObjectProperty;
+static jmethodID method_getObjectInfo;
+static jmethodID method_getObjectFilePath;
+static jmethodID method_deleteFile;
+static jfieldID field_context;
+
+MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
+ return (MtpDatabase *)env->GetIntField(database, field_context);
+}
+
+#ifdef HAVE_ANDROID_OS
+// ----------------------------------------------------------------------------
+
+class MyMtpDatabase : public MtpDatabase {
+private:
+ jobject mDatabase;
+ jintArray mIntBuffer;
+ jlongArray mLongBuffer;
+ jcharArray mStringBuffer;
+
+public:
+ MyMtpDatabase(JNIEnv *env, jobject client);
+ virtual ~MyMtpDatabase();
+ void cleanup(JNIEnv *env);
+
+ virtual MtpObjectHandle beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified);
+
+ virtual void endSendObject(const char* path,
+ MtpObjectHandle handle,
+ MtpObjectFormat format,
+ bool succeeded);
+
+ virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent);
+
+ virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
+ MtpDataPacket& packet);
+
+ virtual bool getObjectFilePath(MtpObjectHandle handle,
+ MtpString& filePath,
+ int64_t& fileLength);
+ virtual bool deleteFile(MtpObjectHandle handle);
+
+ // helper for media scanner
+ virtual MtpObjectHandle* getFileList(int& outCount);
+
+ virtual void beginTransaction();
+ virtual void commitTransaction();
+ virtual void rollbackTransaction();
+
+ bool getPropertyInfo(MtpObjectProperty property, int& type);
+};
+
+MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
+ : mDatabase(env->NewGlobalRef(client)),
+ mIntBuffer(NULL),
+ mLongBuffer(NULL),
+ mStringBuffer(NULL)
+{
+ jintArray intArray;
+ jlongArray longArray;
+ jcharArray charArray;
+
+ // create buffers for out arguments
+ // we don't need to be thread-safe so this is OK
+ intArray = env->NewIntArray(3);
+ if (!intArray)
+ goto out_of_memory;
+ mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
+ longArray = env->NewLongArray(2);
+ if (!longArray)
+ goto out_of_memory;
+ mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
+ charArray = env->NewCharArray(256);
+ if (!charArray)
+ goto out_of_memory;
+ mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
+ return;
+
+out_of_memory:
+ env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
+}
+
+void MyMtpDatabase::cleanup(JNIEnv *env) {
+ env->DeleteGlobalRef(mDatabase);
+ env->DeleteGlobalRef(mIntBuffer);
+ env->DeleteGlobalRef(mLongBuffer);
+ env->DeleteGlobalRef(mStringBuffer);
+}
+
+MyMtpDatabase::~MyMtpDatabase() {
+}
+
+MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ return env->CallIntMethod(mDatabase, method_beginSendObject, env->NewStringUTF(path),
+ (jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified);
+}
+
+void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
+ MtpObjectFormat format, bool succeeded) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path),
+ (jint)handle, (jint)format, (jboolean)succeeded);
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
+ (jint)storageID, (jint)format, (jint)parent);
+ if (!array)
+ return NULL;
+ MtpObjectHandleList* list = new MtpObjectHandleList();
+ jint* handles = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+LOGD("getObjectList length: %d", length);
+ for (int i = 0; i < length; i++) {
+LOGD("push: %d", handles[i]);
+ list->push(handles[i]);
+ }
+ env->ReleaseIntArrayElements(array, handles, 0);
+ return list;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ int type;
+
+ if (!getPropertyInfo(property, type))
+ return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
+ (jint)handle, (jint)property, mLongBuffer, mStringBuffer);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+ jlong longValue = longValues[0];
+ env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+ switch (type) {
+ case MTP_TYPE_INT8:
+ packet.putInt8(longValue);
+ break;
+ case MTP_TYPE_UINT8:
+ packet.putUInt8(longValue);
+ break;
+ case MTP_TYPE_INT16:
+ packet.putInt16(longValue);
+ break;
+ case MTP_TYPE_UINT16:
+ packet.putUInt16(longValue);
+ break;
+ case MTP_TYPE_INT32:
+ packet.putInt32(longValue);
+ break;
+ case MTP_TYPE_UINT32:
+ packet.putUInt32(longValue);
+ break;
+ case MTP_TYPE_INT64:
+ packet.putInt64(longValue);
+ break;
+ case MTP_TYPE_UINT64:
+ packet.putUInt64(longValue);
+ break;
+ case MTP_TYPE_STR:
+ {
+ jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+ packet.putString(str);
+ env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+ break;
+ }
+ default:
+ LOGE("unsupported object type\n");
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
+ MtpDataPacket& packet) {
+ char date[20];
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
+ (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
+ if (!result)
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+
+ jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
+ MtpStorageID storageID = intValues[0];
+ MtpObjectFormat format = intValues[1];
+ MtpObjectHandle parent = intValues[2];
+ env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
+
+ jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+ uint64_t size = longValues[0];
+ uint64_t modified = longValues[1];
+ env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+ int associationType = (format == MTP_FORMAT_ASSOCIATION ?
+ MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
+ MTP_ASSOCIATION_TYPE_UNDEFINED);
+
+ packet.putUInt32(storageID);
+ packet.putUInt16(format);
+ packet.putUInt16(0); // protection status
+ packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
+ packet.putUInt16(0); // thumb format
+ packet.putUInt32(0); // thumb compressed size
+ packet.putUInt32(0); // thumb pix width
+ packet.putUInt32(0); // thumb pix height
+ packet.putUInt32(0); // image pix width
+ packet.putUInt32(0); // image pix height
+ packet.putUInt32(0); // image bit depth
+ packet.putUInt32(parent);
+ packet.putUInt16(associationType);
+ packet.putUInt32(0); // association desc
+ packet.putUInt32(0); // sequence number
+
+ jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+ packet.putString(str); // file name
+ env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+
+ packet.putEmptyString();
+ formatDateTime(modified, date, sizeof(date));
+ packet.putString(date); // date modified
+ packet.putEmptyString(); // keywords
+
+ return MTP_RESPONSE_OK;
+}
+
+bool MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
+ MtpString& filePath,
+ int64_t& fileLength) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectFilePath,
+ (jint)handle, mStringBuffer, mLongBuffer);
+ if (!result)
+ return false;
+
+ jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+ filePath.setTo(str, strlen16(str));
+ env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+
+ jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+ fileLength = longValues[0];
+ env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+ return true;
+}
+
+bool MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ return env->CallBooleanMethod(mDatabase, method_deleteFile, (jint)handle);
+}
+
+ // helper for media scanner
+MtpObjectHandle* MyMtpDatabase::getFileList(int& outCount) {
+ // REMOVE ME
+ return NULL;
+}
+
+void MyMtpDatabase::beginTransaction() {
+ // REMOVE ME
+}
+
+void MyMtpDatabase::commitTransaction() {
+ // REMOVE ME
+}
+
+void MyMtpDatabase::rollbackTransaction() {
+ // REMOVE ME
+}
+
+struct PropertyTableEntry {
+ MtpObjectProperty property;
+ int type;
+};
+
+static const PropertyTableEntry kPropertyTable[] = {
+ { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
+ { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
+ { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
+};
+
+bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
+ int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
+ const PropertyTableEntry* entry = kPropertyTable;
+ for (int i = 0; i < count; i++, entry++) {
+ if (entry->property == property) {
+ type = entry->type;
+ return true;
+ }
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+#endif // HAVE_ANDROID_OS
+
+// ----------------------------------------------------------------------------
+
+static void
+android_media_MtpDatabase_setup(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("setup\n");
+ MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
+ env->SetIntField(thiz, field_context, (int)database);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+#endif
+}
+
+static void
+android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("finalize\n");
+ MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
+ database->cleanup(env);
+ delete database;
+ env->SetIntField(thiz, field_context, 0);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+ {"native_setup", "()V", (void *)android_media_MtpDatabase_setup},
+ {"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize},
+};
+
+static const char* const kClassPathName = "android/media/MtpDatabase";
+
+int register_android_media_MtpDatabase(JNIEnv *env)
+{
+ jclass clazz;
+
+ LOGD("register_android_media_MtpDatabase\n");
+
+ clazz = env->FindClass("android/media/MtpDatabase");
+ if (clazz == NULL) {
+ LOGE("Can't find android/media/MtpDatabase");
+ return -1;
+ }
+ method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
+ if (method_beginSendObject == NULL) {
+ LOGE("Can't find beginSendObject");
+ return -1;
+ }
+ method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
+ if (method_endSendObject == NULL) {
+ LOGE("Can't find endSendObject");
+ return -1;
+ }
+ method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
+ if (method_getObjectList == NULL) {
+ LOGE("Can't find getObjectList");
+ return -1;
+ }
+ method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
+ if (method_getObjectProperty == NULL) {
+ LOGE("Can't find getObjectProperty");
+ return -1;
+ }
+ method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
+ if (method_getObjectInfo == NULL) {
+ LOGE("Can't find getObjectInfo");
+ return -1;
+ }
+ method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)Z");
+ if (method_getObjectFilePath == NULL) {
+ LOGE("Can't find getObjectFilePath");
+ return -1;
+ }
+ method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)Z");
+ if (method_deleteFile == NULL) {
+ LOGE("Can't find deleteFile");
+ return -1;
+ }
+ field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (field_context == NULL) {
+ LOGE("Can't find MtpDatabase.mNativeContext");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MtpDatabase", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp
index e6a3835..eddad57 100644
--- a/media/jni/android_media_MtpServer.cpp
+++ b/media/jni/android_media_MtpServer.cpp
@@ -37,9 +37,13 @@
static jfieldID field_context;
+// in android_media_MtpDatabase.cpp
+extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
// ----------------------------------------------------------------------------
+#ifdef HAVE_ANDROID_OS
+
static bool ExceptionCheck(void* env)
{
return ((JNIEnv *)env)->ExceptionCheck();
@@ -47,14 +51,15 @@
class MtpThread : public Thread {
private:
+ MtpDatabase* mDatabase;
+ MtpServer* mServer;
String8 mStoragePath;
- String8 mDatabasePath;
bool mDone;
- bool mScannedOnce;
+ Mutex mMutex;
public:
- MtpThread(const char* storagePath, const char* databasePath)
- : mStoragePath(storagePath), mDatabasePath(databasePath), mDone(false), mScannedOnce(false)
+ MtpThread(MtpDatabase* database, const char* storagePath)
+ : mDatabase(database), mServer(NULL), mStoragePath(storagePath), mDone(false)
{
}
@@ -66,16 +71,19 @@
return false;
}
- MtpServer* server = new MtpServer(fd, mDatabasePath, AID_SDCARD_RW, 0664, 0775);
- server->addStorage(mStoragePath);
+ mMutex.lock();
+ mServer = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
+ mServer->addStorage(mStoragePath);
+ mMutex.unlock();
- // temporary
- LOGD("MtpThread server->scanStorage");
- server->scanStorage();
- LOGD("MtpThread server->run");
- server->run();
+ LOGD("MtpThread mServer->run");
+ mServer->run();
close(fd);
- delete server;
+
+ mMutex.lock();
+ delete mServer;
+ mServer = NULL;
+ mMutex.unlock();
bool done = mDone;
if (done)
@@ -85,21 +93,42 @@
}
void setDone() { mDone = true; }
+
+ void sendObjectAdded(MtpObjectHandle handle) {
+ mMutex.lock();
+ if (mServer)
+ mServer->sendObjectAdded(handle);
+ else
+ LOGE("sendObjectAdded called while disconnected\n");
+ mMutex.unlock();
+ }
+
+ void sendObjectRemoved(MtpObjectHandle handle) {
+ mMutex.lock();
+ if (mServer)
+ mServer->sendObjectRemoved(handle);
+ else
+ LOGE("sendObjectRemoved called while disconnected\n");
+ mMutex.unlock();
+ }
};
+#endif // HAVE_ANDROID_OS
+
static void
-android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jstring storagePath, jstring databasePath)
+android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jstring storagePath)
{
+#ifdef HAVE_ANDROID_OS
LOGD("setup\n");
+ MtpDatabase* database = getMtpDatabase(env, javaDatabase);
const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
- const char *databasePathStr = env->GetStringUTFChars(databasePath, NULL);
- MtpThread* thread = new MtpThread(storagePathStr, databasePathStr);
+ MtpThread* thread = new MtpThread(database, storagePathStr);
env->SetIntField(thiz, field_context, (int)thread);
env->ReleaseStringUTFChars(storagePath, storagePathStr);
- env->ReleaseStringUTFChars(databasePath, databasePathStr);
+#endif
}
static void
@@ -112,29 +141,62 @@
static void
android_media_MtpServer_start(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("start\n");
MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
thread->run("MtpThread");
+#endif // HAVE_ANDROID_OS
}
static void
android_media_MtpServer_stop(JNIEnv *env, jobject thiz)
{
+#ifdef HAVE_ANDROID_OS
LOGD("stop\n");
MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
if (thread) {
thread->setDone();
env->SetIntField(thiz, field_context, 0);
}
+#endif
+}
+
+static void
+android_media_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("send_object_added %d\n", handle);
+ MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+ if (thread)
+ thread->sendObjectAdded(handle);
+ else
+ LOGE("sendObjectAdded called while disconnected\n");
+#endif
+}
+
+static void
+android_media_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("send_object_removed %d\n", handle);
+ MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+ if (thread)
+ thread->sendObjectRemoved(handle);
+ else
+ LOGE("sendObjectRemoved called while disconnected\n");
+#endif
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"native_setup", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_MtpServer_setup},
- {"native_finalize", "()V", (void *)android_media_MtpServer_finalize},
- {"native_start", "()V", (void *)android_media_MtpServer_start},
- {"native_stop", "()V", (void *)android_media_MtpServer_stop},
+ {"native_setup", "(Landroid/media/MtpDatabase;Ljava/lang/String;)V",
+ (void *)android_media_MtpServer_setup},
+ {"native_finalize", "()V", (void *)android_media_MtpServer_finalize},
+ {"native_start", "()V", (void *)android_media_MtpServer_start},
+ {"native_stop", "()V", (void *)android_media_MtpServer_stop},
+ {"native_send_object_added", "(I)V", (void *)android_media_MtpServer_send_object_added},
+ {"native_send_object_removed", "(I)V", (void *)android_media_MtpServer_send_object_removed},
};
static const char* const kClassPathName = "android/media/MtpServer";
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 02474a4..beb3dfc 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -323,8 +323,8 @@
priority,
effectCallback,
&lpJniStorage->mCallbackData,
- 0,
- sessionId);
+ sessionId,
+ 0);
if (lpAudioEffect == NULL) {
LOGE("Error creating AudioEffect");
goto setup_failure;
diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk
index 54e87f3..fc4ceb6 100644
--- a/media/libeffects/Android.mk
+++ b/media/libeffects/Android.mk
@@ -87,7 +87,8 @@
endif
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg)
+ $(call include-path-for, graphics corecg) \
+ system/media/opensles/include
LOCAL_PRELINK_MODULE := false
diff --git a/media/libeffects/EffectEqualizer.cpp b/media/libeffects/EffectEqualizer.cpp
index d19c6b9..af0c411 100644
--- a/media/libeffects/EffectEqualizer.cpp
+++ b/media/libeffects/EffectEqualizer.cpp
@@ -30,6 +30,12 @@
// effect_interface_t interface implementation for equalizer effect
extern "C" const struct effect_interface_s gEqualizerInterface;
+enum equalizer_state_e {
+ EQUALIZER_STATE_UNINITIALIZED,
+ EQUALIZER_STATE_INITIALIZED,
+ EQUALIZER_STATE_ACTIVE,
+};
+
namespace android {
namespace {
@@ -100,6 +106,7 @@
effect_config_t config;
FormatAdapter adapter;
AudioEqualizer * pEqualizer;
+ uint32_t state;
};
//--- local function prototypes
@@ -151,6 +158,7 @@
pContext->itfe = &gEqualizerInterface;
pContext->pEqualizer = NULL;
+ pContext->state = EQUALIZER_STATE_UNINITIALIZED;
ret = Equalizer_init(pContext);
if (ret < 0) {
@@ -160,6 +168,7 @@
}
*pInterface = (effect_interface_t)pContext;
+ pContext->state = EQUALIZER_STATE_INITIALIZED;
LOGV("EffectLibCreateEffect %p, size %d", pContext, AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext));
@@ -175,6 +184,7 @@
return -EINVAL;
}
+ pContext->state = EQUALIZER_STATE_UNINITIALIZED;
pContext->pEqualizer->free();
delete pContext;
@@ -528,6 +538,13 @@
return -EINVAL;
}
+ if (pContext->state == EQUALIZER_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+ if (pContext->state == EQUALIZER_STATE_INITIALIZED) {
+ return -ENODATA;
+ }
+
pContext->adapter.process(inBuffer->raw, outBuffer->raw, outBuffer->frameCount);
return 0;
@@ -539,7 +556,7 @@
android::EqualizerContext * pContext = (android::EqualizerContext *) self;
int retsize;
- if (pContext == NULL) {
+ if (pContext == NULL || pContext->state == EQUALIZER_STATE_UNINITIALIZED) {
return -EINVAL;
}
@@ -594,10 +611,25 @@
p->data + p->psize);
} break;
case EFFECT_CMD_ENABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pContext->state != EQUALIZER_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ pContext->state = EQUALIZER_STATE_ACTIVE;
+ LOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
case EFFECT_CMD_DISABLE:
if (pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
+ if (pContext->state != EQUALIZER_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ pContext->state = EQUALIZER_STATE_INITIALIZED;
+ LOGV("EFFECT_CMD_DISABLE() OK");
*(int *)pReplyData = 0;
break;
case EFFECT_CMD_SET_DEVICE:
diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c
index ada252c..2ce7558 100644
--- a/media/libeffects/EffectReverb.c
+++ b/media/libeffects/EffectReverb.c
@@ -15,8 +15,7 @@
*/
#define LOG_TAG "EffectReverb"
-//
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#include <cutils/log.h>
#include <stdlib.h>
#include <string.h>
@@ -57,7 +56,7 @@
// Google auxiliary preset reverb UUID: 63909320-53a6-11df-bdbd-0002a5d5c51b
static const effect_descriptor_t gAuxPresetReverbDescriptor = {
- {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+ {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
EFFECT_FLAG_TYPE_AUXILIARY,
@@ -69,7 +68,7 @@
// Google insert preset reverb UUID: d93dc6a0-6342-11df-b128-0002a5d5c51b
static const effect_descriptor_t gInsertPresetReverbDescriptor = {
- {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+ {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
@@ -143,6 +142,8 @@
module->itfe = &gReverbInterface;
+ module->context.mState = REVERB_STATE_UNINITIALIZED;
+
if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) {
preset = 1;
}
@@ -158,6 +159,8 @@
*pInterface = (effect_interface_t) module;
+ module->context.mState = REVERB_STATE_INITIALIZED;
+
LOGV("EffectLibCreateEffect %p ,size %d", module, sizeof(reverb_module_t));
return 0;
@@ -171,6 +174,8 @@
return -EINVAL;
}
+ pRvbModule->context.mState = REVERB_STATE_UNINITIALIZED;
+
free(pRvbModule);
return 0;
}
@@ -195,8 +200,15 @@
pReverb = (reverb_object_t*) &pRvbModule->context;
+ if (pReverb->mState == REVERB_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+ if (pReverb->mState == REVERB_STATE_INITIALIZED) {
+ return -ENODATA;
+ }
+
//if bypassed or the preset forces the signal to be completely dry
- if (pReverb->m_bBypass) {
+ if (pReverb->m_bBypass != 0) {
if (inBuffer->raw != outBuffer->raw) {
int16_t smp;
pSrc = inBuffer->s16;
@@ -257,13 +269,15 @@
return 0;
}
+
static int Reverb_Command(effect_interface_t self, int cmdCode, int cmdSize,
void *pCmdData, int *replySize, void *pReplyData) {
reverb_module_t *pRvbModule = (reverb_module_t *) self;
reverb_object_t *pReverb;
int retsize;
- if (pRvbModule == NULL) {
+ if (pRvbModule == NULL ||
+ pRvbModule->context.mState == REVERB_STATE_UNINITIALIZED) {
return -EINVAL;
}
@@ -277,6 +291,9 @@
return -EINVAL;
}
*(int *) pReplyData = Reverb_Init(pRvbModule, pReverb->m_Aux, pReverb->m_Preset);
+ if (*(int *) pReplyData == 0) {
+ pRvbModule->context.mState = REVERB_STATE_INITIALIZED;
+ }
break;
case EFFECT_CMD_CONFIGURE:
if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
@@ -315,10 +332,25 @@
cmd->vsize, cmd->data + sizeof(int32_t));
break;
case EFFECT_CMD_ENABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pReverb->mState != REVERB_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ pReverb->mState = REVERB_STATE_ACTIVE;
+ LOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
case EFFECT_CMD_DISABLE:
if (pReplyData == NULL || *replySize != sizeof(int)) {
return -EINVAL;
}
+ if (pReverb->mState != REVERB_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ pReverb->mState = REVERB_STATE_INITIALIZED;
+ LOGV("EFFECT_CMD_DISABLE() OK");
*(int *)pReplyData = 0;
break;
case EFFECT_CMD_SET_DEVICE:
@@ -520,7 +552,7 @@
pReverb->m_bUseNoise = true;
// for debugging purposes, allow bypass
- pReverb->m_bBypass = false;
+ pReverb->m_bBypass = 0;
pReverb->m_nNextRoom = 1;
@@ -662,248 +694,254 @@
int32_t temp2;
size_t size;
- if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) {
- return -EINVAL;
- }
- if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) {
- return -EINVAL;
- }
-
- switch (param) {
- case REVERB_PARAM_ROOM_LEVEL:
- case REVERB_PARAM_ROOM_HF_LEVEL:
- case REVERB_PARAM_DECAY_HF_RATIO:
- case REVERB_PARAM_REFLECTIONS_LEVEL:
- case REVERB_PARAM_REVERB_LEVEL:
- case REVERB_PARAM_DIFFUSION:
- case REVERB_PARAM_DENSITY:
+ if (pReverb->m_Preset) {
+ if (param != REVERB_PARAM_PRESET || *pSize < sizeof(int16_t)) {
+ return -EINVAL;
+ }
size = sizeof(int16_t);
- break;
-
- case REVERB_PARAM_BYPASS:
- case REVERB_PARAM_PRESET:
- case REVERB_PARAM_DECAY_TIME:
- case REVERB_PARAM_REFLECTIONS_DELAY:
- case REVERB_PARAM_REVERB_DELAY:
- size = sizeof(int32_t);
- break;
-
- case REVERB_PARAM_PROPERTIES:
- size = sizeof(t_reverb_properties);
- break;
-
- default:
- return -EINVAL;
- }
-
- if (*pSize < size) {
- return -EINVAL;
- }
- *pSize = size;
- pValue32 = (int32_t *) pValue;
- pValue16 = (int16_t *) pValue;
- pProperties = (t_reverb_properties *) pValue;
-
- switch (param) {
- case REVERB_PARAM_BYPASS:
- *(int32_t *) pValue = (int32_t) pReverb->m_bBypass;
- break;
- case REVERB_PARAM_PRESET:
- *(int32_t *) pValue = (int8_t) pReverb->m_nCurrentRoom;
- break;
-
- case REVERB_PARAM_PROPERTIES:
- pValue16 = &pProperties->roomLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_ROOM_LEVEL:
- // Convert m_nRoomLpfFwd to millibels
- temp = (pReverb->m_nRoomLpfFwd << 15)
- / (32767 - pReverb->m_nRoomLpfFbk);
- *pValue16 = Effects_Linear16ToMillibels(temp);
-
- LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
-
- if (param == REVERB_PARAM_ROOM_LEVEL) {
- break;
- }
- pValue16 = &pProperties->roomHFLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_ROOM_HF_LEVEL:
- // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is:
- // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where:
- // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk
- // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz
-
- temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk);
- LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp);
- temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz)
- << 1;
- LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2);
- temp = 32767 + temp - temp2;
- LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp);
- temp = Effects_Sqrt(temp) * 181;
- LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp);
- temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp;
-
- LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
-
- *pValue16 = Effects_Linear16ToMillibels(temp);
-
- if (param == REVERB_PARAM_ROOM_HF_LEVEL) {
- break;
- }
- pValue32 = &pProperties->decayTime;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DECAY_TIME:
- // Calculate reverb feedback path gain
- temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk);
- temp = Effects_Linear16ToMillibels(temp);
-
- // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time
- temp = (-6000 * pReverb->m_nLateDelay) / temp;
-
- // Convert samples to ms
- *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate;
-
- LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32);
-
- if (param == REVERB_PARAM_DECAY_TIME) {
- break;
- }
- pValue16 = &pProperties->decayHFRatio;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DECAY_HF_RATIO:
- // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have:
- // DT_5000Hz = DT_0Hz * r
- // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so :
- // r = G_0Hz/G_5000Hz in millibels
- // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where:
- // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk
- // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd
- // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz
- if (pReverb->m_nRvbLpfFbk == 0) {
- *pValue16 = 1000;
- LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16);
+ pValue16 = (int16_t *)pValue;
+ // REVERB_PRESET_NONE is mapped to bypass
+ if (pReverb->m_bBypass != 0) {
+ *pValue16 = (int16_t)REVERB_PRESET_NONE;
} else {
- temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk);
- temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz)
+ *pValue16 = (int16_t)(pReverb->m_nNextRoom + 1);
+ }
+ LOGV("get REVERB_PARAM_PRESET, preset %d", *pValue16);
+ } else {
+ switch (param) {
+ case REVERB_PARAM_ROOM_LEVEL:
+ case REVERB_PARAM_ROOM_HF_LEVEL:
+ case REVERB_PARAM_DECAY_HF_RATIO:
+ case REVERB_PARAM_REFLECTIONS_LEVEL:
+ case REVERB_PARAM_REVERB_LEVEL:
+ case REVERB_PARAM_DIFFUSION:
+ case REVERB_PARAM_DENSITY:
+ size = sizeof(int16_t);
+ break;
+
+ case REVERB_PARAM_BYPASS:
+ case REVERB_PARAM_DECAY_TIME:
+ case REVERB_PARAM_REFLECTIONS_DELAY:
+ case REVERB_PARAM_REVERB_DELAY:
+ size = sizeof(int32_t);
+ break;
+
+ case REVERB_PARAM_PROPERTIES:
+ size = sizeof(t_reverb_properties);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (*pSize < size) {
+ return -EINVAL;
+ }
+
+ pValue32 = (int32_t *) pValue;
+ pValue16 = (int16_t *) pValue;
+ pProperties = (t_reverb_properties *) pValue;
+
+ switch (param) {
+ case REVERB_PARAM_BYPASS:
+ *pValue32 = (int32_t) pReverb->m_bBypass;
+ break;
+
+ case REVERB_PARAM_PROPERTIES:
+ pValue16 = &pProperties->roomLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_ROOM_LEVEL:
+ // Convert m_nRoomLpfFwd to millibels
+ temp = (pReverb->m_nRoomLpfFwd << 15)
+ / (32767 - pReverb->m_nRoomLpfFbk);
+ *pValue16 = Effects_Linear16ToMillibels(temp);
+
+ LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
+
+ if (param == REVERB_PARAM_ROOM_LEVEL) {
+ break;
+ }
+ pValue16 = &pProperties->roomHFLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_ROOM_HF_LEVEL:
+ // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is:
+ // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where:
+ // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk
+ // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz
+
+ temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk);
+ LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp);
+ temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz)
<< 1;
+ LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2);
temp = 32767 + temp - temp2;
+ LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp);
temp = Effects_Sqrt(temp) * 181;
- temp = (pReverb->m_nRvbLpfFwd << 15) / temp;
- // The linear gain at 0Hz is b0 / (a1 + 1)
- temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767
- - pReverb->m_nRvbLpfFbk);
+ LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp);
+ temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp;
+ LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
+
+ *pValue16 = Effects_Linear16ToMillibels(temp);
+
+ if (param == REVERB_PARAM_ROOM_HF_LEVEL) {
+ break;
+ }
+ pValue32 = &pProperties->decayTime;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DECAY_TIME:
+ // Calculate reverb feedback path gain
+ temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk);
temp = Effects_Linear16ToMillibels(temp);
- temp2 = Effects_Linear16ToMillibels(temp2);
- LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2);
- if (temp == 0)
- temp = 1;
- temp = (int16_t) ((1000 * temp2) / temp);
+ // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time
+ temp = (-6000 * pReverb->m_nLateDelay) / temp;
+
+ // Convert samples to ms
+ *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate;
+
+ LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32);
+
+ if (param == REVERB_PARAM_DECAY_TIME) {
+ break;
+ }
+ pValue16 = &pProperties->decayHFRatio;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DECAY_HF_RATIO:
+ // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have:
+ // DT_5000Hz = DT_0Hz * r
+ // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so :
+ // r = G_0Hz/G_5000Hz in millibels
+ // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where:
+ // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk
+ // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd
+ // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz
+ if (pReverb->m_nRvbLpfFbk == 0) {
+ *pValue16 = 1000;
+ LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16);
+ } else {
+ temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk);
+ temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz)
+ << 1;
+ temp = 32767 + temp - temp2;
+ temp = Effects_Sqrt(temp) * 181;
+ temp = (pReverb->m_nRvbLpfFwd << 15) / temp;
+ // The linear gain at 0Hz is b0 / (a1 + 1)
+ temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767
+ - pReverb->m_nRvbLpfFbk);
+
+ temp = Effects_Linear16ToMillibels(temp);
+ temp2 = Effects_Linear16ToMillibels(temp2);
+ LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2);
+
+ if (temp == 0)
+ temp = 1;
+ temp = (int16_t) ((1000 * temp2) / temp);
+ if (temp > 1000)
+ temp = 1000;
+
+ *pValue16 = temp;
+ LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16);
+ }
+
+ if (param == REVERB_PARAM_DECAY_HF_RATIO) {
+ break;
+ }
+ pValue16 = &pProperties->reflectionsLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REFLECTIONS_LEVEL:
+ *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain);
+
+ LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16);
+ if (param == REVERB_PARAM_REFLECTIONS_LEVEL) {
+ break;
+ }
+ pValue32 = &pProperties->reflectionsDelay;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REFLECTIONS_DELAY:
+ // convert samples to ms
+ *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate;
+
+ LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32);
+
+ if (param == REVERB_PARAM_REFLECTIONS_DELAY) {
+ break;
+ }
+ pValue16 = &pProperties->reverbLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REVERB_LEVEL:
+ // Convert linear gain to millibels
+ *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2);
+
+ LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16);
+
+ if (param == REVERB_PARAM_REVERB_LEVEL) {
+ break;
+ }
+ pValue32 = &pProperties->reverbDelay;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REVERB_DELAY:
+ // convert samples to ms
+ *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate;
+
+ LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32);
+
+ if (param == REVERB_PARAM_REVERB_DELAY) {
+ break;
+ }
+ pValue16 = &pProperties->diffusion;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DIFFUSION:
+ temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE))
+ / AP0_GAIN_RANGE);
+
+ if (temp < 0)
+ temp = 0;
if (temp > 1000)
temp = 1000;
*pValue16 = temp;
- LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16);
- }
+ LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain);
- if (param == REVERB_PARAM_DECAY_HF_RATIO) {
+ if (param == REVERB_PARAM_DIFFUSION) {
+ break;
+ }
+ pValue16 = &pProperties->density;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DENSITY:
+ // Calculate AP delay in time units
+ temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16)
+ / pReverb->m_nSamplingRate;
+
+ temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE);
+
+ if (temp < 0)
+ temp = 0;
+ if (temp > 1000)
+ temp = 1000;
+
+ *pValue16 = temp;
+
+ LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn);
+ break;
+
+ default:
break;
}
- pValue16 = &pProperties->reflectionsLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REFLECTIONS_LEVEL:
- *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain);
-
- LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16);
- if (param == REVERB_PARAM_REFLECTIONS_LEVEL) {
- break;
- }
- pValue32 = &pProperties->reflectionsDelay;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REFLECTIONS_DELAY:
- // convert samples to ms
- *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate;
-
- LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32);
-
- if (param == REVERB_PARAM_REFLECTIONS_DELAY) {
- break;
- }
- pValue16 = &pProperties->reverbLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REVERB_LEVEL:
- // Convert linear gain to millibels
- *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2);
-
- LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16);
-
- if (param == REVERB_PARAM_REVERB_LEVEL) {
- break;
- }
- pValue32 = &pProperties->reverbDelay;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REVERB_DELAY:
- // convert samples to ms
- *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate;
-
- LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32);
-
- if (param == REVERB_PARAM_REVERB_DELAY) {
- break;
- }
- pValue16 = &pProperties->diffusion;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DIFFUSION:
- temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE))
- / AP0_GAIN_RANGE);
-
- if (temp < 0)
- temp = 0;
- if (temp > 1000)
- temp = 1000;
-
- *pValue16 = temp;
- LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain);
-
- if (param == REVERB_PARAM_DIFFUSION) {
- break;
- }
- pValue16 = &pProperties->density;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DENSITY:
- // Calculate AP delay in time units
- temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16)
- / pReverb->m_nSamplingRate;
-
- temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE);
-
- if (temp < 0)
- temp = 0;
- if (temp > 1000)
- temp = 1000;
-
- *pValue16 = temp;
-
- LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn);
- break;
-
- default:
- break;
}
+ *pSize = size;
+
LOGV("Reverb_getParameter, context %p, param %d, value %d",
pReverb, param, *(int *)pValue);
@@ -945,382 +983,386 @@
LOGV("Reverb_setParameter, context %p, param %d, value16 %d, value32 %d",
pReverb, param, *(int16_t *)pValue, *(int32_t *)pValue);
- if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) {
- return -EINVAL;
- }
- if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) {
- return -EINVAL;
- }
-
- switch (param) {
- case REVERB_PARAM_ROOM_LEVEL:
- case REVERB_PARAM_ROOM_HF_LEVEL:
- case REVERB_PARAM_DECAY_HF_RATIO:
- case REVERB_PARAM_REFLECTIONS_LEVEL:
- case REVERB_PARAM_REVERB_LEVEL:
- case REVERB_PARAM_DIFFUSION:
- case REVERB_PARAM_DENSITY:
- paramSize = sizeof(int16_t);
- break;
-
- case REVERB_PARAM_BYPASS:
- case REVERB_PARAM_PRESET:
- case REVERB_PARAM_DECAY_TIME:
- case REVERB_PARAM_REFLECTIONS_DELAY:
- case REVERB_PARAM_REVERB_DELAY:
- paramSize = sizeof(int32_t);
- break;
-
- case REVERB_PARAM_PROPERTIES:
- paramSize = sizeof(t_reverb_properties);
- break;
-
- default:
- return -EINVAL;
- }
-
- if (size != paramSize) {
- return -EINVAL;
- }
-
- if (paramSize == sizeof(int16_t)) {
- value16 = *(int16_t *) pValue;
- } else if (paramSize == sizeof(int32_t)) {
- value32 = *(int32_t *) pValue;
- } else {
- pProperties = (t_reverb_properties *) pValue;
- }
-
- pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nCurrentRoom];
-
- switch (param) {
- case REVERB_PARAM_BYPASS:
- pReverb->m_bBypass = (uint16_t)value32;
- break;
- case REVERB_PARAM_PRESET:
- if (value32 != REVERB_PRESET_LARGE_HALL && value32
- != REVERB_PRESET_HALL && value32 != REVERB_PRESET_CHAMBER
- && value32 != REVERB_PRESET_ROOM)
+ if (pReverb->m_Preset) {
+ if (param != REVERB_PARAM_PRESET || size != sizeof(int16_t)) {
return -EINVAL;
- pReverb->m_nNextRoom = (int16_t) value32;
- break;
-
- case REVERB_PARAM_PROPERTIES:
- value16 = pProperties->roomLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_ROOM_LEVEL:
- // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd
- if (value16 > 0)
- return -EINVAL;
-
- temp = Effects_MillibelsToLinear16(value16);
-
- pReverb->m_nRoomLpfFwd
- = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk));
-
- LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
- if (param == REVERB_PARAM_ROOM_LEVEL)
- break;
- value16 = pProperties->roomHFLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_ROOM_HF_LEVEL:
-
- // Limit to 0 , -40dB range because of low pass implementation
- if (value16 > 0 || value16 < -4000)
- return -EINVAL;
- // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk
- // m_nRoomLpfFbk is -a1 where a1 is the solution of:
- // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where:
- // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz)
- // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz)
-
- // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged
- // while changing HF level
- temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767
- - pReverb->m_nRoomLpfFbk);
- if (value16 == 0) {
- pReverb->m_nRoomLpfFbk = 0;
- } else {
- int32_t dG2, b, delta;
-
- // dG^2
- temp = Effects_MillibelsToLinear16(value16);
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp);
- temp = (1 << 30) / temp;
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp);
- dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15);
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2);
- // b = 2*(C-dG^2)/(1-dG^2)
- b = (int32_t) ((((int64_t) 1 << (15 + 1))
- * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2))
- / ((int64_t) 32767 - (int64_t) dG2));
-
- // delta = b^2 - 4
- delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15
- + 2)));
-
- LOGV_IF(delta > (1<<30), " delta overflow %d", delta);
-
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz);
- // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2
- pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1;
}
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d",
- temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd);
-
- pReverb->m_nRoomLpfFwd
- = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk));
- LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd);
-
- if (param == REVERB_PARAM_ROOM_HF_LEVEL)
- break;
- value32 = pProperties->decayTime;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DECAY_TIME:
-
- // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk)
- // convert ms to samples
- value32 = (value32 * pReverb->m_nSamplingRate) / 1000;
-
- // calculate valid decay time range as a function of current reverb delay and
- // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB
- // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels.
- // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time
- averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion;
- averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn)
- + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1;
-
- temp = (-6000 * averageDelay) / value32;
- LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp);
- if (temp < -4000 || temp > -100)
+ value16 = *(int16_t *)pValue;
+ LOGV("set REVERB_PARAM_PRESET, preset %d", value16);
+ if (value16 < REVERB_PRESET_NONE || value16 > REVERB_PRESET_PLATE) {
return -EINVAL;
-
- // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output
- // xfade and sum gain (max +9dB)
- temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900;
- temp = Effects_MillibelsToLinear16(temp);
-
- // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk)
- pReverb->m_nRvbLpfFwd
- = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk));
-
- LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain));
-
- if (param == REVERB_PARAM_DECAY_TIME)
- break;
- value16 = pProperties->decayHFRatio;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DECAY_HF_RATIO:
-
- // We limit max value to 1000 because reverb filter is lowpass only
- if (value16 < 100 || value16 > 1000)
- return -EINVAL;
- // Convert per mille to => m_nLpfFwd, m_nLpfFbk
-
- // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged
- // while changing HF level
- temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk);
-
- if (value16 == 1000) {
- pReverb->m_nRvbLpfFbk = 0;
+ }
+ // REVERB_PRESET_NONE is mapped to bypass
+ if (value16 == REVERB_PRESET_NONE) {
+ pReverb->m_bBypass = 1;
} else {
- int32_t dG2, b, delta;
+ pReverb->m_bBypass = 0;
+ pReverb->m_nNextRoom = value16 - 1;
+ }
+ } else {
+ switch (param) {
+ case REVERB_PARAM_ROOM_LEVEL:
+ case REVERB_PARAM_ROOM_HF_LEVEL:
+ case REVERB_PARAM_DECAY_HF_RATIO:
+ case REVERB_PARAM_REFLECTIONS_LEVEL:
+ case REVERB_PARAM_REVERB_LEVEL:
+ case REVERB_PARAM_DIFFUSION:
+ case REVERB_PARAM_DENSITY:
+ paramSize = sizeof(int16_t);
+ break;
- temp = Effects_Linear16ToMillibels(temp2);
- // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels
+ case REVERB_PARAM_BYPASS:
+ case REVERB_PARAM_DECAY_TIME:
+ case REVERB_PARAM_REFLECTIONS_DELAY:
+ case REVERB_PARAM_REVERB_DELAY:
+ paramSize = sizeof(int32_t);
+ break;
- value32 = ((int32_t) 1000 << 15) / (int32_t) value16;
- LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32);
+ case REVERB_PARAM_PROPERTIES:
+ paramSize = sizeof(t_reverb_properties);
+ break;
- temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15);
+ default:
+ return -EINVAL;
+ }
- if (temp < -4000) {
- LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp);
- temp = -4000;
+ if (size != paramSize) {
+ return -EINVAL;
+ }
+
+ if (paramSize == sizeof(int16_t)) {
+ value16 = *(int16_t *) pValue;
+ } else if (paramSize == sizeof(int32_t)) {
+ value32 = *(int32_t *) pValue;
+ } else {
+ pProperties = (t_reverb_properties *) pValue;
+ }
+
+ pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom];
+
+ switch (param) {
+ case REVERB_PARAM_BYPASS:
+ pReverb->m_bBypass = (uint16_t)value32;
+ break;
+
+ case REVERB_PARAM_PROPERTIES:
+ value16 = pProperties->roomLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_ROOM_LEVEL:
+ // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd
+ if (value16 > 0)
+ return -EINVAL;
+
+ temp = Effects_MillibelsToLinear16(value16);
+
+ pReverb->m_nRoomLpfFwd
+ = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk));
+
+ LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk);
+ if (param == REVERB_PARAM_ROOM_LEVEL)
+ break;
+ value16 = pProperties->roomHFLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_ROOM_HF_LEVEL:
+
+ // Limit to 0 , -40dB range because of low pass implementation
+ if (value16 > 0 || value16 < -4000)
+ return -EINVAL;
+ // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk
+ // m_nRoomLpfFbk is -a1 where a1 is the solution of:
+ // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where:
+ // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz)
+ // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz)
+
+ // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged
+ // while changing HF level
+ temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767
+ - pReverb->m_nRoomLpfFbk);
+ if (value16 == 0) {
+ pReverb->m_nRoomLpfFbk = 0;
+ } else {
+ int32_t dG2, b, delta;
+
+ // dG^2
+ temp = Effects_MillibelsToLinear16(value16);
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp);
+ temp = (1 << 30) / temp;
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp);
+ dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15);
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2);
+ // b = 2*(C-dG^2)/(1-dG^2)
+ b = (int32_t) ((((int64_t) 1 << (15 + 1))
+ * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2))
+ / ((int64_t) 32767 - (int64_t) dG2));
+
+ // delta = b^2 - 4
+ delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15
+ + 2)));
+
+ LOGV_IF(delta > (1<<30), " delta overflow %d", delta);
+
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz);
+ // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2
+ pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1;
+ }
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d",
+ temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd);
+
+ pReverb->m_nRoomLpfFwd
+ = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk));
+ LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd);
+
+ if (param == REVERB_PARAM_ROOM_HF_LEVEL)
+ break;
+ value32 = pProperties->decayTime;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DECAY_TIME:
+
+ // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk)
+ // convert ms to samples
+ value32 = (value32 * pReverb->m_nSamplingRate) / 1000;
+
+ // calculate valid decay time range as a function of current reverb delay and
+ // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB
+ // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels.
+ // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time
+ averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion;
+ averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn)
+ + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1;
+
+ temp = (-6000 * averageDelay) / value32;
+ LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp);
+ if (temp < -4000 || temp > -100)
+ return -EINVAL;
+
+ // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output
+ // xfade and sum gain (max +9dB)
+ temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900;
+ temp = Effects_MillibelsToLinear16(temp);
+
+ // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk)
+ pReverb->m_nRvbLpfFwd
+ = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk));
+
+ LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain));
+
+ if (param == REVERB_PARAM_DECAY_TIME)
+ break;
+ value16 = pProperties->decayHFRatio;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DECAY_HF_RATIO:
+
+ // We limit max value to 1000 because reverb filter is lowpass only
+ if (value16 < 100 || value16 > 1000)
+ return -EINVAL;
+ // Convert per mille to => m_nLpfFwd, m_nLpfFbk
+
+ // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged
+ // while changing HF level
+ temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk);
+
+ if (value16 == 1000) {
+ pReverb->m_nRvbLpfFbk = 0;
+ } else {
+ int32_t dG2, b, delta;
+
+ temp = Effects_Linear16ToMillibels(temp2);
+ // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels
+
+ value32 = ((int32_t) 1000 << 15) / (int32_t) value16;
+ LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32);
+
+ temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15);
+
+ if (temp < -4000) {
+ LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp);
+ temp = -4000;
+ }
+
+ temp = Effects_MillibelsToLinear16(temp);
+ LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp);
+ // dG^2
+ temp = (temp2 << 15) / temp;
+ dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15);
+
+ // b = 2*(C-dG^2)/(1-dG^2)
+ b = (int32_t) ((((int64_t) 1 << (15 + 1))
+ * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2))
+ / ((int64_t) 32767 - (int64_t) dG2));
+
+ // delta = b^2 - 4
+ delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15
+ + 2)));
+
+ // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2
+ pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1;
+
+ LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta);
+
}
- temp = Effects_MillibelsToLinear16(temp);
- LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp);
- // dG^2
- temp = (temp2 << 15) / temp;
- dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15);
+ LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd);
- // b = 2*(C-dG^2)/(1-dG^2)
- b = (int32_t) ((((int64_t) 1 << (15 + 1))
- * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2))
- / ((int64_t) 32767 - (int64_t) dG2));
+ pReverb->m_nRvbLpfFwd
+ = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk));
- // delta = b^2 - 4
- delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15
- + 2)));
+ if (param == REVERB_PARAM_DECAY_HF_RATIO)
+ break;
+ value16 = pProperties->reflectionsLevel;
+ /* FALL THROUGH */
- // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2
- pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1;
+ case REVERB_PARAM_REFLECTIONS_LEVEL:
+ // We limit max value to 0 because gain is limited to 0dB
+ if (value16 > 0 || value16 < -6000)
+ return -EINVAL;
- LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta);
+ // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i].
+ value16 = Effects_MillibelsToLinear16(value16);
+ for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) {
+ pReverb->m_sEarlyL.m_nGain[i]
+ = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16);
+ pReverb->m_sEarlyR.m_nGain[i]
+ = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16);
+ }
+ pReverb->m_nEarlyGain = value16;
+ LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain);
+ if (param == REVERB_PARAM_REFLECTIONS_LEVEL)
+ break;
+ value32 = pProperties->reflectionsDelay;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REFLECTIONS_DELAY:
+ // We limit max value MAX_EARLY_TIME
+ // convert ms to time units
+ temp = (value32 * 65536) / 1000;
+ if (temp < 0 || temp > MAX_EARLY_TIME)
+ return -EINVAL;
+
+ maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate)
+ >> 16;
+ temp = (temp * pReverb->m_nSamplingRate) >> 16;
+ for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) {
+ temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i]
+ * pReverb->m_nSamplingRate) >> 16);
+ if (temp2 > maxSamples)
+ temp2 = maxSamples;
+ pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2;
+ temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i]
+ * pReverb->m_nSamplingRate) >> 16);
+ if (temp2 > maxSamples)
+ temp2 = maxSamples;
+ pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2;
+ }
+ pReverb->m_nEarlyDelay = temp;
+
+ LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples);
+
+ // Convert milliseconds to sample count => m_nEarlyDelay
+ if (param == REVERB_PARAM_REFLECTIONS_DELAY)
+ break;
+ value16 = pProperties->reverbLevel;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REVERB_LEVEL:
+ // We limit max value to 0 because gain is limited to 0dB
+ if (value16 > 0 || value16 < -6000)
+ return -EINVAL;
+ // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain.
+ pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2;
+
+ LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain);
+
+ if (param == REVERB_PARAM_REVERB_LEVEL)
+ break;
+ value32 = pProperties->reverbDelay;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_REVERB_DELAY:
+ // We limit max value to MAX_DELAY_TIME
+ // convert ms to time units
+ temp = (value32 * 65536) / 1000;
+ if (temp < 0 || temp > MAX_DELAY_TIME)
+ return -EINVAL;
+
+ maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate)
+ >> 16;
+ temp = (temp * pReverb->m_nSamplingRate) >> 16;
+ if ((temp + pReverb->m_nMaxExcursion) > maxSamples) {
+ temp = maxSamples - pReverb->m_nMaxExcursion;
+ }
+ if (temp < pReverb->m_nMaxExcursion) {
+ temp = pReverb->m_nMaxExcursion;
+ }
+
+ temp -= pReverb->m_nLateDelay;
+ pReverb->m_nDelay0Out += temp;
+ pReverb->m_nDelay1Out += temp;
+ pReverb->m_nLateDelay += temp;
+
+ LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples);
+
+ // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion
+ if (param == REVERB_PARAM_REVERB_DELAY)
+ break;
+
+ value16 = pProperties->diffusion;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DIFFUSION:
+ if (value16 < 0 || value16 > 1000)
+ return -EINVAL;
+
+ // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain
+ pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16
+ * AP0_GAIN_RANGE) / 1000;
+ pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16
+ * AP1_GAIN_RANGE) / 1000;
+
+ LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain);
+
+ if (param == REVERB_PARAM_DIFFUSION)
+ break;
+
+ value16 = pProperties->density;
+ /* FALL THROUGH */
+
+ case REVERB_PARAM_DENSITY:
+ if (value16 < 0 || value16 > 1000)
+ return -EINVAL;
+
+ // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut
+ maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16;
+
+ temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000;
+ /*lint -e{702} shift for performance */
+ temp = (temp * pReverb->m_nSamplingRate) >> 16;
+ if (temp > maxSamples)
+ temp = maxSamples;
+ pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp);
+
+ LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp);
+
+ temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000;
+ /*lint -e{702} shift for performance */
+ temp = (temp * pReverb->m_nSamplingRate) >> 16;
+ if (temp > maxSamples)
+ temp = maxSamples;
+ pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp);
+
+ LOGV("Ap1 delay smps %d", temp);
+
+ break;
+
+ default:
+ break;
}
-
- LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd);
-
- pReverb->m_nRvbLpfFwd
- = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk));
-
- if (param == REVERB_PARAM_DECAY_HF_RATIO)
- break;
- value16 = pProperties->reflectionsLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REFLECTIONS_LEVEL:
- // We limit max value to 0 because gain is limited to 0dB
- if (value16 > 0 || value16 < -6000)
- return -EINVAL;
-
- // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i].
- value16 = Effects_MillibelsToLinear16(value16);
- for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) {
- pReverb->m_sEarlyL.m_nGain[i]
- = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16);
- pReverb->m_sEarlyR.m_nGain[i]
- = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16);
- }
- pReverb->m_nEarlyGain = value16;
- LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain);
-
- if (param == REVERB_PARAM_REFLECTIONS_LEVEL)
- break;
- value32 = pProperties->reflectionsDelay;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REFLECTIONS_DELAY:
- // We limit max value MAX_EARLY_TIME
- // convert ms to time units
- temp = (value32 * 65536) / 1000;
- if (temp < 0 || temp > MAX_EARLY_TIME)
- return -EINVAL;
-
- maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate)
- >> 16;
- temp = (temp * pReverb->m_nSamplingRate) >> 16;
- for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) {
- temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i]
- * pReverb->m_nSamplingRate) >> 16);
- if (temp2 > maxSamples)
- temp2 = maxSamples;
- pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2;
- temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i]
- * pReverb->m_nSamplingRate) >> 16);
- if (temp2 > maxSamples)
- temp2 = maxSamples;
- pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2;
- }
- pReverb->m_nEarlyDelay = temp;
-
- LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples);
-
- // Convert milliseconds to sample count => m_nEarlyDelay
- if (param == REVERB_PARAM_REFLECTIONS_DELAY)
- break;
- value16 = pProperties->reverbLevel;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REVERB_LEVEL:
- // We limit max value to 0 because gain is limited to 0dB
- if (value16 > 0 || value16 < -6000)
- return -EINVAL;
- // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain.
- pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2;
-
- LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain);
-
- if (param == REVERB_PARAM_REVERB_LEVEL)
- break;
- value32 = pProperties->reverbDelay;
- /* FALL THROUGH */
-
- case REVERB_PARAM_REVERB_DELAY:
- // We limit max value to MAX_DELAY_TIME
- // convert ms to time units
- temp = (value32 * 65536) / 1000;
- if (temp < 0 || temp > MAX_DELAY_TIME)
- return -EINVAL;
-
- maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate)
- >> 16;
- temp = (temp * pReverb->m_nSamplingRate) >> 16;
- if ((temp + pReverb->m_nMaxExcursion) > maxSamples) {
- temp = maxSamples - pReverb->m_nMaxExcursion;
- }
- if (temp < pReverb->m_nMaxExcursion) {
- temp = pReverb->m_nMaxExcursion;
- }
-
- temp -= pReverb->m_nLateDelay;
- pReverb->m_nDelay0Out += temp;
- pReverb->m_nDelay1Out += temp;
- pReverb->m_nLateDelay += temp;
-
- LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples);
-
- // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion
- if (param == REVERB_PARAM_REVERB_DELAY)
- break;
-
- value16 = pProperties->diffusion;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DIFFUSION:
- if (value16 < 0 || value16 > 1000)
- return -EINVAL;
-
- // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain
- pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16
- * AP0_GAIN_RANGE) / 1000;
- pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16
- * AP1_GAIN_RANGE) / 1000;
-
- LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain);
-
- if (param == REVERB_PARAM_DIFFUSION)
- break;
-
- value16 = pProperties->density;
- /* FALL THROUGH */
-
- case REVERB_PARAM_DENSITY:
- if (value16 < 0 || value16 > 1000)
- return -EINVAL;
-
- // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut
- maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16;
-
- temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000;
- /*lint -e{702} shift for performance */
- temp = (temp * pReverb->m_nSamplingRate) >> 16;
- if (temp > maxSamples)
- temp = maxSamples;
- pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp);
-
- LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp);
-
- temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000;
- /*lint -e{702} shift for performance */
- temp = (temp * pReverb->m_nSamplingRate) >> 16;
- if (temp > maxSamples)
- temp = maxSamples;
- pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp);
-
- LOGV("Ap1 delay smps %d", temp);
-
- break;
-
- default:
- break;
}
+
return 0;
} /* end Reverb_setParameter */
@@ -1905,139 +1947,15 @@
*/
static int ReverbReadInPresets(reverb_object_t *pReverb) {
- int preset = 0;
- int defaultPreset = 0;
+ int preset;
- //now init any remaining presets to defaults
- for (defaultPreset = preset; defaultPreset < REVERB_MAX_ROOM_TYPE; defaultPreset++) {
- reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[defaultPreset];
- if (defaultPreset == 0 || defaultPreset > REVERB_MAX_ROOM_TYPE - 1) {
- pPreset->m_nRvbLpfFbk = 8307;
- pPreset->m_nRvbLpfFwd = 14768;
- pPreset->m_nEarlyGain = 27690;
- pPreset->m_nEarlyDelay = 1311;
- pPreset->m_nLateGain = 8191;
- pPreset->m_nLateDelay = 3932;
- pPreset->m_nRoomLpfFbk = 3692;
- pPreset->m_nRoomLpfFwd = 24569;
- pPreset->m_sEarlyL.m_zDelay[0] = 1376;
- pPreset->m_sEarlyL.m_nGain[0] = 22152;
- pPreset->m_sEarlyL.m_zDelay[1] = 2163;
- pPreset->m_sEarlyL.m_nGain[1] = 17537;
- pPreset->m_sEarlyL.m_zDelay[2] = 0;
- pPreset->m_sEarlyL.m_nGain[2] = 14768;
- pPreset->m_sEarlyL.m_zDelay[3] = 1835;
- pPreset->m_sEarlyL.m_nGain[3] = 14307;
- pPreset->m_sEarlyL.m_zDelay[4] = 0;
- pPreset->m_sEarlyL.m_nGain[4] = 13384;
- pPreset->m_sEarlyR.m_zDelay[0] = 721;
- pPreset->m_sEarlyR.m_nGain[0] = 20306;
- pPreset->m_sEarlyR.m_zDelay[1] = 2621;
- pPreset->m_sEarlyR.m_nGain[1] = 17537;
- pPreset->m_sEarlyR.m_zDelay[2] = 0;
- pPreset->m_sEarlyR.m_nGain[2] = 14768;
- pPreset->m_sEarlyR.m_zDelay[3] = 0;
- pPreset->m_sEarlyR.m_nGain[3] = 16153;
- pPreset->m_sEarlyR.m_zDelay[4] = 0;
- pPreset->m_sEarlyR.m_nGain[4] = 13384;
- pPreset->m_nMaxExcursion = 127;
- pPreset->m_nXfadeInterval = 6388;
- pPreset->m_nAp0_ApGain = 15691;
- pPreset->m_nAp0_ApOut = 711;
- pPreset->m_nAp1_ApGain = 16317;
- pPreset->m_nAp1_ApOut = 1029;
- pPreset->m_rfu4 = 0;
- pPreset->m_rfu5 = 0;
- pPreset->m_rfu6 = 0;
- pPreset->m_rfu7 = 0;
- pPreset->m_rfu8 = 0;
- pPreset->m_rfu9 = 0;
- pPreset->m_rfu10 = 0;
- } else if (defaultPreset == 1) {
- pPreset->m_nRvbLpfFbk = 6461;
- pPreset->m_nRvbLpfFwd = 14307;
- pPreset->m_nEarlyGain = 27690;
- pPreset->m_nEarlyDelay = 1311;
- pPreset->m_nLateGain = 8191;
- pPreset->m_nLateDelay = 3932;
- pPreset->m_nRoomLpfFbk = 3692;
- pPreset->m_nRoomLpfFwd = 24569;
- pPreset->m_sEarlyL.m_zDelay[0] = 1376;
- pPreset->m_sEarlyL.m_nGain[0] = 22152;
- pPreset->m_sEarlyL.m_zDelay[1] = 1462;
- pPreset->m_sEarlyL.m_nGain[1] = 17537;
- pPreset->m_sEarlyL.m_zDelay[2] = 0;
- pPreset->m_sEarlyL.m_nGain[2] = 14768;
- pPreset->m_sEarlyL.m_zDelay[3] = 1835;
- pPreset->m_sEarlyL.m_nGain[3] = 14307;
- pPreset->m_sEarlyL.m_zDelay[4] = 0;
- pPreset->m_sEarlyL.m_nGain[4] = 13384;
- pPreset->m_sEarlyR.m_zDelay[0] = 721;
- pPreset->m_sEarlyR.m_nGain[0] = 20306;
- pPreset->m_sEarlyR.m_zDelay[1] = 2621;
- pPreset->m_sEarlyR.m_nGain[1] = 17537;
- pPreset->m_sEarlyR.m_zDelay[2] = 0;
- pPreset->m_sEarlyR.m_nGain[2] = 14768;
- pPreset->m_sEarlyR.m_zDelay[3] = 0;
- pPreset->m_sEarlyR.m_nGain[3] = 16153;
- pPreset->m_sEarlyR.m_zDelay[4] = 0;
- pPreset->m_sEarlyR.m_nGain[4] = 13384;
- pPreset->m_nMaxExcursion = 127;
- pPreset->m_nXfadeInterval = 6391;
- pPreset->m_nAp0_ApGain = 15230;
- pPreset->m_nAp0_ApOut = 708;
- pPreset->m_nAp1_ApGain = 15547;
- pPreset->m_nAp1_ApOut = 1023;
- pPreset->m_rfu4 = 0;
- pPreset->m_rfu5 = 0;
- pPreset->m_rfu6 = 0;
- pPreset->m_rfu7 = 0;
- pPreset->m_rfu8 = 0;
- pPreset->m_rfu9 = 0;
- pPreset->m_rfu10 = 0;
- } else if (defaultPreset == 2) {
- pPreset->m_nRvbLpfFbk = 5077;
- pPreset->m_nRvbLpfFwd = 12922;
- pPreset->m_nEarlyGain = 27690;
- pPreset->m_nEarlyDelay = 1311;
- pPreset->m_nLateGain = 8191;
- pPreset->m_nLateDelay = 3932;
- pPreset->m_nRoomLpfFbk = 3692;
- pPreset->m_nRoomLpfFwd = 21703;
- pPreset->m_sEarlyL.m_zDelay[0] = 1376;
- pPreset->m_sEarlyL.m_nGain[0] = 22152;
- pPreset->m_sEarlyL.m_zDelay[1] = 1462;
- pPreset->m_sEarlyL.m_nGain[1] = 17537;
- pPreset->m_sEarlyL.m_zDelay[2] = 0;
- pPreset->m_sEarlyL.m_nGain[2] = 14768;
- pPreset->m_sEarlyL.m_zDelay[3] = 1835;
- pPreset->m_sEarlyL.m_nGain[3] = 14307;
- pPreset->m_sEarlyL.m_zDelay[4] = 0;
- pPreset->m_sEarlyL.m_nGain[4] = 13384;
- pPreset->m_sEarlyR.m_zDelay[0] = 721;
- pPreset->m_sEarlyR.m_nGain[0] = 20306;
- pPreset->m_sEarlyR.m_zDelay[1] = 2621;
- pPreset->m_sEarlyR.m_nGain[1] = 17537;
- pPreset->m_sEarlyR.m_zDelay[2] = 0;
- pPreset->m_sEarlyR.m_nGain[2] = 14768;
- pPreset->m_sEarlyR.m_zDelay[3] = 0;
- pPreset->m_sEarlyR.m_nGain[3] = 16153;
- pPreset->m_sEarlyR.m_zDelay[4] = 0;
- pPreset->m_sEarlyR.m_nGain[4] = 13384;
- pPreset->m_nMaxExcursion = 127;
- pPreset->m_nXfadeInterval = 6449;
- pPreset->m_nAp0_ApGain = 15691;
- pPreset->m_nAp0_ApOut = 774;
- pPreset->m_nAp1_ApGain = 16317;
- pPreset->m_nAp1_ApOut = 1155;
- pPreset->m_rfu4 = 0;
- pPreset->m_rfu5 = 0;
- pPreset->m_rfu6 = 0;
- pPreset->m_rfu7 = 0;
- pPreset->m_rfu8 = 0;
- pPreset->m_rfu9 = 0;
- pPreset->m_rfu10 = 0;
- } else if (defaultPreset == 3) {
+ // this is for test only. OpenSL ES presets are mapped to 4 presets.
+ // REVERB_PRESET_NONE is mapped to bypass
+ for (preset = 0; preset < REVERB_NUM_PRESETS; preset++) {
+ reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[preset];
+ switch (preset + 1) {
+ case REVERB_PRESET_PLATE:
+ case REVERB_PRESET_SMALLROOM:
pPreset->m_nRvbLpfFbk = 5077;
pPreset->m_nRvbLpfFwd = 11076;
pPreset->m_nEarlyGain = 27690;
@@ -2079,6 +1997,137 @@
pPreset->m_rfu8 = 0;
pPreset->m_rfu9 = 0;
pPreset->m_rfu10 = 0;
+ break;
+ case REVERB_PRESET_MEDIUMROOM:
+ case REVERB_PRESET_LARGEROOM:
+ pPreset->m_nRvbLpfFbk = 5077;
+ pPreset->m_nRvbLpfFwd = 12922;
+ pPreset->m_nEarlyGain = 27690;
+ pPreset->m_nEarlyDelay = 1311;
+ pPreset->m_nLateGain = 8191;
+ pPreset->m_nLateDelay = 3932;
+ pPreset->m_nRoomLpfFbk = 3692;
+ pPreset->m_nRoomLpfFwd = 21703;
+ pPreset->m_sEarlyL.m_zDelay[0] = 1376;
+ pPreset->m_sEarlyL.m_nGain[0] = 22152;
+ pPreset->m_sEarlyL.m_zDelay[1] = 1462;
+ pPreset->m_sEarlyL.m_nGain[1] = 17537;
+ pPreset->m_sEarlyL.m_zDelay[2] = 0;
+ pPreset->m_sEarlyL.m_nGain[2] = 14768;
+ pPreset->m_sEarlyL.m_zDelay[3] = 1835;
+ pPreset->m_sEarlyL.m_nGain[3] = 14307;
+ pPreset->m_sEarlyL.m_zDelay[4] = 0;
+ pPreset->m_sEarlyL.m_nGain[4] = 13384;
+ pPreset->m_sEarlyR.m_zDelay[0] = 721;
+ pPreset->m_sEarlyR.m_nGain[0] = 20306;
+ pPreset->m_sEarlyR.m_zDelay[1] = 2621;
+ pPreset->m_sEarlyR.m_nGain[1] = 17537;
+ pPreset->m_sEarlyR.m_zDelay[2] = 0;
+ pPreset->m_sEarlyR.m_nGain[2] = 14768;
+ pPreset->m_sEarlyR.m_zDelay[3] = 0;
+ pPreset->m_sEarlyR.m_nGain[3] = 16153;
+ pPreset->m_sEarlyR.m_zDelay[4] = 0;
+ pPreset->m_sEarlyR.m_nGain[4] = 13384;
+ pPreset->m_nMaxExcursion = 127;
+ pPreset->m_nXfadeInterval = 6449;
+ pPreset->m_nAp0_ApGain = 15691;
+ pPreset->m_nAp0_ApOut = 774;
+ pPreset->m_nAp1_ApGain = 16317;
+ pPreset->m_nAp1_ApOut = 1155;
+ pPreset->m_rfu4 = 0;
+ pPreset->m_rfu5 = 0;
+ pPreset->m_rfu6 = 0;
+ pPreset->m_rfu7 = 0;
+ pPreset->m_rfu8 = 0;
+ pPreset->m_rfu9 = 0;
+ pPreset->m_rfu10 = 0;
+ break;
+ case REVERB_PRESET_MEDIUMHALL:
+ pPreset->m_nRvbLpfFbk = 6461;
+ pPreset->m_nRvbLpfFwd = 14307;
+ pPreset->m_nEarlyGain = 27690;
+ pPreset->m_nEarlyDelay = 1311;
+ pPreset->m_nLateGain = 8191;
+ pPreset->m_nLateDelay = 3932;
+ pPreset->m_nRoomLpfFbk = 3692;
+ pPreset->m_nRoomLpfFwd = 24569;
+ pPreset->m_sEarlyL.m_zDelay[0] = 1376;
+ pPreset->m_sEarlyL.m_nGain[0] = 22152;
+ pPreset->m_sEarlyL.m_zDelay[1] = 1462;
+ pPreset->m_sEarlyL.m_nGain[1] = 17537;
+ pPreset->m_sEarlyL.m_zDelay[2] = 0;
+ pPreset->m_sEarlyL.m_nGain[2] = 14768;
+ pPreset->m_sEarlyL.m_zDelay[3] = 1835;
+ pPreset->m_sEarlyL.m_nGain[3] = 14307;
+ pPreset->m_sEarlyL.m_zDelay[4] = 0;
+ pPreset->m_sEarlyL.m_nGain[4] = 13384;
+ pPreset->m_sEarlyR.m_zDelay[0] = 721;
+ pPreset->m_sEarlyR.m_nGain[0] = 20306;
+ pPreset->m_sEarlyR.m_zDelay[1] = 2621;
+ pPreset->m_sEarlyR.m_nGain[1] = 17537;
+ pPreset->m_sEarlyR.m_zDelay[2] = 0;
+ pPreset->m_sEarlyR.m_nGain[2] = 14768;
+ pPreset->m_sEarlyR.m_zDelay[3] = 0;
+ pPreset->m_sEarlyR.m_nGain[3] = 16153;
+ pPreset->m_sEarlyR.m_zDelay[4] = 0;
+ pPreset->m_sEarlyR.m_nGain[4] = 13384;
+ pPreset->m_nMaxExcursion = 127;
+ pPreset->m_nXfadeInterval = 6391;
+ pPreset->m_nAp0_ApGain = 15230;
+ pPreset->m_nAp0_ApOut = 708;
+ pPreset->m_nAp1_ApGain = 15547;
+ pPreset->m_nAp1_ApOut = 1023;
+ pPreset->m_rfu4 = 0;
+ pPreset->m_rfu5 = 0;
+ pPreset->m_rfu6 = 0;
+ pPreset->m_rfu7 = 0;
+ pPreset->m_rfu8 = 0;
+ pPreset->m_rfu9 = 0;
+ pPreset->m_rfu10 = 0;
+ break;
+ case REVERB_PRESET_LARGEHALL:
+ pPreset->m_nRvbLpfFbk = 8307;
+ pPreset->m_nRvbLpfFwd = 14768;
+ pPreset->m_nEarlyGain = 27690;
+ pPreset->m_nEarlyDelay = 1311;
+ pPreset->m_nLateGain = 8191;
+ pPreset->m_nLateDelay = 3932;
+ pPreset->m_nRoomLpfFbk = 3692;
+ pPreset->m_nRoomLpfFwd = 24569;
+ pPreset->m_sEarlyL.m_zDelay[0] = 1376;
+ pPreset->m_sEarlyL.m_nGain[0] = 22152;
+ pPreset->m_sEarlyL.m_zDelay[1] = 2163;
+ pPreset->m_sEarlyL.m_nGain[1] = 17537;
+ pPreset->m_sEarlyL.m_zDelay[2] = 0;
+ pPreset->m_sEarlyL.m_nGain[2] = 14768;
+ pPreset->m_sEarlyL.m_zDelay[3] = 1835;
+ pPreset->m_sEarlyL.m_nGain[3] = 14307;
+ pPreset->m_sEarlyL.m_zDelay[4] = 0;
+ pPreset->m_sEarlyL.m_nGain[4] = 13384;
+ pPreset->m_sEarlyR.m_zDelay[0] = 721;
+ pPreset->m_sEarlyR.m_nGain[0] = 20306;
+ pPreset->m_sEarlyR.m_zDelay[1] = 2621;
+ pPreset->m_sEarlyR.m_nGain[1] = 17537;
+ pPreset->m_sEarlyR.m_zDelay[2] = 0;
+ pPreset->m_sEarlyR.m_nGain[2] = 14768;
+ pPreset->m_sEarlyR.m_zDelay[3] = 0;
+ pPreset->m_sEarlyR.m_nGain[3] = 16153;
+ pPreset->m_sEarlyR.m_zDelay[4] = 0;
+ pPreset->m_sEarlyR.m_nGain[4] = 13384;
+ pPreset->m_nMaxExcursion = 127;
+ pPreset->m_nXfadeInterval = 6388;
+ pPreset->m_nAp0_ApGain = 15691;
+ pPreset->m_nAp0_ApOut = 711;
+ pPreset->m_nAp1_ApGain = 16317;
+ pPreset->m_nAp1_ApOut = 1029;
+ pPreset->m_rfu4 = 0;
+ pPreset->m_rfu5 = 0;
+ pPreset->m_rfu6 = 0;
+ pPreset->m_rfu7 = 0;
+ pPreset->m_rfu8 = 0;
+ pPreset->m_rfu9 = 0;
+ pPreset->m_rfu10 = 0;
+ break;
}
}
diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h
index f5aadfa..ee8e390 100644
--- a/media/libeffects/EffectReverb.h
+++ b/media/libeffects/EffectReverb.h
@@ -17,7 +17,8 @@
#ifndef ANDROID_EFFECTREVERB_H_
#define ANDROID_EFFECTREVERB_H_
-#include <media/EffectReverbApi.h>
+#include <media/EffectEnvironmentalReverbApi.h>
+#include <media/EffectPresetReverbApi.h>
/*------------------------------------
@@ -43,7 +44,7 @@
#define REVERB_BUFFER_SIZE_IN_SAMPLES_MAX 16384
-#define REVERB_MAX_ROOM_TYPE 4 // any room numbers larger than this are invalid
+#define REVERB_NUM_PRESETS REVERB_PRESET_PLATE // REVERB_PRESET_NONE is not included
#define REVERB_MAX_NUM_REFLECTIONS 5 // max num reflections per channel
@@ -113,6 +114,12 @@
#define AP1_GAIN_RANGE (int)(22936-6553)
+enum reverb_state_e {
+ REVERB_STATE_UNINITIALIZED,
+ REVERB_STATE_INITIALIZED,
+ REVERB_STATE_ACTIVE,
+};
+
/* parameters for each allpass */
typedef struct
{
@@ -171,7 +178,7 @@
typedef struct
{
- reverb_preset_t m_sPreset[REVERB_MAX_ROOM_TYPE]; //array of presets
+ reverb_preset_t m_sPreset[REVERB_NUM_PRESETS]; // array of presets(does not include REVERB_PRESET_NONE)
} reverb_preset_bank_t;
@@ -278,6 +285,7 @@
uint16_t m_Aux; // if TRUE, is connected as auxiliary effect
uint16_t m_Preset; // if TRUE, expose preset revert interface
+ uint32_t mState;
} reverb_object_t;
diff --git a/media/libeffects/EffectVisualizer.cpp b/media/libeffects/EffectVisualizer.cpp
index f27e296..bcda06e 100644
--- a/media/libeffects/EffectVisualizer.cpp
+++ b/media/libeffects/EffectVisualizer.cpp
@@ -231,7 +231,7 @@
return -EINVAL;
}
if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
- return -ENOSYS;
+ return -ENODATA;
}
if (inBuffer == NULL || inBuffer->raw == NULL ||
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 783249d..df0f73b 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -52,7 +52,7 @@
)
: mStatus(NO_INIT)
{
- mStatus = set(type, uuid, priority, cbf, user, output, sessionId);
+ mStatus = set(type, uuid, priority, cbf, user, sessionId, output);
}
AudioEffect::AudioEffect(const char *typeStr,
@@ -84,7 +84,7 @@
}
}
- mStatus = set(pType, pUuid, priority, cbf, user, output, sessionId);
+ mStatus = set(pType, pUuid, priority, cbf, user, sessionId, output);
}
status_t AudioEffect::set(const effect_uuid_t *type,
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 24426ff..ba98f04 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -81,13 +81,13 @@
}
static bool fileMatchesExtension(const char* path, const char* extensions) {
- char* extension = strrchr(path, '.');
+ const char* extension = strrchr(path, '.');
if (!extension) return false;
++extension; // skip the dot
if (extension[0] == 0) return false;
while (extensions[0]) {
- char* comma = strchr(extensions, ',');
+ const char* comma = strchr(extensions, ',');
size_t length = (comma ? comma - extensions : strlen(extensions));
if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
extensions += length;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 4872047..5401ec0 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -511,11 +511,17 @@
sp<Client> c = mClients[i].promote();
if (c != 0) c->dump(fd, args);
}
- for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) {
- result.append(" MediaRecorderClient\n");
- sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote();
- snprintf(buffer, 255, " pid(%d)\n\n", c->mPid);
- result.append(buffer);
+ if (mMediaRecorderClients.size() == 0) {
+ result.append(" No media recorder client\n\n");
+ } else {
+ for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) {
+ sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote();
+ snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mPid);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ result = "\n";
+ c->dump(fd, args);
+ }
}
result.append(" Files opened and/or mapped:\n");
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 80b1cfd..fef3e6e 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -329,5 +329,12 @@
return mRecorder->setListener(listener);
}
+status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) const {
+ if (mRecorder != NULL) {
+ return mRecorder->dump(fd, args);
+ }
+ return OK;
+}
+
}; // namespace android
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index b53d950..d12e558 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -28,7 +28,7 @@
class MediaRecorderClient : public BnMediaRecorder
{
public:
- virtual status_t setCamera(const sp<ICamera>& camera);
+ virtual status_t setCamera(const sp<ICamera>& camera);
virtual status_t setPreviewSurface(const sp<ISurface>& surface);
virtual status_t setVideoSource(int vs);
virtual status_t setAudioSource(int as);
@@ -45,21 +45,22 @@
virtual status_t getMaxAmplitude(int* max);
virtual status_t start();
virtual status_t stop();
- virtual status_t reset();
+ virtual status_t reset();
virtual status_t init();
virtual status_t close();
virtual status_t release();
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
private:
- friend class MediaPlayerService; // for accessing private constructor
+ friend class MediaPlayerService; // for accessing private constructor
- MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid);
- virtual ~MediaRecorderClient();
+ MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid);
+ virtual ~MediaRecorderClient();
- pid_t mPid;
- Mutex mLock;
- MediaRecorderBase *mRecorder;
- sp<MediaPlayerService> mMediaPlayerService;
+ pid_t mPid;
+ Mutex mLock;
+ MediaRecorderBase *mRecorder;
+ sp<MediaPlayerService> mMediaPlayerService;
};
}; // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 509c6fd..24b0e7b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -381,12 +381,12 @@
return OK;
}
-// If interval < 0, only the first frame is I frame, and rest are all P frames
-// If interval == 0, all frames are encoded as I frames. No P frames
-// If interval > 0, it is the time spacing (seconds) between 2 neighboring I frames
-status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
- LOGV("setParamVideoIFramesInterval: %d seconds", interval);
- mIFramesInterval = interval;
+// If seconds < 0, only the first frame is I frame, and rest are all P frames
+// If seconds == 0, all frames are encoded as I frames. No P frames
+// If seconds > 0, it is the time spacing (seconds) between 2 neighboring I frames
+status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t seconds) {
+ LOGV("setParamVideoIFramesInterval: %d seconds", seconds);
+ mIFramesIntervalSec = seconds;
return OK;
}
@@ -444,6 +444,44 @@
return OK;
}
+status_t StagefrightRecorder::setParamMovieTimeScale(int32_t timeScale) {
+ LOGV("setParamMovieTimeScale: %d", timeScale);
+
+ // The range is set to be the same as the audio's time scale range
+ // since audio's time scale has a wider range.
+ if (timeScale < 600 || timeScale > 96000) {
+ LOGE("Time scale (%d) for movie is out of range [600, 96000]", timeScale);
+ return BAD_VALUE;
+ }
+ mMovieTimeScale = timeScale;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamVideoTimeScale(int32_t timeScale) {
+ LOGV("setParamVideoTimeScale: %d", timeScale);
+
+ // 60000 is chosen to make sure that each video frame from a 60-fps
+ // video has 1000 ticks.
+ if (timeScale < 600 || timeScale > 60000) {
+ LOGE("Time scale (%d) for video is out of range [600, 60000]", timeScale);
+ return BAD_VALUE;
+ }
+ mVideoTimeScale = timeScale;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamAudioTimeScale(int32_t timeScale) {
+ LOGV("setParamAudioTimeScale: %d", timeScale);
+
+ // 96000 Hz is the highest sampling rate support in AAC.
+ if (timeScale < 600 || timeScale > 96000) {
+ LOGE("Time scale (%d) for audio is out of range [600, 96000]", timeScale);
+ return BAD_VALUE;
+ }
+ mAudioTimeScale = timeScale;
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -462,6 +500,11 @@
if (safe_strtoi32(value.string(), &durationUs)) {
return setParamInterleaveDuration(durationUs);
}
+ } else if (key == "param-movie-time-scale") {
+ int32_t timeScale;
+ if (safe_strtoi32(value.string(), &timeScale)) {
+ return setParamMovieTimeScale(timeScale);
+ }
} else if (key == "param-use-64bit-offset") {
int32_t use64BitOffset;
if (safe_strtoi32(value.string(), &use64BitOffset)) {
@@ -492,15 +535,20 @@
if (safe_strtoi32(value.string(), &audio_bitrate)) {
return setParamAudioEncodingBitRate(audio_bitrate);
}
+ } else if (key == "audio-param-time-scale") {
+ int32_t timeScale;
+ if (safe_strtoi32(value.string(), &timeScale)) {
+ return setParamAudioTimeScale(timeScale);
+ }
} else if (key == "video-param-encoding-bitrate") {
int32_t video_bitrate;
if (safe_strtoi32(value.string(), &video_bitrate)) {
return setParamVideoEncodingBitRate(video_bitrate);
}
} else if (key == "video-param-i-frames-interval") {
- int32_t interval;
- if (safe_strtoi32(value.string(), &interval)) {
- return setParamVideoIFramesInterval(interval);
+ int32_t seconds;
+ if (safe_strtoi32(value.string(), &seconds)) {
+ return setParamVideoIFramesInterval(seconds);
}
} else if (key == "video-param-encoder-profile") {
int32_t profile;
@@ -517,6 +565,11 @@
if (safe_strtoi32(value.string(), &cameraId)) {
return setParamVideoCameraId(cameraId);
}
+ } else if (key == "video-param-time-scale") {
+ int32_t timeScale;
+ if (safe_strtoi32(value.string(), &timeScale)) {
+ return setParamVideoTimeScale(timeScale);
+ }
} else {
LOGE("setParameter: failed to find key %s", key.string());
}
@@ -637,6 +690,7 @@
encMeta->setInt32(kKeyChannelCount, mAudioChannels);
encMeta->setInt32(kKeySampleRate, mSampleRate);
encMeta->setInt32(kKeyBitRate, mAudioBitRate);
+ encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -871,17 +925,20 @@
sp<MetaData> meta = cameraSource->getFormat();
- int32_t width, height, stride, sliceHeight;
+ int32_t width, height, stride, sliceHeight, colorFormat;
CHECK(meta->findInt32(kKeyWidth, &width));
CHECK(meta->findInt32(kKeyHeight, &height));
CHECK(meta->findInt32(kKeyStride, &stride));
CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
+ CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
+ enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec);
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
+ enc_meta->setInt32(kKeyColorFormat, colorFormat);
+ enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -919,6 +976,7 @@
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
}
+
writer->addSource(audioEncoder);
return OK;
}
@@ -955,6 +1013,7 @@
meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+ meta->setInt32(kKeyTimeScale, mMovieTimeScale);
if (mTrackEveryNumberOfFrames > 0) {
meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames);
}
@@ -1025,9 +1084,12 @@
mAudioChannels = 1;
mAudioBitRate = 12200;
mInterleaveDurationUs = 0;
- mIFramesInterval = 1;
+ mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
+ mMovieTimeScale = 1000;
+ mAudioTimeScale = 1000;
+ mVideoTimeScale = 1000;
mCameraId = 0;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
@@ -1061,4 +1123,64 @@
return OK;
}
+status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, " Recorder: %p", this);
+ snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Max file size (bytes): %lld\n", mMaxFileSizeBytes);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Max file duration (us): %lld\n", mMaxFileDurationUs);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Progress notification: %d frames\n", mTrackEveryNumberOfFrames);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Progress notification: %lld us\n", mTrackEveryTimeDurationUs);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Audio\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Source: %d\n", mAudioSource);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Encoder: %d\n", mAudioEncoder);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mAudioBitRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Sampling rate (hz): %d\n", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Number of channels: %d\n", mAudioChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Max amplitude: %d\n", mAudioSourceNode == 0? 0: mAudioSourceNode->getMaxAmplitude());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Video\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Source: %d\n", mVideoSource);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Camera flags: %d\n", mFlags);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesIntervalSec);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Frame rate (fps): %d\n", mFrameRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mVideoBitRate);
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return OK;
+}
} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 2090666..f51d7f8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -54,6 +54,7 @@
virtual status_t close();
virtual status_t reset();
virtual status_t getMaxAmplitude(int *max);
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
private:
enum CameraFlags {
@@ -80,10 +81,13 @@
int32_t mAudioChannels;
int32_t mSampleRate;
int32_t mInterleaveDurationUs;
- int32_t mIFramesInterval;
+ int32_t mIFramesIntervalSec;
int32_t mCameraId;
int32_t mVideoEncoderProfile;
int32_t mVideoEncoderLevel;
+ int32_t mMovieTimeScale;
+ int32_t mVideoTimeScale;
+ int32_t mAudioTimeScale;
int64_t mMaxFileSizeBytes;
int64_t mMaxFileDurationUs;
int32_t mTrackEveryNumberOfFrames;
@@ -110,17 +114,20 @@
status_t setParamAudioEncodingBitRate(int32_t bitRate);
status_t setParamAudioNumberOfChannels(int32_t channles);
status_t setParamAudioSamplingRate(int32_t sampleRate);
+ status_t setParamAudioTimeScale(int32_t timeScale);
status_t setParamVideoEncodingBitRate(int32_t bitRate);
- status_t setParamVideoIFramesInterval(int32_t interval);
+ status_t setParamVideoIFramesInterval(int32_t seconds);
status_t setParamVideoEncoderProfile(int32_t profile);
status_t setParamVideoEncoderLevel(int32_t level);
status_t setParamVideoCameraId(int32_t cameraId);
+ status_t setParamVideoTimeScale(int32_t timeScale);
status_t setParamTrackTimeStatus(int64_t timeDurationUs);
status_t setParamTrackFrameStatus(int32_t nFrames);
status_t setParamInterleaveDuration(int32_t durationUs);
status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxFileDurationUs(int64_t timeUs);
status_t setParamMaxFileSizeBytes(int64_t bytes);
+ status_t setParamMovieTimeScale(int32_t timeScale);
void clipVideoBitRate();
void clipVideoFrameRate();
void clipVideoFrameWidth();
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 60d0233..49cf647 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -66,6 +66,7 @@
libstagefright_amrwbdec \
libstagefright_amrwbenc \
libstagefright_avcdec \
+ libstagefright_avcenc \
libstagefright_m4vh263dec \
libstagefright_mp3dec \
libstagefright_vorbisdec \
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6a4a131..b7388bb 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,6 +41,7 @@
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
+
~Track();
status_t start(MetaData *params);
@@ -61,12 +62,13 @@
volatile bool mResumed;
int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
+ int32_t mTimeScale;
pthread_t mThread;
struct SampleInfo {
size_t size;
- int64_t timestamp;
+ int64_t timestampUs;
};
List<SampleInfo> mSampleInfos;
bool mSamplesHaveSameSize;
@@ -92,11 +94,11 @@
struct SttsTableEntry {
- SttsTableEntry(uint32_t count, uint32_t duration)
- : sampleCount(count), sampleDuration(duration) {}
+ SttsTableEntry(uint32_t count, uint32_t durationUs)
+ : sampleCount(count), sampleDurationUs(durationUs) {}
uint32_t sampleCount;
- uint32_t sampleDuration;
+ uint32_t sampleDurationUs;
};
List<SttsTableEntry> mSttsTableEntries;
@@ -270,6 +272,13 @@
return OK;
}
+ if (!param ||
+ !param->findInt32(kKeyTimeScale, &mTimeScale)) {
+ mTimeScale = 1000;
+ }
+ CHECK(mTimeScale > 0);
+ LOGV("movie time scale: %d", mTimeScale);
+
mStreamableFile = true;
mWriteMoovBoxToMemory = false;
mMoovBoxBuffer = NULL;
@@ -336,14 +345,14 @@
return;
}
- int64_t max_duration = 0;
+ int64_t maxDurationUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
(*it)->stop();
- int64_t duration = (*it)->getDurationUs();
- if (duration > max_duration) {
- max_duration = duration;
+ int64_t durationUs = (*it)->getDurationUs();
+ if (durationUs > maxDurationUs) {
+ maxDurationUs = durationUs;
}
}
@@ -367,8 +376,7 @@
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t timeScale = 1000;
- int32_t duration = max_duration / timeScale;
+ int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
beginBox("moov");
@@ -376,7 +384,7 @@
writeInt32(0); // version=0, flags=0
writeInt32(now); // creation time
writeInt32(now); // modification time
- writeInt32(timeScale); // timescale
+ writeInt32(mTimeScale); // mvhd timescale
writeInt32(duration);
writeInt32(0x10000); // rate: 1.0
writeInt16(0x100); // volume
@@ -655,7 +663,6 @@
}
int64_t MPEG4Writer::getStartTimestampUs() {
- LOGI("getStartTimestampUs: %lld", mStartTimestampUs);
Mutex::Autolock autoLock(mLock);
return mStartTimestampUs;
}
@@ -683,6 +690,11 @@
mGotAllCodecSpecificData(false),
mReachedEOS(false) {
getCodecSpecificDataFromInputFormatIfPossible();
+
+ if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
+ mTimeScale = 1000;
+ }
+ CHECK(mTimeScale > 0);
}
void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
@@ -927,9 +939,9 @@
int64_t chunkTimestampUs = 0;
int32_t nChunks = 0;
int32_t nZeroLengthFrames = 0;
- int64_t lastTimestamp = 0; // Timestamp of the previous sample
- int64_t lastDuration = 0; // Time spacing between the previous two samples
- int32_t sampleCount = 1; // Sample count in the current stts table entry
+ int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
+ int64_t lastDurationUs = 0; // Between the previous two samples in ms
+ 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;
sp<MetaData> meta_data;
@@ -1113,7 +1125,7 @@
}
if (mResumed) {
- previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - 1000 * lastDuration);
+ previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - lastDurationUs);
mResumed = false;
}
@@ -1124,12 +1136,11 @@
mMaxTimeStampUs = timestampUs;
}
- // Our timestamp is in ms.
- info.timestamp = (timestampUs + 500) / 1000;
+ info.timestampUs = timestampUs;
mSampleInfos.push_back(info);
if (mSampleInfos.size() > 2) {
- if (lastDuration != info.timestamp - lastTimestamp) {
- SttsTableEntry sttsEntry(sampleCount, lastDuration);
+ if (lastDurationUs != info.timestampUs - lastTimestampUs) {
+ SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
} else {
@@ -1142,8 +1153,8 @@
}
previousSampleSize = info.size;
}
- lastDuration = info.timestamp - lastTimestamp;
- lastTimestamp = info.timestamp;
+ lastDurationUs = info.timestampUs - lastTimestampUs;
+ lastTimestampUs = info.timestampUs;
if (isSync != 0) {
mStssTableEntries.push_back(mSampleInfos.size());
@@ -1213,11 +1224,11 @@
// there is no frame time after it, just repeat the previous
// frame's duration.
if (mSampleInfos.size() == 1) {
- lastDuration = 0; // A single sample's duration
+ lastDurationUs = 0; // A single sample's duration
} else {
++sampleCount; // Count for the last sample
}
- SttsTableEntry sttsEntry(sampleCount, lastDuration);
+ SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
mReachedEOS = true;
LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
@@ -1249,12 +1260,13 @@
void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
int32_t *min, int32_t *avg, int32_t *max) {
CHECK(!mSampleInfos.empty());
- int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000/ mSampleInfos.size();
+ int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000 / mSampleInfos.size();
int32_t minSampleDurationMs = 0x7FFFFFFF;
int32_t maxSampleDurationMs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
- int32_t sampleDurationMs = static_cast<int32_t>(it->sampleDuration);
+ int32_t sampleDurationMs =
+ (static_cast<int32_t>(it->sampleDurationUs) + 500) / 1000;
if (sampleDurationMs > maxSampleDurationMs) {
maxSampleDurationMs = sampleDurationMs;
} else if (sampleDurationMs < minSampleDurationMs) {
@@ -1370,10 +1382,13 @@
CHECK(success);
bool is_audio = !strncasecmp(mime, "audio/", 6);
- int32_t timeScale = 1000;
- int32_t duration = getDurationUs() / timeScale;
+ LOGV("%s track time scale: %d",
+ is_audio? "Audio": "Video", mTimeScale);
+
time_t now = time(NULL);
+ int32_t mvhdTimeScale = mOwner->getTimeScale();
+ int64_t trakDurationUs = getDurationUs();
mOwner->beginBox("trak");
@@ -1385,7 +1400,9 @@
mOwner->writeInt32(now); // modification time
mOwner->writeInt32(trackID);
mOwner->writeInt32(0); // reserved
- mOwner->writeInt32(duration);
+ int32_t tkhdDuration =
+ (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(tkhdDuration); // in mvhd timescale
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // layer
@@ -1423,12 +1440,17 @@
mOwner->beginBox("elst");
mOwner->writeInt32(0); // version=0, flags=0: 32-bit time
mOwner->writeInt32(2); // never ends with an empty list
- int64_t durationMs =
- (mStartTimestampUs - moovStartTimeUs) / 1000;
- mOwner->writeInt32(durationMs); // edit duration
- mOwner->writeInt32(-1); // empty edit box to signal starting time offset
- mOwner->writeInt32(1 << 16); // x1 rate
- mOwner->writeInt32(duration);
+
+ // First elst entry: specify the starting time offset
+ int64_t offsetUs = mStartTimestampUs - moovStartTimeUs;
+ int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(seg); // in mvhd timecale
+ mOwner->writeInt32(-1); // starting time offset
+ mOwner->writeInt32(1 << 16); // rate = 1.0
+
+ // Second elst entry: specify the track duration
+ seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(seg); // in mvhd timescale
mOwner->writeInt32(0);
mOwner->writeInt32(1 << 16);
mOwner->endBox();
@@ -1441,8 +1463,9 @@
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(now); // creation time
mOwner->writeInt32(now); // modification time
- mOwner->writeInt32(timeScale); // timescale
- mOwner->writeInt32(duration); // duration
+ mOwner->writeInt32(mTimeScale); // media timescale
+ int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(mdhdDuration); // use media timescale
// Language follows the three letter standard ISO-639-2/T
// 'e', 'n', 'g' for "English", for instance.
// Each character is packed as the difference between its ASCII value and 0x60.
@@ -1664,7 +1687,8 @@
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
- mOwner->writeInt32(it->sampleDuration);
+ int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+ mOwner->writeInt32(dur);
}
mOwner->endBox(); // stts
@@ -1717,7 +1741,7 @@
mOwner->writeInt64((*it));
}
}
- mOwner->endBox(); // co64
+ mOwner->endBox(); // stco or co64
mOwner->endBox(); // stbl
mOwner->endBox(); // minf
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index ab9285d..332bab3 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -42,7 +42,7 @@
path->setTo(slashPos);
}
- char *colonPos = strchr(host->string(), ':');
+ const char *colonPos = strchr(host->string(), ':');
if (colonPos != NULL) {
unsigned long x;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 83f7040..157897b 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -25,6 +25,7 @@
#include "include/AMRWBDecoder.h"
#include "include/AMRWBEncoder.h"
#include "include/AVCDecoder.h"
+#include "include/AVCEncoder.h"
#include "include/M4vH263Decoder.h"
#include "include/MP3Decoder.h"
#include "include/VorbisDecoder.h"
@@ -81,6 +82,7 @@
FACTORY_CREATE_ENCODER(AMRNBEncoder)
FACTORY_CREATE_ENCODER(AMRWBEncoder)
FACTORY_CREATE_ENCODER(AACEncoder)
+FACTORY_CREATE_ENCODER(AVCEncoder)
static sp<MediaSource> InstantiateSoftwareEncoder(
const char *name, const sp<MediaSource> &source,
@@ -94,6 +96,7 @@
FACTORY_REF(AMRNBEncoder)
FACTORY_REF(AMRWBEncoder)
FACTORY_REF(AACEncoder)
+ FACTORY_REF(AVCEncoder)
};
for (size_t i = 0;
i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) {
@@ -186,6 +189,7 @@
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "AVCEncoder" },
// { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
};
@@ -948,7 +952,7 @@
int32_t supportedProfile = static_cast<int32_t>(param.eProfile);
int32_t supportedLevel = static_cast<int32_t>(param.eLevel);
- CODEC_LOGV("Supported profile: %ld, level %ld",
+ CODEC_LOGV("Supported profile: %d, level %d",
supportedProfile, supportedLevel);
if (profile == supportedProfile &&
@@ -3208,6 +3212,12 @@
void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
mOutputFormat = new MetaData;
mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
+ if (mIsEncoder) {
+ int32_t timeScale;
+ if (inputFormat->findInt32(kKeyTimeScale, &timeScale)) {
+ mOutputFormat->setInt32(kKeyTimeScale, timeScale);
+ }
+ }
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index 2bc4448..f3b281f 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -15,6 +15,7 @@
*/
#include "AACDecoder.h"
+#define LOG_TAG "AACDecoder"
#include "../../include/ESDS.h"
@@ -36,26 +37,33 @@
mAnchorTimeUs(0),
mNumSamplesOutput(0),
mInputBuffer(NULL) {
-}
-AACDecoder::~AACDecoder() {
- if (mStarted) {
- stop();
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t sampleRate;
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ mMeta = new MetaData;
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+
+ // We'll always output stereo, regardless of how many channels are
+ // present in the input due to decoder limitations.
+ mMeta->setInt32(kKeyChannelCount, 2);
+ mMeta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ mMeta->setInt64(kKeyDuration, durationUs);
}
+ mMeta->setCString(kKeyDecoderComponent, "AACDecoder");
- delete mConfig;
- mConfig = NULL;
+ mInitCheck = initCheck();
}
-status_t AACDecoder::start(MetaData *params) {
- CHECK(!mStarted);
-
- mBufferGroup = new MediaBufferGroup;
- mBufferGroup->add_buffer(new MediaBuffer(2048 * 2));
-
+status_t AACDecoder::initCheck() {
+ memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal));
mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED;
- mConfig->aacPlusUpsamplingFactor = 0;
- mConfig->aacPlusEnabled = false;
+ mConfig->aacPlusEnabled = 1;
// The software decoder doesn't properly support mono output on
// AACplus files. Always output stereo.
@@ -64,8 +72,11 @@
UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements();
mDecoderBuf = malloc(memRequirements);
- CHECK_EQ(PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf),
- MP4AUDEC_SUCCESS);
+ status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ LOGE("Failed to initialize MP4 audio decoder");
+ return UNKNOWN_ERROR;
+ }
uint32_t type;
const void *data;
@@ -83,18 +94,38 @@
mConfig->pInputBuffer = (UChar *)codec_specific_data;
mConfig->inputBufferCurrentLength = codec_specific_data_size;
mConfig->inputBufferMaxLength = 0;
- mConfig->inputBufferUsedLength = 0;
- mConfig->remainderBits = 0;
-
- mConfig->pOutputBuffer = NULL;
- mConfig->pOutputBuffer_plus = NULL;
- mConfig->repositionFlag = false;
if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf)
!= MP4AUDEC_SUCCESS) {
return ERROR_UNSUPPORTED;
}
+
+ // Check on the sampling rate to see whether it is changed.
+ int32_t sampleRate;
+ CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
+ if (mConfig->samplingRate != sampleRate) {
+ mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
+ LOGW("Sample rate was %d, but now is %d",
+ sampleRate, mConfig->samplingRate);
+ }
}
+ return OK;
+}
+
+AACDecoder::~AACDecoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+status_t AACDecoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(new MediaBuffer(4096 * 2));
mSource->start();
@@ -127,28 +158,7 @@
}
sp<MetaData> AACDecoder::getFormat() {
- sp<MetaData> srcFormat = mSource->getFormat();
-
- int32_t sampleRate;
- CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
-
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-
- // We'll always output stereo, regardless of how many channels are
- // present in the input due to decoder limitations.
- meta->setInt32(kKeyChannelCount, 2);
-
- meta->setInt32(kKeySampleRate, sampleRate);
-
- int64_t durationUs;
- if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
- meta->setInt64(kKeyDuration, durationUs);
- }
-
- meta->setCString(kKeyDecoderComponent, "AACDecoder");
-
- return meta;
+ return mMeta;
}
status_t AACDecoder::read(
@@ -200,13 +210,19 @@
mConfig->remainderBits = 0;
mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data());
- mConfig->pOutputBuffer_plus = NULL;
+ mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048];
mConfig->repositionFlag = false;
Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
size_t numOutBytes =
mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
+ if (mConfig->aacPlusUpsamplingFactor == 2) {
+ if (mConfig->desiredChannels == 1) {
+ memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2);
+ }
+ numOutBytes *= 2;
+ }
if (decoderErr != MP4AUDEC_SUCCESS) {
LOGW("AAC decoder returned error %d, substituting silence", decoderErr);
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
new file mode 100644
index 0000000..d5eb156
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -0,0 +1,492 @@
+/*
+ * 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 "AVCEncoder"
+#include <utils/Log.h>
+
+#include "AVCEncoder.h"
+
+#include "avcenc_api.h"
+#include "avcenc_int.h"
+#include "OMX_Video.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>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+inline static void ConvertYUV420SemiPlanarToYUV420Planar(
+ uint8_t *inyuv, uint8_t* outyuv,
+ int32_t width, int32_t height) {
+
+ int32_t outYsize = width * height;
+ uint32_t *outy = (uint32_t *) outyuv;
+ uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
+ uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
+
+ /* Y copying */
+ memcpy(outy, inyuv, outYsize);
+
+ /* U & V copying */
+ uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
+ for (int32_t i = height >> 1; i > 0; --i) {
+ for (int32_t j = width >> 2; j > 0; --j) {
+ uint32_t temp = *inyuv_4++;
+ uint32_t tempU = temp & 0xFF;
+ tempU = tempU | ((temp >> 8) & 0xFF00);
+
+ uint32_t tempV = (temp >> 8) & 0xFF;
+ tempV = tempV | ((temp >> 16) & 0xFF00);
+
+ // Flip U and V
+ *outcb++ = tempV;
+ *outcr++ = tempU;
+ }
+ }
+}
+
+static int32_t MallocWrapper(
+ void *userData, int32_t size, int32_t attrs) {
+ return reinterpret_cast<int32_t>(malloc(size));
+}
+
+static void FreeWrapper(void *userData, int32_t ptr) {
+ free(reinterpret_cast<void *>(ptr));
+}
+
+static int32_t DpbAllocWrapper(void *userData,
+ unsigned int sizeInMbs, unsigned int numBuffers) {
+ AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
+ CHECK(encoder != NULL);
+ return encoder->allocOutputBuffers(sizeInMbs, numBuffers);
+}
+
+static int32_t BindFrameWrapper(
+ void *userData, int32_t index, uint8_t **yuv) {
+ AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
+ CHECK(encoder != NULL);
+ return encoder->bindOutputBuffer(index, yuv);
+}
+
+static void UnbindFrameWrapper(void *userData, int32_t index) {
+ AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
+ CHECK(encoder != NULL);
+ return encoder->unbindOutputBuffer(index);
+}
+
+AVCEncoder::AVCEncoder(
+ const sp<MediaSource>& source,
+ const sp<MetaData>& meta)
+ : mSource(source),
+ mMeta(meta),
+ mNumInputFrames(-1),
+ mStarted(false),
+ mInputBuffer(NULL),
+ mInputFrameData(NULL),
+ mGroup(NULL) {
+
+ LOGV("Construct software AVCEncoder");
+
+ mHandle = new tagAVCHandle;
+ memset(mHandle, 0, sizeof(tagAVCHandle));
+ mHandle->AVCObject = NULL;
+ mHandle->userData = this;
+ mHandle->CBAVC_DPBAlloc = DpbAllocWrapper;
+ mHandle->CBAVC_FrameBind = BindFrameWrapper;
+ mHandle->CBAVC_FrameUnbind = UnbindFrameWrapper;
+ mHandle->CBAVC_Malloc = MallocWrapper;
+ mHandle->CBAVC_Free = FreeWrapper;
+
+ mInitCheck = initCheck(meta);
+}
+
+AVCEncoder::~AVCEncoder() {
+ LOGV("Destruct software AVCEncoder");
+ if (mStarted) {
+ stop();
+ }
+
+ delete mEncParams;
+ delete mHandle;
+}
+
+status_t AVCEncoder::initCheck(const sp<MetaData>& meta) {
+ LOGV("initCheck");
+ CHECK(meta->findInt32(kKeyWidth, &mVideoWidth));
+ CHECK(meta->findInt32(kKeyHeight, &mVideoHeight));
+ CHECK(meta->findInt32(kKeySampleRate, &mVideoFrameRate));
+ CHECK(meta->findInt32(kKeyBitRate, &mVideoBitRate));
+
+ // XXX: Add more color format support
+ CHECK(meta->findInt32(kKeyColorFormat, &mVideoColorFormat));
+ if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
+ if (mVideoColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) {
+ LOGE("Color format %d is not supported", mVideoColorFormat);
+ return BAD_VALUE;
+ }
+ // Allocate spare buffer only when color conversion is needed.
+ // Assume the color format is OMX_COLOR_FormatYUV420SemiPlanar.
+ mInputFrameData =
+ (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
+ CHECK(mInputFrameData);
+ }
+
+ // XXX: Remove this restriction
+ if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) {
+ LOGE("Video frame size %dx%d must be a multiple of 16",
+ mVideoWidth, mVideoHeight);
+ return BAD_VALUE;
+ }
+
+ mEncParams = new tagAVCEncParam;
+ memset(mEncParams, 0, sizeof(mEncParams));
+ mEncParams->width = mVideoWidth;
+ mEncParams->height = mVideoHeight;
+ mEncParams->frame_rate = 1000 * mVideoFrameRate; // In frames/ms!
+ mEncParams->rate_control = AVC_ON;
+ mEncParams->bitrate = mVideoBitRate;
+ mEncParams->initQP = 0;
+ mEncParams->init_CBP_removal_delay = 1600;
+ mEncParams->CPB_size = (uint32_t) (mVideoBitRate >> 1);
+
+ mEncParams->intramb_refresh = 0;
+ mEncParams->auto_scd = AVC_ON;
+ mEncParams->out_of_band_param_set = AVC_ON;
+ mEncParams->poc_type = 2;
+ mEncParams->log2_max_poc_lsb_minus_4 = 12;
+ mEncParams->delta_poc_zero_flag = 0;
+ mEncParams->offset_poc_non_ref = 0;
+ mEncParams->offset_top_bottom = 0;
+ mEncParams->num_ref_in_cycle = 0;
+ mEncParams->offset_poc_ref = NULL;
+
+ mEncParams->num_ref_frame = 1;
+ mEncParams->num_slice_group = 1;
+ mEncParams->fmo_type = 0;
+
+ mEncParams->db_filter = AVC_ON;
+ mEncParams->disable_db_idc = 0;
+
+ mEncParams->alpha_offset = 0;
+ mEncParams->beta_offset = 0;
+ mEncParams->constrained_intra_pred = AVC_OFF;
+
+ mEncParams->data_par = AVC_OFF;
+ mEncParams->fullsearch = AVC_OFF;
+ mEncParams->search_range = 16;
+ mEncParams->sub_pel = AVC_OFF;
+ mEncParams->submb_pred = AVC_OFF;
+ mEncParams->rdopt_mode = AVC_OFF;
+ mEncParams->bidir_pred = AVC_OFF;
+ int32_t nMacroBlocks = ((((mVideoWidth + 15) >> 4) << 4) *
+ (((mVideoHeight + 15) >> 4) << 4)) >> 8;
+ uint32_t *sliceGroup = (uint32_t *) malloc(sizeof(uint32_t) * nMacroBlocks);
+ for (int ii = 0, idx = 0; ii < nMacroBlocks; ++ii) {
+ sliceGroup[ii] = idx++;
+ if (idx >= mEncParams->num_slice_group) {
+ idx = 0;
+ }
+ }
+ mEncParams->slice_group = sliceGroup;
+
+ mEncParams->use_overrun_buffer = AVC_OFF;
+
+ // Set IDR frame refresh interval
+ int32_t iFramesIntervalSec;
+ CHECK(meta->findInt32(kKeyIFramesInterval, &iFramesIntervalSec));
+ if (iFramesIntervalSec < 0) {
+ mEncParams->idr_period = -1;
+ } else if (iFramesIntervalSec == 0) {
+ mEncParams->idr_period = 1; // All I frames
+ } else {
+ mEncParams->idr_period =
+ (iFramesIntervalSec * mVideoFrameRate);
+ }
+ LOGV("idr_period: %d, I-frames interval: %d seconds, and frame rate: %d",
+ mEncParams->idr_period, iFramesIntervalSec, mVideoFrameRate);
+
+ // Set profile and level
+ // If profile and level setting is not correct, failure
+ // is reported when the encoder is initialized.
+ mEncParams->profile = AVC_BASELINE;
+ mEncParams->level = AVC_LEVEL3_2;
+ int32_t profile, level;
+ if (meta->findInt32(kKeyVideoProfile, &profile)) {
+ mEncParams->profile = (AVCProfile) profile;
+ }
+ if (meta->findInt32(kKeyVideoLevel, &level)) {
+ mEncParams->level = (AVCLevel) level;
+ }
+
+
+ mFormat = new MetaData;
+ mFormat->setInt32(kKeyWidth, mVideoWidth);
+ mFormat->setInt32(kKeyHeight, mVideoHeight);
+ mFormat->setInt32(kKeyBitRate, mVideoBitRate);
+ mFormat->setInt32(kKeySampleRate, mVideoFrameRate);
+ mFormat->setInt32(kKeyColorFormat, mVideoColorFormat);
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ mFormat->setCString(kKeyDecoderComponent, "AVCEncoder");
+ return OK;
+}
+
+status_t AVCEncoder::start(MetaData *params) {
+ LOGV("start");
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mStarted) {
+ LOGW("Call start() when encoder already started");
+ return OK;
+ }
+
+ AVCEnc_Status err;
+ err = PVAVCEncInitialize(mHandle, mEncParams, NULL, NULL);
+ if (err != AVCENC_SUCCESS) {
+ LOGE("Failed to initialize the encoder: %d", err);
+ return UNKNOWN_ERROR;
+ }
+
+ mGroup = new MediaBufferGroup();
+ int32_t maxSize;
+ if (AVCENC_SUCCESS !=
+ PVAVCEncGetMaxOutputBufferSize(mHandle, &maxSize)) {
+ maxSize = 31584; // Magic #
+ }
+ mGroup->add_buffer(new MediaBuffer(maxSize));
+
+ mSource->start(params);
+ mNumInputFrames = -2; // 1st two buffers contain SPS and PPS
+ mStarted = true;
+ mSpsPpsHeaderReceived = false;
+ mReadyForNextFrame = true;
+ mIsIDRFrame = 0;
+
+ return OK;
+}
+
+status_t AVCEncoder::stop() {
+ LOGV("stop");
+ if (!mStarted) {
+ LOGW("Call stop() when encoder has not started");
+ return OK;
+ }
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ if (mGroup) {
+ delete mGroup;
+ mGroup = NULL;
+ }
+
+ if (mInputFrameData) {
+ delete mInputFrameData;
+ mInputFrameData = NULL;
+ }
+
+ PVAVCCleanUpEncoder(mHandle);
+ mSource->stop();
+ releaseOutputBuffers();
+ mStarted = false;
+
+ return OK;
+}
+
+void AVCEncoder::releaseOutputBuffers() {
+ LOGV("releaseOutputBuffers");
+ for (size_t i = 0; i < mOutputBuffers.size(); ++i) {
+ MediaBuffer *buffer = mOutputBuffers.editItemAt(i);
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+ mOutputBuffers.clear();
+}
+
+sp<MetaData> AVCEncoder::getFormat() {
+ LOGV("getFormat");
+ return mFormat;
+}
+
+status_t AVCEncoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+
+ CHECK(!options);
+ *out = NULL;
+
+ MediaBuffer *outputBuffer;
+ CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
+ uint8_t *outPtr = (uint8_t *) outputBuffer->data();
+ uint32_t dataLength = outputBuffer->size();
+
+ int32_t type;
+ AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
+
+ // Return SPS and PPS for the first two buffers
+ if (!mSpsPpsHeaderReceived) {
+ encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
+ if (encoderStatus == AVCENC_WRONG_STATE) {
+ mSpsPpsHeaderReceived = true;
+ CHECK_EQ(0, mNumInputFrames); // 1st video frame is 0
+ } 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);
+ *out = outputBuffer;
+ return OK;
+ default:
+ LOGE("Nal type (%d) other than SPS/PPS is unexpected", type);
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
+ // Get next input video frame
+ if (mReadyForNextFrame) {
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+ status_t err = mSource->read(&mInputBuffer, options);
+ if (err != OK) {
+ LOGE("Failed to read input video frame: %d", err);
+ outputBuffer->release();
+ return err;
+ }
+ int64_t timeUs;
+ CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ AVCFrameIO videoInput;
+ memset(&videoInput, 0, sizeof(videoInput));
+ videoInput.height = ((mVideoHeight + 15) >> 4) << 4;
+ videoInput.pitch = ((mVideoWidth + 15) >> 4) << 4;
+ videoInput.coding_timestamp = (timeUs + 500) / 1000; // in ms
+ uint8_t *inputData = (uint8_t *) mInputBuffer->data();
+
+ if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
+ CHECK(mInputFrameData);
+ CHECK(mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar);
+ ConvertYUV420SemiPlanarToYUV420Planar(
+ inputData, mInputFrameData, mVideoWidth, mVideoHeight);
+ inputData = mInputFrameData;
+ }
+ CHECK(inputData != NULL);
+ videoInput.YCbCr[0] = inputData;
+ videoInput.YCbCr[1] = videoInput.YCbCr[0] + videoInput.height * videoInput.pitch;
+ videoInput.YCbCr[2] = videoInput.YCbCr[1] +
+ ((videoInput.height * videoInput.pitch) >> 2);
+ videoInput.disp_order = mNumInputFrames;
+
+ encoderStatus = PVAVCEncSetInput(mHandle, &videoInput);
+ if (encoderStatus == AVCENC_SUCCESS ||
+ encoderStatus == AVCENC_NEW_IDR) {
+ mReadyForNextFrame = false;
+ ++mNumInputFrames;
+ if (encoderStatus == AVCENC_NEW_IDR) {
+ mIsIDRFrame = 1;
+ }
+ } else {
+ if (encoderStatus < AVCENC_SUCCESS) {
+ outputBuffer->release();
+ return UNKNOWN_ERROR;
+ } else {
+ outputBuffer->set_range(0, 0);
+ *out = outputBuffer;
+ return OK;
+ }
+ }
+ }
+
+ // Encode an input video frame
+ CHECK(encoderStatus == AVCENC_SUCCESS ||
+ encoderStatus == AVCENC_NEW_IDR);
+ dataLength = outputBuffer->size(); // Reset the output buffer length
+ encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
+ if (encoderStatus == AVCENC_SUCCESS) {
+ outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
+ CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+ } else if (encoderStatus == AVCENC_PICTURE_READY) {
+ CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
+ if (mIsIDRFrame) {
+ outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
+ mIsIDRFrame = 0;
+ LOGV("Output an IDR frame");
+ }
+ mReadyForNextFrame = true;
+ AVCFrameIO recon;
+ if (PVAVCEncGetRecon(mHandle, &recon) == AVCENC_SUCCESS) {
+ PVAVCEncReleaseRecon(mHandle, &recon);
+ }
+ } else {
+ dataLength = 0;
+ mReadyForNextFrame = true;
+ }
+ if (encoderStatus < AVCENC_SUCCESS) {
+ outputBuffer->release();
+ return UNKNOWN_ERROR;
+ }
+
+ outputBuffer->set_range(0, dataLength);
+ *out = outputBuffer;
+ return OK;
+}
+
+int32_t AVCEncoder::allocOutputBuffers(
+ unsigned int sizeInMbs, unsigned int numBuffers) {
+ CHECK(mOutputBuffers.isEmpty());
+ size_t frameSize = (sizeInMbs << 7) * 3;
+ for (unsigned int i = 0; i < numBuffers; ++i) {
+ MediaBuffer *buffer = new MediaBuffer(frameSize);
+ buffer->setObserver(this);
+ mOutputBuffers.push(buffer);
+ }
+
+ return 1;
+}
+
+void AVCEncoder::unbindOutputBuffer(int32_t index) {
+ CHECK(index >= 0);
+}
+
+int32_t AVCEncoder::bindOutputBuffer(int32_t index, uint8_t **yuv) {
+ CHECK(index >= 0);
+ CHECK(index < (int32_t) mOutputBuffers.size());
+ int64_t timeUs;
+ CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ mOutputBuffers[index]->meta_data()->setInt64(kKeyTime, timeUs);
+
+ *yuv = (uint8_t *) mOutputBuffers[index]->data();
+
+ return 1;
+}
+
+void AVCEncoder::signalBufferReturned(MediaBuffer *buffer) {
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
new file mode 100644
index 0000000..100f239
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AVCEncoder.cpp \
+ src/avcenc_api.cpp \
+ src/bitstream_io.cpp \
+ src/block.cpp \
+ src/findhalfpel.cpp \
+ src/header.cpp \
+ src/init.cpp \
+ src/intra_est.cpp \
+ src/motion_comp.cpp \
+ src/motion_est.cpp \
+ src/rate_control.cpp \
+ src/residual.cpp \
+ src/sad.cpp \
+ src/sad_halfpel.cpp \
+ src/slice.cpp \
+ src/vlc_encode.cpp
+
+
+LOCAL_MODULE := libstagefright_avcenc
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/src \
+ $(LOCAL_PATH)/../common/include \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/frameworks/base/media/libstagefright/include
+
+LOCAL_CFLAGS := \
+ -D__arm__ \
+ -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/avc/enc/src/avcenc_api.cpp b/media/libstagefright/codecs/avc/enc/src/avcenc_api.cpp
new file mode 100644
index 0000000..d39885d
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/avcenc_api.cpp
@@ -0,0 +1,744 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_api.h"
+#include "avcenc_lib.h"
+
+/* ======================================================================== */
+/* Function : PVAVCGetNALType() */
+/* Date : 11/4/2003 */
+/* Purpose : Sniff NAL type from the bitstream */
+/* In/out : */
+/* Return : AVCENC_SUCCESS if succeed, AVCENC_FAIL if fail. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncGetNALType(unsigned char *bitstream, int size,
+ int *nal_type, int *nal_ref_idc)
+{
+ int forbidden_zero_bit;
+ if (size > 0)
+ {
+ forbidden_zero_bit = bitstream[0] >> 7;
+ if (forbidden_zero_bit != 0)
+ return AVCENC_FAIL;
+ *nal_ref_idc = (bitstream[0] & 0x60) >> 5;
+ *nal_type = bitstream[0] & 0x1F;
+ return AVCENC_SUCCESS;
+ }
+
+ return AVCENC_FAIL;
+}
+
+
+/* ======================================================================== */
+/* Function : PVAVCEncInitialize() */
+/* Date : 3/18/2004 */
+/* Purpose : Initialize the encoder library, allocate memory and verify */
+/* the profile/level support/settings. */
+/* In/out : Encoding parameters. */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncInitialize(AVCHandle *avcHandle, AVCEncParams *encParam,
+ void* extSPS, void* extPPS)
+{
+ AVCEnc_Status status;
+ AVCEncObject *encvid;
+ AVCCommonObj *video;
+ uint32 *userData = (uint32*) avcHandle->userData;
+ int framesize;
+
+ if (avcHandle->AVCObject != NULL)
+ {
+ return AVCENC_ALREADY_INITIALIZED; /* It's already initialized, need to cleanup first */
+ }
+
+ /* not initialized */
+
+ /* allocate videoObject */
+ avcHandle->AVCObject = (void*)avcHandle->CBAVC_Malloc(userData, sizeof(AVCEncObject), DEFAULT_ATTR);
+ if (avcHandle->AVCObject == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ encvid = (AVCEncObject*) avcHandle->AVCObject;
+ memset(encvid, 0, sizeof(AVCEncObject)); /* reset everything */
+
+ encvid->enc_state = AVCEnc_Initializing;
+
+ encvid->avcHandle = avcHandle;
+
+ encvid->common = (AVCCommonObj*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCCommonObj), DEFAULT_ATTR);
+ if (encvid->common == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ video = encvid->common;
+ memset(video, 0, sizeof(AVCCommonObj));
+
+ /* allocate bitstream structure */
+ encvid->bitstream = (AVCEncBitstream*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCEncBitstream), DEFAULT_ATTR);
+ if (encvid->bitstream == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ encvid->bitstream->encvid = encvid; /* to point back for reallocation */
+
+ /* allocate sequence parameter set structure */
+ video->currSeqParams = (AVCSeqParamSet*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCSeqParamSet), DEFAULT_ATTR);
+ if (video->currSeqParams == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(video->currSeqParams, 0, sizeof(AVCSeqParamSet));
+
+ /* allocate picture parameter set structure */
+ video->currPicParams = (AVCPicParamSet*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCPicParamSet), DEFAULT_ATTR);
+ if (video->currPicParams == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(video->currPicParams, 0, sizeof(AVCPicParamSet));
+
+ /* allocate slice header structure */
+ video->sliceHdr = (AVCSliceHeader*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCSliceHeader), DEFAULT_ATTR);
+ if (video->sliceHdr == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(video->sliceHdr, 0, sizeof(AVCSliceHeader));
+
+ /* allocate encoded picture buffer structure*/
+ video->decPicBuf = (AVCDecPicBuffer*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCDecPicBuffer), DEFAULT_ATTR);
+ if (video->decPicBuf == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(video->decPicBuf, 0, sizeof(AVCDecPicBuffer));
+
+ /* allocate rate control structure */
+ encvid->rateCtrl = (AVCRateControl*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCRateControl), DEFAULT_ATTR);
+ if (encvid->rateCtrl == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(encvid->rateCtrl, 0, sizeof(AVCRateControl));
+
+ /* reset frame list, not really needed */
+ video->currPic = NULL;
+ video->currFS = NULL;
+ encvid->currInput = NULL;
+ video->prevRefPic = NULL;
+
+ /* now read encParams, and allocate dimension-dependent variables */
+ /* such as mblock */
+ status = SetEncodeParam(avcHandle, encParam, extSPS, extPPS); /* initialized variables to be used in SPS*/
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ if (encParam->use_overrun_buffer == AVC_ON)
+ {
+ /* allocate overrun buffer */
+ encvid->oBSize = encvid->rateCtrl->cpbSize;
+ if (encvid->oBSize > DEFAULT_OVERRUN_BUFFER_SIZE)
+ {
+ encvid->oBSize = DEFAULT_OVERRUN_BUFFER_SIZE;
+ }
+ encvid->overrunBuffer = (uint8*) avcHandle->CBAVC_Malloc(userData, encvid->oBSize, DEFAULT_ATTR);
+ if (encvid->overrunBuffer == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ }
+ else
+ {
+ encvid->oBSize = 0;
+ encvid->overrunBuffer = NULL;
+ }
+
+ /* allocate frame size dependent structures */
+ framesize = video->FrameHeightInMbs * video->PicWidthInMbs;
+
+ video->mblock = (AVCMacroblock*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCMacroblock) * framesize, DEFAULT_ATTR);
+ if (video->mblock == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ video->MbToSliceGroupMap = (int*) avcHandle->CBAVC_Malloc(userData, sizeof(uint) * video->PicSizeInMapUnits * 2, DEFAULT_ATTR);
+ if (video->MbToSliceGroupMap == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ encvid->mot16x16 = (AVCMV*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCMV) * framesize, DEFAULT_ATTR);
+ if (encvid->mot16x16 == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ memset(encvid->mot16x16, 0, sizeof(AVCMV)*framesize);
+
+ encvid->intraSearch = (uint8*) avcHandle->CBAVC_Malloc(userData, sizeof(uint8) * framesize, DEFAULT_ATTR);
+ if (encvid->intraSearch == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ encvid->min_cost = (int*) avcHandle->CBAVC_Malloc(userData, sizeof(int) * framesize, DEFAULT_ATTR);
+ if (encvid->min_cost == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ /* initialize motion search related memory */
+ if (AVCENC_SUCCESS != InitMotionSearchModule(avcHandle))
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ if (AVCENC_SUCCESS != InitRateControlModule(avcHandle))
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ /* intialize function pointers */
+ encvid->functionPointer = (AVCEncFuncPtr*) avcHandle->CBAVC_Malloc(userData, sizeof(AVCEncFuncPtr), DEFAULT_ATTR);
+ if (encvid->functionPointer == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+ encvid->functionPointer->SAD_Macroblock = &AVCSAD_Macroblock_C;
+ encvid->functionPointer->SAD_MB_HalfPel[0] = NULL;
+ encvid->functionPointer->SAD_MB_HalfPel[1] = &AVCSAD_MB_HalfPel_Cxh;
+ encvid->functionPointer->SAD_MB_HalfPel[2] = &AVCSAD_MB_HalfPel_Cyh;
+ encvid->functionPointer->SAD_MB_HalfPel[3] = &AVCSAD_MB_HalfPel_Cxhyh;
+
+ /* initialize timing control */
+ encvid->modTimeRef = 0; /* ALWAYS ASSUME THAT TIMESTAMP START FROM 0 !!!*/
+ video->prevFrameNum = 0;
+ encvid->prevCodedFrameNum = 0;
+ encvid->dispOrdPOCRef = 0;
+
+ if (encvid->outOfBandParamSet == TRUE)
+ {
+ encvid->enc_state = AVCEnc_Encoding_SPS;
+ }
+ else
+ {
+ encvid->enc_state = AVCEnc_Analyzing_Frame;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+/* ======================================================================== */
+/* Function : PVAVCEncGetMaxOutputSize() */
+/* Date : 11/29/2008 */
+/* Purpose : Return max output buffer size that apps should allocate for */
+/* output buffer. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : size */
+/* ======================================================================== */
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncGetMaxOutputBufferSize(AVCHandle *avcHandle, int* size)
+{
+ AVCEncObject *encvid = (AVCEncObject*)avcHandle->AVCObject;
+
+ if (encvid == NULL)
+ {
+ return AVCENC_UNINITIALIZED;
+ }
+
+ *size = encvid->rateCtrl->cpbSize;
+
+ return AVCENC_SUCCESS;
+}
+
+/* ======================================================================== */
+/* Function : PVAVCEncSetInput() */
+/* Date : 4/18/2004 */
+/* Purpose : To feed an unencoded original frame to the encoder library. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncSetInput(AVCHandle *avcHandle, AVCFrameIO *input)
+{
+ AVCEncObject *encvid = (AVCEncObject*)avcHandle->AVCObject;
+ AVCCommonObj *video = encvid->common;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+
+ AVCEnc_Status status;
+ uint frameNum;
+
+ if (encvid == NULL)
+ {
+ return AVCENC_UNINITIALIZED;
+ }
+
+ if (encvid->enc_state == AVCEnc_WaitingForBuffer)
+ {
+ goto RECALL_INITFRAME;
+ }
+ else if (encvid->enc_state != AVCEnc_Analyzing_Frame)
+ {
+ return AVCENC_FAIL;
+ }
+
+ if (input->pitch > 0xFFFF)
+ {
+ return AVCENC_NOT_SUPPORTED; // we use 2-bytes for pitch
+ }
+
+ /***********************************/
+
+ /* Let's rate control decide whether to encode this frame or not */
+ /* Also set video->nal_unit_type, sliceHdr->slice_type, video->slice_type */
+ if (AVCENC_SUCCESS != RCDetermineFrameNum(encvid, rateCtrl, input->coding_timestamp, &frameNum))
+ {
+ return AVCENC_SKIPPED_PICTURE; /* not time to encode, thus skipping */
+ }
+
+ /* we may not need this line */
+ //nextFrmModTime = (uint32)((((frameNum+1)*1000)/rateCtrl->frame_rate) + modTimeRef); /* rec. time */
+ //encvid->nextModTime = nextFrmModTime - (encvid->frameInterval>>1) - 1; /* between current and next frame */
+
+ encvid->currInput = input;
+ encvid->currInput->coding_order = frameNum;
+
+RECALL_INITFRAME:
+ /* initialize and analyze the frame */
+ status = InitFrame(encvid);
+
+ if (status == AVCENC_SUCCESS)
+ {
+ encvid->enc_state = AVCEnc_Encoding_Frame;
+ }
+ else if (status == AVCENC_NEW_IDR)
+ {
+ if (encvid->outOfBandParamSet == TRUE)
+ {
+ encvid->enc_state = AVCEnc_Encoding_Frame;
+ }
+ else // assuming that in-band paramset keeps sending new SPS and PPS.
+ {
+ encvid->enc_state = AVCEnc_Encoding_SPS;
+ //video->currSeqParams->seq_parameter_set_id++;
+ //if(video->currSeqParams->seq_parameter_set_id > 31) // range check
+ {
+ video->currSeqParams->seq_parameter_set_id = 0; // reset
+ }
+ }
+
+ video->sliceHdr->idr_pic_id++;
+ if (video->sliceHdr->idr_pic_id > 65535) // range check
+ {
+ video->sliceHdr->idr_pic_id = 0; // reset
+ }
+ }
+ /* the following logics need to be revisited */
+ else if (status == AVCENC_PICTURE_READY) // no buffers returned back to the encoder
+ {
+ encvid->enc_state = AVCEnc_WaitingForBuffer; // Input accepted but can't continue
+ // need to free up some memory before proceeding with Encode
+ }
+
+ return status; // return status, including the AVCENC_FAIL case and all 3 above.
+}
+
+/* ======================================================================== */
+/* Function : PVAVCEncodeNAL() */
+/* Date : 4/29/2004 */
+/* Purpose : To encode one NAL/slice. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncodeNAL(AVCHandle *avcHandle, unsigned char *buffer, unsigned int *buf_nal_size, int *nal_type)
+{
+ AVCEncObject *encvid = (AVCEncObject*)avcHandle->AVCObject;
+ AVCCommonObj *video = encvid->common;
+ AVCEncBitstream *bitstream = encvid->bitstream;
+ AVCEnc_Status status;
+
+ if (encvid == NULL)
+ {
+ return AVCENC_UNINITIALIZED;
+ }
+
+ switch (encvid->enc_state)
+ {
+ case AVCEnc_Initializing:
+ return AVCENC_UNINITIALIZED;
+ case AVCEnc_Encoding_SPS:
+ /* initialized the structure */
+ BitstreamEncInit(bitstream, buffer, *buf_nal_size, NULL, 0);
+ BitstreamWriteBits(bitstream, 8, (1 << 5) | AVC_NALTYPE_SPS);
+
+ /* encode SPS */
+ status = EncodeSPS(encvid, bitstream);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ /* closing the NAL with trailing bits */
+ status = BitstreamTrailingBits(bitstream, buf_nal_size);
+ if (status == AVCENC_SUCCESS)
+ {
+ encvid->enc_state = AVCEnc_Encoding_PPS;
+ video->currPicParams->seq_parameter_set_id = video->currSeqParams->seq_parameter_set_id;
+ video->currPicParams->pic_parameter_set_id++;
+ *nal_type = AVC_NALTYPE_SPS;
+ *buf_nal_size = bitstream->write_pos;
+ }
+ break;
+ case AVCEnc_Encoding_PPS:
+ /* initialized the structure */
+ BitstreamEncInit(bitstream, buffer, *buf_nal_size, NULL, 0);
+ BitstreamWriteBits(bitstream, 8, (1 << 5) | AVC_NALTYPE_PPS);
+
+ /* encode PPS */
+ status = EncodePPS(encvid, bitstream);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ /* closing the NAL with trailing bits */
+ status = BitstreamTrailingBits(bitstream, buf_nal_size);
+ if (status == AVCENC_SUCCESS)
+ {
+ if (encvid->outOfBandParamSet == TRUE) // already extract PPS, SPS
+ {
+ encvid->enc_state = AVCEnc_Analyzing_Frame;
+ }
+ else // SetInput has been called before SPS and PPS.
+ {
+ encvid->enc_state = AVCEnc_Encoding_Frame;
+ }
+
+ *nal_type = AVC_NALTYPE_PPS;
+ *buf_nal_size = bitstream->write_pos;
+ }
+ break;
+
+ case AVCEnc_Encoding_Frame:
+ /* initialized the structure */
+ BitstreamEncInit(bitstream, buffer, *buf_nal_size, encvid->overrunBuffer, encvid->oBSize);
+ BitstreamWriteBits(bitstream, 8, (video->nal_ref_idc << 5) | (video->nal_unit_type));
+
+ /* Re-order the reference list according to the ref_pic_list_reordering() */
+ /* We don't have to reorder the list for the encoder here. This can only be done
+ after we encode this slice. We can run thru a second-pass to see if new ordering
+ would save more bits. Too much delay !! */
+ /* status = ReOrderList(video);*/
+ status = InitSlice(encvid);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ /* when we have everything, we encode the slice header */
+ status = EncodeSliceHeader(encvid, bitstream);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = AVCEncodeSlice(encvid);
+
+ video->slice_id++;
+
+ /* closing the NAL with trailing bits */
+ BitstreamTrailingBits(bitstream, buf_nal_size);
+
+ *buf_nal_size = bitstream->write_pos;
+
+ encvid->rateCtrl->numFrameBits += ((*buf_nal_size) << 3);
+
+ *nal_type = video->nal_unit_type;
+
+ if (status == AVCENC_PICTURE_READY)
+ {
+ status = RCUpdateFrame(encvid);
+ if (status == AVCENC_SKIPPED_PICTURE) /* skip current frame */
+ {
+ DPBReleaseCurrentFrame(avcHandle, video);
+ encvid->enc_state = AVCEnc_Analyzing_Frame;
+
+ return status;
+ }
+
+ /* perform loop-filtering on the entire frame */
+ DeblockPicture(video);
+
+ /* update the original frame array */
+ encvid->prevCodedFrameNum = encvid->currInput->coding_order;
+
+ /* store the encoded picture in the DPB buffer */
+ StorePictureInDPB(avcHandle, video);
+
+ if (video->currPic->isReference)
+ {
+ video->PrevRefFrameNum = video->sliceHdr->frame_num;
+ }
+
+ /* update POC related variables */
+ PostPOC(video);
+
+ encvid->enc_state = AVCEnc_Analyzing_Frame;
+ status = AVCENC_PICTURE_READY;
+
+ }
+ break;
+ default:
+ status = AVCENC_WRONG_STATE;
+ }
+
+ return status;
+}
+
+/* ======================================================================== */
+/* Function : PVAVCEncGetOverrunBuffer() */
+/* Purpose : To retrieve the overrun buffer. Check whether overrun buffer */
+/* is used or not before returning */
+/* In/out : */
+/* Return : Pointer to the internal overrun buffer. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF uint8* PVAVCEncGetOverrunBuffer(AVCHandle* avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*)avcHandle->AVCObject;
+ AVCEncBitstream *bitstream = encvid->bitstream;
+
+ if (bitstream->overrunBuffer == bitstream->bitstreamBuffer) /* OB is used */
+ {
+ return encvid->overrunBuffer;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+/* ======================================================================== */
+/* Function : PVAVCEncGetRecon() */
+/* Date : 4/29/2004 */
+/* Purpose : To retrieve the most recently encoded frame. */
+/* assume that user will make a copy if they want to hold on */
+/* to it. Otherwise, it is not guaranteed to be reserved. */
+/* Most applications prefer to see original frame rather than */
+/* reconstructed frame. So, we are staying aware from complex */
+/* buffering mechanism. If needed, can be added later. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncGetRecon(AVCHandle *avcHandle, AVCFrameIO *recon)
+{
+ AVCEncObject *encvid = (AVCEncObject*)avcHandle->AVCObject;
+ AVCCommonObj *video = encvid->common;
+ AVCFrameStore *currFS = video->currFS;
+
+ if (encvid == NULL)
+ {
+ return AVCENC_UNINITIALIZED;
+ }
+
+ recon->YCbCr[0] = currFS->frame.Sl;
+ recon->YCbCr[1] = currFS->frame.Scb;
+ recon->YCbCr[2] = currFS->frame.Scr;
+ recon->height = currFS->frame.height;
+ recon->pitch = currFS->frame.pitch;
+ recon->disp_order = currFS->PicOrderCnt;
+ recon->coding_order = currFS->FrameNum;
+ recon->id = (uint32) currFS->base_dpb; /* use the pointer as the id */
+
+ currFS->IsOutputted |= 1;
+
+ return AVCENC_SUCCESS;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncReleaseRecon(AVCHandle *avcHandle, AVCFrameIO *recon)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+ OSCL_UNUSED_ARG(recon);
+
+ return AVCENC_SUCCESS; //for now
+}
+
+/* ======================================================================== */
+/* Function : PVAVCCleanUpEncoder() */
+/* Date : 4/18/2004 */
+/* Purpose : To clean up memories allocated by PVAVCEncInitialize() */
+/* In/out : */
+/* Return : AVCENC_SUCCESS for success. */
+/* Modified : */
+/* ======================================================================== */
+OSCL_EXPORT_REF void PVAVCCleanUpEncoder(AVCHandle *avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCCommonObj *video;
+ uint32 *userData = (uint32*) avcHandle->userData;
+
+ if (encvid != NULL)
+ {
+ CleanMotionSearchModule(avcHandle);
+
+ CleanupRateControlModule(avcHandle);
+
+ if (encvid->functionPointer != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->functionPointer);
+ }
+
+ if (encvid->min_cost)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->min_cost);
+ }
+
+ if (encvid->intraSearch)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->intraSearch);
+ }
+
+ if (encvid->mot16x16)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->mot16x16);
+ }
+
+ if (encvid->rateCtrl)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->rateCtrl);
+ }
+
+ if (encvid->overrunBuffer)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->overrunBuffer);
+ }
+
+ video = encvid->common;
+ if (video != NULL)
+ {
+ if (video->MbToSliceGroupMap)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video->MbToSliceGroupMap);
+ }
+ if (video->mblock != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video->mblock);
+ }
+ if (video->decPicBuf != NULL)
+ {
+ CleanUpDPB(avcHandle, video);
+ avcHandle->CBAVC_Free(userData, (int)video->decPicBuf);
+ }
+ if (video->sliceHdr != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video->sliceHdr);
+ }
+ if (video->currPicParams != NULL)
+ {
+ if (video->currPicParams->slice_group_id)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video->currPicParams->slice_group_id);
+ }
+
+ avcHandle->CBAVC_Free(userData, (int)video->currPicParams);
+ }
+ if (video->currSeqParams != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video->currSeqParams);
+ }
+ if (encvid->bitstream != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)encvid->bitstream);
+ }
+ if (video != NULL)
+ {
+ avcHandle->CBAVC_Free(userData, (int)video);
+ }
+ }
+
+ avcHandle->CBAVC_Free(userData, (int)encvid);
+
+ avcHandle->AVCObject = NULL;
+ }
+
+ return ;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncUpdateBitRate(AVCHandle *avcHandle, uint32 bitrate)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+ OSCL_UNUSED_ARG(bitrate);
+
+ return AVCENC_FAIL;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncUpdateFrameRate(AVCHandle *avcHandle, uint32 num, uint32 denom)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+ OSCL_UNUSED_ARG(num);
+ OSCL_UNUSED_ARG(denom);
+
+ return AVCENC_FAIL;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncUpdateIDRInterval(AVCHandle *avcHandle, int IDRInterval)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+ OSCL_UNUSED_ARG(IDRInterval);
+
+ return AVCENC_FAIL;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncIDRRequest(AVCHandle *avcHandle)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+
+ return AVCENC_FAIL;
+}
+
+OSCL_EXPORT_REF AVCEnc_Status PVAVCEncUpdateIMBRefresh(AVCHandle *avcHandle, int numMB)
+{
+ OSCL_UNUSED_ARG(avcHandle);
+ OSCL_UNUSED_ARG(numMB);
+
+ return AVCENC_FAIL;
+}
+
+void PVAVCEncGetFrameStats(AVCHandle *avcHandle, AVCEncFrameStats *avcStats)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+
+ avcStats->avgFrameQP = GetAvgFrameQP(rateCtrl);
+ avcStats->numIntraMBs = encvid->numIntraMB;
+
+ return ;
+}
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/avcenc_api.h b/media/libstagefright/codecs/avc/enc/src/avcenc_api.h
new file mode 100644
index 0000000..6841ec3
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/avcenc_api.h
@@ -0,0 +1,323 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 file contains application function interfaces to the AVC encoder library
+and necessary type defitionitions and enumerations.
+@publishedAll
+*/
+
+#ifndef AVCENC_API_H_INCLUDED
+#define AVCENC_API_H_INCLUDED
+
+#ifndef AVCAPI_COMMON_H_INCLUDED
+#include "avcapi_common.h"
+#endif
+
+// For memset, etc
+#include <string.h>
+
+/**
+ This enumeration is used for the status returned from the library interface.
+*/
+typedef enum
+{
+ /**
+ Fail information, need to add more error code for more specific info
+ */
+ AVCENC_TRAILINGONES_FAIL = -35,
+ AVCENC_SLICE_EMPTY = -34,
+ AVCENC_POC_FAIL = -33,
+ AVCENC_CONSECUTIVE_NONREF = -32,
+ AVCENC_CABAC_FAIL = -31,
+ AVCENC_PRED_WEIGHT_TAB_FAIL = -30,
+ AVCENC_DEC_REF_PIC_MARK_FAIL = -29,
+ AVCENC_SPS_FAIL = -28,
+ AVCENC_BITSTREAM_BUFFER_FULL = -27,
+ AVCENC_BITSTREAM_INIT_FAIL = -26,
+ AVCENC_CHROMA_QP_FAIL = -25,
+ AVCENC_INIT_QS_FAIL = -24,
+ AVCENC_INIT_QP_FAIL = -23,
+ AVCENC_WEIGHTED_BIPRED_FAIL = -22,
+ AVCENC_INVALID_INTRA_PERIOD = -21,
+ AVCENC_INVALID_CHANGE_RATE = -20,
+ AVCENC_INVALID_BETA_OFFSET = -19,
+ AVCENC_INVALID_ALPHA_OFFSET = -18,
+ AVCENC_INVALID_DEBLOCK_IDC = -17,
+ AVCENC_INVALID_REDUNDANT_PIC = -16,
+ AVCENC_INVALID_FRAMERATE = -15,
+ AVCENC_INVALID_NUM_SLICEGROUP = -14,
+ AVCENC_INVALID_POC_LSB = -13,
+ AVCENC_INVALID_NUM_REF = -12,
+ AVCENC_INVALID_FMO_TYPE = -11,
+ AVCENC_ENCPARAM_MEM_FAIL = -10,
+ AVCENC_LEVEL_NOT_SUPPORTED = -9,
+ AVCENC_LEVEL_FAIL = -8,
+ AVCENC_PROFILE_NOT_SUPPORTED = -7,
+ AVCENC_TOOLS_NOT_SUPPORTED = -6,
+ AVCENC_WRONG_STATE = -5,
+ AVCENC_UNINITIALIZED = -4,
+ AVCENC_ALREADY_INITIALIZED = -3,
+ AVCENC_NOT_SUPPORTED = -2,
+ AVCENC_MEMORY_FAIL = AVC_MEMORY_FAIL,
+ AVCENC_FAIL = AVC_FAIL,
+ /**
+ Generic success value
+ */
+ AVCENC_SUCCESS = AVC_SUCCESS,
+ AVCENC_PICTURE_READY = 2,
+ AVCENC_NEW_IDR = 3, /* upon getting this, users have to call PVAVCEncodeSPS and PVAVCEncodePPS to get a new SPS and PPS*/
+ AVCENC_SKIPPED_PICTURE = 4 /* continuable error message */
+
+} AVCEnc_Status;
+
+#define MAX_NUM_SLICE_GROUP 8 /* maximum for all the profiles */
+
+/**
+This structure contains the encoding parameters.
+*/
+typedef struct tagAVCEncParam
+{
+ /* if profile/level is set to zero, encoder will choose the closest one for you */
+ AVCProfile profile; /* profile of the bitstream to be compliant with*/
+ AVCLevel level; /* level of the bitstream to be compliant with*/
+
+ int width; /* width of an input frame in pixel */
+ int height; /* height of an input frame in pixel */
+
+ int poc_type; /* picture order count mode, 0,1 or 2 */
+ /* for poc_type == 0 */
+ uint log2_max_poc_lsb_minus_4; /* specify maximum value of POC Lsb, range 0..12*/
+ /* for poc_type == 1 */
+ uint delta_poc_zero_flag; /* delta POC always zero */
+ int offset_poc_non_ref; /* offset for non-reference pic */
+ int offset_top_bottom; /* offset between top and bottom field */
+ uint num_ref_in_cycle; /* number of reference frame in one cycle */
+ int *offset_poc_ref; /* array of offset for ref pic, dimension [num_ref_in_cycle] */
+
+ int num_ref_frame; /* number of reference frame used */
+ int num_slice_group; /* number of slice group */
+ int fmo_type; /* 0: interleave, 1: dispersed, 2: foreground with left-over
+ 3: box-out, 4:raster scan, 5:wipe, 6:explicit */
+ /* for fmo_type == 0 */
+ uint run_length_minus1[MAX_NUM_SLICE_GROUP]; /* array of size num_slice_group, in round robin fasion */
+ /* fmo_type == 2*/
+ uint top_left[MAX_NUM_SLICE_GROUP-1]; /* array of co-ordinates of each slice_group */
+ uint bottom_right[MAX_NUM_SLICE_GROUP-1]; /* except the last one which is the background. */
+ /* fmo_type == 3,4,5 */
+ AVCFlag change_dir_flag; /* slice group change direction flag */
+ uint change_rate_minus1;
+ /* fmo_type == 6 */
+ uint *slice_group; /* array of size MBWidth*MBHeight */
+
+ AVCFlag db_filter; /* enable deblocking loop filter */
+ int disable_db_idc; /* 0: filter everywhere, 1: no filter, 2: no filter across slice boundary */
+ int alpha_offset; /* alpha offset range -6,...,6 */
+ int beta_offset; /* beta offset range -6,...,6 */
+
+ AVCFlag constrained_intra_pred; /* constrained intra prediction flag */
+
+ AVCFlag auto_scd; /* scene change detection on or off */
+ int idr_period; /* idr frame refresh rate in number of target encoded frame (no concept of actual time).*/
+ int intramb_refresh; /* minimum number of intra MB per frame */
+ AVCFlag data_par; /* enable data partitioning */
+
+ AVCFlag fullsearch; /* enable full-pel full-search mode */
+ int search_range; /* search range for motion vector in (-search_range,+search_range) pixels */
+ AVCFlag sub_pel; /* enable sub pel prediction */
+ AVCFlag submb_pred; /* enable sub MB partition mode */
+ AVCFlag rdopt_mode; /* RD optimal mode selection */
+ AVCFlag bidir_pred; /* enable bi-directional for B-slice, this flag forces the encoder to encode
+ any frame with POC less than the previously encoded frame as a B-frame.
+ If it's off, then such frames will remain P-frame. */
+
+ AVCFlag rate_control; /* rate control enable, on: RC on, off: constant QP */
+ int initQP; /* initial QP */
+ uint32 bitrate; /* target encoding bit rate in bits/second */
+ uint32 CPB_size; /* coded picture buffer in number of bits */
+ uint32 init_CBP_removal_delay; /* initial CBP removal delay in msec */
+
+ uint32 frame_rate; /* frame rate in the unit of frames per 1000 second */
+ /* note, frame rate is only needed by the rate control, AVC is timestamp agnostic. */
+
+ AVCFlag out_of_band_param_set; /* flag to set whether param sets are to be retrieved up front or not */
+
+ AVCFlag use_overrun_buffer; /* do not throw away the frame if output buffer is not big enough.
+ copy excess bits to the overrun buffer */
+} AVCEncParams;
+
+
+/**
+This structure contains current frame encoding statistics for debugging purpose.
+*/
+typedef struct tagAVCEncFrameStats
+{
+ int avgFrameQP; /* average frame QP */
+ int numIntraMBs; /* number of intra MBs */
+ int numFalseAlarm;
+ int numMisDetected;
+ int numDetected;
+
+} AVCEncFrameStats;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ /** THE FOLLOWINGS ARE APIS */
+ /**
+ This function initializes the encoder library. It verifies the validity of the
+ encoding parameters against the specified profile/level and the list of supported
+ tools by this library. It allocates necessary memories required to perform encoding.
+ For re-encoding application, if users want to setup encoder in a more precise way,
+ users can give the external SPS and PPS to the encoder to follow.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "encParam" "Pointer to the encoding parameter structure."
+ \param "extSPS" "External SPS used for re-encoding purpose. NULL if not present"
+ \param "extPPS" "External PPS used for re-encoding purpose. NULL if not present"
+ \return "AVCENC_SUCCESS for success,
+ AVCENC_NOT_SUPPORTED for the use of unsupported tools,
+ AVCENC_MEMORY_FAIL for memory allocation failure,
+ AVCENC_FAIL for generic failure."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncInitialize(AVCHandle *avcHandle, AVCEncParams *encParam, void* extSPS, void* extPPS);
+
+
+ /**
+ Since the output buffer size is not known prior to encoding a frame, users need to
+ allocate big enough buffer otherwise, that frame will be dropped. This function returns
+ the size of the output buffer to be allocated by the users that guarantees to hold one frame.
+ It follows the CPB spec for a particular level. However, when the users set use_overrun_buffer
+ flag, this API is useless as excess output bits are saved in the overrun buffer waiting to be
+ copied out in small chunks, i.e. users can allocate any size of output buffer.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "size" "Pointer to the size to be modified."
+ \return "AVCENC_SUCCESS for success, AVCENC_UNINITIALIZED when level is not known.
+ */
+
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncGetMaxOutputBufferSize(AVCHandle *avcHandle, int* size);
+
+ /**
+ Users call this function to provide an input structure to the encoder library which will keep
+ a list of input structures it receives in case the users call this function many time before
+ calling PVAVCEncodeSlice. The encoder library will encode them according to the frame_num order.
+ Users should not modify the content of a particular frame until this frame is encoded and
+ returned thru CBAVCEnc_ReturnInput() callback function.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "input" "Pointer to the input structure."
+ \return "AVCENC_SUCCESS for success,
+ AVCENC_FAIL if the encoder is not in the right state to take a new input frame.
+ AVCENC_NEW_IDR for the detection or determination of a new IDR, with this status,
+ the returned NAL is an SPS NAL,
+ AVCENC_NO_PICTURE if the input frame coding timestamp is too early, users must
+ get next frame or adjust the coding timestamp."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncSetInput(AVCHandle *avcHandle, AVCFrameIO *input);
+
+ /**
+ This function is called to encode a NAL unit which can be an SPS NAL, a PPS NAL or
+ a VCL (video coding layer) NAL which contains one slice of data. It could be a
+ fixed number of macroblocks, as specified in the encoder parameters set, or the
+ maximum number of macroblocks fitted into the given input argument "buffer". The
+ input frame is taken from the oldest unencoded input frame retrieved by users by
+ PVAVCEncGetInput API.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "buffer" "Pointer to the output AVC bitstream buffer, the format will be EBSP,
+ not RBSP."
+ \param "buf_nal_size" "As input, the size of the buffer in bytes.
+ This is the physical limitation of the buffer. As output, the size of the EBSP."
+ \param "nal_type" "Pointer to the NAL type of the returned buffer."
+ \return "AVCENC_SUCCESS for success of encoding one slice,
+ AVCENC_PICTURE_READY for the completion of a frame encoding,
+ AVCENC_FAIL for failure (this should not occur, though)."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncodeNAL(AVCHandle *avcHandle, uint8 *buffer, uint *buf_nal_size, int *nal_type);
+
+ /**
+ This function sniffs the nal_unit_type such that users can call corresponding APIs.
+ This function is identical to PVAVCDecGetNALType() in the decoder.
+ \param "bitstream" "Pointer to the beginning of a NAL unit (start with forbidden_zero_bit, etc.)."
+ \param "size" "size of the bitstream (NumBytesInNALunit + 1)."
+ \param "nal_unit_type" "Pointer to the return value of nal unit type."
+ \return "AVCENC_SUCCESS if success, AVCENC_FAIL otherwise."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncGetNALType(uint8 *bitstream, int size, int *nal_type, int *nal_ref_idc);
+
+ /**
+ This function returns the pointer to internal overrun buffer. Users can call this to query
+ whether the overrun buffer has been used to encode the current NAL.
+ \param "avcHandle" "Pointer to the handle."
+ \return "Pointer to overrun buffer if it is used, otherwise, NULL."
+ */
+ OSCL_IMPORT_REF uint8* PVAVCEncGetOverrunBuffer(AVCHandle* avcHandle);
+
+ /**
+ This function returns the reconstructed frame of the most recently encoded frame.
+ Note that this frame is not returned to the users yet. Users should only read the
+ content of this frame.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "output" "Pointer to the input structure."
+ \return "AVCENC_SUCCESS for success, AVCENC_NO_PICTURE if no picture to be outputted."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncGetRecon(AVCHandle *avcHandle, AVCFrameIO *recon);
+
+ /**
+ This function is used to return the recontructed frame back to the AVC encoder library
+ in order to be re-used for encoding operation. If users want the content of it to remain
+ unchanged for a long time, they should make a copy of it and release the memory back to
+ the encoder. The encoder relies on the id element in the AVCFrameIO structure,
+ thus users should not change the id value.
+ \param "avcHandle" "Handle to the AVC decoder library object."
+ \param "output" "Pointer to the AVCFrameIO structure."
+ \return "AVCENC_SUCCESS for success, AVCENC_FAIL for fail for id not found."
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncReleaseRecon(AVCHandle *avcHandle, AVCFrameIO *recon);
+
+ /**
+ This function performs clean up operation including memory deallocation.
+ The encoder will also clear the list of input structures it has not released.
+ This implies that users must keep track of the number of input structure they have allocated
+ and free them accordingly.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ */
+ OSCL_IMPORT_REF void PVAVCCleanUpEncoder(AVCHandle *avcHandle);
+
+ /**
+ This function extracts statistics of the current frame. If the encoder has not finished
+ with the current frame, the result is not accurate.
+ \param "avcHandle" "Handle to the AVC encoder library object."
+ \param "avcStats" "Pointer to AVCEncFrameStats structure."
+ \return "void."
+ */
+ void PVAVCEncGetFrameStats(AVCHandle *avcHandle, AVCEncFrameStats *avcStats);
+
+ /**
+ These functions are used for the modification of encoding parameters.
+ To be polished.
+ */
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncUpdateBitRate(AVCHandle *avcHandle, uint32 bitrate);
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncUpdateFrameRate(AVCHandle *avcHandle, uint32 num, uint32 denom);
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncUpdateIDRInterval(AVCHandle *avcHandle, int IDRInterval);
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncIDRRequest(AVCHandle *avcHandle);
+ OSCL_IMPORT_REF AVCEnc_Status PVAVCEncUpdateIMBRefresh(AVCHandle *avcHandle, int numMB);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _AVCENC_API_H_ */
+
diff --git a/media/libstagefright/codecs/avc/enc/src/avcenc_int.h b/media/libstagefright/codecs/avc/enc/src/avcenc_int.h
new file mode 100644
index 0000000..3fe08a1
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/avcenc_int.h
@@ -0,0 +1,471 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 file contains application function interfaces to the AVC encoder library
+and necessary type defitionitions and enumerations.
+@publishedAll
+*/
+
+#ifndef AVCENC_INT_H_INCLUDED
+#define AVCENC_INT_H_INCLUDED
+
+#ifndef AVCINT_COMMON_H_INCLUDED
+#include "avcint_common.h"
+#endif
+#ifndef AVCENC_API_H_INCLUDED
+#include "avcenc_api.h"
+#endif
+
+typedef float OsclFloat;
+
+/* Definition for the structures below */
+#define DEFAULT_ATTR 0 /* default memory attribute */
+#define MAX_INPUT_FRAME 30 /* some arbitrary number, it can be much higher than this. */
+#define MAX_REF_FRAME 16 /* max size of the RefPicList0 and RefPicList1 */
+#define MAX_REF_PIC_LIST 33
+
+#define MIN_QP 0
+#define MAX_QP 51
+#define SHIFT_QP 12
+#define LAMBDA_ACCURACY_BITS 16
+#define LAMBDA_FACTOR(lambda) ((int)((double)(1<<LAMBDA_ACCURACY_BITS)*lambda+0.5))
+
+
+#define DISABLE_THRESHOLDING 0
+// for better R-D performance
+#define _LUMA_COEFF_COST_ 4 //!< threshold for luma coeffs
+#define _CHROMA_COEFF_COST_ 4 //!< threshold for chroma coeffs, used to be 7
+#define _LUMA_MB_COEFF_COST_ 5 //!< threshold for luma coeffs of inter Macroblocks
+#define _LUMA_8x8_COEFF_COST_ 5 //!< threshold for luma coeffs of 8x8 Inter Partition
+#define MAX_VALUE 999999 //!< used for start value for some variables
+
+#define WEIGHTED_COST(factor,bits) (((factor)*(bits))>>LAMBDA_ACCURACY_BITS)
+#define MV_COST(f,s,cx,cy,px,py) (WEIGHTED_COST(f,mvbits[((cx)<<(s))-px]+mvbits[((cy)<<(s))-py]))
+#define MV_COST_S(f,cx,cy,px,py) (WEIGHTED_COST(f,mvbits[cx-px]+mvbits[cy-py]))
+
+/* for sub-pel search and interpolation */
+#define SUBPEL_PRED_BLK_SIZE 576 // 24x24
+#define REF_CENTER 75
+#define V2Q_H0Q 1
+#define V0Q_H2Q 2
+#define V2Q_H2Q 3
+
+/*
+#define V3Q_H0Q 1
+#define V3Q_H1Q 2
+#define V0Q_H1Q 3
+#define V1Q_H1Q 4
+#define V1Q_H0Q 5
+#define V1Q_H3Q 6
+#define V0Q_H3Q 7
+#define V3Q_H3Q 8
+#define V2Q_H3Q 9
+#define V2Q_H0Q 10
+#define V2Q_H1Q 11
+#define V2Q_H2Q 12
+#define V3Q_H2Q 13
+#define V0Q_H2Q 14
+#define V1Q_H2Q 15
+*/
+
+
+#define DEFAULT_OVERRUN_BUFFER_SIZE 1000
+
+// associated with the above cost model
+const uint8 COEFF_COST[2][16] =
+{
+ {3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}
+};
+
+
+
+//! convert from H.263 QP to H.264 quant given by: quant=pow(2,QP/6)
+const int QP2QUANT[40] =
+{
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 4, 4, 4, 5, 6,
+ 6, 7, 8, 9, 10, 11, 13, 14,
+ 16, 18, 20, 23, 25, 29, 32, 36,
+ 40, 45, 51, 57, 64, 72, 81, 91
+};
+
+
+/**
+This enumeration keeps track of the internal status of the encoder whether it is doing
+something. The encoding flow follows the order in which these states are.
+@publishedAll
+*/
+typedef enum
+{
+ AVCEnc_Initializing = 0,
+ AVCEnc_Encoding_SPS,
+ AVCEnc_Encoding_PPS,
+ AVCEnc_Analyzing_Frame,
+ AVCEnc_WaitingForBuffer, // pending state
+ AVCEnc_Encoding_Frame,
+} AVCEnc_State ;
+
+/**
+Bitstream structure contains bitstream related parameters such as the pointer
+to the buffer, the current byte position and bit position. The content of the
+bitstreamBuffer will be in EBSP format as the emulation prevention codes are
+automatically inserted as the RBSP is recorded.
+@publishedAll
+*/
+typedef struct tagEncBitstream
+{
+ uint8 *bitstreamBuffer; /* pointer to buffer memory */
+ int buf_size; /* size of the buffer memory */
+ int write_pos; /* next position to write to bitstreamBuffer */
+ int count_zeros; /* count number of consecutive zero */
+ uint current_word; /* byte-swapped (MSB left) current word to write to buffer */
+ int bit_left; /* number of bit left in current_word */
+ uint8 *overrunBuffer; /* extra output buffer to prevent current skip due to output buffer overrun*/
+ int oBSize; /* size of allocated overrun buffer */
+ void *encvid; /* pointer to the main object */
+
+} AVCEncBitstream;
+
+/**
+This structure is used for rate control purpose and other performance related control
+variables such as, RD cost, statistics, motion search stuffs, etc.
+should be in this structure.
+@publishedAll
+*/
+
+
+typedef struct tagRDInfo
+{
+ int QP;
+ int actual_bits;
+ OsclFloat mad;
+ OsclFloat R_D;
+} RDInfo;
+
+typedef struct tagMultiPass
+{
+ /* multipass rate control data */
+ int target_bits; /* target bits for current frame, = rc->T */
+ int actual_bits; /* actual bits for current frame obtained after encoding, = rc->Rc*/
+ int QP; /* quantization level for current frame, = rc->Qc*/
+ int prev_QP; /* quantization level for previous frame */
+ int prev_prev_QP; /* quantization level for previous frame before last*/
+ OsclFloat mad; /* mad for current frame, = video->avgMAD*/
+ int bitrate; /* bitrate for current frame */
+ OsclFloat framerate; /* framerate for current frame*/
+
+ int nRe_Quantized; /* control variable for multipass encoding, */
+ /* 0 : first pass */
+ /* 1 : intermediate pass(quantization and VLC loop only) */
+ /* 2 : final pass(de-quantization, idct, etc) */
+ /* 3 : macroblock level rate control */
+
+ int encoded_frames; /* counter for all encoded frames */
+ int re_encoded_frames; /* counter for all multipass encoded frames*/
+ int re_encoded_times; /* counter for all times of multipass frame encoding */
+
+ /* Multiple frame prediction*/
+ RDInfo **pRDSamples; /* pRDSamples[30][32], 30->30fps, 32 -> 5 bit quantizer, 32 candidates*/
+ int framePos; /* specific position in previous multiple frames*/
+ int frameRange; /* number of overall previous multiple frames */
+ int samplesPerFrame[30]; /* number of samples per frame, 30->30fps */
+
+ /* Bit allocation for scene change frames and high motion frames */
+ OsclFloat sum_mad;
+ int counter_BTsrc; /* BT = Bit Transfer, bit transfer from low motion frames or less complicatedly compressed frames */
+ int counter_BTdst; /* BT = Bit Transfer, bit transfer to scene change frames or high motion frames or more complicatedly compressed frames */
+ OsclFloat sum_QP;
+ int diff_counter; /* diff_counter = -diff_counter_BTdst, or diff_counter_BTsrc */
+
+ /* For target bitrate or framerate update */
+ OsclFloat target_bits_per_frame; /* = C = bitrate/framerate */
+ OsclFloat target_bits_per_frame_prev; /* previous C */
+ OsclFloat aver_mad; /* so-far average mad could replace sum_mad */
+ OsclFloat aver_mad_prev; /* previous average mad */
+ int overlapped_win_size; /* transition period of time */
+ int encoded_frames_prev; /* previous encoded_frames */
+} MultiPass;
+
+
+typedef struct tagdataPointArray
+{
+ int Qp;
+ int Rp;
+ OsclFloat Mp; /* for MB-based RC */
+ struct tagdataPointArray *next;
+ struct tagdataPointArray *prev;
+} dataPointArray;
+
+typedef struct tagAVCRateControl
+{
+
+ /* these parameters are initialized by the users AVCEncParams */
+ /* bitrate-robustness tradeoff */
+ uint scdEnable; /* enable scene change detection */
+ int idrPeriod; /* IDR period in number of frames */
+ int intraMBRate; /* intra MB refresh rate per frame */
+ uint dpEnable; /* enable data partitioning */
+
+ /* quality-complexity tradeoff */
+ uint subPelEnable; /* enable quarter pel search */
+ int mvRange; /* motion vector search range in +/- pixel */
+ uint subMBEnable; /* enable sub MB prediction mode (4x4, 4x8, 8x4) */
+ uint rdOptEnable; /* enable RD-opt mode selection */
+ uint twoPass; /* flag for 2 pass encoding ( for future )*/
+ uint bidirPred; /* bi-directional prediction for B-frame. */
+
+ uint rcEnable; /* enable rate control, '1' on, '0' const QP */
+ int initQP; /* initial QP */
+
+ /* note the following 3 params are for HRD, these triplets can be a series
+ of triplets as the generalized HRD allows. SEI message must be generated in this case. */
+ /* We no longer have to differentiate between CBR and VBR. The users to the
+ AVC encoder lib will do the mapping from CBR/VBR to these parameters. */
+ int32 bitRate; /* target bit rate for the overall clip in bits/second*/
+ int32 cpbSize; /* coded picture buffer size in bytes */
+ int32 initDelayOffset; /* initial CBP removal delay in bits */
+
+ OsclFloat frame_rate; /* frame rate */
+ int srcInterval; /* source frame rate in msec */
+ int basicUnit; /* number of macroblocks per BU */
+
+ /* Then internal parameters for the operation */
+ uint first_frame; /* a flag for the first frame */
+ int lambda_mf; /* for example */
+ int totalSAD; /* SAD of current frame */
+
+ /*******************************************/
+ /* this part comes from MPEG4 rate control */
+ int alpha; /* weight for I frame */
+ int Rs; /*bit rate for the sequence (or segment) e.g., 24000 bits/sec */
+ int Rc; /*bits used for the current frame. It is the bit count obtained after encoding. */
+ int Rp; /*bits to be removed from the buffer per picture. */
+ /*? is this the average one, or just the bits coded for the previous frame */
+ int Rps; /*bit to be removed from buffer per src frame */
+ OsclFloat Ts; /*number of seconds for the sequence (or segment). e.g., 10 sec */
+ OsclFloat Ep;
+ OsclFloat Ec; /*mean absolute difference for the current frame after motion compensation.*/
+ /*If the macroblock is intra coded, the original spatial pixel values are summed.*/
+ int Qc; /*quantization level used for the current frame. */
+ int Nr; /*number of P frames remaining for encoding.*/
+ int Rr; /*number of bits remaining for encoding this sequence (or segment).*/
+ int Rr_Old;
+ int T; /*target bit to be used for the current frame.*/
+ int S; /*number of bits used for encoding the previous frame.*/
+ int Hc; /*header and motion vector bits used in the current frame. It includes all the information except to the residual information.*/
+ int Hp; /*header and motion vector bits used in the previous frame. It includes all the information except to the residual information.*/
+ int Ql; /*quantization level used in the previous frame */
+ int Bs; /*buffer size e.g., R/2 */
+ int B; /*current buffer level e.g., R/4 - start from the middle of the buffer */
+ OsclFloat X1;
+ OsclFloat X2;
+ OsclFloat X11;
+ OsclFloat M; /*safe margin for the buffer */
+ OsclFloat smTick; /*ratio of src versus enc frame rate */
+ double remnant; /*remainder frame of src/enc frame for fine frame skipping */
+ int timeIncRes; /* vol->timeIncrementResolution */
+
+ dataPointArray *end; /*quantization levels for the past (20) frames */
+
+ int frameNumber; /* ranging from 0 to 20 nodes*/
+ int w;
+ int Nr_Original;
+ int Nr_Old, Nr_Old2;
+ int skip_next_frame;
+ int Qdep; /* smooth Q adjustment */
+ int VBR_Enabled;
+
+ int totalFrameNumber; /* total coded frames, for debugging!!*/
+
+ char oFirstTime;
+
+ int numFrameBits; /* keep track of number of bits of the current frame */
+ int NumberofHeaderBits;
+ int NumberofTextureBits;
+ int numMBHeaderBits;
+ int numMBTextureBits;
+ double *MADofMB;
+ int32 bitsPerFrame;
+
+ /* BX rate control, something like TMN8 rate control*/
+
+ MultiPass *pMP;
+
+ int TMN_W;
+ int TMN_TH;
+ int VBV_fullness;
+ int max_BitVariance_num; /* the number of the maximum bit variance within the given buffer with the unit of 10% of bitrate/framerate*/
+ int encoded_frames; /* counter for all encoded frames */
+ int low_bound; /* bound for underflow detection, usually low_bound=-Bs/2, but could be changed in H.263 mode */
+ int VBV_fullness_offset; /* offset of VBV_fullness, usually is zero, but can be changed in H.263 mode*/
+ /* End BX */
+
+} AVCRateControl;
+
+
+/**
+This structure is for the motion vector information. */
+typedef struct tagMV
+{
+ int x;
+ int y;
+ uint sad;
+} AVCMV;
+
+/**
+This structure contains function pointers for different platform dependent implementation of
+functions. */
+typedef struct tagAVCEncFuncPtr
+{
+
+ int (*SAD_MB_HalfPel[4])(uint8*, uint8*, int, void *);
+ int (*SAD_Macroblock)(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+
+} AVCEncFuncPtr;
+
+/**
+This structure contains information necessary for correct padding.
+*/
+typedef struct tagPadInfo
+{
+ int i;
+ int width;
+ int j;
+ int height;
+} AVCPadInfo;
+
+
+#ifdef HTFM
+typedef struct tagHTFM_Stat
+{
+ int abs_dif_mad_avg;
+ uint countbreak;
+ int offsetArray[16];
+ int offsetRef[16];
+} HTFM_Stat;
+#endif
+
+
+/**
+This structure is the main object for AVC encoder library providing access to all
+global variables. It is allocated at PVAVCInitEncoder and freed at PVAVCCleanUpEncoder.
+@publishedAll
+*/
+typedef struct tagEncObject
+{
+
+ AVCCommonObj *common;
+
+ AVCEncBitstream *bitstream; /* for current NAL */
+ uint8 *overrunBuffer; /* extra output buffer to prevent current skip due to output buffer overrun*/
+ int oBSize; /* size of allocated overrun buffer */
+
+ /* rate control */
+ AVCRateControl *rateCtrl; /* pointer to the rate control structure */
+
+ /* encoding operation */
+ AVCEnc_State enc_state; /* encoding state */
+
+ AVCFrameIO *currInput; /* pointer to the current input frame */
+
+ int currSliceGroup; /* currently encoded slice group id */
+
+ int level[24][16], run[24][16]; /* scratch memory */
+ int leveldc[16], rundc[16]; /* for DC component */
+ int levelcdc[16], runcdc[16]; /* for chroma DC component */
+ int numcoefcdc[2]; /* number of coefficient for chroma DC */
+ int numcoefdc; /* number of coefficients for DC component */
+
+ int qp_const;
+ int qp_const_c;
+ /********* intra prediction scratch memory **********************/
+ uint8 pred_i16[AVCNumI16PredMode][256]; /* save prediction for MB */
+ uint8 pred_i4[AVCNumI4PredMode][16]; /* save prediction for blk */
+ uint8 pred_ic[AVCNumIChromaMode][128]; /* for 2 chroma */
+
+ int mostProbableI4Mode[16]; /* in raster scan order */
+ /********* motion compensation related variables ****************/
+ AVCMV *mot16x16; /* Saved motion vectors for 16x16 block*/
+ AVCMV(*mot16x8)[2]; /* Saved motion vectors for 16x8 block*/
+ AVCMV(*mot8x16)[2]; /* Saved motion vectors for 8x16 block*/
+ AVCMV(*mot8x8)[4]; /* Saved motion vectors for 8x8 block*/
+
+ /********* subpel position **************************************/
+ uint32 subpel_pred[SUBPEL_PRED_BLK_SIZE/*<<2*/]; /* all 16 sub-pel positions */
+ uint8 *hpel_cand[9]; /* pointer to half-pel position */
+ int best_hpel_pos; /* best position */
+ uint8 qpel_cand[8][24*16]; /* pointer to quarter-pel position */
+ int best_qpel_pos;
+ uint8 *bilin_base[9][4]; /* pointer to 4 position at top left of bilinear quarter-pel */
+
+ /* need for intra refresh rate */
+ uint8 *intraSearch; /* Intra Array for MBs to be intra searched */
+ uint firstIntraRefreshMBIndx; /* keep track for intra refresh */
+
+ int i4_sad; /* temporary for i4 mode SAD */
+ int *min_cost; /* Minimum cost for the all MBs */
+ int lambda_mode; /* Lagrange parameter for mode selection */
+ int lambda_motion; /* Lagrange parameter for MV selection */
+
+ uint8 *mvbits_array; /* Table for bits spent in the cost funciton */
+ uint8 *mvbits; /* An offset to the above array. */
+
+ /* to speedup the SAD calculation */
+ void *sad_extra_info;
+ uint8 currYMB[256]; /* interleaved current macroblock in HTFM order */
+
+#ifdef HTFM
+ int nrmlz_th[48]; /* Threshold for fast SAD calculation using HTFM */
+ HTFM_Stat htfm_stat; /* For statistics collection */
+#endif
+
+ /* statistics */
+ int numIntraMB; /* keep track of number of intra MB */
+
+ /* encoding complexity control */
+ uint fullsearch_enable; /* flag to enable full-pel full-search */
+
+ /* misc.*/
+ bool outOfBandParamSet; /* flag to enable out-of-band param set */
+
+ AVCSeqParamSet extSPS; /* for external SPS */
+ AVCPicParamSet extPPS; /* for external PPS */
+
+ /* time control */
+ uint32 prevFrameNum; /* previous frame number starting from modTimeRef */
+ uint32 modTimeRef; /* Reference modTime update every I-Vop*/
+ uint32 wrapModTime; /* Offset to modTime Ref, rarely used */
+
+ uint prevProcFrameNum; /* previously processed frame number, could be skipped */
+ uint prevCodedFrameNum; /* previously encoded frame number */
+ /* POC related variables */
+ uint32 dispOrdPOCRef; /* reference POC is displayer order unit. */
+
+ /* Function pointers */
+ AVCEncFuncPtr *functionPointer; /* store pointers to platform specific functions */
+
+ /* Application control data */
+ AVCHandle *avcHandle;
+
+
+} AVCEncObject;
+
+
+#endif /*AVCENC_INT_H_INCLUDED*/
+
diff --git a/media/libstagefright/codecs/avc/enc/src/avcenc_lib.h b/media/libstagefright/codecs/avc/enc/src/avcenc_lib.h
new file mode 100644
index 0000000..17e28ef
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/avcenc_lib.h
@@ -0,0 +1,1020 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 file contains declarations of internal functions for AVC decoder library.
+@publishedAll
+*/
+#ifndef AVCENC_LIB_H_INCLUDED
+#define AVCENC_LIB_H_INCLUDED
+
+#ifndef AVCLIB_COMMON_H_INCLUDED
+#include "avclib_common.h"
+#endif
+#ifndef AVCENC_INT_H_INCLUDED
+#include "avcenc_int.h"
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ /*------------- block.c -------------------------*/
+
+ /**
+ This function perform residue calculation, transform, quantize, inverse quantize,
+ inverse transform and residue compensation on a 4x4 block.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "blkidx" "raster scan block index of the current 4x4 block."
+ \param "cur" "Pointer to the reconstructed block."
+ \param "org" "Pointer to the original block."
+ \param "coef_cost" "Pointer to the coefficient cost to be filled in and returned."
+ \return "Number of non-zero coefficients."
+ */
+ int dct_luma(AVCEncObject *encvid, int blkidx, uint8 *cur, uint8 *org, int *coef_cost);
+
+ /**
+ This function performs IDCT on an INTER macroblock.
+ \param "video" "Pointer to AVCCommonObj."
+ \param "curL" "Pointer to the origin of the macroblock on the current frame."
+ \param "currMB" "Pointer to the AVCMacroblock structure."
+ \param "picPitch" "Pitch of the current frame."
+ \return "void".
+ */
+ void MBInterIdct(AVCCommonObj *video, uint8 *curL, AVCMacroblock *currMB, int picPitch);
+
+ /**
+ This function perform residue calculation, transform, quantize, inverse quantize,
+ inverse transform and residue compensation on a macroblock.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "curL" "Pointer to the reconstructed MB."
+ \param "orgL" "Pointer to the original MB."
+ \return "void"
+ */
+ void dct_luma_16x16(AVCEncObject *encvid, uint8 *curL, uint8 *orgL);
+
+ /**
+ This function perform residue calculation, transform, quantize, inverse quantize,
+ inverse transform and residue compensation for chroma components of an MB.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "curC" "Pointer to the reconstructed MB."
+ \param "orgC" "Pointer to the original MB."
+ \param "cr" "Flag whether it is Cr or not."
+ \return "void"
+ */
+ void dct_chroma(AVCEncObject *encvid, uint8 *curC, uint8 *orgC, int cr);
+
+ /*----------- init.c ------------------*/
+ /**
+ This function interprets the encoding parameters provided by users in encParam.
+ The results are kept in AVCEncObject, AVCSeqParamSet, AVCPicParamSet and AVCSliceHeader.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "encParam" "Pointer to AVCEncParam."
+ \param "extSPS" "External SPS template to be followed. NULL if not present."
+ \param "extPPS" "External PPS template to be followed. NULL if not present."
+ \return "see AVCEnc_Status."
+ */
+ AVCEnc_Status SetEncodeParam(AVCHandle *avcHandle, AVCEncParams *encParam,
+ void *extSPS, void *extPPS);
+
+ /**
+ This function verifies the encoding parameters whether they meet the set of supported
+ tool by a specific profile. If the profile is not set, it will just find the closest
+ profile instead of verifying it.
+ \param "video" "Pointer to AVCEncObject."
+ \param "seqParam" "Pointer to AVCSeqParamSet."
+ \param "picParam" "Pointer to AVCPicParamSet."
+ \return "AVCENC_SUCCESS if success,
+ AVCENC_PROFILE_NOT_SUPPORTED if the specified profile
+ is not supported by this version of the library,
+ AVCENC_TOOLS_NOT_SUPPORTED if any of the specified encoding tools are
+ not supported by the user-selected profile."
+ */
+ AVCEnc_Status VerifyProfile(AVCEncObject *video, AVCSeqParamSet *seqParam, AVCPicParamSet *picParam);
+
+ /**
+ This function verifies the encoding parameters whether they meet the requirement
+ for a specific level. If the level is not set, it will just find the closest
+ level instead of verifying it.
+ \param "video" "Pointer to AVCEncObject."
+ \param "seqParam" "Pointer to AVCSeqParamSet."
+ \param "picParam" "Pointer to AVCPicParamSet."
+ \return "AVCENC_SUCCESS if success,
+ AVCENC_LEVEL_NOT_SUPPORTED if the specified level
+ is not supported by this version of the library,
+ AVCENC_LEVEL_FAIL if any of the encoding parameters exceed
+ the range of the user-selected level."
+ */
+ AVCEnc_Status VerifyLevel(AVCEncObject *video, AVCSeqParamSet *seqParam, AVCPicParamSet *picParam);
+
+ /**
+ This funciton initializes the frame encoding by setting poc/frame_num related parameters. it
+ also performs motion estimation.
+ \param "encvid" "Pointer to the AVCEncObject."
+ \return "AVCENC_SUCCESS if success, AVCENC_NO_PICTURE if there is no input picture
+ in the queue to encode, AVCENC_POC_FAIL or AVCENC_CONSECUTIVE_NONREF for POC
+ related errors, AVCENC_NEW_IDR if new IDR is detected."
+ */
+ AVCEnc_Status InitFrame(AVCEncObject *encvid);
+
+ /**
+ This function initializes slice header related variables and other variables necessary
+ for decoding one slice.
+ \param "encvid" "Pointer to the AVCEncObject."
+ \return "AVCENC_SUCCESS if success."
+ */
+ AVCEnc_Status InitSlice(AVCEncObject *encvid);
+
+ /*----------- header.c ----------------*/
+ /**
+ This function performs bitstream encoding of the sequence parameter set NAL.
+ \param "encvid" "Pointer to the AVCEncObject."
+ \param "stream" "Pointer to AVCEncBitstream."
+ \return "AVCENC_SUCCESS if success or AVCENC_SPS_FAIL or others for unexpected failure which
+ should not occur. The SPS parameters should all be verified before this function is called."
+ */
+ AVCEnc_Status EncodeSPS(AVCEncObject *encvid, AVCEncBitstream *stream);
+
+ /**
+ This function encodes the VUI parameters into the sequence parameter set bitstream.
+ \param "stream" "Pointer to AVCEncBitstream."
+ \param "vui" "Pointer to AVCVUIParams."
+ \return "nothing."
+ */
+ void EncodeVUI(AVCEncBitstream* stream, AVCVUIParams* vui);
+
+ /**
+ This function encodes HRD parameters into the sequence parameter set bitstream
+ \param "stream" "Pointer to AVCEncBitstream."
+ \param "hrd" "Pointer to AVCHRDParams."
+ \return "nothing."
+ */
+ void EncodeHRD(AVCEncBitstream* stream, AVCHRDParams* hrd);
+
+
+ /**
+ This function performs bitstream encoding of the picture parameter set NAL.
+ \param "encvid" "Pointer to the AVCEncObject."
+ \param "stream" "Pointer to AVCEncBitstream."
+ \return "AVCENC_SUCCESS if success or AVCENC_PPS_FAIL or others for unexpected failure which
+ should not occur. The SPS parameters should all be verified before this function is called."
+ */
+ AVCEnc_Status EncodePPS(AVCEncObject *encvid, AVCEncBitstream *stream);
+
+ /**
+ This function encodes slice header information which has been initialized or fabricated
+ prior to entering this funciton.
+ \param "encvid" "Pointer to the AVCEncObject."
+ \param "stream" "Pointer to AVCEncBitstream."
+ \return "AVCENC_SUCCESS if success or bitstream fail statuses."
+ */
+ AVCEnc_Status EncodeSliceHeader(AVCEncObject *encvid, AVCEncBitstream *stream);
+
+ /**
+ This function encodes reference picture list reordering relted syntax.
+ \param "video" "Pointer to AVCCommonObj."
+ \param "stream" "Pointer to AVCEncBitstream."
+ \param "sliceHdr" "Pointer to AVCSliceHdr."
+ \param "slice_type" "Value of slice_type - 5 if greater than 5."
+ \return "AVCENC_SUCCESS for success and AVCENC_FAIL otherwise."
+ */
+ AVCEnc_Status ref_pic_list_reordering(AVCCommonObj *video, AVCEncBitstream *stream, AVCSliceHeader *sliceHdr, int slice_type);
+
+ /**
+ This function encodes dec_ref_pic_marking related syntax.
+ \param "video" "Pointer to AVCCommonObj."
+ \param "stream" "Pointer to AVCEncBitstream."
+ \param "sliceHdr" "Pointer to AVCSliceHdr."
+ \return "AVCENC_SUCCESS for success and AVCENC_FAIL otherwise."
+ */
+ AVCEnc_Status dec_ref_pic_marking(AVCCommonObj *video, AVCEncBitstream *stream, AVCSliceHeader *sliceHdr);
+
+ /**
+ This function initializes the POC related variables and the POC syntax to be encoded
+ to the slice header derived from the disp_order and is_reference flag of the original
+ input frame to be encoded.
+ \param "video" "Pointer to the AVCEncObject."
+ \return "AVCENC_SUCCESS if success,
+ AVCENC_POC_FAIL if the poc type is undefined or
+ AVCENC_CONSECUTIVE_NONREF if there are consecutive non-reference frame for POC type 2."
+ */
+ AVCEnc_Status InitPOC(AVCEncObject *video);
+
+ /**
+ This function performs POC related operation after a picture is decoded.
+ \param "video" "Pointer to AVCCommonObj."
+ \return "AVCENC_SUCCESS"
+ */
+ AVCEnc_Status PostPOC(AVCCommonObj *video);
+
+ /*----------- bitstream_io.c ----------------*/
+ /**
+ This function initializes the bitstream structure with the information given by
+ the users.
+ \param "bitstream" "Pointer to the AVCEncBitstream structure."
+ \param "buffer" "Pointer to the unsigned char buffer for output."
+ \param "buf_size" "The size of the buffer in bytes."
+ \param "overrunBuffer" "Pointer to extra overrun buffer."
+ \param "oBSize" "Size of overrun buffer in bytes."
+ \return "AVCENC_SUCCESS if success, AVCENC_BITSTREAM_INIT_FAIL if fail"
+ */
+ AVCEnc_Status BitstreamEncInit(AVCEncBitstream *bitstream, uint8 *buffer, int buf_size,
+ uint8 *overrunBuffer, int oBSize);
+
+ /**
+ This function writes the data from the cache into the bitstream buffer. It also adds the
+ emulation prevention code if necessary.
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \return "AVCENC_SUCCESS if success or AVCENC_BITSTREAM_BUFFER_FULL if fail."
+ */
+ AVCEnc_Status AVCBitstreamSaveWord(AVCEncBitstream *stream);
+
+ /**
+ This function writes the codeword into the cache which will eventually be written to
+ the bitstream buffer.
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \param "nBits" "Number of bits in the codeword."
+ \param "code" "The codeword."
+ \return "AVCENC_SUCCESS if success or AVCENC_BITSTREAM_BUFFER_FULL if fail."
+ */
+ AVCEnc_Status BitstreamWriteBits(AVCEncBitstream *stream, int nBits, uint code);
+
+ /**
+ This function writes one bit of data into the cache which will eventually be written
+ to the bitstream buffer.
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \param "code" "The codeword."
+ \return "AVCENC_SUCCESS if success or AVCENC_BITSTREAM_BUFFER_FULL if fail."
+ */
+ AVCEnc_Status BitstreamWrite1Bit(AVCEncBitstream *stream, uint code);
+
+ /**
+ This function adds trailing bits to the bitstream and reports back the final EBSP size.
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \param "nal_size" "Output the final NAL size."
+ \return "AVCENC_SUCCESS if success or AVCENC_BITSTREAM_BUFFER_FULL if fail."
+ */
+ AVCEnc_Status BitstreamTrailingBits(AVCEncBitstream *bitstream, uint *nal_size);
+
+ /**
+ This function checks whether the current bit position is byte-aligned or not.
+ \param "stream" "Pointer to the bitstream structure."
+ \return "true if byte-aligned, false otherwise."
+ */
+ bool byte_aligned(AVCEncBitstream *stream);
+
+
+ /**
+ This function checks the availability of overrun buffer and switches to use it when
+ normal bufffer is not big enough.
+ \param "stream" "Pointer to the bitstream structure."
+ \param "numExtraBytes" "Number of extra byte needed."
+ \return "AVCENC_SUCCESS or AVCENC_FAIL."
+ */
+ AVCEnc_Status AVCBitstreamUseOverrunBuffer(AVCEncBitstream* stream, int numExtraBytes);
+
+
+ /*-------------- intra_est.c ---------------*/
+
+ /** This function performs intra/inter decision based on ABE.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "min_cost" "Best inter cost."
+ \param "curL" "Pointer to the current MB origin in reconstructed frame."
+ \param "picPitch" "Pitch of the reconstructed frame."
+ \return "Boolean for intra mode."
+ */
+
+//bool IntraDecisionABE(AVCEncObject *encvid, int min_cost, uint8 *curL, int picPitch);
+ bool IntraDecision(int *min_cost, uint8 *cur, int pitch, bool ave);
+
+ /**
+ This function performs intra prediction mode search.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "mbnum" "Current MB number."
+ \param "curL" "Pointer to the current MB origin in reconstructed frame."
+ \param "picPitch" "Pitch of the reconstructed frame."
+ \return "void."
+ */
+ void MBIntraSearch(AVCEncObject *encvid, int mbnum, uint8 *curL, int picPitch);
+
+ /**
+ This function generates all the I16 prediction modes for an MB and keep it in
+ encvid->pred_i16.
+ \param "encvid" "Pointer to AVCEncObject."
+ \return "void"
+ */
+ void intrapred_luma_16x16(AVCEncObject *encvid);
+
+ /**
+ This function calculate the cost of all I16 modes and compare them to get the minimum.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "orgY" "Pointer to the original luma MB."
+ \param "min_cost" "Pointer to the minimal cost so-far."
+ \return "void"
+ */
+ void find_cost_16x16(AVCEncObject *encvid, uint8 *orgY, int *min_cost);
+
+ /**
+ This function calculates the cost of each I16 mode.
+ \param "org" "Pointer to the original luma MB."
+ \param "org_pitch" "Stride size of the original frame."
+ \param "pred" "Pointer to the prediction values."
+ \param "min_cost" "Minimal cost so-far."
+ \return "Cost"
+ */
+
+ int cost_i16(uint8 *org, int org_pitch, uint8 *pred, int min_cost);
+
+ /**
+ This function generates all the I4 prediction modes and select the best one
+ for all the blocks inside a macroblock.It also calls dct_luma to generate the reconstructed
+ MB, and transform coefficients to be encoded.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "min_cost" "Pointer to the minimal cost so-far."
+ \return "void"
+ */
+ void mb_intra4x4_search(AVCEncObject *encvid, int *min_cost);
+
+ /**
+ This function calculates the most probable I4 mode of a given 4x4 block
+ from neighboring informationaccording to AVC/H.264 standard.
+ \param "video" "Pointer to AVCCommonObj."
+ \param "blkidx" "The current block index."
+ \return "Most probable mode."
+ */
+ int FindMostProbableI4Mode(AVCCommonObj *video, int blkidx);
+
+ /**
+ This function is where a lot of actions take place in the 4x4 block level inside
+ mb_intra4x4_search.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "blkidx" "The current 4x4 block index."
+ \param "cur" "Pointer to the reconstructed block."
+ \param "org" "Pointer to the original block."
+ \return "Minimal cost, also set currMB->i4Mode"
+ */
+ int blk_intra4x4_search(AVCEncObject *encvid, int blkidx, uint8 *cur, uint8 *org);
+
+ /**
+ This function calculates the cost of a given I4 prediction mode.
+ \param "org" "Pointer to the original block."
+ \param "org_pitch" "Stride size of the original frame."
+ \param "pred" "Pointer to the prediction block. (encvid->pred_i4)"
+ \param "cost" "Pointer to the minimal cost (to be updated)."
+ \return "void"
+ */
+ void cost_i4(uint8 *org, int org_pitch, uint8 *pred, uint16 *cost);
+
+ /**
+ This function performs chroma intra search. Each mode is saved in encvid->pred_ic.
+ \param "encvid" "Pointer to AVCEncObject."
+ \return "void"
+ */
+ void chroma_intra_search(AVCEncObject *encvid);
+
+ /**
+ This function calculates the cost of a chroma prediction mode.
+ \param "orgCb" "Pointer to the original Cb block."
+ \param "orgCr" "Pointer to the original Cr block."
+ \param "org_pitch" "Stride size of the original frame."
+ \param "pred" "Pointer to the prediction block (encvid->pred_ic)"
+ \param "mincost" "Minimal cost so far."
+ \return "Cost."
+ */
+
+ int SATDChroma(uint8 *orgCb, uint8 *orgCr, int org_pitch, uint8 *pred, int mincost);
+
+ /*-------------- motion_comp.c ---------------*/
+
+ /**
+ This is a main function to peform inter prediction.
+ \param "encvid" "Pointer to AVCEncObject."
+ \param "video" "Pointer to AVCCommonObj."
+ \return "void".
+ */
+ void AVCMBMotionComp(AVCEncObject *encvid, AVCCommonObj *video);
+
+
+ /**
+ This function is called for luma motion compensation.
+ \param "ref" "Pointer to the origin of a reference luma."
+ \param "picwidth" "Width of the picture."
+ \param "picheight" "Height of the picture."
+ \param "x_pos" "X-coordinate of the predicted block in quarter pel resolution."
+ \param "y_pos" "Y-coordinate of the predicted block in quarter pel resolution."
+ \param "pred" "Pointer to the output predicted block."
+ \param "pred_pitch" "Width of pred."
+ \param "blkwidth" "Width of the current partition."
+ \param "blkheight" "Height of the current partition."
+ \return "void"
+ */
+ void eLumaMotionComp(uint8 *ref, int picwidth, int picheight,
+ int x_pos, int y_pos,
+ uint8 *pred, int pred_pitch,
+ int blkwidth, int blkheight);
+
+ void eFullPelMC(uint8 *in, int inwidth, uint8 *out, int outpitch,
+ int blkwidth, int blkheight);
+
+ void eHorzInterp1MC(uint8 *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dx);
+
+ void eHorzInterp2MC(int *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dx);
+
+ void eHorzInterp3MC(uint8 *in, int inpitch, int *out, int outpitch,
+ int blkwidth, int blkheight);
+
+ void eVertInterp1MC(uint8 *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dy);
+
+ void eVertInterp2MC(uint8 *in, int inpitch, int *out, int outpitch,
+ int blkwidth, int blkheight);
+
+ void eVertInterp3MC(int *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dy);
+
+ void eDiagonalInterpMC(uint8 *in1, uint8 *in2, int inpitch,
+ uint8 *out, int outpitch,
+ int blkwidth, int blkheight);
+
+ void eChromaMotionComp(uint8 *ref, int picwidth, int picheight,
+ int x_pos, int y_pos, uint8 *pred, int pred_pitch,
+ int blkwidth, int blkheight);
+
+ void eChromaDiagonalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaHorizontalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaVerticalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaFullMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaVerticalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaHorizontalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+ void eChromaDiagonalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight);
+
+
+ /*-------------- motion_est.c ---------------*/
+
+ /**
+ Allocate and initialize arrays necessary for motion search algorithm.
+ \param "envid" "Pointer to AVCEncObject."
+ \return "AVC_SUCCESS or AVC_MEMORY_FAIL."
+ */
+ AVCEnc_Status InitMotionSearchModule(AVCHandle *avcHandle);
+
+ /**
+ Clean up memory allocated in InitMotionSearchModule.
+ \param "envid" "Pointer to AVCEncObject."
+ \return "void."
+ */
+ void CleanMotionSearchModule(AVCHandle *avcHandle);
+
+
+ /**
+ This function performs motion estimation of all macroblocks in a frame during the InitFrame.
+ The goal is to find the best MB partition for inter and find out if intra search is needed for
+ any MBs. This intra MB tendency can be used for scene change detection.
+ \param "encvid" "Pointer to AVCEncObject."
+ \return "void"
+ */
+ void AVCMotionEstimation(AVCEncObject *encvid);
+
+ /**
+ This function performs repetitive edge padding to the reference picture by adding 16 pixels
+ around the luma and 8 pixels around the chromas.
+ \param "refPic" "Pointer to the reference picture."
+ \return "void"
+ */
+ void AVCPaddingEdge(AVCPictureData *refPic);
+
+ /**
+ This function keeps track of intra refresh macroblock locations.
+ \param "encvid" "Pointer to the global array structure AVCEncObject."
+ \param "mblock" "Pointer to the array of AVCMacroblock structures."
+ \param "totalMB" "Total number of MBs in a frame."
+ \param "numRefresh" "Number of MB to be intra refresh in a single frame."
+ \return "void"
+ */
+ void AVCRasterIntraUpdate(AVCEncObject *encvid, AVCMacroblock *mblock, int totalMB, int numRefresh);
+
+#ifdef HTFM
+ void InitHTFM(VideoEncData *encvid, HTFM_Stat *htfm_stat, double *newvar, int *collect);
+ void UpdateHTFM(AVCEncObject *encvid, double *newvar, double *exp_lamda, HTFM_Stat *htfm_stat);
+ void CalcThreshold(double pf, double exp_lamda[], int nrmlz_th[]);
+ void HTFMPrepareCurMB_AVC(AVCEncObject *encvid, HTFM_Stat *htfm_stat, uint8 *cur, int pitch);
+#endif
+
+ /**
+ This function reads the input MB into a smaller faster memory space to minimize the cache miss.
+ \param "encvid" "Pointer to the global AVCEncObject."
+ \param "cur" "Pointer to the original input macroblock."
+ \param "pitch" "Stride size of the input frame (luma)."
+ \return "void"
+ */
+ void AVCPrepareCurMB(AVCEncObject *encvid, uint8 *cur, int pitch);
+
+ /**
+ Performs motion vector search for a macroblock.
+ \param "encvid" "Pointer to AVCEncObject structure."
+ \param "cur" "Pointer to the current macroblock in the input frame."
+ \param "best_cand" "Array of best candidates (to be filled in and returned)."
+ \param "i0" "X-coordinate of the macroblock."
+ \param "j0" "Y-coordinate of the macroblock."
+ \param "type_pred" "Indicates the type of operations."
+ \param "FS_en" "Flag for fullsearch enable."
+ \param "hp_guess" "Guess for half-pel search."
+ \return "void"
+ */
+ void AVCMBMotionSearch(AVCEncObject *encvid, uint8 *cur, uint8 *best_cand[],
+ int i0, int j0, int type_pred, int FS_en, int *hp_guess);
+
+//AVCEnc_Status AVCMBMotionSearch(AVCEncObject *encvid, AVCMacroblock *currMB, int mbNum,
+// int num_pass);
+
+ /**
+ Perform full-pel exhaustive search around the predicted MV.
+ \param "encvid" "Pointer to AVCEncObject structure."
+ \param "prev" "Pointer to the reference frame."
+ \param "cur" "Pointer to the input macroblock."
+ \param "imin" "Pointer to minimal mv (x)."
+ \param "jmin" "Pointer to minimal mv (y)."
+ \param "ilow, ihigh, jlow, jhigh" "Lower bound on search range."
+ \param "cmvx, cmvy" "Predicted MV value."
+
+ \return "The cost function of the best candidate."
+ */
+ int AVCFullSearch(AVCEncObject *encvid, uint8 *prev, uint8 *cur,
+ int *imin, int *jmin, int ilow, int ihigh, int jlow, int jhigh,
+ int cmvx, int cmvy);
+
+ /**
+ Select candidates from neighboring blocks according to the type of the
+ prediction selection.
+ \param "mvx" "Pointer to the candidate, x-coordinate."
+ \param "mvy" "Pointer to the candidate, y-coordinate."
+ \param "num_can" "Pointer to the number of candidates returned."
+ \param "imb" "The MB index x-coordinate."
+ \param "jmb" "The MB index y-coordinate."
+ \param "type_pred" "Type of the prediction."
+ \param "cmvx, cmvy" "Pointer to predicted MV (modified version)."
+ \return "void."
+ */
+ void AVCCandidateSelection(int *mvx, int *mvy, int *num_can, int imb, int jmb,
+ AVCEncObject *encvid, int type_pred, int *cmvx, int *cmvy);
+
+ /**
+ Utility function to move the values in the array dn according to the new
+ location to avoid redundant calculation.
+ \param "dn" "Array of integer of size 9."
+ \param "new_loc" "New location index."
+ \return "void."
+ */
+ void AVCMoveNeighborSAD(int dn[], int new_loc);
+
+ /**
+ Find minimum index of dn.
+ \param "dn" "Array of integer of size 9."
+ \return "The index of dn with the smallest dn[] value."
+ */
+ int AVCFindMin(int dn[]);
+
+
+ /*------------- findhalfpel.c -------------------*/
+
+ /**
+ Search for the best half-pel resolution MV around the full-pel MV.
+ \param "encvid" "Pointer to the global AVCEncObject structure."
+ \param "cur" "Pointer to the current macroblock."
+ \param "mot" "Pointer to the AVCMV array of the frame."
+ \param "ncand" "Pointer to the origin of the fullsearch result."
+ \param "xpos" "The current MB position in x."
+ \param "ypos" "The current MB position in y."
+ \param "hp_guess" "Input to help speedup the search."
+ \param "cmvx, cmvy" "Predicted motion vector use for mvcost."
+ \return "Minimal cost (SATD) without MV cost. (for rate control purpose)"
+ */
+ int AVCFindHalfPelMB(AVCEncObject *encvid, uint8 *cur, AVCMV *mot, uint8 *ncand,
+ int xpos, int ypos, int hp_guess, int cmvx, int cmvy);
+
+ /**
+ This function generates sub-pel pixels required to do subpel MV search.
+ \param "subpel_pred" "Pointer to 2-D array, each array for each position."
+ \param "ncand" "Pointer to the full-pel center position in ref frame."
+ \param "lx" "Pitch of the ref frame."
+ \return "void"
+ */
+ void GenerateHalfPelPred(uint8 *subpel_pred, uint8 *ncand, int lx);
+
+ /**
+ This function calculate vertical interpolation at half-point of size 4x17.
+ \param "dst" "Pointer to destination."
+ \param "ref" "Pointer to the starting reference pixel."
+ \return "void."
+ */
+ void VertInterpWClip(uint8 *dst, uint8 *ref);
+
+ /**
+ This function generates quarter-pel pixels around the best half-pel result
+ during the sub-pel MV search.
+ \param "bilin_base" "Array of pointers to be used as basis for q-pel interp."
+ \param "qpel_pred" "Array of pointers pointing to quarter-pel candidates."
+ \param "hpel_pos" "Best half-pel position at the center."
+ \return "void"
+ */
+ void GenerateQuartPelPred(uint8 **bilin_base, uint8 *qpel_pred, int hpel_pos);
+
+ /**
+ This function calculates the SATD of a subpel candidate.
+ \param "cand" "Pointer to a candidate."
+ \param "cur" "Pointer to the current block."
+ \param "dmin" "Min-so-far SATD."
+ \return "Sum of Absolute Transformed Difference."
+ */
+ int SATD_MB(uint8 *cand, uint8 *cur, int dmin);
+
+ /*------------- rate_control.c -------------------*/
+
+ /** This function is a utility function. It returns average QP of the previously encoded frame.
+ \param "rateCtrl" "Pointer to AVCRateControl structure."
+ \return "Average QP."
+ */
+ int GetAvgFrameQP(AVCRateControl *rateCtrl);
+
+ /**
+ This function takes the timestamp of the input and determine whether it should be encoded
+ or skipped.
+ \param "encvid" "Pointer to the AVCEncObject structure."
+ \param "rateCtrl" "Pointer to the AVCRateControl structure."
+ \param "modTime" "The 32 bit timestamp of the input frame."
+ \param "frameNum" "Pointer to the frame number if to be encoded."
+ \return "AVC_SUCCESS or else."
+ */
+ AVCEnc_Status RCDetermineFrameNum(AVCEncObject *encvid, AVCRateControl *rateCtrl, uint32 modTime, uint *frameNum);
+
+ /**
+ This function updates the buffer fullness when frames are dropped either by the
+ rate control algorithm or by the users to make sure that target bit rate is still met.
+ \param "video" "Pointer to the common object structure."
+ \param "rateCtrl" "Pointer to rate control structure."
+ \param "frameInc" "Difference of the current frame number and previous frame number."
+ \return "void."
+ */
+ void RCUpdateBuffer(AVCCommonObj *video, AVCRateControl *rateCtrl, int frameInc);
+
+ /**
+ This function initializes rate control module and allocates necessary bufferes to do the job.
+ \param "avcHandle" "Pointer to the encoder handle."
+ \return "AVCENC_SUCCESS or AVCENC_MEMORY_FAIL."
+ */
+ AVCEnc_Status InitRateControlModule(AVCHandle *avcHandle);
+
+ /**
+ This function frees buffers allocated in InitRateControlModule.
+ \param "avcHandle" "Pointer to the encoder handle."
+ \return "void."
+ */
+ void CleanupRateControlModule(AVCHandle *avcHandle);
+
+ /**
+ This function is called at the beginning of each GOP or the first IDR frame. It calculates
+ target bits for a GOP.
+ \param "encvid" "Pointer to the encoder object."
+ \return "void."
+ */
+ void RCInitGOP(AVCEncObject *encvid);
+
+ /**
+ This function calculates target bits for a particular frame.
+ \param "video" "Pointer to the AVCEncObject structure."
+ \return "void"
+ */
+ void RCInitFrameQP(AVCEncObject *video);
+
+ /**
+ This function calculates QP for the upcoming frame or basic unit.
+ \param "encvid" "Pointer to the encoder object."
+ \param "rateCtrl" "Pointer to the rate control object."
+ \return "QP value ranging from 0-51."
+ */
+ int RCCalculateQP(AVCEncObject *encvid, AVCRateControl *rateCtrl);
+
+ /**
+ This function translates the luma QP to chroma QP and calculates lambda based on QP.
+ \param "video" "Pointer to the AVCEncObject structure."
+ \return "void"
+ */
+ void RCInitChromaQP(AVCEncObject *encvid);
+
+ /**
+ This function is called before encoding each macroblock.
+ \param "encvid" "Pointer to the encoder object."
+ \return "void."
+ */
+ void RCInitMBQP(AVCEncObject *encvid);
+
+ /**
+ This function updates bits usage stats after encoding an macroblock.
+ \param "video" "Pointer to AVCCommonObj."
+ \param "rateCtrl" "Pointer to AVCRateControl."
+ \param "num_header_bits" "Number of bits used for MB header."
+ \param "num_texture_bits" "Number of bits used for MB texture."
+ \return "void"
+ */
+ void RCPostMB(AVCCommonObj *video, AVCRateControl *rateCtrl, int num_header_bits, int num_texture_bits);
+
+ /**
+ This function calculates the difference between prediction and original MB.
+ \param "encvid" "Pointer to the encoder object."
+ \param "currMB" "Pointer to the current macroblock structure."
+ \param "orgL" "Pointer to the original MB."
+ \param "orgPitch" "Pointer to the original picture pitch."
+ \return "void."
+ */
+ void RCCalculateMAD(AVCEncObject *encvid, AVCMacroblock *currMB, uint8 *orgL, int orgPitch);
+
+ /**
+ Restore QP related parameters of previous MB when current MB is skipped.
+ \param "currMB" "Pointer to the current macroblock."
+ \param "video" "Pointer to the common video structure."
+ \param "encvid" "Pointer to the global encoding structure."
+ \return "void"
+ */
+ void RCRestoreQP(AVCMacroblock *currMB, AVCCommonObj *video, AVCEncObject *encvid);
+
+ /**
+ This function is called after done with a frame.
+ \param "encvid" "Pointer to the encoder object."
+ \return "AVCENC_SUCCESS or AVCENC_SKIPPED_PICTURE when bufer overflow (need to discard current frame)."
+ */
+ AVCEnc_Status RCUpdateFrame(AVCEncObject *encvid);
+
+ /*--------- residual.c -------------------*/
+
+ /**
+ This function encodes the intra pcm data and fill it in the corresponding location
+ on the current picture.
+ \param "video" "Pointer to AVCEncObject."
+ \return "AVCENC_SUCCESS if success, or else for bitstream errors."
+ */
+ AVCEnc_Status EncodeIntraPCM(AVCEncObject *video);
+
+ /**
+ This function performs CAVLC syntax encoding on the run and level information of the coefficients.
+ The level and run arrays are elements in AVCEncObject structure, populated by TransQuantZZ,
+ TransQuantIntraDC and TransQuantChromaDC functions.
+ \param "video" "Pointer to AVCEncObject."
+ \param "type" "One of AVCResidualType for a particular 4x4 block."
+ \param "bindx" "Block index or number of nonzero coefficients for AVC_Intra16DC and AVC_ChromaDC mode."
+ \param "currMB" "Pointer to the current macroblock structure."
+ \return "AVCENC_SUCCESS for success."
+ \Note "This function has 32-bit machine specific instruction!!!!"
+ */
+ AVCEnc_Status enc_residual_block(AVCEncObject *encvid, AVCResidualType type, int bindx, AVCMacroblock *currMB);
+
+
+ /*------------- sad.c ---------------------------*/
+
+
+ int AVCSAD_MB_HalfPel_Cxhyh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HalfPel_Cyh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HalfPel_Cxh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_Macroblock_C(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+
+#ifdef HTFM /* 3/2/1, Hypothesis Testing Fast Matching */
+ int AVCSAD_MB_HP_HTFM_Collectxhyh(uint8 *ref, uint8 *blk, int dmin_x, void *extra_info);
+ int AVCSAD_MB_HP_HTFM_Collectyh(uint8 *ref, uint8 *blk, int dmin_x, void *extra_info);
+ int AVCSAD_MB_HP_HTFM_Collectxh(uint8 *ref, uint8 *blk, int dmin_x, void *extra_info);
+ int AVCSAD_MB_HP_HTFMxhyh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HP_HTFMyh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HP_HTFMxh(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HTFM_Collect(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+ int AVCSAD_MB_HTFM(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info);
+#endif
+
+
+ /*------------- slice.c -------------------------*/
+
+ /**
+ This function performs the main encoding loop for a slice.
+ \param "encvid" "Pointer to AVCEncObject."
+ \return "AVCENC_SUCCESS for success, AVCENC_PICTURE_READY for end-of-picture and
+ AVCENC_FAIL or AVCENC_SLICE_EMPTY otherwise."
+ */
+ AVCEnc_Status AVCEncodeSlice(AVCEncObject *encvid);
+
+ /**
+ This function performs the main encoding operation for one macroblock.
+ \param "video" "pointer to AVCEncObject."
+ \return "AVCENC_SUCCESS for success, or other bitstream related failure status."
+ */
+ AVCEnc_Status EncodeMB(AVCEncObject *video);
+
+ /**
+ This function calls prediction INTRA/INTER functions, transform,
+ quantization and zigzag scanning to get the run-level symbols.
+ \param "encvid" "pointer to AVCEncObject."
+ \param "curL" "pointer to Luma component of the current frame.
+ \param "curCb" "pointer to Cb component of the current frame.
+ \param "curCr" "pointer to Cr component of the current frame.
+ \return "void for now."
+ */
+ void MBPredTransQuantZZ(AVCEncObject *encvid, uint8 *curL, uint8 *curCb, uint8 *curCr);
+
+ /**
+ This function copies the content of the prediction MB into the reconstructed YUV
+ frame directly.
+ \param "curL" "Pointer to the destination Y component."
+ \param "curCb" "Pointer to the destination Cb component."
+ \param "curCr" "Pointer to the destination Cr component."
+ \param "predBlock" "Pointer to the prediction MB."
+ \param "picWidth" "The width of the frame."
+ \return "None."
+ */
+ void Copy_MB(uint8 *curL, uint8 *curCb, uint8 *curCr, uint8 *predBlock, int picWidth);
+
+ /**
+ This function encodes the mb_type, CBP, prediction mode, ref idx and MV.
+ \param "currMB" "Pointer to the current macroblock structure."
+ \param "video" "Pointer to the AVCEncObject structure."
+ \return "AVCENC_SUCCESS for success or else for fail."
+ */
+ AVCEnc_Status EncodeMBHeader(AVCMacroblock *currMB, AVCEncObject *video);
+
+ /**
+ This function finds the right mb_type for a macroblock given the mbMode, CBP,
+ NumPart, PredPartMode.
+ \param "currMB" "Pointer to the current macroblock structure."
+ \param "slice_type" "Value of the slice_type."
+ \return "mb_type."
+ */
+ uint InterpretMBType(AVCMacroblock *currMB, int slice_type);
+
+ /**
+ This function encodes the mb_pred part of the macroblock data.
+ \param "video" "Pointer to the AVCCommonObj structure."
+ \param "currMB" "Pointer to the current macroblock structure."
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \return "AVCENC_SUCCESS for success or bitstream fail status."
+ */
+ AVCEnc_Status mb_pred(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream);
+
+ /**
+ This function encodes the sub_mb_pred part of the macroblock data.
+ \param "video" "Pointer to the AVCCommonObj structure."
+ \param "currMB" "Pointer to the current macroblock structure."
+ \param "stream" "Pointer to the AVCEncBitstream structure."
+ \return "AVCENC_SUCCESS for success or bitstream fail status."
+ */
+ AVCEnc_Status sub_mb_pred(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream);
+
+ /**
+ This function interprets the sub_mb_type and sets necessary information
+ when the slice type is AVC_P_SLICE.
+ in the macroblock structure.
+ \param "mblock" "Pointer to current AVCMacroblock."
+ \param "sub_mb_type" "From the syntax bitstream."
+ \return "void"
+ */
+ void InterpretSubMBTypeP(AVCMacroblock *mblock, uint *sub_mb_type);
+
+ /**
+ This function interprets the sub_mb_type and sets necessary information
+ when the slice type is AVC_B_SLICE.
+ in the macroblock structure.
+ \param "mblock" "Pointer to current AVCMacroblock."
+ \param "sub_mb_type" "From the syntax bitstream."
+ \return "void"
+ */
+ void InterpretSubMBTypeB(AVCMacroblock *mblock, uint *sub_mb_type);
+
+ /**
+ This function encodes intra 4x4 mode. It calculates the predicted I4x4 mode and the
+ remnant to be encoded.
+ \param "video" "Pointer to AVCEncObject structure."
+ \param "currMB" "Pointer to the AVCMacroblock structure."
+ \param "stream" "Pointer to AVCEncBitstream sructure."
+ \return "AVCENC_SUCCESS for success."
+ */
+ AVCEnc_Status EncodeIntra4x4Mode(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream);
+
+ /*------------- vlc_encode.c -----------------------*/
+ /**
+ This function encodes and writes a value into an Exp-Golomb codeword.
+ \param "bitstream" "Pointer to AVCEncBitstream."
+ \param "codeNum" "Pointer to the value of the codeNum."
+ \return "AVCENC_SUCCESS for success or bitstream error messages for fail."
+ */
+ AVCEnc_Status ue_v(AVCEncBitstream *bitstream, uint codeNum);
+
+ /**
+ This function maps and encodes signed Exp-Golomb codes.
+ \param "bitstream" "Pointer to AVCEncBitstream."
+ \param "value" "Pointer to syntax element value."
+ \return "AVCENC_SUCCESS or AVCENC_FAIL."
+ */
+ AVCEnc_Status se_v(AVCEncBitstream *bitstream, int value);
+
+ /**
+ This function maps and encodes truncated Exp-Golomb codes.
+ \param "bitstream" "Pointer to AVCEncBitstream."
+ \param "value" "Pointer to syntax element value."
+ \param "range" "Range of the value as input to determine the algorithm."
+ \return "AVCENC_SUCCESS or AVCENC_FAIL."
+ */
+ AVCEnc_Status te_v(AVCEncBitstream *bitstream, uint value, uint range);
+
+ /**
+ This function creates Exp-Golomb codeword from codeNum.
+ \param "bitstream" "Pointer to AVCEncBitstream."
+ \param "codeNum" "Pointer to the codeNum value."
+ \return "AVCENC_SUCCESS for success or bitstream error messages for fail."
+ */
+ AVCEnc_Status SetEGBitstring(AVCEncBitstream *bitstream, uint codeNum);
+
+ /**
+ This function performs CAVLC encoding of the CBP (coded block pattern) of a macroblock
+ by calling ue_v() and then mapping the CBP to the corresponding VLC codeNum.
+ \param "currMB" "Pointer to the current AVCMacroblock structure."
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \return "void"
+ */
+ AVCEnc_Status EncodeCBP(AVCMacroblock *currMB, AVCEncBitstream *stream);
+
+ /**
+ This function encodes trailing ones and total coefficient.
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \param "TrailingOnes" "The trailing one variable output."
+ \param "TotalCoeff" "The total coefficient variable output."
+ \param "nC" "Context for number of nonzero coefficient (prediction context)."
+ \return "AVCENC_SUCCESS for success or else for bitstream failure."
+ */
+ AVCEnc_Status ce_TotalCoeffTrailingOnes(AVCEncBitstream *stream, int TrailingOnes, int TotalCoeff, int nC);
+
+ /**
+ This function encodes trailing ones and total coefficient for chroma DC block.
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \param "TrailingOnes" "The trailing one variable output."
+ \param "TotalCoeff" "The total coefficient variable output."
+ \return "AVCENC_SUCCESS for success or else for bitstream failure."
+ */
+ AVCEnc_Status ce_TotalCoeffTrailingOnesChromaDC(AVCEncBitstream *stream, int TrailingOnes, int TotalCoeff);
+
+ /**
+ This function encodes total_zeros value as in Table 9-7 and 9-8.
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \param "TotalZeros" "The total_zeros value."
+ \param "TotalCoeff" "The total coefficient variable output."
+ \return "AVCENC_SUCCESS for success or else for bitstream failure."
+ */
+ AVCEnc_Status ce_TotalZeros(AVCEncBitstream *stream, int total_zeros, int TotalCoeff);
+
+ /**
+ This function encodes total_zeros VLC syntax for chroma DC as in Table 9-9.
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \param "TotalZeros" "The total_zeros value."
+ \param "TotalCoeff" "The total coefficient variable output."
+ \return "AVCENC_SUCCESS for success or else for bitstream failure."
+ */
+ AVCEnc_Status ce_TotalZerosChromaDC(AVCEncBitstream *stream, int total_zeros, int TotalCoeff);
+
+ /**
+ This function encodes run_before VLC syntax as in Table 9-10.
+ \param "stream" "Pointer to the AVCEncBitstream."
+ \param "run_before" "The run_before value."
+ \param "zerosLeft" "The context for number of zeros left."
+ \return "AVCENC_SUCCESS for success or else for bitstream failure."
+ */
+ AVCEnc_Status ce_RunBefore(AVCEncBitstream *stream, int run_before, int zerosLeft);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _AVCENC_LIB_H_ */
+
diff --git a/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp b/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp
new file mode 100644
index 0000000..75ab514
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/bitstream_io.cpp
@@ -0,0 +1,336 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+#define WORD_SIZE 32
+
+/* array for trailing bit pattern as function of number of bits */
+/* the first one is unused. */
+const static uint8 trailing_bits[9] = {0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+
+/* ======================================================================== */
+/* Function : BitstreamInit() */
+/* Date : 11/4/2003 */
+/* Purpose : Populate bitstream structure with bitstream buffer and size */
+/* it also initializes internal data */
+/* In/out : */
+/* Return : AVCENC_SUCCESS if successed, AVCENC_FAIL if failed. */
+/* Modified : */
+/* ======================================================================== */
+/* |--------|--------|----~~~~~-----|---------|---------|---------|
+ ^ ^write_pos ^buf_size
+ bitstreamBuffer <--------->
+ current_word
+
+ |-----xxxxxxxxxxxxx| = current_word 32 or 16 bits
+ <---->
+ bit_left
+ ======================================================================== */
+
+AVCEnc_Status BitstreamEncInit(AVCEncBitstream *stream, uint8 *buffer, int buf_size,
+ uint8 *overrunBuffer, int oBSize)
+{
+ if (stream == NULL || buffer == NULL || buf_size <= 0)
+ {
+ return AVCENC_BITSTREAM_INIT_FAIL;
+ }
+
+ stream->bitstreamBuffer = buffer;
+
+ stream->buf_size = buf_size;
+
+ stream->write_pos = 0;
+
+ stream->count_zeros = 0;
+
+ stream->current_word = 0;
+
+ stream->bit_left = WORD_SIZE;
+
+ stream->overrunBuffer = overrunBuffer;
+
+ stream->oBSize = oBSize;
+
+ return AVCENC_SUCCESS;
+}
+
+/* ======================================================================== */
+/* Function : AVCBitstreamSaveWord() */
+/* Date : 3/29/2004 */
+/* Purpose : Save the current_word into the buffer, byte-swap, and */
+/* add emulation prevention insertion. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS if successed, AVCENC_WRITE_FAIL if buffer is */
+/* full. */
+/* Modified : */
+/* ======================================================================== */
+AVCEnc_Status AVCBitstreamSaveWord(AVCEncBitstream *stream)
+{
+ int num_bits;
+ uint8 *write_pnt, byte;
+ uint current_word;
+
+ /* check number of bytes in current_word, must always be byte-aligned!!!! */
+ num_bits = WORD_SIZE - stream->bit_left; /* must be multiple of 8 !!*/
+
+ if (stream->buf_size - stream->write_pos <= (num_bits >> 3) + 2) /* 2 more bytes for possible EPBS */
+ {
+ if (AVCENC_SUCCESS != AVCBitstreamUseOverrunBuffer(stream, (num_bits >> 3) + 2))
+ {
+ return AVCENC_BITSTREAM_BUFFER_FULL;
+ }
+ }
+
+ /* write word, byte-by-byte */
+ write_pnt = stream->bitstreamBuffer + stream->write_pos;
+ current_word = stream->current_word;
+ while (num_bits) /* no need to check stream->buf_size and stream->write_pos, taken care already */
+ {
+ num_bits -= 8;
+ byte = (current_word >> num_bits) & 0xFF;
+ if (byte != 0)
+ {
+ *write_pnt++ = byte;
+ stream->write_pos++;
+ stream->count_zeros = 0;
+ }
+ else
+ {
+ stream->count_zeros++;
+ *write_pnt++ = byte;
+ stream->write_pos++;
+ if (stream->count_zeros == 2)
+ { /* for num_bits = 32, this can add 2 more bytes extra for EPBS */
+ *write_pnt++ = 0x3;
+ stream->write_pos++;
+ stream->count_zeros = 0;
+ }
+ }
+ }
+
+ /* reset current_word and bit_left */
+ stream->current_word = 0;
+ stream->bit_left = WORD_SIZE;
+
+ return AVCENC_SUCCESS;
+}
+
+/* ======================================================================== */
+/* Function : BitstreamWriteBits() */
+/* Date : 3/29/2004 */
+/* Purpose : Write up to machine word. */
+/* In/out : Unused bits in 'code' must be all zeros. */
+/* Return : AVCENC_SUCCESS if successed, AVCENC_WRITE_FAIL if buffer is */
+/* full. */
+/* Modified : */
+/* ======================================================================== */
+AVCEnc_Status BitstreamWriteBits(AVCEncBitstream *stream, int nBits, uint code)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ int bit_left = stream->bit_left;
+ uint current_word = stream->current_word;
+
+ //DEBUG_LOG(userData,AVC_LOGTYPE_INFO,"BitstreamWriteBits",nBits,-1);
+
+ if (nBits > WORD_SIZE) /* has to be taken care of specially */
+ {
+ return AVCENC_FAIL; /* for now */
+ /* otherwise, break it down to 2 write of less than 16 bits at a time. */
+ }
+
+ if (nBits <= bit_left) /* more bits left in current_word */
+ {
+ stream->current_word = (current_word << nBits) | code;
+ stream->bit_left -= nBits;
+ if (stream->bit_left == 0) /* prepare for the next word */
+ {
+ status = AVCBitstreamSaveWord(stream);
+ return status;
+ }
+ }
+ else
+ {
+ stream->current_word = (current_word << bit_left) | (code >> (nBits - bit_left));
+
+ nBits -= bit_left;
+
+ stream->bit_left = 0;
+
+ status = AVCBitstreamSaveWord(stream); /* save current word */
+
+ stream->bit_left = WORD_SIZE - nBits;
+
+ stream->current_word = code; /* no extra masking for code, must be handled before saving */
+ }
+
+ return status;
+}
+
+
+/* ======================================================================== */
+/* Function : BitstreamWrite1Bit() */
+/* Date : 3/30/2004 */
+/* Purpose : Write 1 bit */
+/* In/out : Unused bits in 'code' must be all zeros. */
+/* Return : AVCENC_SUCCESS if successed, AVCENC_WRITE_FAIL if buffer is */
+/* full. */
+/* Modified : */
+/* ======================================================================== */
+AVCEnc_Status BitstreamWrite1Bit(AVCEncBitstream *stream, uint code)
+{
+ AVCEnc_Status status;
+ uint current_word = stream->current_word;
+
+ //DEBUG_LOG(userData,AVC_LOGTYPE_INFO,"BitstreamWrite1Bit",code,-1);
+
+ //if(1 <= bit_left) /* more bits left in current_word */
+ /* we can assume that there always be positive bit_left in the current word */
+ stream->current_word = (current_word << 1) | code;
+ stream->bit_left--;
+ if (stream->bit_left == 0) /* prepare for the next word */
+ {
+ status = AVCBitstreamSaveWord(stream);
+ return status;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+
+/* ======================================================================== */
+/* Function : BitstreamTrailingBits() */
+/* Date : 3/31/2004 */
+/* Purpose : Add trailing bits and report the final EBSP size. */
+/* In/out : */
+/* Return : AVCENC_SUCCESS if successed, AVCENC_WRITE_FAIL if buffer is */
+/* full. */
+/* Modified : */
+/* ======================================================================== */
+AVCEnc_Status BitstreamTrailingBits(AVCEncBitstream *bitstream, uint *nal_size)
+{
+ (void)(nal_size);
+
+ AVCEnc_Status status;
+ int bit_left = bitstream->bit_left;
+
+ bit_left &= 0x7; /* modulo by 8 */
+ if (bit_left == 0) bit_left = 8;
+ /* bitstream->bit_left == 0 cannot happen here since it would have been Saved already */
+
+ status = BitstreamWriteBits(bitstream, bit_left, trailing_bits[bit_left]);
+
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ /* if it's not saved, save it. */
+ //if(bitstream->bit_left<(WORD_SIZE<<3)) /* in fact, no need to check */
+ {
+ status = AVCBitstreamSaveWord(bitstream);
+ }
+
+ return status;
+}
+
+/* check whether it's byte-aligned */
+bool byte_aligned(AVCEncBitstream *stream)
+{
+ if (stream->bit_left % 8)
+ return false;
+ else
+ return true;
+}
+
+
+/* determine whether overrun buffer can be used or not */
+AVCEnc_Status AVCBitstreamUseOverrunBuffer(AVCEncBitstream* stream, int numExtraBytes)
+{
+ AVCEncObject *encvid = (AVCEncObject*)stream->encvid;
+
+ if (stream->overrunBuffer != NULL) // overrunBuffer is set
+ {
+ if (stream->bitstreamBuffer != stream->overrunBuffer) // not already used
+ {
+ if (stream->write_pos + numExtraBytes >= stream->oBSize)
+ {
+ stream->oBSize = stream->write_pos + numExtraBytes + 100;
+ stream->oBSize &= (~0x3); // make it multiple of 4
+
+ // allocate new overrun Buffer
+ if (encvid->overrunBuffer)
+ {
+ encvid->avcHandle->CBAVC_Free((uint32*)encvid->avcHandle->userData,
+ (int)encvid->overrunBuffer);
+ }
+
+ encvid->oBSize = stream->oBSize;
+ encvid->overrunBuffer = (uint8*) encvid->avcHandle->CBAVC_Malloc(encvid->avcHandle->userData,
+ stream->oBSize, DEFAULT_ATTR);
+
+ stream->overrunBuffer = encvid->overrunBuffer;
+ if (stream->overrunBuffer == NULL)
+ {
+ return AVCENC_FAIL;
+ }
+ }
+
+ // copy everything to overrun buffer and start using it.
+ memcpy(stream->overrunBuffer, stream->bitstreamBuffer, stream->write_pos);
+ stream->bitstreamBuffer = stream->overrunBuffer;
+ stream->buf_size = stream->oBSize;
+ }
+ else // overrun buffer is already used
+ {
+ stream->oBSize = stream->write_pos + numExtraBytes + 100;
+ stream->oBSize &= (~0x3); // make it multiple of 4
+
+ // allocate new overrun buffer
+ encvid->oBSize = stream->oBSize;
+ encvid->overrunBuffer = (uint8*) encvid->avcHandle->CBAVC_Malloc(encvid->avcHandle->userData,
+ stream->oBSize, DEFAULT_ATTR);
+
+ if (encvid->overrunBuffer == NULL)
+ {
+ return AVCENC_FAIL;
+ }
+
+
+ // copy from the old buffer to new buffer
+ memcpy(encvid->overrunBuffer, stream->overrunBuffer, stream->write_pos);
+ // free old buffer
+ encvid->avcHandle->CBAVC_Free((uint32*)encvid->avcHandle->userData,
+ (int)stream->overrunBuffer);
+
+ // assign pointer to new buffer
+ stream->overrunBuffer = encvid->overrunBuffer;
+ stream->bitstreamBuffer = stream->overrunBuffer;
+ stream->buf_size = stream->oBSize;
+ }
+
+ return AVCENC_SUCCESS;
+ }
+ else // overrunBuffer is not enable.
+ {
+ return AVCENC_FAIL;
+ }
+
+}
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/block.cpp b/media/libstagefright/codecs/avc/enc/src/block.cpp
new file mode 100644
index 0000000..01e26a6
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/block.cpp
@@ -0,0 +1,1283 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+/* subtract with the prediction and do transformation */
+void trans(uint8 *cur, int pitch, uint8 *predBlock, int16 *dataBlock)
+{
+ int16 *ptr = dataBlock;
+ int r0, r1, r2, r3, j;
+ int curpitch = (uint)pitch >> 16;
+ int predpitch = (pitch & 0xFFFF);
+
+ /* horizontal */
+ j = 4;
+ while (j > 0)
+ {
+ /* calculate the residue first */
+ r0 = cur[0] - predBlock[0];
+ r1 = cur[1] - predBlock[1];
+ r2 = cur[2] - predBlock[2];
+ r3 = cur[3] - predBlock[3];
+
+ r0 += r3; //ptr[0] + ptr[3];
+ r3 = r0 - (r3 << 1); //ptr[0] - ptr[3];
+ r1 += r2; //ptr[1] + ptr[2];
+ r2 = r1 - (r2 << 1); //ptr[1] - ptr[2];
+
+ ptr[0] = r0 + r1;
+ ptr[2] = r0 - r1;
+ ptr[1] = (r3 << 1) + r2;
+ ptr[3] = r3 - (r2 << 1);
+
+ ptr += 16;
+ predBlock += predpitch;
+ cur += curpitch;
+ j--;
+ }
+ /* vertical */
+ ptr = dataBlock;
+ j = 4;
+ while (j > 0)
+ {
+ r0 = ptr[0] + ptr[48];
+ r3 = ptr[0] - ptr[48];
+ r1 = ptr[16] + ptr[32];
+ r2 = ptr[16] - ptr[32];
+
+ ptr[0] = r0 + r1;
+ ptr[32] = r0 - r1;
+ ptr[16] = (r3 << 1) + r2;
+ ptr[48] = r3 - (r2 << 1);
+
+ ptr++;
+ j--;
+ }
+
+ return ;
+}
+
+
+/* do residue transform quant invquant, invtrans and write output out */
+int dct_luma(AVCEncObject *encvid, int blkidx, uint8 *cur, uint8 *org, int *coef_cost)
+{
+ AVCCommonObj *video = encvid->common;
+ int org_pitch = encvid->currInput->pitch;
+ int pitch = video->currPic->pitch;
+ int16 *coef = video->block;
+ uint8 *pred = video->pred_block; // size 16 for a 4x4 block
+ int pred_pitch = video->pred_pitch;
+ int r0, r1, r2, r3, j, k, idx;
+ int *level, *run;
+ int Qq, Rq, q_bits, qp_const, quant;
+ int data, lev, zero_run;
+ int numcoeff;
+
+ coef += ((blkidx & 0x3) << 2) + ((blkidx >> 2) << 6); /* point to the 4x4 block */
+
+ /* first take a 4x4 transform */
+ /* horizontal */
+ j = 4;
+ while (j > 0)
+ {
+ /* calculate the residue first */
+ r0 = org[0] - pred[0]; /* OPTIMIZEABLE */
+ r1 = org[1] - pred[1];
+ r2 = org[2] - pred[2];
+ r3 = org[3] - pred[3];
+
+ r0 += r3; //ptr[0] + ptr[3];
+ r3 = r0 - (r3 << 1); //ptr[0] - ptr[3];
+ r1 += r2; //ptr[1] + ptr[2];
+ r2 = r1 - (r2 << 1); //ptr[1] - ptr[2];
+
+ coef[0] = r0 + r1;
+ coef[2] = r0 - r1;
+ coef[1] = (r3 << 1) + r2;
+ coef[3] = r3 - (r2 << 1);
+
+ coef += 16;
+ org += org_pitch;
+ pred += pred_pitch;
+ j--;
+ }
+ /* vertical */
+ coef -= 64;
+ pred -= (pred_pitch << 2);
+ j = 4;
+ while (j > 0) /* OPTIMIZABLE */
+ {
+ r0 = coef[0] + coef[48];
+ r3 = coef[0] - coef[48];
+ r1 = coef[16] + coef[32];
+ r2 = coef[16] - coef[32];
+
+ coef[0] = r0 + r1;
+ coef[32] = r0 - r1;
+ coef[16] = (r3 << 1) + r2;
+ coef[48] = r3 - (r2 << 1);
+
+ coef++;
+ j--;
+ }
+
+ coef -= 4;
+
+ /* quant */
+ level = encvid->level[ras2dec[blkidx]];
+ run = encvid->run[ras2dec[blkidx]];
+
+ Rq = video->QPy_mod_6;
+ Qq = video->QPy_div_6;
+ qp_const = encvid->qp_const;
+ q_bits = 15 + Qq;
+
+ zero_run = 0;
+ numcoeff = 0;
+ for (k = 0; k < 16; k++)
+ {
+ idx = ZZ_SCAN_BLOCK[k]; /* map back to raster scan order */
+ data = coef[idx];
+ quant = quant_coef[Rq][k];
+ if (data > 0)
+ {
+ lev = data * quant + qp_const;
+ }
+ else
+ {
+ lev = -data * quant + qp_const;
+ }
+ lev >>= q_bits;
+ if (lev)
+ {
+ *coef_cost += ((lev > 1) ? MAX_VALUE : COEFF_COST[DISABLE_THRESHOLDING][zero_run]);
+
+ /* dequant */
+ quant = dequant_coefres[Rq][k];
+ if (data > 0)
+ {
+ level[numcoeff] = lev;
+ coef[idx] = (lev * quant) << Qq;
+ }
+ else
+ {
+ level[numcoeff] = -lev;
+ coef[idx] = (-lev * quant) << Qq;
+ }
+ run[numcoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ coef[idx] = 0;
+ }
+ }
+
+ if (video->currMB->mb_intra) // only do inverse transform with intra block
+ {
+ if (numcoeff) /* then do inverse transform */
+ {
+ for (j = 4; j > 0; j--) /* horizontal */
+ {
+ r0 = coef[0] + coef[2];
+ r1 = coef[0] - coef[2];
+ r2 = (coef[1] >> 1) - coef[3];
+ r3 = coef[1] + (coef[3] >> 1);
+
+ coef[0] = r0 + r3;
+ coef[1] = r1 + r2;
+ coef[2] = r1 - r2;
+ coef[3] = r0 - r3;
+
+ coef += 16;
+ }
+
+ coef -= 64;
+ for (j = 4; j > 0; j--) /* vertical, has to be done after horizontal */
+ {
+ r0 = coef[0] + coef[32];
+ r1 = coef[0] - coef[32];
+ r2 = (coef[16] >> 1) - coef[48];
+ r3 = coef[16] + (coef[48] >> 1);
+ r0 += r3;
+ r3 = (r0 - (r3 << 1)); /* r0-r3 */
+ r1 += r2;
+ r2 = (r1 - (r2 << 1)); /* r1-r2 */
+ r0 += 32;
+ r1 += 32;
+ r2 += 32;
+ r3 += 32;
+
+ r0 = pred[0] + (r0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = *(pred += pred_pitch) + (r1 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = *(pred += pred_pitch) + (r2 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[pred_pitch] + (r3 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+
+ *cur = r0;
+ *(cur += pitch) = r1;
+ *(cur += pitch) = r2;
+ cur[pitch] = r3;
+ cur -= (pitch << 1);
+ cur++;
+ pred -= (pred_pitch << 1);
+ pred++;
+ coef++;
+ }
+ }
+ else // copy from pred to cur
+ {
+ *((uint32*)cur) = *((uint32*)pred);
+ *((uint32*)(cur += pitch)) = *((uint32*)(pred += pred_pitch));
+ *((uint32*)(cur += pitch)) = *((uint32*)(pred += pred_pitch));
+ *((uint32*)(cur += pitch)) = *((uint32*)(pred += pred_pitch));
+ }
+ }
+
+ return numcoeff;
+}
+
+
+void MBInterIdct(AVCCommonObj *video, uint8 *curL, AVCMacroblock *currMB, int picPitch)
+{
+ int16 *coef, *coef8 = video->block;
+ uint8 *cur; // the same as curL
+ int b8, b4;
+ int r0, r1, r2, r3, j, blkidx;
+
+ for (b8 = 0; b8 < 4; b8++)
+ {
+ cur = curL;
+ coef = coef8;
+
+ if (currMB->CBP&(1 << b8))
+ {
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ blkidx = blkIdx2blkXY[b8][b4];
+ /* do IDCT */
+ if (currMB->nz_coeff[blkidx])
+ {
+ for (j = 4; j > 0; j--) /* horizontal */
+ {
+ r0 = coef[0] + coef[2];
+ r1 = coef[0] - coef[2];
+ r2 = (coef[1] >> 1) - coef[3];
+ r3 = coef[1] + (coef[3] >> 1);
+
+ coef[0] = r0 + r3;
+ coef[1] = r1 + r2;
+ coef[2] = r1 - r2;
+ coef[3] = r0 - r3;
+
+ coef += 16;
+ }
+
+ coef -= 64;
+ for (j = 4; j > 0; j--) /* vertical, has to be done after horizontal */
+ {
+ r0 = coef[0] + coef[32];
+ r1 = coef[0] - coef[32];
+ r2 = (coef[16] >> 1) - coef[48];
+ r3 = coef[16] + (coef[48] >> 1);
+ r0 += r3;
+ r3 = (r0 - (r3 << 1)); /* r0-r3 */
+ r1 += r2;
+ r2 = (r1 - (r2 << 1)); /* r1-r2 */
+ r0 += 32;
+ r1 += 32;
+ r2 += 32;
+ r3 += 32;
+
+ r0 = cur[0] + (r0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ *cur = r0;
+ r1 = *(cur += picPitch) + (r1 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ *cur = r1;
+ r2 = *(cur += picPitch) + (r2 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ *cur = r2;
+ r3 = cur[picPitch] + (r3 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ cur[picPitch] = r3;
+
+ cur -= (picPitch << 1);
+ cur++;
+ coef++;
+ }
+ cur -= 4;
+ coef -= 4;
+ }
+ if (b4&1)
+ {
+ cur += ((picPitch << 2) - 4);
+ coef += 60;
+ }
+ else
+ {
+ cur += 4;
+ coef += 4;
+ }
+ }
+ }
+
+ if (b8&1)
+ {
+ curL += ((picPitch << 3) - 8);
+ coef8 += 120;
+ }
+ else
+ {
+ curL += 8;
+ coef8 += 8;
+ }
+ }
+
+ return ;
+}
+
+/* performa dct, quant, iquant, idct for the entire MB */
+void dct_luma_16x16(AVCEncObject *encvid, uint8 *curL, uint8 *orgL)
+{
+ AVCCommonObj *video = encvid->common;
+ int pitch = video->currPic->pitch;
+ int org_pitch = encvid->currInput->pitch;
+ AVCMacroblock *currMB = video->currMB;
+ int16 *coef = video->block;
+ uint8 *pred = encvid->pred_i16[currMB->i16Mode];
+ int blk_x, blk_y, j, k, idx, b8, b4;
+ int r0, r1, r2, r3, m0, m1, m2 , m3;
+ int data, lev;
+ int *level, *run, zero_run, ncoeff;
+ int Rq, Qq, quant, q_bits, qp_const;
+ int offset_cur[4], offset_pred[4], offset;
+
+ /* horizontal */
+ for (j = 16; j > 0; j--)
+ {
+ for (blk_x = 4; blk_x > 0; blk_x--)
+ {
+ /* calculate the residue first */
+ r0 = *orgL++ - *pred++;
+ r1 = *orgL++ - *pred++;
+ r2 = *orgL++ - *pred++;
+ r3 = *orgL++ - *pred++;
+
+ r0 += r3; //ptr[0] + ptr[3];
+ r3 = r0 - (r3 << 1); //ptr[0] - ptr[3];
+ r1 += r2; //ptr[1] + ptr[2];
+ r2 = r1 - (r2 << 1); //ptr[1] - ptr[2];
+
+ *coef++ = r0 + r1;
+ *coef++ = (r3 << 1) + r2;
+ *coef++ = r0 - r1;
+ *coef++ = r3 - (r2 << 1);
+ }
+ orgL += (org_pitch - 16);
+ }
+ pred -= 256;
+ coef -= 256;
+ /* vertical */
+ for (blk_y = 4; blk_y > 0; blk_y--)
+ {
+ for (j = 16; j > 0; j--)
+ {
+ r0 = coef[0] + coef[48];
+ r3 = coef[0] - coef[48];
+ r1 = coef[16] + coef[32];
+ r2 = coef[16] - coef[32];
+
+ coef[0] = r0 + r1;
+ coef[32] = r0 - r1;
+ coef[16] = (r3 << 1) + r2;
+ coef[48] = r3 - (r2 << 1);
+
+ coef++;
+ }
+ coef += 48;
+ }
+
+ /* then perform DC transform */
+ coef -= 256;
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[12];
+ r3 = coef[0] - coef[12];
+ r1 = coef[4] + coef[8];
+ r2 = coef[4] - coef[8];
+
+ coef[0] = r0 + r1;
+ coef[8] = r0 - r1;
+ coef[4] = r3 + r2;
+ coef[12] = r3 - r2;
+ coef += 64;
+ }
+ coef -= 256;
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[192];
+ r3 = coef[0] - coef[192];
+ r1 = coef[64] + coef[128];
+ r2 = coef[64] - coef[128];
+
+ coef[0] = (r0 + r1) >> 1;
+ coef[128] = (r0 - r1) >> 1;
+ coef[64] = (r3 + r2) >> 1;
+ coef[192] = (r3 - r2) >> 1;
+ coef += 4;
+ }
+
+ coef -= 16;
+ // then quantize DC
+ level = encvid->leveldc;
+ run = encvid->rundc;
+
+ Rq = video->QPy_mod_6;
+ Qq = video->QPy_div_6;
+ quant = quant_coef[Rq][0];
+ q_bits = 15 + Qq;
+ qp_const = encvid->qp_const;
+
+ zero_run = 0;
+ ncoeff = 0;
+ for (k = 0; k < 16; k++) /* in zigzag scan order */
+ {
+ idx = ZIGZAG2RASTERDC[k];
+ data = coef[idx];
+ if (data > 0) // quant
+ {
+ lev = data * quant + (qp_const << 1);
+ }
+ else
+ {
+ lev = -data * quant + (qp_const << 1);
+ }
+ lev >>= (q_bits + 1);
+ if (lev) // dequant
+ {
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ coef[idx] = lev;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ coef[idx] = -lev;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ coef[idx] = 0;
+ }
+ }
+
+ /* inverse transform DC */
+ encvid->numcoefdc = ncoeff;
+ if (ncoeff)
+ {
+ quant = dequant_coefres[Rq][0];
+
+ for (j = 0; j < 4; j++)
+ {
+ m0 = coef[0] + coef[4];
+ m1 = coef[0] - coef[4];
+ m2 = coef[8] + coef[12];
+ m3 = coef[8] - coef[12];
+
+
+ coef[0] = m0 + m2;
+ coef[4] = m0 - m2;
+ coef[8] = m1 - m3;
+ coef[12] = m1 + m3;
+ coef += 64;
+ }
+
+ coef -= 256;
+
+ if (Qq >= 2) /* this way should be faster than JM */
+ { /* they use (((m4*scale)<<(QPy/6))+2)>>2 for both cases. */
+ Qq -= 2;
+ for (j = 0; j < 4; j++)
+ {
+ m0 = coef[0] + coef[64];
+ m1 = coef[0] - coef[64];
+ m2 = coef[128] + coef[192];
+ m3 = coef[128] - coef[192];
+
+ coef[0] = ((m0 + m2) * quant) << Qq;
+ coef[64] = ((m0 - m2) * quant) << Qq;
+ coef[128] = ((m1 - m3) * quant) << Qq;
+ coef[192] = ((m1 + m3) * quant) << Qq;
+ coef += 4;
+ }
+ Qq += 2; /* restore the value */
+ }
+ else
+ {
+ Qq = 2 - Qq;
+ offset = 1 << (Qq - 1);
+
+ for (j = 0; j < 4; j++)
+ {
+ m0 = coef[0] + coef[64];
+ m1 = coef[0] - coef[64];
+ m2 = coef[128] + coef[192];
+ m3 = coef[128] - coef[192];
+
+ coef[0] = (((m0 + m2) * quant + offset) >> Qq);
+ coef[64] = (((m0 - m2) * quant + offset) >> Qq);
+ coef[128] = (((m1 - m3) * quant + offset) >> Qq);
+ coef[192] = (((m1 + m3) * quant + offset) >> Qq);
+ coef += 4;
+ }
+ Qq = 2 - Qq; /* restore the value */
+ }
+ coef -= 16; /* back to the origin */
+ }
+
+ /* now zigzag scan ac coefs, quant, iquant and itrans */
+ run = encvid->run[0];
+ level = encvid->level[0];
+
+ /* offset btw 4x4 block */
+ offset_cur[0] = 0;
+ offset_cur[1] = (pitch << 2) - 8;
+
+ /* offset btw 8x8 block */
+ offset_cur[2] = 8 - (pitch << 3);
+ offset_cur[3] = -8;
+
+ /* similarly for pred */
+ offset_pred[0] = 0;
+ offset_pred[1] = 56;
+ offset_pred[2] = -120;
+ offset_pred[3] = -8;
+
+ currMB->CBP = 0;
+
+ for (b8 = 0; b8 < 4; b8++)
+ {
+ for (b4 = 0; b4 < 4; b4++)
+ {
+
+ zero_run = 0;
+ ncoeff = 0;
+
+ for (k = 1; k < 16; k++)
+ {
+ idx = ZZ_SCAN_BLOCK[k]; /* map back to raster scan order */
+ data = coef[idx];
+ quant = quant_coef[Rq][k];
+ if (data > 0)
+ {
+ lev = data * quant + qp_const;
+ }
+ else
+ {
+ lev = -data * quant + qp_const;
+ }
+ lev >>= q_bits;
+ if (lev)
+ { /* dequant */
+ quant = dequant_coefres[Rq][k];
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ coef[idx] = (lev * quant) << Qq;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ coef[idx] = (-lev * quant) << Qq;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ coef[idx] = 0;
+ }
+ }
+
+ currMB->nz_coeff[blkIdx2blkXY[b8][b4]] = ncoeff; /* in raster scan !!! */
+ if (ncoeff)
+ {
+ currMB->CBP |= (1 << b8);
+
+ // do inverse transform here
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[2];
+ r1 = coef[0] - coef[2];
+ r2 = (coef[1] >> 1) - coef[3];
+ r3 = coef[1] + (coef[3] >> 1);
+
+ coef[0] = r0 + r3;
+ coef[1] = r1 + r2;
+ coef[2] = r1 - r2;
+ coef[3] = r0 - r3;
+
+ coef += 16;
+ }
+ coef -= 64;
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[32];
+ r1 = coef[0] - coef[32];
+ r2 = (coef[16] >> 1) - coef[48];
+ r3 = coef[16] + (coef[48] >> 1);
+
+ r0 += r3;
+ r3 = (r0 - (r3 << 1)); /* r0-r3 */
+ r1 += r2;
+ r2 = (r1 - (r2 << 1)); /* r1-r2 */
+ r0 += 32;
+ r1 += 32;
+ r2 += 32;
+ r3 += 32;
+ r0 = pred[0] + (r0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = pred[16] + (r1 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = pred[32] + (r2 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[48] + (r3 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ *curL = r0;
+ *(curL += pitch) = r1;
+ *(curL += pitch) = r2;
+ curL[pitch] = r3;
+ curL -= (pitch << 1);
+ curL++;
+ pred++;
+ coef++;
+ }
+ }
+ else // do DC-only inverse
+ {
+ m0 = coef[0] + 32;
+
+ for (j = 4; j > 0; j--)
+ {
+ r0 = pred[0] + (m0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = pred[16] + (m0 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = pred[32] + (m0 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[48] + (m0 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ *curL = r0;
+ *(curL += pitch) = r1;
+ *(curL += pitch) = r2;
+ curL[pitch] = r3;
+ curL -= (pitch << 1);
+ curL++;
+ pred++;
+ }
+ coef += 4;
+ }
+
+ run += 16; // follow coding order
+ level += 16;
+ curL += offset_cur[b4&1];
+ pred += offset_pred[b4&1];
+ coef += offset_pred[b4&1];
+ }
+
+ curL += offset_cur[2 + (b8&1)];
+ pred += offset_pred[2 + (b8&1)];
+ coef += offset_pred[2 + (b8&1)];
+ }
+
+ return ;
+}
+
+
+void dct_chroma(AVCEncObject *encvid, uint8 *curC, uint8 *orgC, int cr)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMacroblock *currMB = video->currMB;
+ int org_pitch = (encvid->currInput->pitch) >> 1;
+ int pitch = (video->currPic->pitch) >> 1;
+ int pred_pitch = 16;
+ int16 *coef = video->block + 256;
+ uint8 *pred = video->pred_block;
+ int j, blk_x, blk_y, k, idx, b4;
+ int r0, r1, r2, r3, m0;
+ int Qq, Rq, qp_const, q_bits, quant;
+ int *level, *run, zero_run, ncoeff;
+ int data, lev;
+ int offset_cur[2], offset_pred[2], offset_coef[2];
+ uint8 nz_temp[4];
+ int coeff_cost;
+
+ if (cr)
+ {
+ coef += 8;
+ pred += 8;
+ }
+
+ if (currMB->mb_intra == 0) // inter mode
+ {
+ pred = curC;
+ pred_pitch = pitch;
+ }
+
+ /* do 4x4 transform */
+ /* horizontal */
+ for (j = 8; j > 0; j--)
+ {
+ for (blk_x = 2; blk_x > 0; blk_x--)
+ {
+ /* calculate the residue first */
+ r0 = *orgC++ - *pred++;
+ r1 = *orgC++ - *pred++;
+ r2 = *orgC++ - *pred++;
+ r3 = *orgC++ - *pred++;
+
+ r0 += r3; //ptr[0] + ptr[3];
+ r3 = r0 - (r3 << 1); //ptr[0] - ptr[3];
+ r1 += r2; //ptr[1] + ptr[2];
+ r2 = r1 - (r2 << 1); //ptr[1] - ptr[2];
+
+ *coef++ = r0 + r1;
+ *coef++ = (r3 << 1) + r2;
+ *coef++ = r0 - r1;
+ *coef++ = r3 - (r2 << 1);
+
+ }
+ coef += 8; // coef pitch is 16
+ pred += (pred_pitch - 8); // pred_pitch is 16
+ orgC += (org_pitch - 8);
+ }
+ pred -= (pred_pitch << 3);
+ coef -= 128;
+ /* vertical */
+ for (blk_y = 2; blk_y > 0; blk_y--)
+ {
+ for (j = 8; j > 0; j--)
+ {
+ r0 = coef[0] + coef[48];
+ r3 = coef[0] - coef[48];
+ r1 = coef[16] + coef[32];
+ r2 = coef[16] - coef[32];
+
+ coef[0] = r0 + r1;
+ coef[32] = r0 - r1;
+ coef[16] = (r3 << 1) + r2;
+ coef[48] = r3 - (r2 << 1);
+
+ coef++;
+ }
+ coef += 56;
+ }
+ /* then perform DC transform */
+ coef -= 128;
+
+ /* 2x2 transform of DC components*/
+ r0 = coef[0];
+ r1 = coef[4];
+ r2 = coef[64];
+ r3 = coef[68];
+
+ coef[0] = r0 + r1 + r2 + r3;
+ coef[4] = r0 - r1 + r2 - r3;
+ coef[64] = r0 + r1 - r2 - r3;
+ coef[68] = r0 - r1 - r2 + r3;
+
+ Qq = video->QPc_div_6;
+ Rq = video->QPc_mod_6;
+ quant = quant_coef[Rq][0];
+ q_bits = 15 + Qq;
+ qp_const = encvid->qp_const_c;
+
+ zero_run = 0;
+ ncoeff = 0;
+ run = encvid->runcdc + (cr << 2);
+ level = encvid->levelcdc + (cr << 2);
+
+ /* in zigzag scan order */
+ for (k = 0; k < 4; k++)
+ {
+ idx = ((k >> 1) << 6) + ((k & 1) << 2);
+ data = coef[idx];
+ if (data > 0)
+ {
+ lev = data * quant + (qp_const << 1);
+ }
+ else
+ {
+ lev = -data * quant + (qp_const << 1);
+ }
+ lev >>= (q_bits + 1);
+ if (lev)
+ {
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ coef[idx] = lev;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ coef[idx] = -lev;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ coef[idx] = 0;
+ }
+ }
+
+ encvid->numcoefcdc[cr] = ncoeff;
+
+ if (ncoeff)
+ {
+ currMB->CBP |= (1 << 4); // DC present
+ // do inverse transform
+ quant = dequant_coefres[Rq][0];
+
+ r0 = coef[0] + coef[4];
+ r1 = coef[0] - coef[4];
+ r2 = coef[64] + coef[68];
+ r3 = coef[64] - coef[68];
+
+ r0 += r2;
+ r2 = r0 - (r2 << 1);
+ r1 += r3;
+ r3 = r1 - (r3 << 1);
+
+ if (Qq >= 1)
+ {
+ Qq -= 1;
+ coef[0] = (r0 * quant) << Qq;
+ coef[4] = (r1 * quant) << Qq;
+ coef[64] = (r2 * quant) << Qq;
+ coef[68] = (r3 * quant) << Qq;
+ Qq++;
+ }
+ else
+ {
+ coef[0] = (r0 * quant) >> 1;
+ coef[4] = (r1 * quant) >> 1;
+ coef[64] = (r2 * quant) >> 1;
+ coef[68] = (r3 * quant) >> 1;
+ }
+ }
+
+ /* now do AC zigzag scan, quant, iquant and itrans */
+ if (cr)
+ {
+ run = encvid->run[20];
+ level = encvid->level[20];
+ }
+ else
+ {
+ run = encvid->run[16];
+ level = encvid->level[16];
+ }
+
+ /* offset btw 4x4 block */
+ offset_cur[0] = 0;
+ offset_cur[1] = (pitch << 2) - 8;
+ offset_pred[0] = 0;
+ offset_pred[1] = (pred_pitch << 2) - 8;
+ offset_coef[0] = 0;
+ offset_coef[1] = 56;
+
+ coeff_cost = 0;
+
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ zero_run = 0;
+ ncoeff = 0;
+ for (k = 1; k < 16; k++) /* in zigzag scan order */
+ {
+ idx = ZZ_SCAN_BLOCK[k]; /* map back to raster scan order */
+ data = coef[idx];
+ quant = quant_coef[Rq][k];
+ if (data > 0)
+ {
+ lev = data * quant + qp_const;
+ }
+ else
+ {
+ lev = -data * quant + qp_const;
+ }
+ lev >>= q_bits;
+ if (lev)
+ {
+ /* for RD performance*/
+ if (lev > 1)
+ coeff_cost += MAX_VALUE; // set high cost, shall not be discarded
+ else
+ coeff_cost += COEFF_COST[DISABLE_THRESHOLDING][zero_run];
+
+ /* dequant */
+ quant = dequant_coefres[Rq][k];
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ coef[idx] = (lev * quant) << Qq;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ coef[idx] = (-lev * quant) << Qq;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ coef[idx] = 0;
+ }
+ }
+
+ nz_temp[b4] = ncoeff; // raster scan
+
+ // just advance the pointers for now, do IDCT later
+ coef += 4;
+ run += 16;
+ level += 16;
+ coef += offset_coef[b4&1];
+ }
+
+ /* rewind the pointers */
+ coef -= 128;
+
+ if (coeff_cost < _CHROMA_COEFF_COST_)
+ {
+ /* if it's not efficient to encode any blocks.
+ Just do DC only */
+ /* We can reset level and run also, but setting nz to zero should be enough. */
+ currMB->nz_coeff[16+(cr<<1)] = 0;
+ currMB->nz_coeff[17+(cr<<1)] = 0;
+ currMB->nz_coeff[20+(cr<<1)] = 0;
+ currMB->nz_coeff[21+(cr<<1)] = 0;
+
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ // do DC-only inverse
+ m0 = coef[0] + 32;
+
+ for (j = 4; j > 0; j--)
+ {
+ r0 = pred[0] + (m0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = *(pred += pred_pitch) + (m0 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = pred[pred_pitch] + (m0 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[pred_pitch<<1] + (m0 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ *curC = r0;
+ *(curC += pitch) = r1;
+ *(curC += pitch) = r2;
+ curC[pitch] = r3;
+ curC -= (pitch << 1);
+ curC++;
+ pred += (1 - pred_pitch);
+ }
+ coef += 4;
+ curC += offset_cur[b4&1];
+ pred += offset_pred[b4&1];
+ coef += offset_coef[b4&1];
+ }
+ }
+ else // not dropping anything, continue with the IDCT
+ {
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ ncoeff = nz_temp[b4] ; // in raster scan
+ currMB->nz_coeff[16+(b4&1)+(cr<<1)+((b4>>1)<<2)] = ncoeff; // in raster scan
+
+ if (ncoeff) // do a check on the nonzero-coeff
+ {
+ currMB->CBP |= (2 << 4);
+
+ // do inverse transform here
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[2];
+ r1 = coef[0] - coef[2];
+ r2 = (coef[1] >> 1) - coef[3];
+ r3 = coef[1] + (coef[3] >> 1);
+
+ coef[0] = r0 + r3;
+ coef[1] = r1 + r2;
+ coef[2] = r1 - r2;
+ coef[3] = r0 - r3;
+
+ coef += 16;
+ }
+ coef -= 64;
+ for (j = 4; j > 0; j--)
+ {
+ r0 = coef[0] + coef[32];
+ r1 = coef[0] - coef[32];
+ r2 = (coef[16] >> 1) - coef[48];
+ r3 = coef[16] + (coef[48] >> 1);
+
+ r0 += r3;
+ r3 = (r0 - (r3 << 1)); /* r0-r3 */
+ r1 += r2;
+ r2 = (r1 - (r2 << 1)); /* r1-r2 */
+ r0 += 32;
+ r1 += 32;
+ r2 += 32;
+ r3 += 32;
+ r0 = pred[0] + (r0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = *(pred += pred_pitch) + (r1 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = pred[pred_pitch] + (r2 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[pred_pitch<<1] + (r3 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ *curC = r0;
+ *(curC += pitch) = r1;
+ *(curC += pitch) = r2;
+ curC[pitch] = r3;
+ curC -= (pitch << 1);
+ curC++;
+ pred += (1 - pred_pitch);
+ coef++;
+ }
+ }
+ else
+ {
+ // do DC-only inverse
+ m0 = coef[0] + 32;
+
+ for (j = 4; j > 0; j--)
+ {
+ r0 = pred[0] + (m0 >> 6);
+ if ((uint)r0 > 0xFF) r0 = 0xFF & (~(r0 >> 31)); /* clip */
+ r1 = *(pred += pred_pitch) + (m0 >> 6);
+ if ((uint)r1 > 0xFF) r1 = 0xFF & (~(r1 >> 31)); /* clip */
+ r2 = pred[pred_pitch] + (m0 >> 6);
+ if ((uint)r2 > 0xFF) r2 = 0xFF & (~(r2 >> 31)); /* clip */
+ r3 = pred[pred_pitch<<1] + (m0 >> 6);
+ if ((uint)r3 > 0xFF) r3 = 0xFF & (~(r3 >> 31)); /* clip */
+ *curC = r0;
+ *(curC += pitch) = r1;
+ *(curC += pitch) = r2;
+ curC[pitch] = r3;
+ curC -= (pitch << 1);
+ curC++;
+ pred += (1 - pred_pitch);
+ }
+ coef += 4;
+ }
+ curC += offset_cur[b4&1];
+ pred += offset_pred[b4&1];
+ coef += offset_coef[b4&1];
+ }
+ }
+
+ return ;
+}
+
+
+/* only DC transform */
+int TransQuantIntra16DC(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ int16 *block = video->block;
+ int *level = encvid->leveldc;
+ int *run = encvid->rundc;
+ int16 *ptr = block;
+ int r0, r1, r2, r3, j;
+ int Qq = video->QPy_div_6;
+ int Rq = video->QPy_mod_6;
+ int q_bits, qp_const, quant;
+ int data, lev, zero_run;
+ int k, ncoeff, idx;
+
+ /* DC transform */
+ /* horizontal */
+ j = 4;
+ while (j)
+ {
+ r0 = ptr[0] + ptr[12];
+ r3 = ptr[0] - ptr[12];
+ r1 = ptr[4] + ptr[8];
+ r2 = ptr[4] - ptr[8];
+
+ ptr[0] = r0 + r1;
+ ptr[8] = r0 - r1;
+ ptr[4] = r3 + r2;
+ ptr[12] = r3 - r2;
+ ptr += 64;
+ j--;
+ }
+ /* vertical */
+ ptr = block;
+ j = 4;
+ while (j)
+ {
+ r0 = ptr[0] + ptr[192];
+ r3 = ptr[0] - ptr[192];
+ r1 = ptr[64] + ptr[128];
+ r2 = ptr[64] - ptr[128];
+
+ ptr[0] = (r0 + r1) >> 1;
+ ptr[128] = (r0 - r1) >> 1;
+ ptr[64] = (r3 + r2) >> 1;
+ ptr[192] = (r3 - r2) >> 1;
+ ptr += 4;
+ j--;
+ }
+
+ quant = quant_coef[Rq][0];
+ q_bits = 15 + Qq;
+ qp_const = (1 << q_bits) / 3; // intra
+
+ zero_run = 0;
+ ncoeff = 0;
+
+ for (k = 0; k < 16; k++) /* in zigzag scan order */
+ {
+ idx = ZIGZAG2RASTERDC[k];
+ data = block[idx];
+ if (data > 0)
+ {
+ lev = data * quant + (qp_const << 1);
+ }
+ else
+ {
+ lev = -data * quant + (qp_const << 1);
+ }
+ lev >>= (q_bits + 1);
+ if (lev)
+ {
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ block[idx] = lev;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ block[idx] = -lev;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ block[idx] = 0;
+ }
+ }
+ return ncoeff;
+}
+
+int TransQuantChromaDC(AVCEncObject *encvid, int16 *block, int slice_type, int cr)
+{
+ AVCCommonObj *video = encvid->common;
+ int *level, *run;
+ int r0, r1, r2, r3;
+ int Qq, Rq, q_bits, qp_const, quant;
+ int data, lev, zero_run;
+ int k, ncoeff, idx;
+
+ level = encvid->levelcdc + (cr << 2); /* cb or cr */
+ run = encvid->runcdc + (cr << 2);
+
+ /* 2x2 transform of DC components*/
+ r0 = block[0];
+ r1 = block[4];
+ r2 = block[64];
+ r3 = block[68];
+
+ block[0] = r0 + r1 + r2 + r3;
+ block[4] = r0 - r1 + r2 - r3;
+ block[64] = r0 + r1 - r2 - r3;
+ block[68] = r0 - r1 - r2 + r3;
+
+ Qq = video->QPc_div_6;
+ Rq = video->QPc_mod_6;
+ quant = quant_coef[Rq][0];
+ q_bits = 15 + Qq;
+ if (slice_type == AVC_I_SLICE)
+ {
+ qp_const = (1 << q_bits) / 3;
+ }
+ else
+ {
+ qp_const = (1 << q_bits) / 6;
+ }
+
+ zero_run = 0;
+ ncoeff = 0;
+
+ for (k = 0; k < 4; k++) /* in zigzag scan order */
+ {
+ idx = ((k >> 1) << 6) + ((k & 1) << 2);
+ data = block[idx];
+ if (data > 0)
+ {
+ lev = data * quant + (qp_const << 1);
+ }
+ else
+ {
+ lev = -data * quant + (qp_const << 1);
+ }
+ lev >>= (q_bits + 1);
+ if (lev)
+ {
+ if (data > 0)
+ {
+ level[ncoeff] = lev;
+ block[idx] = lev;
+ }
+ else
+ {
+ level[ncoeff] = -lev;
+ block[idx] = -lev;
+ }
+ run[ncoeff++] = zero_run;
+ zero_run = 0;
+ }
+ else
+ {
+ zero_run++;
+ block[idx] = 0;
+ }
+ }
+ return ncoeff;
+}
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp b/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp
new file mode 100644
index 0000000..38a2a15
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/findhalfpel.cpp
@@ -0,0 +1,622 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+/* 3/29/01 fast half-pel search based on neighboring guess */
+/* value ranging from 0 to 4, high complexity (more accurate) to
+ low complexity (less accurate) */
+#define HP_DISTANCE_TH 5 // 2 /* half-pel distance threshold */
+
+#define PREF_16_VEC 129 /* 1MV bias versus 4MVs*/
+
+const static int distance_tab[9][9] = /* [hp_guess][k] */
+{
+ {0, 1, 1, 1, 1, 1, 1, 1, 1},
+ {1, 0, 1, 2, 3, 4, 3, 2, 1},
+ {1, 0, 0, 0, 1, 2, 3, 2, 1},
+ {1, 2, 1, 0, 1, 2, 3, 4, 3},
+ {1, 2, 1, 0, 0, 0, 1, 2, 3},
+ {1, 4, 3, 2, 1, 0, 1, 2, 3},
+ {1, 2, 3, 2, 1, 0, 0, 0, 1},
+ {1, 2, 3, 4, 3, 2, 1, 0, 1},
+ {1, 0, 1, 2, 3, 2, 1, 0, 0}
+};
+
+#define CLIP_RESULT(x) if((uint)x > 0xFF){ \
+ x = 0xFF & (~(x>>31));}
+
+#define CLIP_UPPER16(x) if((uint)x >= 0x20000000){ \
+ x = 0xFF0000 & (~(x>>31));} \
+ else { \
+ x = (x>>5)&0xFF0000; \
+ }
+
+/*=====================================================================
+ Function: AVCFindHalfPelMB
+ Date: 10/31/2007
+ Purpose: Find half pel resolution MV surrounding the full-pel MV
+=====================================================================*/
+
+int AVCFindHalfPelMB(AVCEncObject *encvid, uint8 *cur, AVCMV *mot, uint8 *ncand,
+ int xpos, int ypos, int hp_guess, int cmvx, int cmvy)
+{
+ AVCPictureData *currPic = encvid->common->currPic;
+ int lx = currPic->pitch;
+ int d, dmin, satd_min;
+ uint8* cand;
+ int lambda_motion = encvid->lambda_motion;
+ uint8 *mvbits = encvid->mvbits;
+ int mvcost;
+ /* list of candidate to go through for half-pel search*/
+ uint8 *subpel_pred = (uint8*) encvid->subpel_pred; // all 16 sub-pel positions
+ uint8 **hpel_cand = (uint8**) encvid->hpel_cand; /* half-pel position */
+
+ int xh[9] = {0, 0, 2, 2, 2, 0, -2, -2, -2};
+ int yh[9] = {0, -2, -2, 0, 2, 2, 2, 0, -2};
+ int xq[8] = {0, 1, 1, 1, 0, -1, -1, -1};
+ int yq[8] = { -1, -1, 0, 1, 1, 1, 0, -1};
+ int h, hmin, q, qmin;
+
+ OSCL_UNUSED_ARG(xpos);
+ OSCL_UNUSED_ARG(ypos);
+ OSCL_UNUSED_ARG(hp_guess);
+
+ GenerateHalfPelPred(subpel_pred, ncand, lx);
+
+ cur = encvid->currYMB; // pre-load current original MB
+
+ cand = hpel_cand[0];
+
+ // find cost for the current full-pel position
+ dmin = SATD_MB(cand, cur, 65535); // get Hadamaard transform SAD
+ mvcost = MV_COST_S(lambda_motion, mot->x, mot->y, cmvx, cmvy);
+ satd_min = dmin;
+ dmin += mvcost;
+ hmin = 0;
+
+ /* find half-pel */
+ for (h = 1; h < 9; h++)
+ {
+ d = SATD_MB(hpel_cand[h], cur, dmin);
+ mvcost = MV_COST_S(lambda_motion, mot->x + xh[h], mot->y + yh[h], cmvx, cmvy);
+ d += mvcost;
+
+ if (d < dmin)
+ {
+ dmin = d;
+ hmin = h;
+ satd_min = d - mvcost;
+ }
+ }
+
+ mot->sad = dmin;
+ mot->x += xh[hmin];
+ mot->y += yh[hmin];
+ encvid->best_hpel_pos = hmin;
+
+ /*** search for quarter-pel ****/
+ GenerateQuartPelPred(encvid->bilin_base[hmin], &(encvid->qpel_cand[0][0]), hmin);
+
+ encvid->best_qpel_pos = qmin = -1;
+
+ for (q = 0; q < 8; q++)
+ {
+ d = SATD_MB(encvid->qpel_cand[q], cur, dmin);
+ mvcost = MV_COST_S(lambda_motion, mot->x + xq[q], mot->y + yq[q], cmvx, cmvy);
+ d += mvcost;
+ if (d < dmin)
+ {
+ dmin = d;
+ qmin = q;
+ satd_min = d - mvcost;
+ }
+ }
+
+ if (qmin != -1)
+ {
+ mot->sad = dmin;
+ mot->x += xq[qmin];
+ mot->y += yq[qmin];
+ encvid->best_qpel_pos = qmin;
+ }
+
+ return satd_min;
+}
+
+
+
+/** This function generates sub-pel prediction around the full-pel candidate.
+Each sub-pel position array is 20 pixel wide (for word-alignment) and 17 pixel tall. */
+/** The sub-pel position is labeled in spiral manner from the center. */
+
+void GenerateHalfPelPred(uint8* subpel_pred, uint8 *ncand, int lx)
+{
+ /* let's do straightforward way first */
+ uint8 *ref;
+ uint8 *dst;
+ uint8 tmp8;
+ int32 tmp32;
+ int16 tmp_horz[18*22], *dst_16, *src_16;
+ register int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; // temp register
+ int msk;
+ int i, j;
+
+ /* first copy full-pel to the first array */
+ /* to be optimized later based on byte-offset load */
+ ref = ncand - 3 - lx - (lx << 1); /* move back (-3,-3) */
+ dst = subpel_pred;
+
+ dst -= 4; /* offset */
+ for (j = 0; j < 22; j++) /* 24x22 */
+ {
+ i = 6;
+ while (i > 0)
+ {
+ tmp32 = *ref++;
+ tmp8 = *ref++;
+ tmp32 |= (tmp8 << 8);
+ tmp8 = *ref++;
+ tmp32 |= (tmp8 << 16);
+ tmp8 = *ref++;
+ tmp32 |= (tmp8 << 24);
+ *((uint32*)(dst += 4)) = tmp32;
+ i--;
+ }
+ ref += (lx - 24);
+ }
+
+ /* from the first array, we do horizontal interp */
+ ref = subpel_pred + 2;
+ dst_16 = tmp_horz; /* 17 x 22 */
+
+ for (j = 4; j > 0; j--)
+ {
+ for (i = 16; i > 0; i -= 4)
+ {
+ a = ref[-2];
+ b = ref[-1];
+ c = ref[0];
+ d = ref[1];
+ e = ref[2];
+ f = ref[3];
+ *dst_16++ = a + f - 5 * (b + e) + 20 * (c + d);
+ a = ref[4];
+ *dst_16++ = b + a - 5 * (c + f) + 20 * (d + e);
+ b = ref[5];
+ *dst_16++ = c + b - 5 * (d + a) + 20 * (e + f);
+ c = ref[6];
+ *dst_16++ = d + c - 5 * (e + b) + 20 * (f + a);
+
+ ref += 4;
+ }
+ /* do the 17th column here */
+ d = ref[3];
+ *dst_16 = e + d - 5 * (f + c) + 20 * (a + b);
+ dst_16 += 2; /* stride for tmp_horz is 18 */
+ ref += 8; /* stride for ref is 24 */
+ if (j == 3) // move 18 lines down
+ {
+ dst_16 += 324;//18*18;
+ ref += 432;//18*24;
+ }
+ }
+
+ ref -= 480;//20*24;
+ dst_16 -= 360;//20*18;
+ dst = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE; /* go to the 14th array 17x18*/
+
+ for (j = 18; j > 0; j--)
+ {
+ for (i = 16; i > 0; i -= 4)
+ {
+ a = ref[-2];
+ b = ref[-1];
+ c = ref[0];
+ d = ref[1];
+ e = ref[2];
+ f = ref[3];
+ tmp32 = a + f - 5 * (b + e) + 20 * (c + d);
+ *dst_16++ = tmp32;
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *dst++ = tmp32;
+
+ a = ref[4];
+ tmp32 = b + a - 5 * (c + f) + 20 * (d + e);
+ *dst_16++ = tmp32;
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *dst++ = tmp32;
+
+ b = ref[5];
+ tmp32 = c + b - 5 * (d + a) + 20 * (e + f);
+ *dst_16++ = tmp32;
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *dst++ = tmp32;
+
+ c = ref[6];
+ tmp32 = d + c - 5 * (e + b) + 20 * (f + a);
+ *dst_16++ = tmp32;
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *dst++ = tmp32;
+
+ ref += 4;
+ }
+ /* do the 17th column here */
+ d = ref[3];
+ tmp32 = e + d - 5 * (f + c) + 20 * (a + b);
+ *dst_16 = tmp32;
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *dst = tmp32;
+
+ dst += 8; /* stride for dst is 24 */
+ dst_16 += 2; /* stride for tmp_horz is 18 */
+ ref += 8; /* stride for ref is 24 */
+ }
+
+
+ /* Do middle point filtering*/
+ src_16 = tmp_horz; /* 17 x 22 */
+ dst = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE; /* 12th array 17x17*/
+ dst -= 24; // offset
+ for (i = 0; i < 17; i++)
+ {
+ for (j = 16; j > 0; j -= 4)
+ {
+ a = *src_16;
+ b = *(src_16 += 18);
+ c = *(src_16 += 18);
+ d = *(src_16 += 18);
+ e = *(src_16 += 18);
+ f = *(src_16 += 18);
+
+ tmp32 = a + f - 5 * (b + e) + 20 * (c + d);
+ tmp32 = (tmp32 + 512) >> 10;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32;
+
+ a = *(src_16 += 18);
+ tmp32 = b + a - 5 * (c + f) + 20 * (d + e);
+ tmp32 = (tmp32 + 512) >> 10;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32;
+
+ b = *(src_16 += 18);
+ tmp32 = c + b - 5 * (d + a) + 20 * (e + f);
+ tmp32 = (tmp32 + 512) >> 10;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32;
+
+ c = *(src_16 += 18);
+ tmp32 = d + c - 5 * (e + b) + 20 * (f + a);
+ tmp32 = (tmp32 + 512) >> 10;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32;
+
+ src_16 -= (18 << 2);
+ }
+
+ d = src_16[90]; // 18*5
+ tmp32 = e + d - 5 * (f + c) + 20 * (a + b);
+ tmp32 = (tmp32 + 512) >> 10;
+ CLIP_RESULT(tmp32)
+ dst[24] = tmp32;
+
+ src_16 -= ((18 << 4) - 1);
+ dst -= ((24 << 4) - 1);
+ }
+
+ /* do vertical interpolation */
+ ref = subpel_pred + 2;
+ dst = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE; /* 10th array 18x17 */
+ dst -= 24; // offset
+
+ for (i = 2; i > 0; i--)
+ {
+ for (j = 16; j > 0; j -= 4)
+ {
+ a = *ref;
+ b = *(ref += 24);
+ c = *(ref += 24);
+ d = *(ref += 24);
+ e = *(ref += 24);
+ f = *(ref += 24);
+
+ tmp32 = a + f - 5 * (b + e) + 20 * (c + d);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ a = *(ref += 24);
+ tmp32 = b + a - 5 * (c + f) + 20 * (d + e);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ b = *(ref += 24);
+ tmp32 = c + b - 5 * (d + a) + 20 * (e + f);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ c = *(ref += 24);
+ tmp32 = d + c - 5 * (e + b) + 20 * (f + a);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ ref -= (24 << 2);
+ }
+
+ d = ref[120]; // 24*5
+ tmp32 = e + d - 5 * (f + c) + 20 * (a + b);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ dst[24] = tmp32; // 10th
+
+ dst -= ((24 << 4) - 1);
+ ref -= ((24 << 4) - 1);
+ }
+
+ // note that using SIMD here doesn't help much, the cycle almost stays the same
+ // one can just use the above code and change the for(i=2 to for(i=18
+ for (i = 16; i > 0; i -= 4)
+ {
+ msk = 0;
+ for (j = 17; j > 0; j--)
+ {
+ a = *((uint32*)ref); /* load 4 bytes */
+ b = (a >> 8) & 0xFF00FF; /* second and fourth byte */
+ a &= 0xFF00FF;
+
+ c = *((uint32*)(ref + 120));
+ d = (c >> 8) & 0xFF00FF;
+ c &= 0xFF00FF;
+
+ a += c;
+ b += d;
+
+ e = *((uint32*)(ref + 72)); /* e, f */
+ f = (e >> 8) & 0xFF00FF;
+ e &= 0xFF00FF;
+
+ c = *((uint32*)(ref + 48)); /* c, d */
+ d = (c >> 8) & 0xFF00FF;
+ c &= 0xFF00FF;
+
+ c += e;
+ d += f;
+
+ a += 20 * c;
+ b += 20 * d;
+ a += 0x100010;
+ b += 0x100010;
+
+ e = *((uint32*)(ref += 24)); /* e, f */
+ f = (e >> 8) & 0xFF00FF;
+ e &= 0xFF00FF;
+
+ c = *((uint32*)(ref + 72)); /* c, d */
+ d = (c >> 8) & 0xFF00FF;
+ c &= 0xFF00FF;
+
+ c += e;
+ d += f;
+
+ a -= 5 * c;
+ b -= 5 * d;
+
+ c = a << 16;
+ d = b << 16;
+ CLIP_UPPER16(a)
+ CLIP_UPPER16(c)
+ CLIP_UPPER16(b)
+ CLIP_UPPER16(d)
+
+ a |= (c >> 16);
+ b |= (d >> 16);
+ // a>>=5;
+ // b>>=5;
+ /* clip */
+ // msk |= b; msk|=a;
+ // a &= 0xFF00FF;
+ // b &= 0xFF00FF;
+ a |= (b << 8); /* pack it back */
+
+ *((uint16*)(dst += 24)) = a & 0xFFFF; //dst is not word-aligned.
+ *((uint16*)(dst + 2)) = a >> 16;
+
+ }
+ dst -= 404; // 24*17-4
+ ref -= 404;
+ /* if(msk & 0xFF00FF00) // need clipping
+ {
+ VertInterpWClip(dst,ref); // re-do 4 column with clip
+ }*/
+ }
+
+ return ;
+}
+
+void VertInterpWClip(uint8 *dst, uint8 *ref)
+{
+ int i, j;
+ int a, b, c, d, e, f;
+ int32 tmp32;
+
+ dst -= 4;
+ ref -= 4;
+
+ for (i = 4; i > 0; i--)
+ {
+ for (j = 16; j > 0; j -= 4)
+ {
+ a = *ref;
+ b = *(ref += 24);
+ c = *(ref += 24);
+ d = *(ref += 24);
+ e = *(ref += 24);
+ f = *(ref += 24);
+
+ tmp32 = a + f - 5 * (b + e) + 20 * (c + d);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ a = *(ref += 24);
+ tmp32 = b + a - 5 * (c + f) + 20 * (d + e);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ b = *(ref += 24);
+ tmp32 = c + b - 5 * (d + a) + 20 * (e + f);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ c = *(ref += 24);
+ tmp32 = d + c - 5 * (e + b) + 20 * (f + a);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ *(dst += 24) = tmp32; // 10th
+
+ ref -= (24 << 2);
+ }
+
+ d = ref[120]; // 24*5
+ tmp32 = e + d - 5 * (f + c) + 20 * (a + b);
+ tmp32 = (tmp32 + 16) >> 5;
+ CLIP_RESULT(tmp32)
+ dst[24] = tmp32; // 10th
+
+ dst -= ((24 << 4) - 1);
+ ref -= ((24 << 4) - 1);
+ }
+
+ return ;
+}
+
+
+void GenerateQuartPelPred(uint8 **bilin_base, uint8 *qpel_cand, int hpel_pos)
+{
+ // for even value of hpel_pos, start with pattern 1, otherwise, start with pattern 2
+ int i, j;
+
+ uint8 *c1 = qpel_cand;
+ uint8 *tl = bilin_base[0];
+ uint8 *tr = bilin_base[1];
+ uint8 *bl = bilin_base[2];
+ uint8 *br = bilin_base[3];
+ int a, b, c, d;
+ int offset = 1 - (384 * 7);
+
+ if (!(hpel_pos&1)) // diamond pattern
+ {
+ j = 16;
+ while (j--)
+ {
+ i = 16;
+ while (i--)
+ {
+ d = tr[24];
+ a = *tr++;
+ b = bl[1];
+ c = *br++;
+
+ *c1 = (c + a + 1) >> 1;
+ *(c1 += 384) = (b + a + 1) >> 1; /* c2 */
+ *(c1 += 384) = (b + c + 1) >> 1; /* c3 */
+ *(c1 += 384) = (b + d + 1) >> 1; /* c4 */
+
+ b = *bl++;
+
+ *(c1 += 384) = (c + d + 1) >> 1; /* c5 */
+ *(c1 += 384) = (b + d + 1) >> 1; /* c6 */
+ *(c1 += 384) = (b + c + 1) >> 1; /* c7 */
+ *(c1 += 384) = (b + a + 1) >> 1; /* c8 */
+
+ c1 += offset;
+ }
+ // advance to the next line, pitch is 24
+ tl += 8;
+ tr += 8;
+ bl += 8;
+ br += 8;
+ c1 += 8;
+ }
+ }
+ else // star pattern
+ {
+ j = 16;
+ while (j--)
+ {
+ i = 16;
+ while (i--)
+ {
+ a = *br++;
+ b = *tr++;
+ c = tl[1];
+ *c1 = (a + b + 1) >> 1;
+ b = bl[1];
+ *(c1 += 384) = (a + c + 1) >> 1; /* c2 */
+ c = tl[25];
+ *(c1 += 384) = (a + b + 1) >> 1; /* c3 */
+ b = tr[23];
+ *(c1 += 384) = (a + c + 1) >> 1; /* c4 */
+ c = tl[24];
+ *(c1 += 384) = (a + b + 1) >> 1; /* c5 */
+ b = *bl++;
+ *(c1 += 384) = (a + c + 1) >> 1; /* c6 */
+ c = *tl++;
+ *(c1 += 384) = (a + b + 1) >> 1; /* c7 */
+ *(c1 += 384) = (a + c + 1) >> 1; /* c8 */
+
+ c1 += offset;
+ }
+ // advance to the next line, pitch is 24
+ tl += 8;
+ tr += 8;
+ bl += 8;
+ br += 8;
+ c1 += 8;
+ }
+ }
+
+ return ;
+}
+
+
+/* assuming cand always has a pitch of 24 */
+int SATD_MB(uint8 *cand, uint8 *cur, int dmin)
+{
+ int cost;
+
+
+ dmin = (dmin << 16) | 24;
+ cost = AVCSAD_Macroblock_C(cand, cur, dmin, NULL);
+
+ return cost;
+}
+
+
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/header.cpp b/media/libstagefright/codecs/avc/enc/src/header.cpp
new file mode 100644
index 0000000..9acff9e
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/header.cpp
@@ -0,0 +1,917 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+#include "avcenc_api.h"
+
+/** see subclause 7.4.2.1 */
+/* no need for checking the valid range , already done in SetEncodeParam(),
+if we have to send another SPS, the ranges should be verified first before
+users call PVAVCEncodeSPS() */
+AVCEnc_Status EncodeSPS(AVCEncObject *encvid, AVCEncBitstream *stream)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCSeqParamSet *seqParam = video->currSeqParams;
+ AVCVUIParams *vui = &(seqParam->vui_parameters);
+ int i;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+
+ //DEBUG_LOG(userData,AVC_LOGTYPE_INFO,"EncodeSPS",-1,-1);
+
+ status = BitstreamWriteBits(stream, 8, seqParam->profile_idc);
+ status = BitstreamWrite1Bit(stream, seqParam->constrained_set0_flag);
+ status = BitstreamWrite1Bit(stream, seqParam->constrained_set1_flag);
+ status = BitstreamWrite1Bit(stream, seqParam->constrained_set2_flag);
+ status = BitstreamWrite1Bit(stream, seqParam->constrained_set3_flag);
+ status = BitstreamWriteBits(stream, 4, 0); /* forbidden zero bits */
+ if (status != AVCENC_SUCCESS) /* we can check after each write also */
+ {
+ return status;
+ }
+
+ status = BitstreamWriteBits(stream, 8, seqParam->level_idc);
+ status = ue_v(stream, seqParam->seq_parameter_set_id);
+ status = ue_v(stream, seqParam->log2_max_frame_num_minus4);
+ status = ue_v(stream, seqParam->pic_order_cnt_type);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ if (seqParam->pic_order_cnt_type == 0)
+ {
+ status = ue_v(stream, seqParam->log2_max_pic_order_cnt_lsb_minus4);
+ }
+ else if (seqParam->pic_order_cnt_type == 1)
+ {
+ status = BitstreamWrite1Bit(stream, seqParam->delta_pic_order_always_zero_flag);
+ status = se_v(stream, seqParam->offset_for_non_ref_pic); /* upto 32 bits */
+ status = se_v(stream, seqParam->offset_for_top_to_bottom_field); /* upto 32 bits */
+ status = ue_v(stream, seqParam->num_ref_frames_in_pic_order_cnt_cycle);
+
+ for (i = 0; i < (int)(seqParam->num_ref_frames_in_pic_order_cnt_cycle); i++)
+ {
+ status = se_v(stream, seqParam->offset_for_ref_frame[i]); /* upto 32 bits */
+ }
+ }
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = ue_v(stream, seqParam->num_ref_frames);
+ status = BitstreamWrite1Bit(stream, seqParam->gaps_in_frame_num_value_allowed_flag);
+ status = ue_v(stream, seqParam->pic_width_in_mbs_minus1);
+ status = ue_v(stream, seqParam->pic_height_in_map_units_minus1);
+ status = BitstreamWrite1Bit(stream, seqParam->frame_mbs_only_flag);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ /* if frame_mbs_only_flag is 0, then write, mb_adaptive_frame_field_frame here */
+
+ status = BitstreamWrite1Bit(stream, seqParam->direct_8x8_inference_flag);
+ status = BitstreamWrite1Bit(stream, seqParam->frame_cropping_flag);
+ if (seqParam->frame_cropping_flag)
+ {
+ status = ue_v(stream, seqParam->frame_crop_left_offset);
+ status = ue_v(stream, seqParam->frame_crop_right_offset);
+ status = ue_v(stream, seqParam->frame_crop_top_offset);
+ status = ue_v(stream, seqParam->frame_crop_bottom_offset);
+ }
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = BitstreamWrite1Bit(stream, seqParam->vui_parameters_present_flag);
+ if (seqParam->vui_parameters_present_flag)
+ {
+ /* not supported */
+ //return AVCENC_SPS_FAIL;
+ EncodeVUI(stream, vui);
+ }
+
+ return status;
+}
+
+
+void EncodeVUI(AVCEncBitstream* stream, AVCVUIParams* vui)
+{
+ int temp;
+
+ temp = vui->aspect_ratio_info_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWriteBits(stream, 8, vui->aspect_ratio_idc);
+ if (vui->aspect_ratio_idc == 255)
+ {
+ BitstreamWriteBits(stream, 16, vui->sar_width);
+ BitstreamWriteBits(stream, 16, vui->sar_height);
+ }
+ }
+ temp = vui->overscan_info_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWrite1Bit(stream, vui->overscan_appropriate_flag);
+ }
+ temp = vui->video_signal_type_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWriteBits(stream, 3, vui->video_format);
+ BitstreamWrite1Bit(stream, vui->video_full_range_flag);
+ temp = vui->colour_description_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWriteBits(stream, 8, vui->colour_primaries);
+ BitstreamWriteBits(stream, 8, vui->transfer_characteristics);
+ BitstreamWriteBits(stream, 8, vui->matrix_coefficients);
+ }
+ }
+ temp = vui->chroma_location_info_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ ue_v(stream, vui->chroma_sample_loc_type_top_field);
+ ue_v(stream, vui->chroma_sample_loc_type_bottom_field);
+ }
+
+ temp = vui->timing_info_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWriteBits(stream, 32, vui->num_units_in_tick);
+ BitstreamWriteBits(stream, 32, vui->time_scale);
+ BitstreamWrite1Bit(stream, vui->fixed_frame_rate_flag);
+ }
+
+ temp = vui->nal_hrd_parameters_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ EncodeHRD(stream, &(vui->nal_hrd_parameters));
+ }
+ temp = vui->vcl_hrd_parameters_present_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ EncodeHRD(stream, &(vui->vcl_hrd_parameters));
+ }
+ if (vui->nal_hrd_parameters_present_flag || vui->vcl_hrd_parameters_present_flag)
+ {
+ BitstreamWrite1Bit(stream, vui->low_delay_hrd_flag);
+ }
+ BitstreamWrite1Bit(stream, vui->pic_struct_present_flag);
+ temp = vui->bitstream_restriction_flag;
+ BitstreamWrite1Bit(stream, temp);
+ if (temp)
+ {
+ BitstreamWrite1Bit(stream, vui->motion_vectors_over_pic_boundaries_flag);
+ ue_v(stream, vui->max_bytes_per_pic_denom);
+ ue_v(stream, vui->max_bits_per_mb_denom);
+ ue_v(stream, vui->log2_max_mv_length_horizontal);
+ ue_v(stream, vui->log2_max_mv_length_vertical);
+ ue_v(stream, vui->max_dec_frame_reordering);
+ ue_v(stream, vui->max_dec_frame_buffering);
+ }
+
+ return ;
+}
+
+
+void EncodeHRD(AVCEncBitstream* stream, AVCHRDParams* hrd)
+{
+ int i;
+
+ ue_v(stream, hrd->cpb_cnt_minus1);
+ BitstreamWriteBits(stream, 4, hrd->bit_rate_scale);
+ BitstreamWriteBits(stream, 4, hrd->cpb_size_scale);
+ for (i = 0; i <= (int)hrd->cpb_cnt_minus1; i++)
+ {
+ ue_v(stream, hrd->bit_rate_value_minus1[i]);
+ ue_v(stream, hrd->cpb_size_value_minus1[i]);
+ ue_v(stream, hrd->cbr_flag[i]);
+ }
+ BitstreamWriteBits(stream, 5, hrd->initial_cpb_removal_delay_length_minus1);
+ BitstreamWriteBits(stream, 5, hrd->cpb_removal_delay_length_minus1);
+ BitstreamWriteBits(stream, 5, hrd->dpb_output_delay_length_minus1);
+ BitstreamWriteBits(stream, 5, hrd->time_offset_length);
+
+ return ;
+}
+
+
+
+/** see subclause 7.4.2.2 */
+/* no need for checking the valid range , already done in SetEncodeParam().
+If we have to send another SPS, the ranges should be verified first before
+users call PVAVCEncodeSPS()*/
+AVCEnc_Status EncodePPS(AVCEncObject *encvid, AVCEncBitstream *stream)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ AVCPicParamSet *picParam = video->currPicParams;
+ int i, iGroup, numBits;
+ uint temp;
+
+ status = ue_v(stream, picParam->pic_parameter_set_id);
+ status = ue_v(stream, picParam->seq_parameter_set_id);
+ status = BitstreamWrite1Bit(stream, picParam->entropy_coding_mode_flag);
+ status = BitstreamWrite1Bit(stream, picParam->pic_order_present_flag);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = ue_v(stream, picParam->num_slice_groups_minus1);
+ if (picParam->num_slice_groups_minus1 > 0)
+ {
+ status = ue_v(stream, picParam->slice_group_map_type);
+ if (picParam->slice_group_map_type == 0)
+ {
+ for (iGroup = 0; iGroup <= (int)picParam->num_slice_groups_minus1; iGroup++)
+ {
+ status = ue_v(stream, picParam->run_length_minus1[iGroup]);
+ }
+ }
+ else if (picParam->slice_group_map_type == 2)
+ {
+ for (iGroup = 0; iGroup < (int)picParam->num_slice_groups_minus1; iGroup++)
+ {
+ status = ue_v(stream, picParam->top_left[iGroup]);
+ status = ue_v(stream, picParam->bottom_right[iGroup]);
+ }
+ }
+ else if (picParam->slice_group_map_type == 3 ||
+ picParam->slice_group_map_type == 4 ||
+ picParam->slice_group_map_type == 5)
+ {
+ status = BitstreamWrite1Bit(stream, picParam->slice_group_change_direction_flag);
+ status = ue_v(stream, picParam->slice_group_change_rate_minus1);
+ }
+ else /*if(picParam->slice_group_map_type == 6)*/
+ {
+ status = ue_v(stream, picParam->pic_size_in_map_units_minus1);
+
+ numBits = 0;/* ceil(log2(num_slice_groups_minus1+1)) bits */
+ i = picParam->num_slice_groups_minus1;
+ while (i > 0)
+ {
+ numBits++;
+ i >>= 1;
+ }
+
+ for (i = 0; i <= (int)picParam->pic_size_in_map_units_minus1; i++)
+ {
+ status = BitstreamWriteBits(stream, numBits, picParam->slice_group_id[i]);
+ }
+ }
+ }
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = ue_v(stream, picParam->num_ref_idx_l0_active_minus1);
+ status = ue_v(stream, picParam->num_ref_idx_l1_active_minus1);
+ status = BitstreamWrite1Bit(stream, picParam->weighted_pred_flag);
+ status = BitstreamWriteBits(stream, 2, picParam->weighted_bipred_idc);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = se_v(stream, picParam->pic_init_qp_minus26);
+ status = se_v(stream, picParam->pic_init_qs_minus26);
+ status = se_v(stream, picParam->chroma_qp_index_offset);
+
+ temp = picParam->deblocking_filter_control_present_flag << 2;
+ temp |= (picParam->constrained_intra_pred_flag << 1);
+ temp |= picParam->redundant_pic_cnt_present_flag;
+
+ status = BitstreamWriteBits(stream, 3, temp);
+
+ return status;
+}
+
+/** see subclause 7.4.3 */
+AVCEnc_Status EncodeSliceHeader(AVCEncObject *encvid, AVCEncBitstream *stream)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCPicParamSet *currPPS = video->currPicParams;
+ AVCSeqParamSet *currSPS = video->currSeqParams;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ int slice_type, temp, i;
+ int num_bits;
+
+ num_bits = (stream->write_pos << 3) - stream->bit_left;
+
+ status = ue_v(stream, sliceHdr->first_mb_in_slice);
+
+ slice_type = video->slice_type;
+
+ if (video->mbNum == 0) /* first mb in frame */
+ {
+ status = ue_v(stream, sliceHdr->slice_type);
+ }
+ else
+ {
+ status = ue_v(stream, slice_type);
+ }
+
+ status = ue_v(stream, sliceHdr->pic_parameter_set_id);
+
+ status = BitstreamWriteBits(stream, currSPS->log2_max_frame_num_minus4 + 4, sliceHdr->frame_num);
+
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ /* if frame_mbs_only_flag is 0, encode field_pic_flag, bottom_field_flag here */
+
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ status = ue_v(stream, sliceHdr->idr_pic_id);
+ }
+
+ if (currSPS->pic_order_cnt_type == 0)
+ {
+ status = BitstreamWriteBits(stream, currSPS->log2_max_pic_order_cnt_lsb_minus4 + 4,
+ sliceHdr->pic_order_cnt_lsb);
+
+ if (currPPS->pic_order_present_flag && !sliceHdr->field_pic_flag)
+ {
+ status = se_v(stream, sliceHdr->delta_pic_order_cnt_bottom); /* 32 bits */
+ }
+ }
+ if (currSPS->pic_order_cnt_type == 1 && !currSPS->delta_pic_order_always_zero_flag)
+ {
+ status = se_v(stream, sliceHdr->delta_pic_order_cnt[0]); /* 32 bits */
+ if (currPPS->pic_order_present_flag && !sliceHdr->field_pic_flag)
+ {
+ status = se_v(stream, sliceHdr->delta_pic_order_cnt[1]); /* 32 bits */
+ }
+ }
+
+ if (currPPS->redundant_pic_cnt_present_flag)
+ {
+ status = ue_v(stream, sliceHdr->redundant_pic_cnt);
+ }
+
+ if (slice_type == AVC_B_SLICE)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->direct_spatial_mv_pred_flag);
+ }
+
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ if (slice_type == AVC_P_SLICE || slice_type == AVC_SP_SLICE || slice_type == AVC_B_SLICE)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->num_ref_idx_active_override_flag);
+ if (sliceHdr->num_ref_idx_active_override_flag)
+ {
+ /* we shouldn't enter this part at all */
+ status = ue_v(stream, sliceHdr->num_ref_idx_l0_active_minus1);
+ if (slice_type == AVC_B_SLICE)
+ {
+ status = ue_v(stream, sliceHdr->num_ref_idx_l1_active_minus1);
+ }
+ }
+ }
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ /* ref_pic_list_reordering() */
+ status = ref_pic_list_reordering(video, stream, sliceHdr, slice_type);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ if ((currPPS->weighted_pred_flag && (slice_type == AVC_P_SLICE || slice_type == AVC_SP_SLICE)) ||
+ (currPPS->weighted_bipred_idc == 1 && slice_type == AVC_B_SLICE))
+ {
+ // pred_weight_table(); // not supported !!
+ return AVCENC_PRED_WEIGHT_TAB_FAIL;
+ }
+
+ if (video->nal_ref_idc != 0)
+ {
+ status = dec_ref_pic_marking(video, stream, sliceHdr);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+
+ if (currPPS->entropy_coding_mode_flag && slice_type != AVC_I_SLICE && slice_type != AVC_SI_SLICE)
+ {
+ return AVCENC_CABAC_FAIL;
+ /* ue_v(stream,&(sliceHdr->cabac_init_idc));
+ if(sliceHdr->cabac_init_idc > 2){
+ // not supported !!!!
+ }*/
+ }
+
+ status = se_v(stream, sliceHdr->slice_qp_delta);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ if (slice_type == AVC_SP_SLICE || slice_type == AVC_SI_SLICE)
+ {
+ if (slice_type == AVC_SP_SLICE)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->sp_for_switch_flag);
+ /* if sp_for_switch_flag is 0, P macroblocks in SP slice is decoded using
+ SP decoding process for non-switching pictures in 8.6.1 */
+ /* else, P macroblocks in SP slice is decoded using SP and SI decoding
+ process for switching picture in 8.6.2 */
+ }
+ status = se_v(stream, sliceHdr->slice_qs_delta);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+
+ if (currPPS->deblocking_filter_control_present_flag)
+ {
+
+ status = ue_v(stream, sliceHdr->disable_deblocking_filter_idc);
+
+ if (sliceHdr->disable_deblocking_filter_idc != 1)
+ {
+ status = se_v(stream, sliceHdr->slice_alpha_c0_offset_div2);
+
+ status = se_v(stream, sliceHdr->slice_beta_offset_div_2);
+ }
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+
+ if (currPPS->num_slice_groups_minus1 > 0 && currPPS->slice_group_map_type >= 3
+ && currPPS->slice_group_map_type <= 5)
+ {
+ /* Ceil(Log2(PicSizeInMapUnits/(float)SliceGroupChangeRate + 1)) */
+ temp = video->PicSizeInMapUnits / video->SliceGroupChangeRate;
+ if (video->PicSizeInMapUnits % video->SliceGroupChangeRate)
+ {
+ temp++;
+ }
+ i = 0;
+ while (temp > 1)
+ {
+ temp >>= 1;
+ i++;
+ }
+
+ BitstreamWriteBits(stream, i, sliceHdr->slice_group_change_cycle);
+ }
+
+
+ encvid->rateCtrl->NumberofHeaderBits += (stream->write_pos << 3) - stream->bit_left - num_bits;
+
+ return AVCENC_SUCCESS;
+}
+
+/** see subclause 7.4.3.1 */
+AVCEnc_Status ref_pic_list_reordering(AVCCommonObj *video, AVCEncBitstream *stream, AVCSliceHeader *sliceHdr, int slice_type)
+{
+ (void)(video);
+ int i;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+
+ if (slice_type != AVC_I_SLICE && slice_type != AVC_SI_SLICE)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->ref_pic_list_reordering_flag_l0);
+ if (sliceHdr->ref_pic_list_reordering_flag_l0)
+ {
+ i = 0;
+ do
+ {
+ status = ue_v(stream, sliceHdr->reordering_of_pic_nums_idc_l0[i]);
+ if (sliceHdr->reordering_of_pic_nums_idc_l0[i] == 0 ||
+ sliceHdr->reordering_of_pic_nums_idc_l0[i] == 1)
+ {
+ status = ue_v(stream, sliceHdr->abs_diff_pic_num_minus1_l0[i]);
+ /* this check should be in InitSlice(), if we ever use it */
+ /*if(sliceHdr->reordering_of_pic_nums_idc_l0[i] == 0 &&
+ sliceHdr->abs_diff_pic_num_minus1_l0[i] > video->MaxPicNum/2 -1)
+ {
+ return AVCENC_REF_PIC_REORDER_FAIL; // out of range
+ }
+ if(sliceHdr->reordering_of_pic_nums_idc_l0[i] == 1 &&
+ sliceHdr->abs_diff_pic_num_minus1_l0[i] > video->MaxPicNum/2 -2)
+ {
+ return AVCENC_REF_PIC_REORDER_FAIL; // out of range
+ }*/
+ }
+ else if (sliceHdr->reordering_of_pic_nums_idc_l0[i] == 2)
+ {
+ status = ue_v(stream, sliceHdr->long_term_pic_num_l0[i]);
+ }
+ i++;
+ }
+ while (sliceHdr->reordering_of_pic_nums_idc_l0[i] != 3
+ && i <= (int)sliceHdr->num_ref_idx_l0_active_minus1 + 1) ;
+ }
+ }
+ if (slice_type == AVC_B_SLICE)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->ref_pic_list_reordering_flag_l1);
+ if (sliceHdr->ref_pic_list_reordering_flag_l1)
+ {
+ i = 0;
+ do
+ {
+ status = ue_v(stream, sliceHdr->reordering_of_pic_nums_idc_l1[i]);
+ if (sliceHdr->reordering_of_pic_nums_idc_l1[i] == 0 ||
+ sliceHdr->reordering_of_pic_nums_idc_l1[i] == 1)
+ {
+ status = ue_v(stream, sliceHdr->abs_diff_pic_num_minus1_l1[i]);
+ /* This check should be in InitSlice() if we ever use it
+ if(sliceHdr->reordering_of_pic_nums_idc_l1[i] == 0 &&
+ sliceHdr->abs_diff_pic_num_minus1_l1[i] > video->MaxPicNum/2 -1)
+ {
+ return AVCENC_REF_PIC_REORDER_FAIL; // out of range
+ }
+ if(sliceHdr->reordering_of_pic_nums_idc_l1[i] == 1 &&
+ sliceHdr->abs_diff_pic_num_minus1_l1[i] > video->MaxPicNum/2 -2)
+ {
+ return AVCENC_REF_PIC_REORDER_FAIL; // out of range
+ }*/
+ }
+ else if (sliceHdr->reordering_of_pic_nums_idc_l1[i] == 2)
+ {
+ status = ue_v(stream, sliceHdr->long_term_pic_num_l1[i]);
+ }
+ i++;
+ }
+ while (sliceHdr->reordering_of_pic_nums_idc_l1[i] != 3
+ && i <= (int)sliceHdr->num_ref_idx_l1_active_minus1 + 1) ;
+ }
+ }
+
+ return status;
+}
+
+/** see subclause 7.4.3.3 */
+AVCEnc_Status dec_ref_pic_marking(AVCCommonObj *video, AVCEncBitstream *stream, AVCSliceHeader *sliceHdr)
+{
+ int i;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->no_output_of_prior_pics_flag);
+ status = BitstreamWrite1Bit(stream, sliceHdr->long_term_reference_flag);
+ if (sliceHdr->long_term_reference_flag == 0) /* used for short-term */
+ {
+ video->MaxLongTermFrameIdx = -1; /* no long-term frame indx */
+ }
+ else /* used for long-term */
+ {
+ video->MaxLongTermFrameIdx = 0;
+ video->LongTermFrameIdx = 0;
+ }
+ }
+ else
+ {
+ status = BitstreamWrite1Bit(stream, sliceHdr->adaptive_ref_pic_marking_mode_flag); /* default to zero */
+ if (sliceHdr->adaptive_ref_pic_marking_mode_flag)
+ {
+ i = 0;
+ do
+ {
+ status = ue_v(stream, sliceHdr->memory_management_control_operation[i]);
+ if (sliceHdr->memory_management_control_operation[i] == 1 ||
+ sliceHdr->memory_management_control_operation[i] == 3)
+ {
+ status = ue_v(stream, sliceHdr->difference_of_pic_nums_minus1[i]);
+ }
+ if (sliceHdr->memory_management_control_operation[i] == 2)
+ {
+ status = ue_v(stream, sliceHdr->long_term_pic_num[i]);
+ }
+ if (sliceHdr->memory_management_control_operation[i] == 3 ||
+ sliceHdr->memory_management_control_operation[i] == 6)
+ {
+ status = ue_v(stream, sliceHdr->long_term_frame_idx[i]);
+ }
+ if (sliceHdr->memory_management_control_operation[i] == 4)
+ {
+ status = ue_v(stream, sliceHdr->max_long_term_frame_idx_plus1[i]);
+ }
+ i++;
+ }
+ while (sliceHdr->memory_management_control_operation[i] != 0 && i < MAX_DEC_REF_PIC_MARKING);
+ if (i >= MAX_DEC_REF_PIC_MARKING && sliceHdr->memory_management_control_operation[i] != 0)
+ {
+ return AVCENC_DEC_REF_PIC_MARK_FAIL; /* we're screwed!!, not enough memory */
+ }
+ }
+ }
+
+ return status;
+}
+
+/* see subclause 8.2.1 Decoding process for picture order count.
+See also PostPOC() for initialization of some variables. */
+AVCEnc_Status InitPOC(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCSeqParamSet *currSPS = video->currSeqParams;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCFrameIO *currInput = encvid->currInput;
+ int i;
+
+ switch (currSPS->pic_order_cnt_type)
+ {
+ case 0: /* POC MODE 0 , subclause 8.2.1.1 */
+ /* encoding part */
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ encvid->dispOrdPOCRef = currInput->disp_order;
+ }
+ while (currInput->disp_order < encvid->dispOrdPOCRef)
+ {
+ encvid->dispOrdPOCRef -= video->MaxPicOrderCntLsb;
+ }
+ sliceHdr->pic_order_cnt_lsb = currInput->disp_order - encvid->dispOrdPOCRef;
+ while (sliceHdr->pic_order_cnt_lsb >= video->MaxPicOrderCntLsb)
+ {
+ sliceHdr->pic_order_cnt_lsb -= video->MaxPicOrderCntLsb;
+ }
+ /* decoding part */
+ /* Calculate the MSBs of current picture */
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ video->prevPicOrderCntMsb = 0;
+ video->prevPicOrderCntLsb = 0;
+ }
+ if (sliceHdr->pic_order_cnt_lsb < video->prevPicOrderCntLsb &&
+ (video->prevPicOrderCntLsb - sliceHdr->pic_order_cnt_lsb) >= (video->MaxPicOrderCntLsb / 2))
+ video->PicOrderCntMsb = video->prevPicOrderCntMsb + video->MaxPicOrderCntLsb;
+ else if (sliceHdr->pic_order_cnt_lsb > video->prevPicOrderCntLsb &&
+ (sliceHdr->pic_order_cnt_lsb - video->prevPicOrderCntLsb) > (video->MaxPicOrderCntLsb / 2))
+ video->PicOrderCntMsb = video->prevPicOrderCntMsb - video->MaxPicOrderCntLsb;
+ else
+ video->PicOrderCntMsb = video->prevPicOrderCntMsb;
+
+ /* JVT-I010 page 81 is different from JM7.3 */
+ if (!sliceHdr->field_pic_flag || !sliceHdr->bottom_field_flag)
+ {
+ video->PicOrderCnt = video->TopFieldOrderCnt = video->PicOrderCntMsb + sliceHdr->pic_order_cnt_lsb;
+ }
+
+ if (!sliceHdr->field_pic_flag)
+ {
+ video->BottomFieldOrderCnt = video->TopFieldOrderCnt + sliceHdr->delta_pic_order_cnt_bottom;
+ }
+ else if (sliceHdr->bottom_field_flag)
+ {
+ video->PicOrderCnt = video->BottomFieldOrderCnt = video->PicOrderCntMsb + sliceHdr->pic_order_cnt_lsb;
+ }
+
+ if (!sliceHdr->field_pic_flag)
+ {
+ video->PicOrderCnt = AVC_MIN(video->TopFieldOrderCnt, video->BottomFieldOrderCnt);
+ }
+
+ if (video->currPicParams->pic_order_present_flag && !sliceHdr->field_pic_flag)
+ {
+ sliceHdr->delta_pic_order_cnt_bottom = 0; /* defaulted to zero */
+ }
+
+ break;
+ case 1: /* POC MODE 1, subclause 8.2.1.2 */
+ /* calculate FrameNumOffset */
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ encvid->dispOrdPOCRef = currInput->disp_order; /* reset the reference point */
+ video->prevFrameNumOffset = 0;
+ video->FrameNumOffset = 0;
+ }
+ else if (video->prevFrameNum > sliceHdr->frame_num)
+ {
+ video->FrameNumOffset = video->prevFrameNumOffset + video->MaxFrameNum;
+ }
+ else
+ {
+ video->FrameNumOffset = video->prevFrameNumOffset;
+ }
+ /* calculate absFrameNum */
+ if (currSPS->num_ref_frames_in_pic_order_cnt_cycle)
+ {
+ video->absFrameNum = video->FrameNumOffset + sliceHdr->frame_num;
+ }
+ else
+ {
+ video->absFrameNum = 0;
+ }
+
+ if (video->absFrameNum > 0 && video->nal_ref_idc == 0)
+ {
+ video->absFrameNum--;
+ }
+
+ /* derive picOrderCntCycleCnt and frameNumInPicOrderCntCycle */
+ if (video->absFrameNum > 0)
+ {
+ video->picOrderCntCycleCnt = (video->absFrameNum - 1) / currSPS->num_ref_frames_in_pic_order_cnt_cycle;
+ video->frameNumInPicOrderCntCycle = (video->absFrameNum - 1) % currSPS->num_ref_frames_in_pic_order_cnt_cycle;
+ }
+ /* derive expectedDeltaPerPicOrderCntCycle, this value can be computed up front. */
+ video->expectedDeltaPerPicOrderCntCycle = 0;
+ for (i = 0; i < (int)currSPS->num_ref_frames_in_pic_order_cnt_cycle; i++)
+ {
+ video->expectedDeltaPerPicOrderCntCycle += currSPS->offset_for_ref_frame[i];
+ }
+ /* derive expectedPicOrderCnt */
+ if (video->absFrameNum)
+ {
+ video->expectedPicOrderCnt = video->picOrderCntCycleCnt * video->expectedDeltaPerPicOrderCntCycle;
+ for (i = 0; i <= video->frameNumInPicOrderCntCycle; i++)
+ {
+ video->expectedPicOrderCnt += currSPS->offset_for_ref_frame[i];
+ }
+ }
+ else
+ {
+ video->expectedPicOrderCnt = 0;
+ }
+
+ if (video->nal_ref_idc == 0)
+ {
+ video->expectedPicOrderCnt += currSPS->offset_for_non_ref_pic;
+ }
+ /* derive TopFieldOrderCnt and BottomFieldOrderCnt */
+ /* encoding part */
+ if (!currSPS->delta_pic_order_always_zero_flag)
+ {
+ sliceHdr->delta_pic_order_cnt[0] = currInput->disp_order - encvid->dispOrdPOCRef - video->expectedPicOrderCnt;
+
+ if (video->currPicParams->pic_order_present_flag && !sliceHdr->field_pic_flag)
+ {
+ sliceHdr->delta_pic_order_cnt[1] = sliceHdr->delta_pic_order_cnt[0]; /* should be calculated from currInput->bottom_field->disp_order */
+ }
+ else
+ {
+ sliceHdr->delta_pic_order_cnt[1] = 0;
+ }
+ }
+ else
+ {
+ sliceHdr->delta_pic_order_cnt[0] = sliceHdr->delta_pic_order_cnt[1] = 0;
+ }
+
+ if (sliceHdr->field_pic_flag == 0)
+ {
+ video->TopFieldOrderCnt = video->expectedPicOrderCnt + sliceHdr->delta_pic_order_cnt[0];
+ video->BottomFieldOrderCnt = video->TopFieldOrderCnt + currSPS->offset_for_top_to_bottom_field + sliceHdr->delta_pic_order_cnt[1];
+
+ video->PicOrderCnt = AVC_MIN(video->TopFieldOrderCnt, video->BottomFieldOrderCnt);
+ }
+ else if (sliceHdr->bottom_field_flag == 0)
+ {
+ video->TopFieldOrderCnt = video->expectedPicOrderCnt + sliceHdr->delta_pic_order_cnt[0];
+ video->PicOrderCnt = video->TopFieldOrderCnt;
+ }
+ else
+ {
+ video->BottomFieldOrderCnt = video->expectedPicOrderCnt + currSPS->offset_for_top_to_bottom_field + sliceHdr->delta_pic_order_cnt[0];
+ video->PicOrderCnt = video->BottomFieldOrderCnt;
+ }
+ break;
+
+
+ case 2: /* POC MODE 2, subclause 8.2.1.3 */
+ /* decoding order must be the same as display order */
+ /* we don't check for that. The decoder will just output in decoding order. */
+ /* Check for 2 consecutive non-reference frame */
+ if (video->nal_ref_idc == 0)
+ {
+ if (encvid->dispOrdPOCRef == 1)
+ {
+ return AVCENC_CONSECUTIVE_NONREF;
+ }
+ encvid->dispOrdPOCRef = 1; /* act as a flag for non ref */
+ }
+ else
+ {
+ encvid->dispOrdPOCRef = 0;
+ }
+
+
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ video->FrameNumOffset = 0;
+ }
+ else if (video->prevFrameNum > sliceHdr->frame_num)
+ {
+ video->FrameNumOffset = video->prevFrameNumOffset + video->MaxFrameNum;
+ }
+ else
+ {
+ video->FrameNumOffset = video->prevFrameNumOffset;
+ }
+ /* derive tempPicOrderCnt, we just use PicOrderCnt */
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ video->PicOrderCnt = 0;
+ }
+ else if (video->nal_ref_idc == 0)
+ {
+ video->PicOrderCnt = 2 * (video->FrameNumOffset + sliceHdr->frame_num) - 1;
+ }
+ else
+ {
+ video->PicOrderCnt = 2 * (video->FrameNumOffset + sliceHdr->frame_num);
+ }
+ /* derive TopFieldOrderCnt and BottomFieldOrderCnt */
+ if (sliceHdr->field_pic_flag == 0)
+ {
+ video->TopFieldOrderCnt = video->BottomFieldOrderCnt = video->PicOrderCnt;
+ }
+ else if (sliceHdr->bottom_field_flag)
+ {
+ video->BottomFieldOrderCnt = video->PicOrderCnt;
+ }
+ else
+ {
+ video->TopFieldOrderCnt = video->PicOrderCnt;
+ }
+ break;
+ default:
+ return AVCENC_POC_FAIL;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+/** see subclause 8.2.1 */
+AVCEnc_Status PostPOC(AVCCommonObj *video)
+{
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCSeqParamSet *currSPS = video->currSeqParams;
+
+ video->prevFrameNum = sliceHdr->frame_num;
+
+ switch (currSPS->pic_order_cnt_type)
+ {
+ case 0: /* subclause 8.2.1.1 */
+ if (video->mem_mgr_ctrl_eq_5)
+ {
+ video->prevPicOrderCntMsb = 0;
+ video->prevPicOrderCntLsb = video->TopFieldOrderCnt;
+ }
+ else
+ {
+ video->prevPicOrderCntMsb = video->PicOrderCntMsb;
+ video->prevPicOrderCntLsb = sliceHdr->pic_order_cnt_lsb;
+ }
+ break;
+ case 1: /* subclause 8.2.1.2 and 8.2.1.3 */
+ case 2:
+ if (video->mem_mgr_ctrl_eq_5)
+ {
+ video->prevFrameNumOffset = 0;
+ }
+ else
+ {
+ video->prevFrameNumOffset = video->FrameNumOffset;
+ }
+ break;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
diff --git a/media/libstagefright/codecs/avc/enc/src/init.cpp b/media/libstagefright/codecs/avc/enc/src/init.cpp
new file mode 100644
index 0000000..c258b57
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/init.cpp
@@ -0,0 +1,899 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+#include "avcenc_api.h"
+
+#define LOG2_MAX_FRAME_NUM_MINUS4 12 /* 12 default */
+#define SLICE_GROUP_CHANGE_CYCLE 1 /* default */
+
+/* initialized variables to be used in SPS*/
+AVCEnc_Status SetEncodeParam(AVCHandle* avcHandle, AVCEncParams* encParam,
+ void* extSPS, void* extPPS)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCCommonObj *video = encvid->common;
+ AVCSeqParamSet *seqParam = video->currSeqParams;
+ AVCPicParamSet *picParam = video->currPicParams;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCEnc_Status status;
+ void *userData = avcHandle->userData;
+ int ii, maxFrameNum;
+
+ AVCSeqParamSet* extS = NULL;
+ AVCPicParamSet* extP = NULL;
+
+ if (extSPS) extS = (AVCSeqParamSet*) extSPS;
+ if (extPPS) extP = (AVCPicParamSet*) extPPS;
+
+ /* This part sets the default values of the encoding options this
+ library supports in seqParam, picParam and sliceHdr structures and
+ also copy the values from the encParam into the above 3 structures.
+
+ Some parameters will be assigned later when we encode SPS or PPS such as
+ the seq_parameter_id or pic_parameter_id. Also some of the slice parameters
+ have to be re-assigned per slice basis such as frame_num, slice_type,
+ first_mb_in_slice, pic_order_cnt_lsb, slice_qp_delta, slice_group_change_cycle */
+
+ /* profile_idc, constrained_setx_flag and level_idc is set by VerifyProfile(),
+ and VerifyLevel() functions later. */
+
+ encvid->fullsearch_enable = encParam->fullsearch;
+
+ encvid->outOfBandParamSet = ((encParam->out_of_band_param_set == AVC_ON) ? TRUE : FALSE);
+
+ /* parameters derived from the the encParam that are used in SPS */
+ if (extS)
+ {
+ video->MaxPicOrderCntLsb = 1 << (extS->log2_max_pic_order_cnt_lsb_minus4 + 4);
+ video->PicWidthInMbs = extS->pic_width_in_mbs_minus1 + 1;
+ video->PicHeightInMapUnits = extS->pic_height_in_map_units_minus1 + 1 ;
+ video->FrameHeightInMbs = (2 - extS->frame_mbs_only_flag) * video->PicHeightInMapUnits ;
+ }
+ else
+ {
+ video->MaxPicOrderCntLsb = 1 << (encParam->log2_max_poc_lsb_minus_4 + 4);
+ video->PicWidthInMbs = (encParam->width + 15) >> 4; /* round it to multiple of 16 */
+ video->FrameHeightInMbs = (encParam->height + 15) >> 4; /* round it to multiple of 16 */
+ video->PicHeightInMapUnits = video->FrameHeightInMbs;
+ }
+
+ video->PicWidthInSamplesL = video->PicWidthInMbs * 16 ;
+ if (video->PicWidthInSamplesL + 32 > 0xFFFF)
+ {
+ return AVCENC_NOT_SUPPORTED; // we use 2-bytes for pitch
+ }
+
+ video->PicWidthInSamplesC = video->PicWidthInMbs * 8 ;
+ video->PicHeightInMbs = video->FrameHeightInMbs;
+ video->PicSizeInMapUnits = video->PicWidthInMbs * video->PicHeightInMapUnits ;
+ video->PicHeightInSamplesL = video->PicHeightInMbs * 16;
+ video->PicHeightInSamplesC = video->PicHeightInMbs * 8;
+ video->PicSizeInMbs = video->PicWidthInMbs * video->PicHeightInMbs;
+
+ if (!extS && !extP)
+ {
+ maxFrameNum = (encParam->idr_period == -1) ? (1 << 16) : encParam->idr_period;
+ ii = 0;
+ while (maxFrameNum > 0)
+ {
+ ii++;
+ maxFrameNum >>= 1;
+ }
+ if (ii < 4) ii = 4;
+ else if (ii > 16) ii = 16;
+
+ seqParam->log2_max_frame_num_minus4 = ii - 4;//LOG2_MAX_FRAME_NUM_MINUS4; /* default */
+
+ video->MaxFrameNum = 1 << ii; //(LOG2_MAX_FRAME_NUM_MINUS4 + 4); /* default */
+ video->MaxPicNum = video->MaxFrameNum;
+
+ /************* set the SPS *******************/
+ seqParam->seq_parameter_set_id = 0; /* start with zero */
+ /* POC */
+ seqParam->pic_order_cnt_type = encParam->poc_type; /* POC type */
+ if (encParam->poc_type == 0)
+ {
+ if (/*encParam->log2_max_poc_lsb_minus_4<0 || (no need, it's unsigned)*/
+ encParam->log2_max_poc_lsb_minus_4 > 12)
+ {
+ return AVCENC_INVALID_POC_LSB;
+ }
+ seqParam->log2_max_pic_order_cnt_lsb_minus4 = encParam->log2_max_poc_lsb_minus_4;
+ }
+ else if (encParam->poc_type == 1)
+ {
+ seqParam->delta_pic_order_always_zero_flag = encParam->delta_poc_zero_flag;
+ seqParam->offset_for_non_ref_pic = encParam->offset_poc_non_ref;
+ seqParam->offset_for_top_to_bottom_field = encParam->offset_top_bottom;
+ seqParam->num_ref_frames_in_pic_order_cnt_cycle = encParam->num_ref_in_cycle;
+ if (encParam->offset_poc_ref == NULL)
+ {
+ return AVCENC_ENCPARAM_MEM_FAIL;
+ }
+ for (ii = 0; ii < encParam->num_ref_frame; ii++)
+ {
+ seqParam->offset_for_ref_frame[ii] = encParam->offset_poc_ref[ii];
+ }
+ }
+ /* number of reference frame */
+ if (encParam->num_ref_frame > 16 || encParam->num_ref_frame < 0)
+ {
+ return AVCENC_INVALID_NUM_REF;
+ }
+ seqParam->num_ref_frames = encParam->num_ref_frame; /* num reference frame range 0...16*/
+ seqParam->gaps_in_frame_num_value_allowed_flag = FALSE;
+ seqParam->pic_width_in_mbs_minus1 = video->PicWidthInMbs - 1;
+ seqParam->pic_height_in_map_units_minus1 = video->PicHeightInMapUnits - 1;
+ seqParam->frame_mbs_only_flag = TRUE;
+ seqParam->mb_adaptive_frame_field_flag = FALSE;
+ seqParam->direct_8x8_inference_flag = FALSE; /* default */
+ seqParam->frame_cropping_flag = FALSE;
+ seqParam->frame_crop_bottom_offset = 0;
+ seqParam->frame_crop_left_offset = 0;
+ seqParam->frame_crop_right_offset = 0;
+ seqParam->frame_crop_top_offset = 0;
+ seqParam->vui_parameters_present_flag = FALSE; /* default */
+ }
+ else if (extS) // use external SPS and PPS
+ {
+ seqParam->seq_parameter_set_id = extS->seq_parameter_set_id;
+ seqParam->log2_max_frame_num_minus4 = extS->log2_max_frame_num_minus4;
+ video->MaxFrameNum = 1 << (extS->log2_max_frame_num_minus4 + 4);
+ video->MaxPicNum = video->MaxFrameNum;
+ if (encParam->idr_period > (int)(video->MaxFrameNum) || (encParam->idr_period == -1))
+ {
+ encParam->idr_period = (int)video->MaxFrameNum;
+ }
+
+ seqParam->pic_order_cnt_type = extS->pic_order_cnt_type;
+ if (seqParam->pic_order_cnt_type == 0)
+ {
+ if (/*extS->log2_max_pic_order_cnt_lsb_minus4<0 || (no need it's unsigned)*/
+ extS->log2_max_pic_order_cnt_lsb_minus4 > 12)
+ {
+ return AVCENC_INVALID_POC_LSB;
+ }
+ seqParam->log2_max_pic_order_cnt_lsb_minus4 = extS->log2_max_pic_order_cnt_lsb_minus4;
+ }
+ else if (seqParam->pic_order_cnt_type == 1)
+ {
+ seqParam->delta_pic_order_always_zero_flag = extS->delta_pic_order_always_zero_flag;
+ seqParam->offset_for_non_ref_pic = extS->offset_for_non_ref_pic;
+ seqParam->offset_for_top_to_bottom_field = extS->offset_for_top_to_bottom_field;
+ seqParam->num_ref_frames_in_pic_order_cnt_cycle = extS->num_ref_frames_in_pic_order_cnt_cycle;
+ if (extS->offset_for_ref_frame == NULL)
+ {
+ return AVCENC_ENCPARAM_MEM_FAIL;
+ }
+ for (ii = 0; ii < (int) extS->num_ref_frames; ii++)
+ {
+ seqParam->offset_for_ref_frame[ii] = extS->offset_for_ref_frame[ii];
+ }
+ }
+ /* number of reference frame */
+ if (extS->num_ref_frames > 16 /*|| extS->num_ref_frames<0 (no need, it's unsigned)*/)
+ {
+ return AVCENC_INVALID_NUM_REF;
+ }
+ seqParam->num_ref_frames = extS->num_ref_frames; /* num reference frame range 0...16*/
+ seqParam->gaps_in_frame_num_value_allowed_flag = extS->gaps_in_frame_num_value_allowed_flag;
+ seqParam->pic_width_in_mbs_minus1 = extS->pic_width_in_mbs_minus1;
+ seqParam->pic_height_in_map_units_minus1 = extS->pic_height_in_map_units_minus1;
+ seqParam->frame_mbs_only_flag = extS->frame_mbs_only_flag;
+ if (extS->frame_mbs_only_flag != TRUE)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+ seqParam->mb_adaptive_frame_field_flag = extS->mb_adaptive_frame_field_flag;
+ if (extS->mb_adaptive_frame_field_flag != FALSE)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ seqParam->direct_8x8_inference_flag = extS->direct_8x8_inference_flag;
+ seqParam->frame_cropping_flag = extS->frame_cropping_flag ;
+ if (extS->frame_cropping_flag != FALSE)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ seqParam->frame_crop_bottom_offset = 0;
+ seqParam->frame_crop_left_offset = 0;
+ seqParam->frame_crop_right_offset = 0;
+ seqParam->frame_crop_top_offset = 0;
+ seqParam->vui_parameters_present_flag = extS->vui_parameters_present_flag;
+ if (extS->vui_parameters_present_flag)
+ {
+ memcpy(&(seqParam->vui_parameters), &(extS->vui_parameters), sizeof(AVCVUIParams));
+ }
+ }
+ else
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ /***************** now PPS ******************************/
+ if (!extP && !extS)
+ {
+ picParam->pic_parameter_set_id = (uint)(-1); /* start with zero */
+ picParam->seq_parameter_set_id = (uint)(-1); /* start with zero */
+ picParam->entropy_coding_mode_flag = 0; /* default to CAVLC */
+ picParam->pic_order_present_flag = 0; /* default for now, will need it for B-slice */
+ /* FMO */
+ if (encParam->num_slice_group < 1 || encParam->num_slice_group > MAX_NUM_SLICE_GROUP)
+ {
+ return AVCENC_INVALID_NUM_SLICEGROUP;
+ }
+ picParam->num_slice_groups_minus1 = encParam->num_slice_group - 1;
+
+ if (picParam->num_slice_groups_minus1 > 0)
+ {
+ picParam->slice_group_map_type = encParam->fmo_type;
+ switch (encParam->fmo_type)
+ {
+ case 0:
+ for (ii = 0; ii <= (int)picParam->num_slice_groups_minus1; ii++)
+ {
+ picParam->run_length_minus1[ii] = encParam->run_length_minus1[ii];
+ }
+ break;
+ case 2:
+ for (ii = 0; ii < (int)picParam->num_slice_groups_minus1; ii++)
+ {
+ picParam->top_left[ii] = encParam->top_left[ii];
+ picParam->bottom_right[ii] = encParam->bottom_right[ii];
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ if (encParam->change_dir_flag == AVC_ON)
+ {
+ picParam->slice_group_change_direction_flag = TRUE;
+ }
+ else
+ {
+ picParam->slice_group_change_direction_flag = FALSE;
+ }
+ if (/*encParam->change_rate_minus1 < 0 || (no need it's unsigned) */
+ encParam->change_rate_minus1 > video->PicSizeInMapUnits - 1)
+ {
+ return AVCENC_INVALID_CHANGE_RATE;
+ }
+ picParam->slice_group_change_rate_minus1 = encParam->change_rate_minus1;
+ video->SliceGroupChangeRate = picParam->slice_group_change_rate_minus1 + 1;
+ break;
+ case 6:
+ picParam->pic_size_in_map_units_minus1 = video->PicSizeInMapUnits - 1;
+
+ /* allocate picParam->slice_group_id */
+ picParam->slice_group_id = (uint*)avcHandle->CBAVC_Malloc(userData, sizeof(uint) * video->PicSizeInMapUnits, DEFAULT_ATTR);
+ if (picParam->slice_group_id == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ if (encParam->slice_group == NULL)
+ {
+ return AVCENC_ENCPARAM_MEM_FAIL;
+ }
+ for (ii = 0; ii < (int)video->PicSizeInMapUnits; ii++)
+ {
+ picParam->slice_group_id[ii] = encParam->slice_group[ii];
+ }
+ break;
+ default:
+ return AVCENC_INVALID_FMO_TYPE;
+ }
+ }
+ picParam->num_ref_idx_l0_active_minus1 = encParam->num_ref_frame - 1; /* assume frame only */
+ picParam->num_ref_idx_l1_active_minus1 = 0; /* default value */
+ picParam->weighted_pred_flag = 0; /* no weighted prediction supported */
+ picParam->weighted_bipred_idc = 0; /* range 0,1,2 */
+ if (/*picParam->weighted_bipred_idc < 0 || (no need, it's unsigned) */
+ picParam->weighted_bipred_idc > 2)
+ {
+ return AVCENC_WEIGHTED_BIPRED_FAIL;
+ }
+ picParam->pic_init_qp_minus26 = 0; /* default, will be changed at slice level anyway */
+ if (picParam->pic_init_qp_minus26 < -26 || picParam->pic_init_qp_minus26 > 25)
+ {
+ return AVCENC_INIT_QP_FAIL; /* out of range */
+ }
+ picParam->pic_init_qs_minus26 = 0;
+ if (picParam->pic_init_qs_minus26 < -26 || picParam->pic_init_qs_minus26 > 25)
+ {
+ return AVCENC_INIT_QS_FAIL; /* out of range */
+ }
+
+ picParam->chroma_qp_index_offset = 0; /* default to zero for now */
+ if (picParam->chroma_qp_index_offset < -12 || picParam->chroma_qp_index_offset > 12)
+ {
+ return AVCENC_CHROMA_QP_FAIL; /* out of range */
+ }
+ /* deblocking */
+ picParam->deblocking_filter_control_present_flag = (encParam->db_filter == AVC_ON) ? TRUE : FALSE ;
+ /* constrained intra prediction */
+ picParam->constrained_intra_pred_flag = (encParam->constrained_intra_pred == AVC_ON) ? TRUE : FALSE;
+ picParam->redundant_pic_cnt_present_flag = 0; /* default */
+ }
+ else if (extP)// external PPS
+ {
+ picParam->pic_parameter_set_id = extP->pic_parameter_set_id - 1; /* to be increased by one */
+ picParam->seq_parameter_set_id = extP->seq_parameter_set_id;
+ picParam->entropy_coding_mode_flag = extP->entropy_coding_mode_flag;
+ if (extP->entropy_coding_mode_flag != 0) /* default to CAVLC */
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+ picParam->pic_order_present_flag = extP->pic_order_present_flag; /* default for now, will need it for B-slice */
+ if (extP->pic_order_present_flag != 0)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+ /* FMO */
+ if (/*(extP->num_slice_groups_minus1<0) || (no need it's unsigned) */
+ (extP->num_slice_groups_minus1 > MAX_NUM_SLICE_GROUP - 1))
+ {
+ return AVCENC_INVALID_NUM_SLICEGROUP;
+ }
+ picParam->num_slice_groups_minus1 = extP->num_slice_groups_minus1;
+
+ if (picParam->num_slice_groups_minus1 > 0)
+ {
+ picParam->slice_group_map_type = extP->slice_group_map_type;
+ switch (extP->slice_group_map_type)
+ {
+ case 0:
+ for (ii = 0; ii <= (int)extP->num_slice_groups_minus1; ii++)
+ {
+ picParam->run_length_minus1[ii] = extP->run_length_minus1[ii];
+ }
+ break;
+ case 2:
+ for (ii = 0; ii < (int)picParam->num_slice_groups_minus1; ii++)
+ {
+ picParam->top_left[ii] = extP->top_left[ii];
+ picParam->bottom_right[ii] = extP->bottom_right[ii];
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ picParam->slice_group_change_direction_flag = extP->slice_group_change_direction_flag;
+ if (/*extP->slice_group_change_rate_minus1 < 0 || (no need, it's unsigned) */
+ extP->slice_group_change_rate_minus1 > video->PicSizeInMapUnits - 1)
+ {
+ return AVCENC_INVALID_CHANGE_RATE;
+ }
+ picParam->slice_group_change_rate_minus1 = extP->slice_group_change_rate_minus1;
+ video->SliceGroupChangeRate = picParam->slice_group_change_rate_minus1 + 1;
+ break;
+ case 6:
+ if (extP->pic_size_in_map_units_minus1 != video->PicSizeInMapUnits - 1)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ picParam->pic_size_in_map_units_minus1 = extP->pic_size_in_map_units_minus1;
+
+ /* allocate picParam->slice_group_id */
+ picParam->slice_group_id = (uint*)avcHandle->CBAVC_Malloc(userData, sizeof(uint) * video->PicSizeInMapUnits, DEFAULT_ATTR);
+ if (picParam->slice_group_id == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ if (extP->slice_group_id == NULL)
+ {
+ return AVCENC_ENCPARAM_MEM_FAIL;
+ }
+ for (ii = 0; ii < (int)video->PicSizeInMapUnits; ii++)
+ {
+ picParam->slice_group_id[ii] = extP->slice_group_id[ii];
+ }
+ break;
+ default:
+ return AVCENC_INVALID_FMO_TYPE;
+ }
+ }
+ picParam->num_ref_idx_l0_active_minus1 = extP->num_ref_idx_l0_active_minus1;
+ picParam->num_ref_idx_l1_active_minus1 = extP->num_ref_idx_l1_active_minus1; /* default value */
+ if (picParam->num_ref_idx_l1_active_minus1 != 0)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ if (extP->weighted_pred_flag)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ picParam->weighted_pred_flag = 0; /* no weighted prediction supported */
+ picParam->weighted_bipred_idc = extP->weighted_bipred_idc; /* range 0,1,2 */
+ if (/*picParam->weighted_bipred_idc < 0 || (no need, it's unsigned) */
+ picParam->weighted_bipred_idc > 2)
+ {
+ return AVCENC_WEIGHTED_BIPRED_FAIL;
+ }
+ picParam->pic_init_qp_minus26 = extP->pic_init_qp_minus26; /* default, will be changed at slice level anyway */
+ if (picParam->pic_init_qp_minus26 < -26 || picParam->pic_init_qp_minus26 > 25)
+ {
+ return AVCENC_INIT_QP_FAIL; /* out of range */
+ }
+ picParam->pic_init_qs_minus26 = extP->pic_init_qs_minus26;
+ if (picParam->pic_init_qs_minus26 < -26 || picParam->pic_init_qs_minus26 > 25)
+ {
+ return AVCENC_INIT_QS_FAIL; /* out of range */
+ }
+
+ picParam->chroma_qp_index_offset = extP->chroma_qp_index_offset; /* default to zero for now */
+ if (picParam->chroma_qp_index_offset < -12 || picParam->chroma_qp_index_offset > 12)
+ {
+ return AVCENC_CHROMA_QP_FAIL; /* out of range */
+ }
+ /* deblocking */
+ picParam->deblocking_filter_control_present_flag = extP->deblocking_filter_control_present_flag;
+ /* constrained intra prediction */
+ picParam->constrained_intra_pred_flag = extP->constrained_intra_pred_flag;
+ if (extP->redundant_pic_cnt_present_flag != 0)
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+ picParam->redundant_pic_cnt_present_flag = extP->redundant_pic_cnt_present_flag; /* default */
+ }
+ else
+ {
+ return AVCENC_NOT_SUPPORTED;
+ }
+
+ /****************** now set up some SliceHeader parameters ***********/
+ if (picParam->deblocking_filter_control_present_flag == TRUE)
+ {
+ /* these values only present when db_filter is ON */
+ if (encParam->disable_db_idc > 2)
+ {
+ return AVCENC_INVALID_DEBLOCK_IDC; /* out of range */
+ }
+ sliceHdr->disable_deblocking_filter_idc = encParam->disable_db_idc;
+
+ if (encParam->alpha_offset < -6 || encParam->alpha_offset > 6)
+ {
+ return AVCENC_INVALID_ALPHA_OFFSET;
+ }
+ sliceHdr->slice_alpha_c0_offset_div2 = encParam->alpha_offset;
+
+ if (encParam->beta_offset < -6 || encParam->beta_offset > 6)
+ {
+ return AVCENC_INVALID_BETA_OFFSET;
+ }
+ sliceHdr->slice_beta_offset_div_2 = encParam->beta_offset;
+ }
+ if (encvid->outOfBandParamSet == TRUE)
+ {
+ sliceHdr->idr_pic_id = 0;
+ }
+ else
+ {
+ sliceHdr->idr_pic_id = (uint)(-1); /* start with zero */
+ }
+ sliceHdr->field_pic_flag = FALSE;
+ sliceHdr->bottom_field_flag = FALSE; /* won't be used anyway */
+ video->MbaffFrameFlag = (seqParam->mb_adaptive_frame_field_flag && !sliceHdr->field_pic_flag);
+
+ /* the rest will be set in InitSlice() */
+
+ /* now the rate control and performance related parameters */
+ rateCtrl->scdEnable = (encParam->auto_scd == AVC_ON) ? TRUE : FALSE;
+ rateCtrl->idrPeriod = encParam->idr_period + 1;
+ rateCtrl->intraMBRate = encParam->intramb_refresh;
+ rateCtrl->dpEnable = (encParam->data_par == AVC_ON) ? TRUE : FALSE;
+
+ rateCtrl->subPelEnable = (encParam->sub_pel == AVC_ON) ? TRUE : FALSE;
+ rateCtrl->mvRange = encParam->search_range;
+
+ rateCtrl->subMBEnable = (encParam->submb_pred == AVC_ON) ? TRUE : FALSE;
+ rateCtrl->rdOptEnable = (encParam->rdopt_mode == AVC_ON) ? TRUE : FALSE;
+ rateCtrl->bidirPred = (encParam->bidir_pred == AVC_ON) ? TRUE : FALSE;
+
+ rateCtrl->rcEnable = (encParam->rate_control == AVC_ON) ? TRUE : FALSE;
+ rateCtrl->initQP = encParam->initQP;
+ rateCtrl->initQP = AVC_CLIP3(0, 51, rateCtrl->initQP);
+
+ rateCtrl->bitRate = encParam->bitrate;
+ rateCtrl->cpbSize = encParam->CPB_size;
+ rateCtrl->initDelayOffset = (rateCtrl->bitRate * encParam->init_CBP_removal_delay / 1000);
+
+ if (encParam->frame_rate == 0)
+ {
+ return AVCENC_INVALID_FRAMERATE;
+ }
+
+ rateCtrl->frame_rate = (OsclFloat)(encParam->frame_rate * 1.0 / 1000);
+// rateCtrl->srcInterval = encParam->src_interval;
+ rateCtrl->first_frame = 1; /* set this flag for the first time */
+
+ /* contrained_setx_flag will be set inside the VerifyProfile called below.*/
+ if (!extS && !extP)
+ {
+ seqParam->profile_idc = encParam->profile;
+ seqParam->constrained_set0_flag = FALSE;
+ seqParam->constrained_set1_flag = FALSE;
+ seqParam->constrained_set2_flag = FALSE;
+ seqParam->constrained_set3_flag = FALSE;
+ seqParam->level_idc = encParam->level;
+ }
+ else
+ {
+ seqParam->profile_idc = extS->profile_idc;
+ seqParam->constrained_set0_flag = extS->constrained_set0_flag;
+ seqParam->constrained_set1_flag = extS->constrained_set1_flag;
+ seqParam->constrained_set2_flag = extS->constrained_set2_flag;
+ seqParam->constrained_set3_flag = extS->constrained_set3_flag;
+ seqParam->level_idc = extS->level_idc;
+ }
+
+
+ status = VerifyProfile(encvid, seqParam, picParam);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ status = VerifyLevel(encvid, seqParam, picParam);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+/* verify the profile setting */
+AVCEnc_Status VerifyProfile(AVCEncObject *encvid, AVCSeqParamSet *seqParam, AVCPicParamSet *picParam)
+{
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+
+ if (seqParam->profile_idc == 0) /* find profile for this setting */
+ {
+ /* find the right profile for it */
+ if (seqParam->direct_8x8_inference_flag == TRUE &&
+ picParam->entropy_coding_mode_flag == FALSE &&
+ picParam->num_slice_groups_minus1 <= 7 /*&&
+ picParam->num_slice_groups_minus1>=0 (no need, it's unsigned) */)
+ {
+ seqParam->profile_idc = AVC_EXTENDED;
+ seqParam->constrained_set2_flag = TRUE;
+ }
+
+ if (rateCtrl->dpEnable == FALSE &&
+ picParam->num_slice_groups_minus1 == 0 &&
+ picParam->redundant_pic_cnt_present_flag == FALSE)
+ {
+ seqParam->profile_idc = AVC_MAIN;
+ seqParam->constrained_set1_flag = TRUE;
+ }
+
+ if (rateCtrl->bidirPred == FALSE &&
+ rateCtrl->dpEnable == FALSE &&
+ seqParam->frame_mbs_only_flag == TRUE &&
+ picParam->weighted_pred_flag == FALSE &&
+ picParam->weighted_bipred_idc == 0 &&
+ picParam->entropy_coding_mode_flag == FALSE &&
+ picParam->num_slice_groups_minus1 <= 7 /*&&
+ picParam->num_slice_groups_minus1>=0 (no need, it's unsigned)*/)
+ {
+ seqParam->profile_idc = AVC_BASELINE;
+ seqParam->constrained_set0_flag = TRUE;
+ }
+
+ if (seqParam->profile_idc == 0) /* still zero */
+ {
+ return AVCENC_PROFILE_NOT_SUPPORTED;
+ }
+ }
+
+ /* check the list of supported profile by this library */
+ switch (seqParam->profile_idc)
+ {
+ case AVC_BASELINE:
+ if (rateCtrl->bidirPred == TRUE ||
+ rateCtrl->dpEnable == TRUE ||
+ seqParam->frame_mbs_only_flag != TRUE ||
+ picParam->weighted_pred_flag == TRUE ||
+ picParam->weighted_bipred_idc != 0 ||
+ picParam->entropy_coding_mode_flag == TRUE ||
+ picParam->num_slice_groups_minus1 > 7 /*||
+ picParam->num_slice_groups_minus1<0 (no need, it's unsigned) */)
+ {
+ status = AVCENC_TOOLS_NOT_SUPPORTED;
+ }
+ break;
+
+ case AVC_MAIN:
+ case AVC_EXTENDED:
+ status = AVCENC_PROFILE_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+/* verify the level setting */
+AVCEnc_Status VerifyLevel(AVCEncObject *encvid, AVCSeqParamSet *seqParam, AVCPicParamSet *picParam)
+{
+ (void)(picParam);
+
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCCommonObj *video = encvid->common;
+ int mb_per_sec, ii;
+ int lev_idx;
+ int dpb_size;
+
+ mb_per_sec = (int)(video->PicSizeInMbs * rateCtrl->frame_rate + 0.5);
+ dpb_size = (seqParam->num_ref_frames * video->PicSizeInMbs * 3) >> 6;
+
+ if (seqParam->level_idc == 0) /* find level for this setting */
+ {
+ for (ii = 0; ii < MAX_LEVEL_IDX; ii++)
+ {
+ if (mb_per_sec <= MaxMBPS[ii] &&
+ video->PicSizeInMbs <= (uint)MaxFS[ii] &&
+ rateCtrl->bitRate <= (int32)MaxBR[ii]*1000 &&
+ rateCtrl->cpbSize <= (int32)MaxCPB[ii]*1000 &&
+ rateCtrl->mvRange <= MaxVmvR[ii] &&
+ dpb_size <= MaxDPBX2[ii]*512)
+ {
+ seqParam->level_idc = mapIdx2Lev[ii];
+ break;
+ }
+ }
+ if (seqParam->level_idc == 0)
+ {
+ return AVCENC_LEVEL_NOT_SUPPORTED;
+ }
+ }
+
+ /* check if this level is supported by this library */
+ lev_idx = mapLev2Idx[seqParam->level_idc];
+ if (seqParam->level_idc == AVC_LEVEL1_B)
+ {
+ seqParam->constrained_set3_flag = 1;
+ }
+
+
+ if (lev_idx == 255) /* not defined */
+ {
+ return AVCENC_LEVEL_NOT_SUPPORTED;
+ }
+
+ /* check if the encoding setting complies with the level */
+ if (mb_per_sec > MaxMBPS[lev_idx] ||
+ video->PicSizeInMbs > (uint)MaxFS[lev_idx] ||
+ rateCtrl->bitRate > (int32)MaxBR[lev_idx]*1000 ||
+ rateCtrl->cpbSize > (int32)MaxCPB[lev_idx]*1000 ||
+ rateCtrl->mvRange > MaxVmvR[lev_idx])
+ {
+ return AVCENC_LEVEL_FAIL;
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+/* initialize variables at the beginning of each frame */
+/* determine the picture type */
+/* encode POC */
+/* maybe we should do more stuff here. MotionEstimation+SCD and generate a new SPS and PPS */
+AVCEnc_Status InitFrame(AVCEncObject *encvid)
+{
+ AVCStatus ret;
+ AVCEnc_Status status;
+ AVCCommonObj *video = encvid->common;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+
+ /* look for the next frame in coding_order and look for available picture
+ in the DPB. Note, video->currFS->PicOrderCnt, currFS->FrameNum and currPic->PicNum
+ are set to wrong number in this function (right for decoder). */
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ // call init DPB in here.
+ ret = AVCConfigureSequence(encvid->avcHandle, video, TRUE);
+ if (ret != AVC_SUCCESS)
+ {
+ return AVCENC_FAIL;
+ }
+ }
+
+ /* flexible macroblock ordering (every frame)*/
+ /* populate video->mapUnitToSliceGroupMap and video->MbToSliceGroupMap */
+ /* It changes once per each PPS. */
+ FMOInit(video);
+
+ ret = DPBInitBuffer(encvid->avcHandle, video); // get new buffer
+
+ if (ret != AVC_SUCCESS)
+ {
+ return (AVCEnc_Status)ret; // AVCENC_PICTURE_READY, FAIL
+ }
+
+ DPBInitPic(video, 0); /* 0 is dummy */
+
+ /************* determine picture type IDR or non-IDR ***********/
+ video->currPicType = AVC_FRAME;
+ video->slice_data_partitioning = FALSE;
+ encvid->currInput->is_reference = 1; /* default to all frames */
+ video->nal_ref_idc = 1; /* need to set this for InitPOC */
+ video->currPic->isReference = TRUE;
+
+ /************* set frame_num ********************/
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ video->prevFrameNum = video->MaxFrameNum;
+ video->PrevRefFrameNum = 0;
+ sliceHdr->frame_num = 0;
+ }
+ /* otherwise, it's set to previous reference frame access unit's frame_num in decoding order,
+ see the end of PVAVCDecodeSlice()*/
+ /* There's also restriction on the frame_num, see page 59 of JVT-I1010.doc. */
+ /* Basically, frame_num can't be repeated unless it's opposite fields or non reference fields */
+ else
+ {
+ sliceHdr->frame_num = (video->PrevRefFrameNum + 1) % video->MaxFrameNum;
+ }
+ video->CurrPicNum = sliceHdr->frame_num; /* for field_pic_flag = 0 */
+ //video->CurrPicNum = 2*sliceHdr->frame_num + 1; /* for field_pic_flag = 1 */
+
+ /* assign pic_order_cnt, video->PicOrderCnt */
+ status = InitPOC(encvid);
+ if (status != AVCENC_SUCCESS) /* incorrigable fail */
+ {
+ return status;
+ }
+
+ /* Initialize refListIdx for this picture */
+ RefListInit(video);
+
+ /************* motion estimation and scene analysis ************/
+ // , to move this to MB-based MV search for comparison
+ // use sub-optimal QP for mv search
+ AVCMotionEstimation(encvid); /* AVCENC_SUCCESS or AVCENC_NEW_IDR */
+
+ /* after this point, the picture type will be fixed to either IDR or non-IDR */
+ video->currFS->PicOrderCnt = video->PicOrderCnt;
+ video->currFS->FrameNum = video->sliceHdr->frame_num;
+ video->currPic->PicNum = video->CurrPicNum;
+ video->mbNum = 0; /* start from zero MB */
+ encvid->currSliceGroup = 0; /* start from slice group #0 */
+ encvid->numIntraMB = 0; /* reset this counter */
+
+ if (video->nal_unit_type == AVC_NALTYPE_IDR)
+ {
+ RCInitGOP(encvid);
+
+ /* calculate picture QP */
+ RCInitFrameQP(encvid);
+
+ return AVCENC_NEW_IDR;
+ }
+
+ /* calculate picture QP */
+ RCInitFrameQP(encvid); /* get QP after MV search */
+
+ return AVCENC_SUCCESS;
+}
+
+/* initialize variables for this slice */
+AVCEnc_Status InitSlice(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCPicParamSet *currPPS = video->currPicParams;
+ AVCSeqParamSet *currSPS = video->currSeqParams;
+ int slice_type = video->slice_type;
+
+ sliceHdr->first_mb_in_slice = video->mbNum;
+ if (video->mbNum) // not first slice of a frame
+ {
+ video->sliceHdr->slice_type = (AVCSliceType)slice_type;
+ }
+
+ /* sliceHdr->slice_type already set in InitFrame */
+
+ sliceHdr->pic_parameter_set_id = video->currPicParams->pic_parameter_set_id;
+
+ /* sliceHdr->frame_num already set in InitFrame */
+
+ if (!currSPS->frame_mbs_only_flag) /* we shouldn't need this check */
+ {
+ sliceHdr->field_pic_flag = sliceHdr->bottom_field_flag = FALSE;
+ return AVCENC_TOOLS_NOT_SUPPORTED;
+ }
+
+ /* sliceHdr->idr_pic_id already set in PVAVCEncodeNAL
+
+ sliceHdr->pic_order_cnt_lsb already set in InitFrame..InitPOC
+ sliceHdr->delta_pic_order_cnt_bottom already set in InitPOC
+
+ sliceHdr->delta_pic_order_cnt[0] already set in InitPOC
+ sliceHdr->delta_pic_order_cnt[1] already set in InitPOC
+ */
+
+ sliceHdr->redundant_pic_cnt = 0; /* default if(currPPS->redundant_pic_cnt_present_flag), range 0..127 */
+ sliceHdr->direct_spatial_mv_pred_flag = 0; // default if(slice_type == AVC_B_SLICE)
+
+ sliceHdr->num_ref_idx_active_override_flag = FALSE; /* default, if(slice_type== P,SP or B)*/
+ sliceHdr->num_ref_idx_l0_active_minus1 = 0; /* default, if (num_ref_idx_active_override_flag) */
+ sliceHdr->num_ref_idx_l1_active_minus1 = 0; /* default, if above and B_slice */
+ /* the above 2 values range from 0..15 for frame picture and 0..31 for field picture */
+
+ /* ref_pic_list_reordering(), currently we don't do anything */
+ sliceHdr->ref_pic_list_reordering_flag_l0 = FALSE; /* default */
+ sliceHdr->ref_pic_list_reordering_flag_l1 = FALSE; /* default */
+ /* if the above are TRUE, some other params must be set */
+
+ if ((currPPS->weighted_pred_flag && (slice_type == AVC_P_SLICE || slice_type == AVC_SP_SLICE)) ||
+ (currPPS->weighted_bipred_idc == 1 && slice_type == AVC_B_SLICE))
+ {
+ // pred_weight_table(); // not supported !!
+ return AVCENC_TOOLS_NOT_SUPPORTED;
+ }
+
+ /* dec_ref_pic_marking(), this will be done later*/
+ sliceHdr->no_output_of_prior_pics_flag = FALSE; /* default */
+ sliceHdr->long_term_reference_flag = FALSE; /* for IDR frame, do not make it long term */
+ sliceHdr->adaptive_ref_pic_marking_mode_flag = FALSE; /* default */
+ /* other params are not set here because they are not used */
+
+ sliceHdr->cabac_init_idc = 0; /* default, if entropy_coding_mode_flag && slice_type==I or SI, range 0..2 */
+ sliceHdr->slice_qp_delta = 0; /* default for now */
+ sliceHdr->sp_for_switch_flag = FALSE; /* default, if slice_type == SP */
+ sliceHdr->slice_qs_delta = 0; /* default, if slice_type == SP or SI */
+
+ /* derived variables from encParam */
+ /* deblocking filter */
+ video->FilterOffsetA = video->FilterOffsetB = 0;
+ if (currPPS->deblocking_filter_control_present_flag == TRUE)
+ {
+ video->FilterOffsetA = sliceHdr->slice_alpha_c0_offset_div2 << 1;
+ video->FilterOffsetB = sliceHdr->slice_beta_offset_div_2 << 1;
+ }
+
+ /* flexible macroblock ordering */
+ /* populate video->mapUnitToSliceGroupMap and video->MbToSliceGroupMap */
+ /* We already call it at the end of PVAVCEncInitialize(). It changes once per each PPS. */
+ if (video->currPicParams->num_slice_groups_minus1 > 0 && video->currPicParams->slice_group_map_type >= 3
+ && video->currPicParams->slice_group_map_type <= 5)
+ {
+ sliceHdr->slice_group_change_cycle = SLICE_GROUP_CHANGE_CYCLE; /* default, don't understand how to set it!!!*/
+
+ video->MapUnitsInSliceGroup0 =
+ AVC_MIN(sliceHdr->slice_group_change_cycle * video->SliceGroupChangeRate, video->PicSizeInMapUnits);
+
+ FMOInit(video);
+ }
+
+ /* calculate SliceQPy first */
+ /* calculate QSy first */
+
+ sliceHdr->slice_qp_delta = video->QPy - 26 - currPPS->pic_init_qp_minus26;
+ //sliceHdr->slice_qs_delta = video->QSy - 26 - currPPS->pic_init_qs_minus26;
+
+ return AVCENC_SUCCESS;
+}
+
diff --git a/media/libstagefright/codecs/avc/enc/src/intra_est.cpp b/media/libstagefright/codecs/avc/enc/src/intra_est.cpp
new file mode 100644
index 0000000..17e5985
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/intra_est.cpp
@@ -0,0 +1,2199 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+#define TH_I4 0 /* threshold biasing toward I16 mode instead of I4 mode */
+#define TH_Intra 0 /* threshold biasing toward INTER mode instead of intra mode */
+
+#define FIXED_INTRAPRED_MODE AVC_I16
+#define FIXED_I16_MODE AVC_I16_DC
+#define FIXED_I4_MODE AVC_I4_Diagonal_Down_Left
+#define FIXED_INTRA_CHROMA_MODE AVC_IC_DC
+
+#define CLIP_RESULT(x) if((uint)x > 0xFF){ \
+ x = 0xFF & (~(x>>31));}
+
+
+bool IntraDecisionABE(AVCEncObject *encvid, int min_cost, uint8 *curL, int picPitch)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCFrameIO *currInput = encvid->currInput;
+ int orgPitch = currInput->pitch;
+ int x_pos = (video->mb_x) << 4;
+ int y_pos = (video->mb_y) << 4;
+ uint8 *orgY = currInput->YCbCr[0] + y_pos * orgPitch + x_pos;
+ int j;
+ uint8 *topL, *leftL, *orgY_2, *orgY_3;
+ int temp, SBE, offset;
+ OsclFloat ABE;
+ bool intra = true;
+
+ if (((x_pos >> 4) != (int)video->PicWidthInMbs - 1) &&
+ ((y_pos >> 4) != (int)video->PicHeightInMbs - 1) &&
+ video->intraAvailA &&
+ video->intraAvailB)
+ {
+ SBE = 0;
+ /* top neighbor */
+ topL = curL - picPitch;
+ /* left neighbor */
+ leftL = curL - 1;
+ orgY_2 = orgY - orgPitch;
+
+ for (j = 0; j < 16; j++)
+ {
+ temp = *topL++ - orgY[j];
+ SBE += ((temp >= 0) ? temp : -temp);
+ temp = *(leftL += picPitch) - *(orgY_2 += orgPitch);
+ SBE += ((temp >= 0) ? temp : -temp);
+ }
+
+ /* calculate chroma */
+ offset = (y_pos >> 2) * picPitch + (x_pos >> 1);
+ topL = video->currPic->Scb + offset;
+ orgY_2 = currInput->YCbCr[1] + offset + (y_pos >> 2) * (orgPitch - picPitch);
+
+ leftL = topL - 1;
+ topL -= (picPitch >> 1);
+ orgY_3 = orgY_2 - (orgPitch >> 1);
+ for (j = 0; j < 8; j++)
+ {
+ temp = *topL++ - orgY_2[j];
+ SBE += ((temp >= 0) ? temp : -temp);
+ temp = *(leftL += (picPitch >> 1)) - *(orgY_3 += (orgPitch >> 1));
+ SBE += ((temp >= 0) ? temp : -temp);
+ }
+
+ topL = video->currPic->Scr + offset;
+ orgY_2 = currInput->YCbCr[2] + offset + (y_pos >> 2) * (orgPitch - picPitch);
+
+ leftL = topL - 1;
+ topL -= (picPitch >> 1);
+ orgY_3 = orgY_2 - (orgPitch >> 1);
+ for (j = 0; j < 8; j++)
+ {
+ temp = *topL++ - orgY_2[j];
+ SBE += ((temp >= 0) ? temp : -temp);
+ temp = *(leftL += (picPitch >> 1)) - *(orgY_3 += (orgPitch >> 1));
+ SBE += ((temp >= 0) ? temp : -temp);
+ }
+
+ /* compare mincost/384 and SBE/64 */
+ ABE = SBE / 64.0;
+ if (ABE*0.8 >= min_cost / 384.0)
+ {
+ intra = false;
+ }
+ }
+
+ return intra;
+}
+
+/* perform searching for MB mode */
+/* assuming that this is done inside the encoding loop,
+no need to call InitNeighborAvailability */
+
+void MBIntraSearch(AVCEncObject *encvid, int mbnum, uint8 *curL, int picPitch)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCFrameIO *currInput = encvid->currInput;
+ AVCMacroblock *currMB = video->currMB;
+ int min_cost;
+ uint8 *orgY;
+ int x_pos = (video->mb_x) << 4;
+ int y_pos = (video->mb_y) << 4;
+ uint32 *saved_inter;
+ int j;
+ int orgPitch = currInput->pitch;
+ bool intra = true;
+
+ currMB->CBP = 0;
+
+ /* first do motion vector and variable block size search */
+ min_cost = encvid->min_cost[mbnum];
+
+ /* now perform intra prediction search */
+ /* need to add the check for encvid->intraSearch[video->mbNum] to skip intra
+ if it's not worth checking. */
+ if (video->slice_type == AVC_P_SLICE)
+ {
+ /* Decide whether intra search is necessary or not */
+ /* This one, we do it in the encoding loop so the neighboring pixel are the
+ actual reconstructed pixels. */
+ intra = IntraDecisionABE(encvid, min_cost, curL, picPitch);
+ }
+
+ if (intra == true || video->slice_type == AVC_I_SLICE)
+ {
+ orgY = currInput->YCbCr[0] + y_pos * orgPitch + x_pos;
+
+ /* i16 mode search */
+ /* generate all the predictions */
+ intrapred_luma_16x16(encvid);
+
+ /* evaluate them one by one */
+ find_cost_16x16(encvid, orgY, &min_cost);
+
+ if (video->slice_type == AVC_P_SLICE)
+ {
+ /* save current inter prediction */
+ saved_inter = encvid->subpel_pred; /* reuse existing buffer */
+ j = 16;
+ curL -= 4;
+ picPitch -= 16;
+ while (j--)
+ {
+ *saved_inter++ = *((uint32*)(curL += 4));
+ *saved_inter++ = *((uint32*)(curL += 4));
+ *saved_inter++ = *((uint32*)(curL += 4));
+ *saved_inter++ = *((uint32*)(curL += 4));
+ curL += picPitch;
+ }
+
+ }
+
+ /* i4 mode search */
+ mb_intra4x4_search(encvid, &min_cost);
+
+ encvid->min_cost[mbnum] = min_cost; /* update min_cost */
+ }
+
+
+ if (currMB->mb_intra)
+ {
+ chroma_intra_search(encvid);
+
+ /* need to set this in order for the MBInterPrediction to work!! */
+ memset(currMB->mvL0, 0, sizeof(int32)*16);
+ currMB->ref_idx_L0[0] = currMB->ref_idx_L0[1] =
+ currMB->ref_idx_L0[2] = currMB->ref_idx_L0[3] = -1;
+ }
+ else if (video->slice_type == AVC_P_SLICE && intra == true)
+ {
+ /* restore current inter prediction */
+ saved_inter = encvid->subpel_pred; /* reuse existing buffer */
+ j = 16;
+ curL -= ((picPitch + 16) << 4);
+ while (j--)
+ {
+ *((uint32*)(curL += 4)) = *saved_inter++;
+ *((uint32*)(curL += 4)) = *saved_inter++;
+ *((uint32*)(curL += 4)) = *saved_inter++;
+ *((uint32*)(curL += 4)) = *saved_inter++;
+ curL += picPitch;
+ }
+ }
+
+ return ;
+}
+
+/* generate all the prediction values */
+void intrapred_luma_16x16(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCPictureData *currPic = video->currPic;
+
+ int x_pos = (video->mb_x) << 4;
+ int y_pos = (video->mb_y) << 4;
+ int pitch = currPic->pitch;
+
+ int offset = y_pos * pitch + x_pos;
+
+ uint8 *pred, *top, *left;
+ uint8 *curL = currPic->Sl + offset; /* point to reconstructed frame */
+ uint32 word1, word2, word3, word4;
+ uint32 sum = 0;
+
+ int a_16, b, c, factor_c;
+ uint8 *comp_ref_x0, *comp_ref_x1, *comp_ref_y0, *comp_ref_y1;
+ int H = 0, V = 0, tmp, value;
+ int i;
+
+ if (video->intraAvailB)
+ {
+ //get vertical prediction mode
+ top = curL - pitch;
+
+ pred = encvid->pred_i16[AVC_I16_Vertical] - 16;
+
+ word1 = *((uint32*)(top)); /* read 4 bytes from top */
+ word2 = *((uint32*)(top + 4)); /* read 4 bytes from top */
+ word3 = *((uint32*)(top + 8)); /* read 4 bytes from top */
+ word4 = *((uint32*)(top + 12)); /* read 4 bytes from top */
+
+ for (i = 0; i < 16; i++)
+ {
+ *((uint32*)(pred += 16)) = word1;
+ *((uint32*)(pred + 4)) = word2;
+ *((uint32*)(pred + 8)) = word3;
+ *((uint32*)(pred + 12)) = word4;
+
+ }
+
+ sum = word1 & 0xFF00FF;
+ word1 = (word1 >> 8) & 0xFF00FF;
+ sum += word1;
+ word1 = (word2 & 0xFF00FF);
+ sum += word1;
+ word2 = (word2 >> 8) & 0xFF00FF;
+ sum += word2;
+ word1 = (word3 & 0xFF00FF);
+ sum += word1;
+ word3 = (word3 >> 8) & 0xFF00FF;
+ sum += word3;
+ word1 = (word4 & 0xFF00FF);
+ sum += word1;
+ word4 = (word4 >> 8) & 0xFF00FF;
+ sum += word4;
+
+ sum += (sum >> 16);
+ sum &= 0xFFFF;
+
+ if (!video->intraAvailA)
+ {
+ sum = (sum + 8) >> 4;
+ }
+ }
+
+ if (video->intraAvailA)
+ {
+ // get horizontal mode
+ left = curL - 1 - pitch;
+
+ pred = encvid->pred_i16[AVC_I16_Horizontal] - 16;
+
+ for (i = 0; i < 16; i++)
+ {
+ word1 = *(left += pitch);
+ sum += word1;
+
+ word1 = (word1 << 8) | word1;
+ word1 = (word1 << 16) | word1; /* make it 4 */
+
+ *(uint32*)(pred += 16) = word1;
+ *(uint32*)(pred + 4) = word1;
+ *(uint32*)(pred + 8) = word1;
+ *(uint32*)(pred + 12) = word1;
+ }
+
+ if (!video->intraAvailB)
+ {
+ sum = (sum + 8) >> 4;
+ }
+ else
+ {
+ sum = (sum + 16) >> 5;
+ }
+ }
+
+ // get DC mode
+ if (!video->intraAvailA && !video->intraAvailB)
+ {
+ sum = 0x80808080;
+ }
+ else
+ {
+ sum = (sum << 8) | sum;
+ sum = (sum << 16) | sum;
+ }
+
+ pred = encvid->pred_i16[AVC_I16_DC] - 16;
+ for (i = 0; i < 16; i++)
+ {
+ *((uint32*)(pred += 16)) = sum;
+ *((uint32*)(pred + 4)) = sum;
+ *((uint32*)(pred + 8)) = sum;
+ *((uint32*)(pred + 12)) = sum;
+ }
+
+ // get plane mode
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ pred = encvid->pred_i16[AVC_I16_Plane] - 16;
+
+ comp_ref_x0 = curL - pitch + 8;
+ comp_ref_x1 = curL - pitch + 6;
+ comp_ref_y0 = curL - 1 + (pitch << 3);
+ comp_ref_y1 = curL - 1 + 6 * pitch;
+
+ for (i = 1; i < 8; i++)
+ {
+ H += i * (*comp_ref_x0++ - *comp_ref_x1--);
+ V += i * (*comp_ref_y0 - *comp_ref_y1);
+ comp_ref_y0 += pitch;
+ comp_ref_y1 -= pitch;
+ }
+
+ H += i * (*comp_ref_x0++ - curL[-pitch-1]);
+ V += i * (*comp_ref_y0 - *comp_ref_y1);
+
+
+ a_16 = ((*(curL - pitch + 15) + *(curL - 1 + 15 * pitch)) << 4) + 16;;
+ b = (5 * H + 32) >> 6;
+ c = (5 * V + 32) >> 6;
+
+ tmp = 0;
+ for (i = 0; i < 16; i++)
+ {
+ factor_c = a_16 + c * (tmp++ - 7);
+ factor_c -= 7 * b;
+
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 16);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 24);
+ *((uint32*)(pred += 16)) = word1;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 16);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 24);
+ *((uint32*)(pred + 4)) = word1;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 16);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 24);
+ *((uint32*)(pred + 8)) = word1;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 16);
+ value = factor_c >> 5;
+ CLIP_RESULT(value)
+ word1 = (word1) | (value << 24);
+ *((uint32*)(pred + 12)) = word1;
+ }
+ }
+
+ return ;
+}
+
+
+/* evaluate each prediction mode of I16 */
+void find_cost_16x16(AVCEncObject *encvid, uint8 *orgY, int *min_cost)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMacroblock *currMB = video->currMB;
+ int cost;
+ int org_pitch = encvid->currInput->pitch;
+
+ /* evaluate vertical mode */
+ if (video->intraAvailB)
+ {
+ cost = cost_i16(orgY, org_pitch, encvid->pred_i16[AVC_I16_Vertical], *min_cost);
+ if (cost < *min_cost)
+ {
+ *min_cost = cost;
+ currMB->mbMode = AVC_I16;
+ currMB->mb_intra = 1;
+ currMB->i16Mode = AVC_I16_Vertical;
+ }
+ }
+
+
+ /* evaluate horizontal mode */
+ if (video->intraAvailA)
+ {
+ cost = cost_i16(orgY, org_pitch, encvid->pred_i16[AVC_I16_Horizontal], *min_cost);
+ if (cost < *min_cost)
+ {
+ *min_cost = cost;
+ currMB->mbMode = AVC_I16;
+ currMB->mb_intra = 1;
+ currMB->i16Mode = AVC_I16_Horizontal;
+ }
+ }
+
+ /* evaluate DC mode */
+ cost = cost_i16(orgY, org_pitch, encvid->pred_i16[AVC_I16_DC], *min_cost);
+ if (cost < *min_cost)
+ {
+ *min_cost = cost;
+ currMB->mbMode = AVC_I16;
+ currMB->mb_intra = 1;
+ currMB->i16Mode = AVC_I16_DC;
+ }
+
+ /* evaluate plane mode */
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ cost = cost_i16(orgY, org_pitch, encvid->pred_i16[AVC_I16_Plane], *min_cost);
+ if (cost < *min_cost)
+ {
+ *min_cost = cost;
+ currMB->mbMode = AVC_I16;
+ currMB->mb_intra = 1;
+ currMB->i16Mode = AVC_I16_Plane;
+ }
+ }
+
+ return ;
+}
+
+
+int cost_i16(uint8 *org, int org_pitch, uint8 *pred, int min_cost)
+{
+
+ int cost;
+ int j, k;
+ int16 res[256], *pres; // residue
+ int m0, m1, m2, m3;
+
+ // calculate SATD
+ org_pitch -= 16;
+ pres = res;
+ // horizontal transform
+ for (j = 0; j < 16; j++)
+ {
+ k = 4;
+ while (k > 0)
+ {
+ m0 = org[0] - pred[0];
+ m3 = org[3] - pred[3];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = org[1] - pred[1];
+ m2 = org[2] - pred[2];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[2] = m0 - m1;
+ pres[1] = m2 + m3;
+ pres[3] = m3 - m2;
+
+ org += 4;
+ pres += 4;
+ pred += 4;
+ k--;
+ }
+ org += org_pitch;
+ }
+ /* vertical transform */
+ cost = 0;
+ for (j = 0; j < 4; j++)
+ {
+ pres = res + (j << 6);
+ k = 16;
+ while (k > 0)
+ {
+ m0 = pres[0];
+ m3 = pres[3<<4];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = pres[1<<4];
+ m2 = pres[2<<4];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 = m0 + m1;
+
+ if (k&0x3) // only sum up non DC values.
+ {
+ cost += ((m0 > 0) ? m0 : -m0);
+ }
+
+ m1 = m0 - (m1 << 1);
+ cost += ((m1 > 0) ? m1 : -m1);
+ m3 = m2 + m3;
+ cost += ((m3 > 0) ? m3 : -m3);
+ m2 = m3 - (m2 << 1);
+ cost += ((m2 > 0) ? m2 : -m2);
+
+ pres++;
+ k--;
+ }
+ if ((cost >> 1) > min_cost) /* early drop out */
+ {
+ return (cost >> 1);
+ }
+ }
+
+ /* Hadamard of the DC coefficient */
+ pres = res;
+ k = 4;
+ while (k > 0)
+ {
+ m0 = pres[0];
+ m3 = pres[3<<2];
+ m0 >>= 2;
+ m0 += (m3 >> 2);
+ m3 = m0 - (m3 >> 1);
+ m1 = pres[1<<2];
+ m2 = pres[2<<2];
+ m1 >>= 2;
+ m1 += (m2 >> 2);
+ m2 = m1 - (m2 >> 1);
+ pres[0] = (m0 + m1);
+ pres[2<<2] = (m0 - m1);
+ pres[1<<2] = (m2 + m3);
+ pres[3<<2] = (m3 - m2);
+ pres += (4 << 4);
+ k--;
+ }
+
+ pres = res;
+ k = 4;
+ while (k > 0)
+ {
+ m0 = pres[0];
+ m3 = pres[3<<6];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = pres[1<<6];
+ m2 = pres[2<<6];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ m0 = m0 + m1;
+ cost += ((m0 >= 0) ? m0 : -m0);
+ m1 = m0 - (m1 << 1);
+ cost += ((m1 >= 0) ? m1 : -m1);
+ m3 = m2 + m3;
+ cost += ((m3 >= 0) ? m3 : -m3);
+ m2 = m3 - (m2 << 1);
+ cost += ((m2 >= 0) ? m2 : -m2);
+ pres += 4;
+
+ if ((cost >> 1) > min_cost) /* early drop out */
+ {
+ return (cost >> 1);
+ }
+
+ k--;
+ }
+
+ return (cost >> 1);
+}
+
+
+void mb_intra4x4_search(AVCEncObject *encvid, int *min_cost)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMacroblock *currMB = video->currMB;
+ AVCPictureData *currPic = video->currPic;
+ AVCFrameIO *currInput = encvid->currInput;
+ int pitch = currPic->pitch;
+ int org_pitch = currInput->pitch;
+ int offset;
+ uint8 *curL, *comp, *org4, *org8;
+ int y = video->mb_y << 4;
+ int x = video->mb_x << 4;
+
+ int b8, b4, cost4x4, blkidx;
+ int cost = 0;
+ int numcoef;
+ int dummy = 0;
+ int mb_intra = currMB->mb_intra; // save the original value
+
+ offset = y * pitch + x;
+
+ curL = currPic->Sl + offset;
+ org8 = currInput->YCbCr[0] + y * org_pitch + x;
+ video->pred_pitch = 4;
+
+ cost = (int)(6.0 * encvid->lambda_mode + 0.4999);
+ cost <<= 2;
+
+ currMB->mb_intra = 1; // temporary set this to one to enable the IDCT
+ // operation inside dct_luma
+
+ for (b8 = 0; b8 < 4; b8++)
+ {
+ comp = curL;
+ org4 = org8;
+
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ blkidx = blkIdx2blkXY[b8][b4];
+ cost4x4 = blk_intra4x4_search(encvid, blkidx, comp, org4);
+ cost += cost4x4;
+ if (cost > *min_cost)
+ {
+ currMB->mb_intra = mb_intra; // restore the value
+ return ;
+ }
+
+ /* do residue, Xfrm, Q, invQ, invXfrm, recon and save the DCT coefs.*/
+ video->pred_block = encvid->pred_i4[currMB->i4Mode[blkidx]];
+ numcoef = dct_luma(encvid, blkidx, comp, org4, &dummy);
+ currMB->nz_coeff[blkidx] = numcoef;
+ if (numcoef)
+ {
+ video->cbp4x4 |= (1 << blkidx);
+ currMB->CBP |= (1 << b8);
+ }
+
+ if (b4&1)
+ {
+ comp += ((pitch << 2) - 4);
+ org4 += ((org_pitch << 2) - 4);
+ }
+ else
+ {
+ comp += 4;
+ org4 += 4;
+ }
+ }
+
+ if (b8&1)
+ {
+ curL += ((pitch << 3) - 8);
+ org8 += ((org_pitch << 3) - 8);
+ }
+ else
+ {
+ curL += 8;
+ org8 += 8;
+ }
+ }
+
+ currMB->mb_intra = mb_intra; // restore the value
+
+ if (cost < *min_cost)
+ {
+ *min_cost = cost;
+ currMB->mbMode = AVC_I4;
+ currMB->mb_intra = 1;
+ }
+
+ return ;
+}
+
+
+/* search for i4 mode for a 4x4 block */
+int blk_intra4x4_search(AVCEncObject *encvid, int blkidx, uint8 *cur, uint8 *org)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCNeighborAvailability availability;
+ AVCMacroblock *currMB = video->currMB;
+ bool top_left = FALSE;
+ int pitch = video->currPic->pitch;
+ uint8 mode_avail[AVCNumI4PredMode];
+ uint32 temp, DC;
+ uint8 *pred;
+ int org_pitch = encvid->currInput->pitch;
+ uint16 min_cost, cost;
+
+ int P_x, Q_x, R_x, P_y, Q_y, R_y, D, D0, D1;
+ int P0, Q0, R0, S0, P1, Q1, R1, P2, Q2;
+ uint8 P_A, P_B, P_C, P_D, P_E, P_F, P_G, P_H, P_I, P_J, P_K, P_L, P_X;
+ int r0, r1, r2, r3, r4, r5, r6, r7;
+ int x0, x1, x2, x3, x4, x5;
+ uint32 temp1, temp2;
+
+ int ipmode, mostProbableMode;
+ int fixedcost = 4 * encvid->lambda_mode;
+ int min_sad = 0x7FFF;
+
+ availability.left = TRUE;
+ availability.top = TRUE;
+ if (blkidx <= 3) /* top row block (!block_y) */
+ { /* check availability up */
+ availability.top = video->intraAvailB ;
+ }
+ if (!(blkidx&0x3)) /* left column block (!block_x)*/
+ { /* check availability left */
+ availability.left = video->intraAvailA ;
+ }
+ availability.top_right = BlkTopRight[blkidx];
+
+ if (availability.top_right == 2)
+ {
+ availability.top_right = video->intraAvailB;
+ }
+ else if (availability.top_right == 3)
+ {
+ availability.top_right = video->intraAvailC;
+ }
+
+ if (availability.top == TRUE)
+ {
+ temp = *(uint32*)(cur - pitch);
+ P_A = temp & 0xFF;
+ P_B = (temp >> 8) & 0xFF;
+ P_C = (temp >> 16) & 0xFF;
+ P_D = (temp >> 24) & 0xFF;
+ }
+ else
+ {
+ P_A = P_B = P_C = P_D = 128;
+ }
+
+ if (availability.top_right == TRUE)
+ {
+ temp = *(uint32*)(cur - pitch + 4);
+ P_E = temp & 0xFF;
+ P_F = (temp >> 8) & 0xFF;
+ P_G = (temp >> 16) & 0xFF;
+ P_H = (temp >> 24) & 0xFF;
+ }
+ else
+ {
+ P_E = P_F = P_G = P_H = 128;
+ }
+
+ if (availability.left == TRUE)
+ {
+ cur--;
+ P_I = *cur;
+ P_J = *(cur += pitch);
+ P_K = *(cur += pitch);
+ P_L = *(cur + pitch);
+ cur -= (pitch << 1);
+ cur++;
+ }
+ else
+ {
+ P_I = P_J = P_K = P_L = 128;
+ }
+
+ /* check if top-left pixel is available */
+ if (((blkidx > 3) && (blkidx&0x3)) || ((blkidx > 3) && video->intraAvailA)
+ || ((blkidx&0x3) && video->intraAvailB)
+ || (video->intraAvailA && video->intraAvailD && video->intraAvailB))
+ {
+ top_left = TRUE;
+ P_X = *(cur - pitch - 1);
+ }
+ else
+ {
+ P_X = 128;
+ }
+
+ //===== INTRA PREDICTION FOR 4x4 BLOCK =====
+ /* vertical */
+ mode_avail[AVC_I4_Vertical] = 0;
+ if (availability.top)
+ {
+ mode_avail[AVC_I4_Vertical] = 1;
+ pred = encvid->pred_i4[AVC_I4_Vertical];
+
+ temp = (P_D << 24) | (P_C << 16) | (P_B << 8) | P_A ;
+ *((uint32*)pred) = temp; /* write 4 at a time */
+ *((uint32*)(pred += 4)) = temp;
+ *((uint32*)(pred += 4)) = temp;
+ *((uint32*)(pred += 4)) = temp;
+ }
+ /* horizontal */
+ mode_avail[AVC_I4_Horizontal] = 0;
+ mode_avail[AVC_I4_Horizontal_Up] = 0;
+ if (availability.left)
+ {
+ mode_avail[AVC_I4_Horizontal] = 1;
+ pred = encvid->pred_i4[AVC_I4_Horizontal];
+
+ temp = P_I | (P_I << 8);
+ temp = temp | (temp << 16);
+ *((uint32*)pred) = temp;
+ temp = P_J | (P_J << 8);
+ temp = temp | (temp << 16);
+ *((uint32*)(pred += 4)) = temp;
+ temp = P_K | (P_K << 8);
+ temp = temp | (temp << 16);
+ *((uint32*)(pred += 4)) = temp;
+ temp = P_L | (P_L << 8);
+ temp = temp | (temp << 16);
+ *((uint32*)(pred += 4)) = temp;
+
+ mode_avail[AVC_I4_Horizontal_Up] = 1;
+ pred = encvid->pred_i4[AVC_I4_Horizontal_Up];
+
+ Q0 = (P_J + P_K + 1) >> 1;
+ Q1 = (P_J + (P_K << 1) + P_L + 2) >> 2;
+ P0 = ((P_I + P_J + 1) >> 1);
+ P1 = ((P_I + (P_J << 1) + P_K + 2) >> 2);
+
+ temp = P0 | (P1 << 8); // [P0 P1 Q0 Q1]
+ temp |= (Q0 << 16); // [Q0 Q1 R0 DO]
+ temp |= (Q1 << 24); // [R0 D0 D1 D1]
+ *((uint32*)pred) = temp; // [D1 D1 D1 D1]
+
+ D0 = (P_K + 3 * P_L + 2) >> 2;
+ R0 = (P_K + P_L + 1) >> 1;
+
+ temp = Q0 | (Q1 << 8);
+ temp |= (R0 << 16);
+ temp |= (D0 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ D1 = P_L;
+
+ temp = R0 | (D0 << 8);
+ temp |= (D1 << 16);
+ temp |= (D1 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = D1 | (D1 << 8);
+ temp |= (temp << 16);
+ *((uint32*)(pred += 4)) = temp;
+ }
+ /* DC */
+ mode_avail[AVC_I4_DC] = 1;
+ pred = encvid->pred_i4[AVC_I4_DC];
+ if (availability.left)
+ {
+ DC = P_I + P_J + P_K + P_L;
+
+ if (availability.top)
+ {
+ DC = (P_A + P_B + P_C + P_D + DC + 4) >> 3;
+ }
+ else
+ {
+ DC = (DC + 2) >> 2;
+
+ }
+ }
+ else if (availability.top)
+ {
+ DC = (P_A + P_B + P_C + P_D + 2) >> 2;
+
+ }
+ else
+ {
+ DC = 128;
+ }
+
+ temp = DC | (DC << 8);
+ temp = temp | (temp << 16);
+ *((uint32*)pred) = temp;
+ *((uint32*)(pred += 4)) = temp;
+ *((uint32*)(pred += 4)) = temp;
+ *((uint32*)(pred += 4)) = temp;
+
+ /* Down-left */
+ mode_avail[AVC_I4_Diagonal_Down_Left] = 0;
+
+ if (availability.top)
+ {
+ mode_avail[AVC_I4_Diagonal_Down_Left] = 1;
+
+ pred = encvid->pred_i4[AVC_I4_Diagonal_Down_Left];
+
+ r0 = P_A;
+ r1 = P_B;
+ r2 = P_C;
+ r3 = P_D;
+
+ r0 += (r1 << 1);
+ r0 += r2;
+ r0 += 2;
+ r0 >>= 2;
+ r1 += (r2 << 1);
+ r1 += r3;
+ r1 += 2;
+ r1 >>= 2;
+
+ if (availability.top_right)
+ {
+ r4 = P_E;
+ r5 = P_F;
+ r6 = P_G;
+ r7 = P_H;
+
+ r2 += (r3 << 1);
+ r2 += r4;
+ r2 += 2;
+ r2 >>= 2;
+ r3 += (r4 << 1);
+ r3 += r5;
+ r3 += 2;
+ r3 >>= 2;
+ r4 += (r5 << 1);
+ r4 += r6;
+ r4 += 2;
+ r4 >>= 2;
+ r5 += (r6 << 1);
+ r5 += r7;
+ r5 += 2;
+ r5 >>= 2;
+ r6 += (3 * r7);
+ r6 += 2;
+ r6 >>= 2;
+ temp = r0 | (r1 << 8);
+ temp |= (r2 << 16);
+ temp |= (r3 << 24);
+ *((uint32*)pred) = temp;
+
+ temp = (temp >> 8) | (r4 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = (temp >> 8) | (r5 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = (temp >> 8) | (r6 << 24);
+ *((uint32*)(pred += 4)) = temp;
+ }
+ else
+ {
+ r2 += (r3 * 3);
+ r2 += 2;
+ r2 >>= 2;
+ r3 = ((r3 << 2) + 2);
+ r3 >>= 2;
+
+ temp = r0 | (r1 << 8);
+ temp |= (r2 << 16);
+ temp |= (r3 << 24);
+ *((uint32*)pred) = temp;
+
+ temp = (temp >> 8) | (r3 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = (temp >> 8) | (r3 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = (temp >> 8) | (r3 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ }
+ }
+
+ /* Down Right */
+ mode_avail[AVC_I4_Diagonal_Down_Right] = 0;
+ /* Diagonal Vertical Right */
+ mode_avail[AVC_I4_Vertical_Right] = 0;
+ /* Horizontal Down */
+ mode_avail[AVC_I4_Horizontal_Down] = 0;
+
+ if (top_left == TRUE)
+ {
+ /* Down Right */
+ mode_avail[AVC_I4_Diagonal_Down_Right] = 1;
+ pred = encvid->pred_i4[AVC_I4_Diagonal_Down_Right];
+
+ Q_x = (P_A + 2 * P_B + P_C + 2) >> 2;
+ R_x = (P_B + 2 * P_C + P_D + 2) >> 2;
+ P_x = (P_X + 2 * P_A + P_B + 2) >> 2;
+ D = (P_A + 2 * P_X + P_I + 2) >> 2;
+ P_y = (P_X + 2 * P_I + P_J + 2) >> 2;
+ Q_y = (P_I + 2 * P_J + P_K + 2) >> 2;
+ R_y = (P_J + 2 * P_K + P_L + 2) >> 2;
+
+ /* we can pack these */
+ temp = D | (P_x << 8); //[D P_x Q_x R_x]
+ //[P_y D P_x Q_x]
+ temp |= (Q_x << 16); //[Q_y P_y D P_x]
+ temp |= (R_x << 24); //[R_y Q_y P_y D ]
+ *((uint32*)pred) = temp;
+
+ temp = P_y | (D << 8);
+ temp |= (P_x << 16);
+ temp |= (Q_x << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = Q_y | (P_y << 8);
+ temp |= (D << 16);
+ temp |= (P_x << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = R_y | (Q_y << 8);
+ temp |= (P_y << 16);
+ temp |= (D << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+
+ /* Diagonal Vertical Right */
+ mode_avail[AVC_I4_Vertical_Right] = 1;
+ pred = encvid->pred_i4[AVC_I4_Vertical_Right];
+
+ Q0 = P_A + P_B + 1;
+ R0 = P_B + P_C + 1;
+ S0 = P_C + P_D + 1;
+ P0 = P_X + P_A + 1;
+ D = (P_I + 2 * P_X + P_A + 2) >> 2;
+
+ P1 = (P0 + Q0) >> 2;
+ Q1 = (Q0 + R0) >> 2;
+ R1 = (R0 + S0) >> 2;
+
+ P0 >>= 1;
+ Q0 >>= 1;
+ R0 >>= 1;
+ S0 >>= 1;
+
+ P2 = (P_X + 2 * P_I + P_J + 2) >> 2;
+ Q2 = (P_I + 2 * P_J + P_K + 2) >> 2;
+
+ temp = P0 | (Q0 << 8); //[P0 Q0 R0 S0]
+ //[D P1 Q1 R1]
+ temp |= (R0 << 16); //[P2 P0 Q0 R0]
+ temp |= (S0 << 24); //[Q2 D P1 Q1]
+ *((uint32*)pred) = temp;
+
+ temp = D | (P1 << 8);
+ temp |= (Q1 << 16);
+ temp |= (R1 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = P2 | (P0 << 8);
+ temp |= (Q0 << 16);
+ temp |= (R0 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = Q2 | (D << 8);
+ temp |= (P1 << 16);
+ temp |= (Q1 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+
+ /* Horizontal Down */
+ mode_avail[AVC_I4_Horizontal_Down] = 1;
+ pred = encvid->pred_i4[AVC_I4_Horizontal_Down];
+
+
+ Q2 = (P_A + 2 * P_B + P_C + 2) >> 2;
+ P2 = (P_X + 2 * P_A + P_B + 2) >> 2;
+ D = (P_I + 2 * P_X + P_A + 2) >> 2;
+ P0 = P_X + P_I + 1;
+ Q0 = P_I + P_J + 1;
+ R0 = P_J + P_K + 1;
+ S0 = P_K + P_L + 1;
+
+ P1 = (P0 + Q0) >> 2;
+ Q1 = (Q0 + R0) >> 2;
+ R1 = (R0 + S0) >> 2;
+
+ P0 >>= 1;
+ Q0 >>= 1;
+ R0 >>= 1;
+ S0 >>= 1;
+
+
+ /* we can pack these */
+ temp = P0 | (D << 8); //[P0 D P2 Q2]
+ //[Q0 P1 P0 D ]
+ temp |= (P2 << 16); //[R0 Q1 Q0 P1]
+ temp |= (Q2 << 24); //[S0 R1 R0 Q1]
+ *((uint32*)pred) = temp;
+
+ temp = Q0 | (P1 << 8);
+ temp |= (P0 << 16);
+ temp |= (D << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = R0 | (Q1 << 8);
+ temp |= (Q0 << 16);
+ temp |= (P1 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ temp = S0 | (R1 << 8);
+ temp |= (R0 << 16);
+ temp |= (Q1 << 24);
+ *((uint32*)(pred += 4)) = temp;
+
+ }
+
+ /* vertical left */
+ mode_avail[AVC_I4_Vertical_Left] = 0;
+ if (availability.top)
+ {
+ mode_avail[AVC_I4_Vertical_Left] = 1;
+ pred = encvid->pred_i4[AVC_I4_Vertical_Left];
+
+ x0 = P_A + P_B + 1;
+ x1 = P_B + P_C + 1;
+ x2 = P_C + P_D + 1;
+ if (availability.top_right)
+ {
+ x3 = P_D + P_E + 1;
+ x4 = P_E + P_F + 1;
+ x5 = P_F + P_G + 1;
+ }
+ else
+ {
+ x3 = x4 = x5 = (P_D << 1) + 1;
+ }
+
+ temp1 = (x0 >> 1);
+ temp1 |= ((x1 >> 1) << 8);
+ temp1 |= ((x2 >> 1) << 16);
+ temp1 |= ((x3 >> 1) << 24);
+
+ *((uint32*)pred) = temp1;
+
+ temp2 = ((x0 + x1) >> 2);
+ temp2 |= (((x1 + x2) >> 2) << 8);
+ temp2 |= (((x2 + x3) >> 2) << 16);
+ temp2 |= (((x3 + x4) >> 2) << 24);
+
+ *((uint32*)(pred += 4)) = temp2;
+
+ temp1 = (temp1 >> 8) | ((x4 >> 1) << 24); /* rotate out old value */
+ *((uint32*)(pred += 4)) = temp1;
+
+ temp2 = (temp2 >> 8) | (((x4 + x5) >> 2) << 24); /* rotate out old value */
+ *((uint32*)(pred += 4)) = temp2;
+ }
+
+ //===== LOOP OVER ALL 4x4 INTRA PREDICTION MODES =====
+ // can re-order the search here instead of going in order
+
+ // find most probable mode
+ encvid->mostProbableI4Mode[blkidx] = mostProbableMode = FindMostProbableI4Mode(video, blkidx);
+
+ min_cost = 0xFFFF;
+
+ for (ipmode = 0; ipmode < AVCNumI4PredMode; ipmode++)
+ {
+ if (mode_avail[ipmode] == TRUE)
+ {
+ cost = (ipmode == mostProbableMode) ? 0 : fixedcost;
+ pred = encvid->pred_i4[ipmode];
+
+ cost_i4(org, org_pitch, pred, &cost);
+
+ if (cost < min_cost)
+ {
+ currMB->i4Mode[blkidx] = (AVCIntra4x4PredMode)ipmode;
+ min_cost = cost;
+ min_sad = cost - ((ipmode == mostProbableMode) ? 0 : fixedcost);
+ }
+ }
+ }
+
+ if (blkidx == 0)
+ {
+ encvid->i4_sad = min_sad;
+ }
+ else
+ {
+ encvid->i4_sad += min_sad;
+ }
+
+ return min_cost;
+}
+
+int FindMostProbableI4Mode(AVCCommonObj *video, int blkidx)
+{
+ int dcOnlyPredictionFlag;
+ AVCMacroblock *currMB = video->currMB;
+ int intra4x4PredModeA, intra4x4PredModeB, predIntra4x4PredMode;
+
+
+ dcOnlyPredictionFlag = 0;
+ if (blkidx&0x3)
+ {
+ intra4x4PredModeA = currMB->i4Mode[blkidx-1]; // block to the left
+ }
+ else /* for blk 0, 4, 8, 12 */
+ {
+ if (video->intraAvailA)
+ {
+ if (video->mblock[video->mbAddrA].mbMode == AVC_I4)
+ {
+ intra4x4PredModeA = video->mblock[video->mbAddrA].i4Mode[blkidx + 3];
+ }
+ else
+ {
+ intra4x4PredModeA = AVC_I4_DC;
+ }
+ }
+ else
+ {
+ dcOnlyPredictionFlag = 1;
+ goto PRED_RESULT_READY; // skip below
+ }
+ }
+
+ if (blkidx >> 2)
+ {
+ intra4x4PredModeB = currMB->i4Mode[blkidx-4]; // block above
+ }
+ else /* block 0, 1, 2, 3 */
+ {
+ if (video->intraAvailB)
+ {
+ if (video->mblock[video->mbAddrB].mbMode == AVC_I4)
+ {
+ intra4x4PredModeB = video->mblock[video->mbAddrB].i4Mode[blkidx+12];
+ }
+ else
+ {
+ intra4x4PredModeB = AVC_I4_DC;
+ }
+ }
+ else
+ {
+ dcOnlyPredictionFlag = 1;
+ }
+ }
+
+PRED_RESULT_READY:
+ if (dcOnlyPredictionFlag)
+ {
+ intra4x4PredModeA = intra4x4PredModeB = AVC_I4_DC;
+ }
+
+ predIntra4x4PredMode = AVC_MIN(intra4x4PredModeA, intra4x4PredModeB);
+
+ return predIntra4x4PredMode;
+}
+
+void cost_i4(uint8 *org, int org_pitch, uint8 *pred, uint16 *cost)
+{
+ int k;
+ int16 res[16], *pres;
+ int m0, m1, m2, m3, tmp1;
+ int satd = 0;
+
+ pres = res;
+ // horizontal transform
+ k = 4;
+ while (k > 0)
+ {
+ m0 = org[0] - pred[0];
+ m3 = org[3] - pred[3];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = org[1] - pred[1];
+ m2 = org[2] - pred[2];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[2] = m0 - m1;
+ pres[1] = m2 + m3;
+ pres[3] = m3 - m2;
+
+ org += org_pitch;
+ pres += 4;
+ pred += 4;
+ k--;
+ }
+ /* vertical transform */
+ pres = res;
+ k = 4;
+ while (k > 0)
+ {
+ m0 = pres[0];
+ m3 = pres[12];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = pres[4];
+ m2 = pres[8];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[8] = m0 - m1;
+ pres[4] = m2 + m3;
+ pres[12] = m3 - m2;
+
+ pres++;
+ k--;
+
+ }
+
+ pres = res;
+ k = 4;
+ while (k > 0)
+ {
+ tmp1 = *pres++;
+ satd += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ satd += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ satd += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ satd += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ k--;
+ }
+
+ satd = (satd + 1) >> 1;
+ *cost += satd;
+
+ return ;
+}
+
+void chroma_intra_search(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCPictureData *currPic = video->currPic;
+
+ int x_pos = video->mb_x << 3;
+ int y_pos = video->mb_y << 3;
+ int pitch = currPic->pitch >> 1;
+ int offset = y_pos * pitch + x_pos;
+
+ uint8 *comp_ref_x, *comp_ref_y, *pred;
+ int sum_x0, sum_x1, sum_y0, sum_y1;
+ int pred_0[2], pred_1[2], pred_2[2], pred_3[2];
+ uint32 pred_a, pred_b, pred_c, pred_d;
+ int i, j, component;
+ int a_16, b, c, factor_c, topleft;
+ int H, V, value;
+ uint8 *comp_ref_x0, *comp_ref_x1, *comp_ref_y0, *comp_ref_y1;
+
+ uint8 *curCb = currPic->Scb + offset;
+ uint8 *curCr = currPic->Scr + offset;
+
+ uint8 *orgCb, *orgCr;
+ AVCFrameIO *currInput = encvid->currInput;
+ AVCMacroblock *currMB = video->currMB;
+ int org_pitch;
+ int cost, mincost;
+
+ /* evaluate DC mode */
+ if (video->intraAvailB & video->intraAvailA)
+ {
+ comp_ref_x = curCb - pitch;
+ comp_ref_y = curCb - 1;
+
+ for (i = 0; i < 2; i++)
+ {
+ pred_a = *((uint32*)comp_ref_x);
+ comp_ref_x += 4;
+ pred_b = (pred_a >> 8) & 0xFF00FF;
+ pred_a &= 0xFF00FF;
+ pred_a += pred_b;
+ pred_a += (pred_a >> 16);
+ sum_x0 = pred_a & 0xFFFF;
+
+ pred_a = *((uint32*)comp_ref_x);
+ pred_b = (pred_a >> 8) & 0xFF00FF;
+ pred_a &= 0xFF00FF;
+ pred_a += pred_b;
+ pred_a += (pred_a >> 16);
+ sum_x1 = pred_a & 0xFFFF;
+
+ pred_1[i] = (sum_x1 + 2) >> 2;
+
+ sum_y0 = *comp_ref_y;
+ sum_y0 += *(comp_ref_y += pitch);
+ sum_y0 += *(comp_ref_y += pitch);
+ sum_y0 += *(comp_ref_y += pitch);
+
+ sum_y1 = *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+
+ pred_2[i] = (sum_y1 + 2) >> 2;
+
+ pred_0[i] = (sum_y0 + sum_x0 + 4) >> 3;
+ pred_3[i] = (sum_y1 + sum_x1 + 4) >> 3;
+
+ comp_ref_x = curCr - pitch;
+ comp_ref_y = curCr - 1;
+ }
+ }
+
+ else if (video->intraAvailA)
+ {
+ comp_ref_y = curCb - 1;
+ for (i = 0; i < 2; i++)
+ {
+ sum_y0 = *comp_ref_y;
+ sum_y0 += *(comp_ref_y += pitch);
+ sum_y0 += *(comp_ref_y += pitch);
+ sum_y0 += *(comp_ref_y += pitch);
+
+ sum_y1 = *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+ sum_y1 += *(comp_ref_y += pitch);
+
+ pred_0[i] = pred_1[i] = (sum_y0 + 2) >> 2;
+ pred_2[i] = pred_3[i] = (sum_y1 + 2) >> 2;
+
+ comp_ref_y = curCr - 1;
+ }
+ }
+ else if (video->intraAvailB)
+ {
+ comp_ref_x = curCb - pitch;
+ for (i = 0; i < 2; i++)
+ {
+ pred_a = *((uint32*)comp_ref_x);
+ comp_ref_x += 4;
+ pred_b = (pred_a >> 8) & 0xFF00FF;
+ pred_a &= 0xFF00FF;
+ pred_a += pred_b;
+ pred_a += (pred_a >> 16);
+ sum_x0 = pred_a & 0xFFFF;
+
+ pred_a = *((uint32*)comp_ref_x);
+ pred_b = (pred_a >> 8) & 0xFF00FF;
+ pred_a &= 0xFF00FF;
+ pred_a += pred_b;
+ pred_a += (pred_a >> 16);
+ sum_x1 = pred_a & 0xFFFF;
+
+ pred_0[i] = pred_2[i] = (sum_x0 + 2) >> 2;
+ pred_1[i] = pred_3[i] = (sum_x1 + 2) >> 2;
+
+ comp_ref_x = curCr - pitch;
+ }
+ }
+ else
+ {
+ pred_0[0] = pred_0[1] = pred_1[0] = pred_1[1] =
+ pred_2[0] = pred_2[1] = pred_3[0] = pred_3[1] = 128;
+ }
+
+ pred = encvid->pred_ic[AVC_IC_DC];
+
+ pred_a = pred_0[0];
+ pred_b = pred_1[0];
+ pred_a |= (pred_a << 8);
+ pred_a |= (pred_a << 16);
+ pred_b |= (pred_b << 8);
+ pred_b |= (pred_b << 16);
+
+ pred_c = pred_0[1];
+ pred_d = pred_1[1];
+ pred_c |= (pred_c << 8);
+ pred_c |= (pred_c << 16);
+ pred_d |= (pred_d << 8);
+ pred_d |= (pred_d << 16);
+
+
+ for (j = 0; j < 4; j++) /* 4 lines */
+ {
+ *((uint32*)pred) = pred_a;
+ *((uint32*)(pred + 4)) = pred_b;
+ *((uint32*)(pred + 8)) = pred_c;
+ *((uint32*)(pred + 12)) = pred_d;
+ pred += 16; /* move to the next line */
+ }
+
+ pred_a = pred_2[0];
+ pred_b = pred_3[0];
+ pred_a |= (pred_a << 8);
+ pred_a |= (pred_a << 16);
+ pred_b |= (pred_b << 8);
+ pred_b |= (pred_b << 16);
+
+ pred_c = pred_2[1];
+ pred_d = pred_3[1];
+ pred_c |= (pred_c << 8);
+ pred_c |= (pred_c << 16);
+ pred_d |= (pred_d << 8);
+ pred_d |= (pred_d << 16);
+
+ for (j = 0; j < 4; j++) /* 4 lines */
+ {
+ *((uint32*)pred) = pred_a;
+ *((uint32*)(pred + 4)) = pred_b;
+ *((uint32*)(pred + 8)) = pred_c;
+ *((uint32*)(pred + 12)) = pred_d;
+ pred += 16; /* move to the next line */
+ }
+
+ /* predict horizontal mode */
+ if (video->intraAvailA)
+ {
+ comp_ref_y = curCb - 1;
+ comp_ref_x = curCr - 1;
+ pred = encvid->pred_ic[AVC_IC_Horizontal];
+
+ for (i = 4; i < 6; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ pred_a = *comp_ref_y;
+ comp_ref_y += pitch;
+ pred_a |= (pred_a << 8);
+ pred_a |= (pred_a << 16);
+ *((uint32*)pred) = pred_a;
+ *((uint32*)(pred + 4)) = pred_a;
+
+ pred_a = *comp_ref_x;
+ comp_ref_x += pitch;
+ pred_a |= (pred_a << 8);
+ pred_a |= (pred_a << 16);
+ *((uint32*)(pred + 8)) = pred_a;
+ *((uint32*)(pred + 12)) = pred_a;
+
+ pred += 16;
+ }
+ }
+ }
+
+ /* vertical mode */
+ if (video->intraAvailB)
+ {
+ comp_ref_x = curCb - pitch;
+ comp_ref_y = curCr - pitch;
+ pred = encvid->pred_ic[AVC_IC_Vertical];
+
+ pred_a = *((uint32*)comp_ref_x);
+ pred_b = *((uint32*)(comp_ref_x + 4));
+ pred_c = *((uint32*)comp_ref_y);
+ pred_d = *((uint32*)(comp_ref_y + 4));
+
+ for (j = 0; j < 8; j++)
+ {
+ *((uint32*)pred) = pred_a;
+ *((uint32*)(pred + 4)) = pred_b;
+ *((uint32*)(pred + 8)) = pred_c;
+ *((uint32*)(pred + 12)) = pred_d;
+ pred += 16;
+ }
+ }
+
+ /* Intra_Chroma_Plane */
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ comp_ref_x = curCb - pitch;
+ comp_ref_y = curCb - 1;
+ topleft = curCb[-pitch-1];
+
+ pred = encvid->pred_ic[AVC_IC_Plane];
+ for (component = 0; component < 2; component++)
+ {
+ H = V = 0;
+ comp_ref_x0 = comp_ref_x + 4;
+ comp_ref_x1 = comp_ref_x + 2;
+ comp_ref_y0 = comp_ref_y + (pitch << 2);
+ comp_ref_y1 = comp_ref_y + (pitch << 1);
+ for (i = 1; i < 4; i++)
+ {
+ H += i * (*comp_ref_x0++ - *comp_ref_x1--);
+ V += i * (*comp_ref_y0 - *comp_ref_y1);
+ comp_ref_y0 += pitch;
+ comp_ref_y1 -= pitch;
+ }
+ H += i * (*comp_ref_x0++ - topleft);
+ V += i * (*comp_ref_y0 - *comp_ref_y1);
+
+ a_16 = ((*(comp_ref_x + 7) + *(comp_ref_y + 7 * pitch)) << 4) + 16;
+ b = (17 * H + 16) >> 5;
+ c = (17 * V + 16) >> 5;
+
+ pred_a = 0;
+ for (i = 4; i < 6; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ factor_c = a_16 + c * (pred_a++ - 3);
+
+ factor_c -= 3 * b;
+
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 16);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 24);
+ *((uint32*)pred) = pred_b;
+
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b = value;
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 8);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 16);
+ value = factor_c >> 5;
+ factor_c += b;
+ CLIP_RESULT(value)
+ pred_b |= (value << 24);
+ *((uint32*)(pred + 4)) = pred_b;
+ pred += 16;
+ }
+ }
+
+ pred -= 120; /* point to cr */
+ comp_ref_x = curCr - pitch;
+ comp_ref_y = curCr - 1;
+ topleft = curCr[-pitch-1];
+ }
+ }
+
+ /* now evaluate it */
+
+ org_pitch = (currInput->pitch) >> 1;
+ offset = x_pos + y_pos * org_pitch;
+
+ orgCb = currInput->YCbCr[1] + offset;
+ orgCr = currInput->YCbCr[2] + offset;
+
+ mincost = 0x7fffffff;
+ cost = SATDChroma(orgCb, orgCr, org_pitch, encvid->pred_ic[AVC_IC_DC], mincost);
+ if (cost < mincost)
+ {
+ mincost = cost;
+ currMB->intra_chroma_pred_mode = AVC_IC_DC;
+ }
+
+ if (video->intraAvailA)
+ {
+ cost = SATDChroma(orgCb, orgCr, org_pitch, encvid->pred_ic[AVC_IC_Horizontal], mincost);
+ if (cost < mincost)
+ {
+ mincost = cost;
+ currMB->intra_chroma_pred_mode = AVC_IC_Horizontal;
+ }
+ }
+
+ if (video->intraAvailB)
+ {
+ cost = SATDChroma(orgCb, orgCr, org_pitch, encvid->pred_ic[AVC_IC_Vertical], mincost);
+ if (cost < mincost)
+ {
+ mincost = cost;
+ currMB->intra_chroma_pred_mode = AVC_IC_Vertical;
+ }
+ }
+
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ cost = SATDChroma(orgCb, orgCr, org_pitch, encvid->pred_ic[AVC_IC_Plane], mincost);
+ if (cost < mincost)
+ {
+ mincost = cost;
+ currMB->intra_chroma_pred_mode = AVC_IC_Plane;
+ }
+ }
+
+
+ return ;
+}
+
+
+int SATDChroma(uint8 *orgCb, uint8 *orgCr, int org_pitch, uint8 *pred, int min_cost)
+{
+ int cost;
+ /* first take difference between orgCb, orgCr and pred */
+ int16 res[128], *pres; // residue
+ int m0, m1, m2, m3, tmp1;
+ int j, k;
+
+ pres = res;
+ org_pitch -= 8;
+ // horizontal transform
+ for (j = 0; j < 8; j++)
+ {
+ k = 2;
+ while (k > 0)
+ {
+ m0 = orgCb[0] - pred[0];
+ m3 = orgCb[3] - pred[3];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = orgCb[1] - pred[1];
+ m2 = orgCb[2] - pred[2];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[2] = m0 - m1;
+ pres[1] = m2 + m3;
+ pres[3] = m3 - m2;
+
+ orgCb += 4;
+ pres += 4;
+ pred += 4;
+ k--;
+ }
+ orgCb += org_pitch;
+ k = 2;
+ while (k > 0)
+ {
+ m0 = orgCr[0] - pred[0];
+ m3 = orgCr[3] - pred[3];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = orgCr[1] - pred[1];
+ m2 = orgCr[2] - pred[2];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[2] = m0 - m1;
+ pres[1] = m2 + m3;
+ pres[3] = m3 - m2;
+
+ orgCr += 4;
+ pres += 4;
+ pred += 4;
+ k--;
+ }
+ orgCr += org_pitch;
+ }
+
+ /* vertical transform */
+ for (j = 0; j < 2; j++)
+ {
+ pres = res + (j << 6);
+ k = 16;
+ while (k > 0)
+ {
+ m0 = pres[0];
+ m3 = pres[3<<4];
+ m0 += m3;
+ m3 = m0 - (m3 << 1);
+ m1 = pres[1<<4];
+ m2 = pres[2<<4];
+ m1 += m2;
+ m2 = m1 - (m2 << 1);
+ pres[0] = m0 + m1;
+ pres[2<<4] = m0 - m1;
+ pres[1<<4] = m2 + m3;
+ pres[3<<4] = m3 - m2;
+
+ pres++;
+ k--;
+ }
+ }
+
+ /* now sum of absolute value */
+ pres = res;
+ cost = 0;
+ k = 128;
+ while (k > 0)
+ {
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ tmp1 = *pres++;
+ cost += ((tmp1 >= 0) ? tmp1 : -tmp1);
+ k -= 8;
+ if (cost > min_cost) /* early drop out */
+ {
+ return cost;
+ }
+ }
+
+ return cost;
+}
+
+
+
+///////////////////////////////// old code, unused
+/* find the best intra mode based on original (unencoded) frame */
+/* output is
+ currMB->mb_intra, currMB->mbMode,
+ currMB->i16Mode (if currMB->mbMode == AVC_I16)
+ currMB->i4Mode[..] (if currMB->mbMode == AVC_I4) */
+
+#ifdef FIXED_INTRAPRED_MODE
+void MBIntraSearch(AVCEncObject *encvid, AVCMacroblock *currMB, int mbNum)
+{
+ (void)(mbNum);
+
+ AVCCommonObj *video = encvid->common;
+ int indx, block_x, block_y;
+
+ video->intraAvailA = video->intraAvailB = video->intraAvailC = video->intraAvailD = 0;
+
+ if (!video->currPicParams->constrained_intra_pred_flag)
+ {
+ video->intraAvailA = video->mbAvailA;
+ video->intraAvailB = video->mbAvailB;
+ video->intraAvailC = video->mbAvailC;
+ video->intraAvailD = video->mbAvailD;
+ }
+ else
+ {
+ if (video->mbAvailA)
+ {
+ video->intraAvailA = video->mblock[video->mbAddrA].mb_intra;
+ }
+ if (video->mbAvailB)
+ {
+ video->intraAvailB = video->mblock[video->mbAddrB].mb_intra ;
+ }
+ if (video->mbAvailC)
+ {
+ video->intraAvailC = video->mblock[video->mbAddrC].mb_intra;
+ }
+ if (video->mbAvailD)
+ {
+ video->intraAvailD = video->mblock[video->mbAddrD].mb_intra;
+ }
+ }
+
+ currMB->mb_intra = TRUE;
+ currMB->mbMode = FIXED_INTRAPRED_MODE;
+
+ if (currMB->mbMode == AVC_I16)
+ {
+ currMB->i16Mode = FIXED_I16_MODE;
+
+ if (FIXED_I16_MODE == AVC_I16_Vertical && !video->intraAvailB)
+ {
+ currMB->i16Mode = AVC_I16_DC;
+ }
+
+ if (FIXED_I16_MODE == AVC_I16_Horizontal && !video->intraAvailA)
+ {
+ currMB->i16Mode = AVC_I16_DC;
+ }
+
+ if (FIXED_I16_MODE == AVC_I16_Plane && !(video->intraAvailA && video->intraAvailB && video->intraAvailD))
+ {
+ currMB->i16Mode = AVC_I16_DC;
+ }
+ }
+ else //if(currMB->mbMode == AVC_I4)
+ {
+ for (indx = 0; indx < 16; indx++)
+ {
+ block_x = blkIdx2blkX[indx];
+ block_y = blkIdx2blkY[indx];
+
+ currMB->i4Mode[(block_y<<2)+block_x] = FIXED_I4_MODE;
+
+ if (FIXED_I4_MODE == AVC_I4_Vertical && !(block_y > 0 || video->intraAvailB))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Horizontal && !(block_x || video->intraAvailA))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Diagonal_Down_Left &&
+ (block_y == 0 && !video->intraAvailB))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Diagonal_Down_Right &&
+ !((block_y && block_x)
+ || (block_y && video->intraAvailA)
+ || (block_x && video->intraAvailB)
+ || (video->intraAvailA && video->intraAvailD && video->intraAvailB)))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Vertical_Right &&
+ !((block_y && block_x)
+ || (block_y && video->intraAvailA)
+ || (block_x && video->intraAvailB)
+ || (video->intraAvailA && video->intraAvailD && video->intraAvailB)))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Horizontal_Down &&
+ !((block_y && block_x)
+ || (block_y && video->intraAvailA)
+ || (block_x && video->intraAvailB)
+ || (video->intraAvailA && video->intraAvailD && video->intraAvailB)))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Vertical_Left &&
+ (block_y == 0 && !video->intraAvailB))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+
+ if (FIXED_I4_MODE == AVC_I4_Horizontal_Up && !(block_x || video->intraAvailA))
+ {
+ currMB->i4Mode[(block_y<<2)+block_x] = AVC_I4_DC;
+ }
+ }
+ }
+
+ currMB->intra_chroma_pred_mode = FIXED_INTRA_CHROMA_MODE;
+
+ if (FIXED_INTRA_CHROMA_MODE == AVC_IC_Horizontal && !(video->intraAvailA))
+ {
+ currMB->intra_chroma_pred_mode = AVC_IC_DC;
+ }
+
+ if (FIXED_INTRA_CHROMA_MODE == AVC_IC_Vertical && !(video->intraAvailB))
+ {
+ currMB->intra_chroma_pred_mode = AVC_IC_DC;
+ }
+
+ if (FIXED_INTRA_CHROMA_MODE == AVC_IC_Plane && !(video->intraAvailA && video->intraAvailB && video->intraAvailD))
+ {
+ currMB->intra_chroma_pred_mode = AVC_IC_DC;
+ }
+
+ /* also reset the motion vectors */
+ /* set MV and Ref_Idx codes of Intra blocks in P-slices */
+ memset(currMB->mvL0, 0, sizeof(int32)*16);
+ currMB->ref_idx_L0[0] = -1;
+ currMB->ref_idx_L0[1] = -1;
+ currMB->ref_idx_L0[2] = -1;
+ currMB->ref_idx_L0[3] = -1;
+
+ // output from this function, currMB->mbMode should be set to either
+ // AVC_I4, AVC_I16, or else in AVCMBMode enum, mbType, mb_intra, intra_chroma_pred_mode */
+ return ;
+}
+#else // faster combined prediction+SAD calculation
+void MBIntraSearch(AVCEncObject *encvid, AVCMacroblock *currMB, int mbNum)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCFrameIO *currInput = encvid->currInput;
+ uint8 *curL, *curCb, *curCr;
+ uint8 *comp, *pred_block;
+ int block_x, block_y, offset;
+ uint sad, sad4, sadI4, sadI16;
+ int component, SubBlock_indx, temp;
+ int pitch = video->currPic->pitch;
+
+ /* calculate the cost of each intra prediction mode and compare to the
+ inter mode */
+ /* full search for all intra prediction */
+ offset = (video->mb_y << 4) * pitch + (video->mb_x << 4);
+ curL = currInput->YCbCr[0] + offset;
+ pred_block = video->pred_block + 84;
+
+ /* Assuming that InitNeighborAvailability has been called prior to this function */
+ video->intraAvailA = video->intraAvailB = video->intraAvailC = video->intraAvailD = 0;
+
+ if (!video->currPicParams->constrained_intra_pred_flag)
+ {
+ video->intraAvailA = video->mbAvailA;
+ video->intraAvailB = video->mbAvailB;
+ video->intraAvailC = video->mbAvailC;
+ video->intraAvailD = video->mbAvailD;
+ }
+ else
+ {
+ if (video->mbAvailA)
+ {
+ video->intraAvailA = video->mblock[video->mbAddrA].mb_intra;
+ }
+ if (video->mbAvailB)
+ {
+ video->intraAvailB = video->mblock[video->mbAddrB].mb_intra ;
+ }
+ if (video->mbAvailC)
+ {
+ video->intraAvailC = video->mblock[video->mbAddrC].mb_intra;
+ }
+ if (video->mbAvailD)
+ {
+ video->intraAvailD = video->mblock[video->mbAddrD].mb_intra;
+ }
+ }
+
+ /* currently we're doing exhaustive search. Smart search will be used later */
+
+ /* I16 modes */
+ curL = currInput->YCbCr[0] + offset;
+ video->pintra_pred_top = curL - pitch;
+ video->pintra_pred_left = curL - 1;
+ if (video->mb_y)
+ {
+ video->intra_pred_topleft = *(curL - pitch - 1);
+ }
+
+ /* Intra_16x16_Vertical */
+ sadI16 = 65536;
+ /* check availability of top */
+ if (video->intraAvailB)
+ {
+ sad = SAD_I16_Vert(video, curL, sadI16);
+
+ if (sad < sadI16)
+ {
+ sadI16 = sad;
+ currMB->i16Mode = AVC_I16_Vertical;
+ }
+ }
+ /* Intra_16x16_Horizontal */
+ /* check availability of left */
+ if (video->intraAvailA)
+ {
+ sad = SAD_I16_HorzDC(video, curL, AVC_I16_Horizontal, sadI16);
+
+ if (sad < sadI16)
+ {
+ sadI16 = sad;
+ currMB->i16Mode = AVC_I16_Horizontal;
+ }
+ }
+
+ /* Intra_16x16_DC, default mode */
+ sad = SAD_I16_HorzDC(video, curL, AVC_I16_DC, sadI16);
+ if (sad < sadI16)
+ {
+ sadI16 = sad;
+ currMB->i16Mode = AVC_I16_DC;
+ }
+
+ /* Intra_16x16_Plane */
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ sad = SAD_I16_Plane(video, curL, sadI16);
+
+ if (sad < sadI16)
+ {
+ sadI16 = sad;
+ currMB->i16Mode = AVC_I16_Plane;
+ }
+ }
+
+ sadI16 >>= 1; /* before comparison */
+
+ /* selection between intra4, intra16 or inter mode */
+ if (sadI16 < encvid->min_cost)
+ {
+ currMB->mb_intra = TRUE;
+ currMB->mbMode = AVC_I16;
+ encvid->min_cost = sadI16;
+ }
+
+ if (currMB->mb_intra) /* only do the chrominance search when intra is decided */
+ {
+ /* Note that we might be able to guess the type of prediction from
+ the luma prediction type */
+
+ /* now search for the best chroma intra prediction */
+ offset = (offset >> 2) + (video->mb_x << 2);
+ curCb = currInput->YCbCr[1] + offset;
+ curCr = currInput->YCbCr[2] + offset;
+
+ pitch >>= 1;
+ video->pintra_pred_top_cb = curCb - pitch;
+ video->pintra_pred_left_cb = curCb - 1;
+ video->pintra_pred_top_cr = curCr - pitch;
+ video->pintra_pred_left_cr = curCr - 1;
+
+ if (video->mb_y)
+ {
+ video->intra_pred_topleft_cb = *(curCb - pitch - 1);
+ video->intra_pred_topleft_cr = *(curCr - pitch - 1);
+ }
+
+ /* Intra_Chroma_DC */
+ sad4 = SAD_Chroma_DC(video, curCb, curCr, 65536);
+ currMB->intra_chroma_pred_mode = AVC_IC_DC;
+
+ /* Intra_Chroma_Horizontal */
+ if (video->intraAvailA)
+ {
+ /* check availability of left */
+ sad = SAD_Chroma_Horz(video, curCb, curCr, sad4);
+ if (sad < sad4)
+ {
+ sad4 = sad;
+ currMB->intra_chroma_pred_mode = AVC_IC_Horizontal;
+ }
+ }
+
+ /* Intra_Chroma_Vertical */
+ if (video->intraAvailB)
+ {
+ /* check availability of top */
+ sad = SAD_Chroma_Vert(video, curCb, curCr, sad4);
+
+ if (sad < sad4)
+ {
+ sad4 = sad;
+ currMB->intra_chroma_pred_mode = AVC_IC_Vertical;
+ }
+ }
+
+ /* Intra_Chroma_Plane */
+ if (video->intraAvailA && video->intraAvailB && video->intraAvailD)
+ {
+ /* check availability of top and left */
+ Intra_Chroma_Plane(video, pitch);
+
+ sad = SADChroma(pred_block + 452, curCb, curCr, pitch);
+
+ if (sad < sad4)
+ {
+ sad4 = sad;
+ currMB->intra_chroma_pred_mode = AVC_IC_Plane;
+ }
+ }
+
+ /* also reset the motion vectors */
+ /* set MV and Ref_Idx codes of Intra blocks in P-slices */
+ memset(currMB->mvL0, 0, sizeof(int32)*16);
+ memset(currMB->ref_idx_L0, -1, sizeof(int16)*4);
+
+ }
+
+ // output from this function, currMB->mbMode should be set to either
+ // AVC_I4, AVC_I16, or else in AVCMBMode enum, mbType, mb_intra, intra_chroma_pred_mode */
+
+ return ;
+}
+#endif
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/motion_comp.cpp b/media/libstagefright/codecs/avc/enc/src/motion_comp.cpp
new file mode 100644
index 0000000..ac62d78
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/motion_comp.cpp
@@ -0,0 +1,2156 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+#include "avcenc_int.h"
+
+
+#define CLIP_RESULT(x) if((uint)x > 0xFF){ \
+ x = 0xFF & (~(x>>31));}
+
+/* (blkwidth << 2) + (dy << 1) + dx */
+static void (*const eChromaMC_SIMD[8])(uint8 *, int , int , int , uint8 *, int, int , int) =
+{
+ &eChromaFullMC_SIMD,
+ &eChromaHorizontalMC_SIMD,
+ &eChromaVerticalMC_SIMD,
+ &eChromaDiagonalMC_SIMD,
+ &eChromaFullMC_SIMD,
+ &eChromaHorizontalMC2_SIMD,
+ &eChromaVerticalMC2_SIMD,
+ &eChromaDiagonalMC2_SIMD
+};
+/* Perform motion prediction and compensation with residue if exist. */
+void AVCMBMotionComp(AVCEncObject *encvid, AVCCommonObj *video)
+{
+ (void)(encvid);
+
+ AVCMacroblock *currMB = video->currMB;
+ AVCPictureData *currPic = video->currPic;
+ int mbPartIdx, subMbPartIdx;
+ int ref_idx;
+ int offset_MbPart_indx = 0;
+ int16 *mv;
+ uint32 x_pos, y_pos;
+ uint8 *curL, *curCb, *curCr;
+ uint8 *ref_l, *ref_Cb, *ref_Cr;
+ uint8 *predBlock, *predCb, *predCr;
+ int block_x, block_y, offset_x, offset_y, offsetP, offset;
+ int x_position = (video->mb_x << 4);
+ int y_position = (video->mb_y << 4);
+ int MbHeight, MbWidth, mbPartIdx_X, mbPartIdx_Y, offset_indx;
+ int picWidth = currPic->width;
+ int picPitch = currPic->pitch;
+ int picHeight = currPic->height;
+ uint32 tmp_word;
+
+ tmp_word = y_position * picPitch;
+ curL = currPic->Sl + tmp_word + x_position;
+ offset = (tmp_word >> 2) + (x_position >> 1);
+ curCb = currPic->Scb + offset;
+ curCr = currPic->Scr + offset;
+
+ predBlock = curL;
+ predCb = curCb;
+ predCr = curCr;
+
+ GetMotionVectorPredictor(video, 1);
+
+ for (mbPartIdx = 0; mbPartIdx < currMB->NumMbPart; mbPartIdx++)
+ {
+ MbHeight = currMB->SubMbPartHeight[mbPartIdx];
+ MbWidth = currMB->SubMbPartWidth[mbPartIdx];
+ mbPartIdx_X = ((mbPartIdx + offset_MbPart_indx) & 1);
+ mbPartIdx_Y = (mbPartIdx + offset_MbPart_indx) >> 1;
+ ref_idx = currMB->ref_idx_L0[(mbPartIdx_Y << 1) + mbPartIdx_X];
+ offset_indx = 0;
+
+ ref_l = video->RefPicList0[ref_idx]->Sl;
+ ref_Cb = video->RefPicList0[ref_idx]->Scb;
+ ref_Cr = video->RefPicList0[ref_idx]->Scr;
+
+ for (subMbPartIdx = 0; subMbPartIdx < currMB->NumSubMbPart[mbPartIdx]; subMbPartIdx++)
+ {
+ block_x = (mbPartIdx_X << 1) + ((subMbPartIdx + offset_indx) & 1);
+ block_y = (mbPartIdx_Y << 1) + (((subMbPartIdx + offset_indx) >> 1) & 1);
+ mv = (int16*)(currMB->mvL0 + block_x + (block_y << 2));
+ offset_x = x_position + (block_x << 2);
+ offset_y = y_position + (block_y << 2);
+ x_pos = (offset_x << 2) + *mv++; /*quarter pel */
+ y_pos = (offset_y << 2) + *mv; /*quarter pel */
+
+ //offset = offset_y * currPic->width;
+ //offsetC = (offset >> 2) + (offset_x >> 1);
+ offsetP = (block_y << 2) * picPitch + (block_x << 2);
+ eLumaMotionComp(ref_l, picPitch, picHeight, x_pos, y_pos,
+ /*comp_Sl + offset + offset_x,*/
+ predBlock + offsetP, picPitch, MbWidth, MbHeight);
+
+ offsetP = (block_y * picWidth) + (block_x << 1);
+ eChromaMotionComp(ref_Cb, picWidth >> 1, picHeight >> 1, x_pos, y_pos,
+ /*comp_Scb + offsetC,*/
+ predCb + offsetP, picPitch >> 1, MbWidth >> 1, MbHeight >> 1);
+ eChromaMotionComp(ref_Cr, picWidth >> 1, picHeight >> 1, x_pos, y_pos,
+ /*comp_Scr + offsetC,*/
+ predCr + offsetP, picPitch >> 1, MbWidth >> 1, MbHeight >> 1);
+
+ offset_indx = currMB->SubMbPartWidth[mbPartIdx] >> 3;
+ }
+ offset_MbPart_indx = currMB->MbPartWidth >> 4;
+ }
+
+ return ;
+}
+
+
+/* preform the actual motion comp here */
+void eLumaMotionComp(uint8 *ref, int picpitch, int picheight,
+ int x_pos, int y_pos,
+ uint8 *pred, int pred_pitch,
+ int blkwidth, int blkheight)
+{
+ (void)(picheight);
+
+ int dx, dy;
+ int temp2[21][21]; /* for intermediate results */
+ uint8 *ref2;
+
+ dx = x_pos & 3;
+ dy = y_pos & 3;
+ x_pos = x_pos >> 2; /* round it to full-pel resolution */
+ y_pos = y_pos >> 2;
+
+ /* perform actual motion compensation */
+ if (dx == 0 && dy == 0)
+ { /* fullpel position *//* G */
+
+ ref += y_pos * picpitch + x_pos;
+
+ eFullPelMC(ref, picpitch, pred, pred_pitch, blkwidth, blkheight);
+
+ } /* other positions */
+ else if (dy == 0)
+ { /* no vertical interpolation *//* a,b,c*/
+
+ ref += y_pos * picpitch + x_pos;
+
+ eHorzInterp1MC(ref, picpitch, pred, pred_pitch, blkwidth, blkheight, dx);
+ }
+ else if (dx == 0)
+ { /*no horizontal interpolation *//* d,h,n */
+
+ ref += y_pos * picpitch + x_pos;
+
+ eVertInterp1MC(ref, picpitch, pred, pred_pitch, blkwidth, blkheight, dy);
+ }
+ else if (dy == 2)
+ { /* horizontal cross *//* i, j, k */
+
+ ref += y_pos * picpitch + x_pos - 2; /* move to the left 2 pixels */
+
+ eVertInterp2MC(ref, picpitch, &temp2[0][0], 21, blkwidth + 5, blkheight);
+
+ eHorzInterp2MC(&temp2[0][2], 21, pred, pred_pitch, blkwidth, blkheight, dx);
+ }
+ else if (dx == 2)
+ { /* vertical cross */ /* f,q */
+
+ ref += (y_pos - 2) * picpitch + x_pos; /* move to up 2 lines */
+
+ eHorzInterp3MC(ref, picpitch, &temp2[0][0], 21, blkwidth, blkheight + 5);
+ eVertInterp3MC(&temp2[2][0], 21, pred, pred_pitch, blkwidth, blkheight, dy);
+ }
+ else
+ { /* diagonal *//* e,g,p,r */
+
+ ref2 = ref + (y_pos + (dy / 2)) * picpitch + x_pos;
+
+ ref += (y_pos * picpitch) + x_pos + (dx / 2);
+
+ eDiagonalInterpMC(ref2, ref, picpitch, pred, pred_pitch, blkwidth, blkheight);
+ }
+
+ return ;
+}
+
+void eCreateAlign(uint8 *ref, int picpitch, int y_pos,
+ uint8 *out, int blkwidth, int blkheight)
+{
+ int i, j;
+ int offset, out_offset;
+ uint32 prev_pix, result, pix1, pix2, pix4;
+
+ ref += y_pos * picpitch;// + x_pos;
+ out_offset = 24 - blkwidth;
+
+ //switch(x_pos&0x3){
+ switch (((uint32)ref)&0x3)
+ {
+ case 1:
+ offset = picpitch - blkwidth - 3;
+ for (j = 0; j < blkheight; j++)
+ {
+ pix1 = *ref++;
+ pix2 = *((uint16*)ref);
+ ref += 2;
+ result = (pix2 << 8) | pix1;
+
+ for (i = 3; i < blkwidth; i += 4)
+ {
+ pix4 = *((uint32*)ref);
+ ref += 4;
+ prev_pix = (pix4 << 24) & 0xFF000000; /* mask out byte belong to previous word */
+ result |= prev_pix;
+ *((uint32*)out) = result; /* write 4 bytes */
+ out += 4;
+ result = pix4 >> 8; /* for the next loop */
+ }
+ ref += offset;
+ out += out_offset;
+ }
+ break;
+ case 2:
+ offset = picpitch - blkwidth - 2;
+ for (j = 0; j < blkheight; j++)
+ {
+ result = *((uint16*)ref);
+ ref += 2;
+ for (i = 2; i < blkwidth; i += 4)
+ {
+ pix4 = *((uint32*)ref);
+ ref += 4;
+ prev_pix = (pix4 << 16) & 0xFFFF0000; /* mask out byte belong to previous word */
+ result |= prev_pix;
+ *((uint32*)out) = result; /* write 4 bytes */
+ out += 4;
+ result = pix4 >> 16; /* for the next loop */
+ }
+ ref += offset;
+ out += out_offset;
+ }
+ break;
+ case 3:
+ offset = picpitch - blkwidth - 1;
+ for (j = 0; j < blkheight; j++)
+ {
+ result = *ref++;
+ for (i = 1; i < blkwidth; i += 4)
+ {
+ pix4 = *((uint32*)ref);
+ ref += 4;
+ prev_pix = (pix4 << 8) & 0xFFFFFF00; /* mask out byte belong to previous word */
+ result |= prev_pix;
+ *((uint32*)out) = result; /* write 4 bytes */
+ out += 4;
+ result = pix4 >> 24; /* for the next loop */
+ }
+ ref += offset;
+ out += out_offset;
+ }
+ break;
+ }
+}
+
+void eHorzInterp1MC(uint8 *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dx)
+{
+ uint8 *p_ref;
+ uint32 *p_cur;
+ uint32 tmp, pkres;
+ int result, curr_offset, ref_offset;
+ int j;
+ int32 r0, r1, r2, r3, r4, r5;
+ int32 r13, r6;
+
+ p_cur = (uint32*)out; /* assume it's word aligned */
+ curr_offset = (outpitch - blkwidth) >> 2;
+ p_ref = in;
+ ref_offset = inpitch - blkwidth;
+
+ if (dx&1)
+ {
+ dx = ((dx >> 1) ? -3 : -4); /* use in 3/4 pel */
+ p_ref -= 2;
+ r13 = 0;
+ for (j = blkheight; j > 0; j--)
+ {
+ tmp = (uint32)(p_ref + blkwidth);
+ r0 = p_ref[0];
+ r1 = p_ref[2];
+ r0 |= (r1 << 16); /* 0,c,0,a */
+ r1 = p_ref[1];
+ r2 = p_ref[3];
+ r1 |= (r2 << 16); /* 0,d,0,b */
+ while ((uint32)p_ref < tmp)
+ {
+ r2 = *(p_ref += 4); /* move pointer to e */
+ r3 = p_ref[2];
+ r2 |= (r3 << 16); /* 0,g,0,e */
+ r3 = p_ref[1];
+ r4 = p_ref[3];
+ r3 |= (r4 << 16); /* 0,h,0,f */
+
+ r4 = r0 + r3; /* c+h, a+f */
+ r5 = r0 + r1; /* c+d, a+b */
+ r6 = r2 + r3; /* g+h, e+f */
+ r5 >>= 16;
+ r5 |= (r6 << 16); /* e+f, c+d */
+ r4 += r5 * 20; /* c+20*e+20*f+h, a+20*c+20*d+f */
+ r4 += 0x100010; /* +16, +16 */
+ r5 = r1 + r2; /* d+g, b+e */
+ r4 -= r5 * 5; /* c-5*d+20*e+20*f-5*g+h, a-5*b+20*c+20*d-5*e+f */
+ r4 >>= 5;
+ r13 |= r4; /* check clipping */
+
+ r5 = p_ref[dx+2];
+ r6 = p_ref[dx+4];
+ r5 |= (r6 << 16);
+ r4 += r5;
+ r4 += 0x10001;
+ r4 = (r4 >> 1) & 0xFF00FF;
+
+ r5 = p_ref[4]; /* i */
+ r6 = (r5 << 16);
+ r5 = r6 | (r2 >> 16);/* 0,i,0,g */
+ r5 += r1; /* d+i, b+g */ /* r5 not free */
+ r1 >>= 16;
+ r1 |= (r3 << 16); /* 0,f,0,d */ /* r1 has changed */
+ r1 += r2; /* f+g, d+e */
+ r5 += 20 * r1; /* d+20f+20g+i, b+20d+20e+g */
+ r0 >>= 16;
+ r0 |= (r2 << 16); /* 0,e,0,c */ /* r0 has changed */
+ r0 += r3; /* e+h, c+f */
+ r5 += 0x100010; /* 16,16 */
+ r5 -= r0 * 5; /* d-5e+20f+20g-5h+i, b-5c+20d+20e-5f+g */
+ r5 >>= 5;
+ r13 |= r5; /* check clipping */
+
+ r0 = p_ref[dx+3];
+ r1 = p_ref[dx+5];
+ r0 |= (r1 << 16);
+ r5 += r0;
+ r5 += 0x10001;
+ r5 = (r5 >> 1) & 0xFF00FF;
+
+ r4 |= (r5 << 8); /* pack them together */
+ *p_cur++ = r4;
+ r1 = r3;
+ r0 = r2;
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset; /* ref_offset = inpitch-blkwidth; */
+
+ if (r13&0xFF000700) /* need clipping */
+ {
+ /* move back to the beginning of the line */
+ p_ref -= (ref_offset + blkwidth); /* input */
+ p_cur -= (outpitch >> 2);
+
+ tmp = (uint32)(p_ref + blkwidth);
+ for (; (uint32)p_ref < tmp;)
+ {
+
+ r0 = *p_ref++;
+ r1 = *p_ref++;
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dx] + 1);
+ pkres = (result >> 1) ;
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dx] + 1);
+ result = (result >> 1);
+ pkres |= (result << 8);
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dx] + 1);
+ result = (result >> 1);
+ pkres |= (result << 16);
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dx] + 1);
+ result = (result >> 1);
+ pkres |= (result << 24);
+ *p_cur++ = pkres; /* write 4 pixels */
+ p_ref -= 5; /* offset back to the middle of filter */
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset; /* move to the next line */
+ }
+ }
+ }
+ else
+ {
+ p_ref -= 2;
+ r13 = 0;
+ for (j = blkheight; j > 0; j--)
+ {
+ tmp = (uint32)(p_ref + blkwidth);
+ r0 = p_ref[0];
+ r1 = p_ref[2];
+ r0 |= (r1 << 16); /* 0,c,0,a */
+ r1 = p_ref[1];
+ r2 = p_ref[3];
+ r1 |= (r2 << 16); /* 0,d,0,b */
+ while ((uint32)p_ref < tmp)
+ {
+ r2 = *(p_ref += 4); /* move pointer to e */
+ r3 = p_ref[2];
+ r2 |= (r3 << 16); /* 0,g,0,e */
+ r3 = p_ref[1];
+ r4 = p_ref[3];
+ r3 |= (r4 << 16); /* 0,h,0,f */
+
+ r4 = r0 + r3; /* c+h, a+f */
+ r5 = r0 + r1; /* c+d, a+b */
+ r6 = r2 + r3; /* g+h, e+f */
+ r5 >>= 16;
+ r5 |= (r6 << 16); /* e+f, c+d */
+ r4 += r5 * 20; /* c+20*e+20*f+h, a+20*c+20*d+f */
+ r4 += 0x100010; /* +16, +16 */
+ r5 = r1 + r2; /* d+g, b+e */
+ r4 -= r5 * 5; /* c-5*d+20*e+20*f-5*g+h, a-5*b+20*c+20*d-5*e+f */
+ r4 >>= 5;
+ r13 |= r4; /* check clipping */
+ r4 &= 0xFF00FF; /* mask */
+
+ r5 = p_ref[4]; /* i */
+ r6 = (r5 << 16);
+ r5 = r6 | (r2 >> 16);/* 0,i,0,g */
+ r5 += r1; /* d+i, b+g */ /* r5 not free */
+ r1 >>= 16;
+ r1 |= (r3 << 16); /* 0,f,0,d */ /* r1 has changed */
+ r1 += r2; /* f+g, d+e */
+ r5 += 20 * r1; /* d+20f+20g+i, b+20d+20e+g */
+ r0 >>= 16;
+ r0 |= (r2 << 16); /* 0,e,0,c */ /* r0 has changed */
+ r0 += r3; /* e+h, c+f */
+ r5 += 0x100010; /* 16,16 */
+ r5 -= r0 * 5; /* d-5e+20f+20g-5h+i, b-5c+20d+20e-5f+g */
+ r5 >>= 5;
+ r13 |= r5; /* check clipping */
+ r5 &= 0xFF00FF; /* mask */
+
+ r4 |= (r5 << 8); /* pack them together */
+ *p_cur++ = r4;
+ r1 = r3;
+ r0 = r2;
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset; /* ref_offset = inpitch-blkwidth; */
+
+ if (r13&0xFF000700) /* need clipping */
+ {
+ /* move back to the beginning of the line */
+ p_ref -= (ref_offset + blkwidth); /* input */
+ p_cur -= (outpitch >> 2);
+
+ tmp = (uint32)(p_ref + blkwidth);
+ for (; (uint32)p_ref < tmp;)
+ {
+
+ r0 = *p_ref++;
+ r1 = *p_ref++;
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres = result;
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 8);
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 16);
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 24);
+ *p_cur++ = pkres; /* write 4 pixels */
+ p_ref -= 5;
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset;
+ }
+ }
+ }
+
+ return ;
+}
+
+void eHorzInterp2MC(int *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dx)
+{
+ int *p_ref;
+ uint32 *p_cur;
+ uint32 tmp, pkres;
+ int result, result2, curr_offset, ref_offset;
+ int j, r0, r1, r2, r3, r4, r5;
+
+ p_cur = (uint32*)out; /* assume it's word aligned */
+ curr_offset = (outpitch - blkwidth) >> 2;
+ p_ref = in;
+ ref_offset = inpitch - blkwidth;
+
+ if (dx&1)
+ {
+ dx = ((dx >> 1) ? -3 : -4); /* use in 3/4 pel */
+
+ for (j = blkheight; j > 0 ; j--)
+ {
+ tmp = (uint32)(p_ref + blkwidth);
+ for (; (uint32)p_ref < tmp;)
+ {
+
+ r0 = p_ref[-2];
+ r1 = p_ref[-1];
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dx] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ pkres = (result >> 1);
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dx] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ pkres |= (result << 8);
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dx] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ pkres |= (result << 16);
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dx] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ pkres |= (result << 24);
+ *p_cur++ = pkres; /* write 4 pixels */
+ p_ref -= 3; /* offset back to the middle of filter */
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset; /* move to the next line */
+ }
+ }
+ else
+ {
+ for (j = blkheight; j > 0 ; j--)
+ {
+ tmp = (uint32)(p_ref + blkwidth);
+ for (; (uint32)p_ref < tmp;)
+ {
+
+ r0 = p_ref[-2];
+ r1 = p_ref[-1];
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ pkres = result;
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ pkres |= (result << 8);
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ pkres |= (result << 16);
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ pkres |= (result << 24);
+ *p_cur++ = pkres; /* write 4 pixels */
+ p_ref -= 3; /* offset back to the middle of filter */
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset; /* move to the next line */
+ }
+ }
+
+ return ;
+}
+
+void eHorzInterp3MC(uint8 *in, int inpitch, int *out, int outpitch,
+ int blkwidth, int blkheight)
+{
+ uint8 *p_ref;
+ int *p_cur;
+ uint32 tmp;
+ int result, curr_offset, ref_offset;
+ int j, r0, r1, r2, r3, r4, r5;
+
+ p_cur = out;
+ curr_offset = (outpitch - blkwidth);
+ p_ref = in;
+ ref_offset = inpitch - blkwidth;
+
+ for (j = blkheight; j > 0 ; j--)
+ {
+ tmp = (uint32)(p_ref + blkwidth);
+ for (; (uint32)p_ref < tmp;)
+ {
+
+ r0 = p_ref[-2];
+ r1 = p_ref[-1];
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ *p_cur++ = result;
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ *p_cur++ = result;
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ *p_cur++ = result;
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ *p_cur++ = result;
+ p_ref -= 3; /* move back to the middle of the filter */
+ }
+ p_cur += curr_offset; /* move to the next line */
+ p_ref += ref_offset;
+ }
+
+ return ;
+}
+void eVertInterp1MC(uint8 *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dy)
+{
+ uint8 *p_cur, *p_ref;
+ uint32 tmp;
+ int result, curr_offset, ref_offset;
+ int j, i;
+ int32 r0, r1, r2, r3, r4, r5, r6, r7, r8, r13;
+ uint8 tmp_in[24][24];
+
+ /* not word-aligned */
+ if (((uint32)in)&0x3)
+ {
+ eCreateAlign(in, inpitch, -2, &tmp_in[0][0], blkwidth, blkheight + 5);
+ in = &tmp_in[2][0];
+ inpitch = 24;
+ }
+ p_cur = out;
+ curr_offset = 1 - outpitch * (blkheight - 1); /* offset vertically back up and one pixel to right */
+ ref_offset = blkheight * inpitch; /* for limit */
+
+ curr_offset += 3;
+
+ if (dy&1)
+ {
+ dy = (dy >> 1) ? 0 : -inpitch;
+
+ for (j = 0; j < blkwidth; j += 4, in += 4)
+ {
+ r13 = 0;
+ p_ref = in;
+ p_cur -= outpitch; /* compensate for the first offset */
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp) /* the loop un-rolled */
+ {
+ r0 = *((uint32*)(p_ref - (inpitch << 1))); /* load 4 bytes */
+ p_ref += inpitch;
+ r6 = (r0 >> 8) & 0xFF00FF; /* second and fourth byte */
+ r0 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref + (inpitch << 1))); /* r1, r7, ref[3] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+
+ r0 += r1;
+ r6 += r7;
+
+ r2 = *((uint32*)p_ref); /* r2, r8, ref[1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref - inpitch)); /* r1, r7, ref[0] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 += 20 * r1;
+ r6 += 20 * r7;
+ r0 += 0x100010;
+ r6 += 0x100010;
+
+ r2 = *((uint32*)(p_ref - (inpitch << 1))); /* r2, r8, ref[-1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref + inpitch)); /* r1, r7, ref[2] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 -= 5 * r1;
+ r6 -= 5 * r7;
+
+ r0 >>= 5;
+ r6 >>= 5;
+ /* clip */
+ r13 |= r6;
+ r13 |= r0;
+ //CLIPPACK(r6,result)
+
+ r1 = *((uint32*)(p_ref + dy));
+ r2 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r0 += r1;
+ r6 += r2;
+ r0 += 0x10001;
+ r6 += 0x10001;
+ r0 = (r0 >> 1) & 0xFF00FF;
+ r6 = (r6 >> 1) & 0xFF00FF;
+
+ r0 |= (r6 << 8); /* pack it back */
+ *((uint32*)(p_cur += outpitch)) = r0;
+ }
+ p_cur += curr_offset; /* offset to the next pixel */
+ if (r13 & 0xFF000700) /* this column need clipping */
+ {
+ p_cur -= 4;
+ for (i = 0; i < 4; i++)
+ {
+ p_ref = in + i;
+ p_cur -= outpitch; /* compensate for the first offset */
+
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp)
+ { /* loop un-rolled */
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dy-(inpitch<<1)] + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dy-(inpitch<<1)] + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dy-(inpitch<<1)] + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ /* 3/4 pel, no need to clip */
+ result = (result + p_ref[dy-(inpitch<<1)] + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += (curr_offset - 3);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (j = 0; j < blkwidth; j += 4, in += 4)
+ {
+ r13 = 0;
+ p_ref = in;
+ p_cur -= outpitch; /* compensate for the first offset */
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp) /* the loop un-rolled */
+ {
+ r0 = *((uint32*)(p_ref - (inpitch << 1))); /* load 4 bytes */
+ p_ref += inpitch;
+ r6 = (r0 >> 8) & 0xFF00FF; /* second and fourth byte */
+ r0 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref + (inpitch << 1))); /* r1, r7, ref[3] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+
+ r0 += r1;
+ r6 += r7;
+
+ r2 = *((uint32*)p_ref); /* r2, r8, ref[1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref - inpitch)); /* r1, r7, ref[0] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 += 20 * r1;
+ r6 += 20 * r7;
+ r0 += 0x100010;
+ r6 += 0x100010;
+
+ r2 = *((uint32*)(p_ref - (inpitch << 1))); /* r2, r8, ref[-1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ r1 = *((uint32*)(p_ref + inpitch)); /* r1, r7, ref[2] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 -= 5 * r1;
+ r6 -= 5 * r7;
+
+ r0 >>= 5;
+ r6 >>= 5;
+ /* clip */
+ r13 |= r6;
+ r13 |= r0;
+ //CLIPPACK(r6,result)
+ r0 &= 0xFF00FF;
+ r6 &= 0xFF00FF;
+ r0 |= (r6 << 8); /* pack it back */
+ *((uint32*)(p_cur += outpitch)) = r0;
+ }
+ p_cur += curr_offset; /* offset to the next pixel */
+ if (r13 & 0xFF000700) /* this column need clipping */
+ {
+ p_cur -= 4;
+ for (i = 0; i < 4; i++)
+ {
+ p_ref = in + i;
+ p_cur -= outpitch; /* compensate for the first offset */
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp)
+ { /* loop un-rolled */
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += (curr_offset - 3);
+ }
+ }
+ }
+ }
+
+ return ;
+}
+
+void eVertInterp2MC(uint8 *in, int inpitch, int *out, int outpitch,
+ int blkwidth, int blkheight)
+{
+ int *p_cur;
+ uint8 *p_ref;
+ uint32 tmp;
+ int result, curr_offset, ref_offset;
+ int j, r0, r1, r2, r3, r4, r5;
+
+ p_cur = out;
+ curr_offset = 1 - outpitch * (blkheight - 1); /* offset vertically back up and one pixel to right */
+ ref_offset = blkheight * inpitch; /* for limit */
+
+ for (j = 0; j < blkwidth; j++)
+ {
+ p_cur -= outpitch; /* compensate for the first offset */
+ p_ref = in++;
+
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp)
+ { /* loop un-rolled */
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += curr_offset;
+ }
+
+ return ;
+}
+
+void eVertInterp3MC(int *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight, int dy)
+{
+ uint8 *p_cur;
+ int *p_ref;
+ uint32 tmp;
+ int result, result2, curr_offset, ref_offset;
+ int j, r0, r1, r2, r3, r4, r5;
+
+ p_cur = out;
+ curr_offset = 1 - outpitch * (blkheight - 1); /* offset vertically back up and one pixel to right */
+ ref_offset = blkheight * inpitch; /* for limit */
+
+ if (dy&1)
+ {
+ dy = (dy >> 1) ? -(inpitch << 1) : -(inpitch << 1) - inpitch;
+
+ for (j = 0; j < blkwidth; j++)
+ {
+ p_cur -= outpitch; /* compensate for the first offset */
+ p_ref = in++;
+
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp)
+ { /* loop un-rolled */
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dy] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dy] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dy] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ result2 = ((p_ref[dy] + 16) >> 5);
+ CLIP_RESULT(result2)
+ /* 3/4 pel, no need to clip */
+ result = (result + result2 + 1);
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += curr_offset;
+ }
+ }
+ else
+ {
+ for (j = 0; j < blkwidth; j++)
+ {
+ p_cur -= outpitch; /* compensate for the first offset */
+ p_ref = in++;
+
+ tmp = (uint32)(p_ref + ref_offset); /* limit */
+ while ((uint32)p_ref < tmp)
+ { /* loop un-rolled */
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 512) >> 10;
+ CLIP_RESULT(result)
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += curr_offset;
+ }
+ }
+
+ return ;
+}
+
+void eDiagonalInterpMC(uint8 *in1, uint8 *in2, int inpitch,
+ uint8 *out, int outpitch,
+ int blkwidth, int blkheight)
+{
+ int j, i;
+ int result;
+ uint8 *p_cur, *p_ref, *p_tmp8;
+ int curr_offset, ref_offset;
+ uint8 tmp_res[24][24], tmp_in[24][24];
+ uint32 *p_tmp;
+ uint32 tmp, pkres, tmp_result;
+ int32 r0, r1, r2, r3, r4, r5;
+ int32 r6, r7, r8, r9, r10, r13;
+
+ ref_offset = inpitch - blkwidth;
+ p_ref = in1 - 2;
+ /* perform horizontal interpolation */
+ /* not word-aligned */
+ /* It is faster to read 1 byte at time to avoid calling CreateAlign */
+ /* if(((uint32)p_ref)&0x3)
+ {
+ CreateAlign(p_ref,inpitch,0,&tmp_in[0][0],blkwidth+8,blkheight);
+ p_ref = &tmp_in[0][0];
+ ref_offset = 24-blkwidth;
+ }*/
+
+ p_tmp = (uint32*) & (tmp_res[0][0]);
+ for (j = blkheight; j > 0; j--)
+ {
+ r13 = 0;
+ tmp = (uint32)(p_ref + blkwidth);
+
+ //r0 = *((uint32*)p_ref); /* d,c,b,a */
+ //r1 = (r0>>8)&0xFF00FF; /* 0,d,0,b */
+ //r0 &= 0xFF00FF; /* 0,c,0,a */
+ /* It is faster to read 1 byte at a time */
+ r0 = p_ref[0];
+ r1 = p_ref[2];
+ r0 |= (r1 << 16); /* 0,c,0,a */
+ r1 = p_ref[1];
+ r2 = p_ref[3];
+ r1 |= (r2 << 16); /* 0,d,0,b */
+
+ while ((uint32)p_ref < tmp)
+ {
+ //r2 = *((uint32*)(p_ref+=4));/* h,g,f,e */
+ //r3 = (r2>>8)&0xFF00FF; /* 0,h,0,f */
+ //r2 &= 0xFF00FF; /* 0,g,0,e */
+ /* It is faster to read 1 byte at a time */
+ r2 = *(p_ref += 4);
+ r3 = p_ref[2];
+ r2 |= (r3 << 16); /* 0,g,0,e */
+ r3 = p_ref[1];
+ r4 = p_ref[3];
+ r3 |= (r4 << 16); /* 0,h,0,f */
+
+ r4 = r0 + r3; /* c+h, a+f */
+ r5 = r0 + r1; /* c+d, a+b */
+ r6 = r2 + r3; /* g+h, e+f */
+ r5 >>= 16;
+ r5 |= (r6 << 16); /* e+f, c+d */
+ r4 += r5 * 20; /* c+20*e+20*f+h, a+20*c+20*d+f */
+ r4 += 0x100010; /* +16, +16 */
+ r5 = r1 + r2; /* d+g, b+e */
+ r4 -= r5 * 5; /* c-5*d+20*e+20*f-5*g+h, a-5*b+20*c+20*d-5*e+f */
+ r4 >>= 5;
+ r13 |= r4; /* check clipping */
+ r4 &= 0xFF00FF; /* mask */
+
+ r5 = p_ref[4]; /* i */
+ r6 = (r5 << 16);
+ r5 = r6 | (r2 >> 16);/* 0,i,0,g */
+ r5 += r1; /* d+i, b+g */ /* r5 not free */
+ r1 >>= 16;
+ r1 |= (r3 << 16); /* 0,f,0,d */ /* r1 has changed */
+ r1 += r2; /* f+g, d+e */
+ r5 += 20 * r1; /* d+20f+20g+i, b+20d+20e+g */
+ r0 >>= 16;
+ r0 |= (r2 << 16); /* 0,e,0,c */ /* r0 has changed */
+ r0 += r3; /* e+h, c+f */
+ r5 += 0x100010; /* 16,16 */
+ r5 -= r0 * 5; /* d-5e+20f+20g-5h+i, b-5c+20d+20e-5f+g */
+ r5 >>= 5;
+ r13 |= r5; /* check clipping */
+ r5 &= 0xFF00FF; /* mask */
+
+ r4 |= (r5 << 8); /* pack them together */
+ *p_tmp++ = r4;
+ r1 = r3;
+ r0 = r2;
+ }
+ p_tmp += ((24 - blkwidth) >> 2); /* move to the next line */
+ p_ref += ref_offset; /* ref_offset = inpitch-blkwidth; */
+
+ if (r13&0xFF000700) /* need clipping */
+ {
+ /* move back to the beginning of the line */
+ p_ref -= (ref_offset + blkwidth); /* input */
+ p_tmp -= 6; /* intermediate output */
+ tmp = (uint32)(p_ref + blkwidth);
+ while ((uint32)p_ref < tmp)
+ {
+ r0 = *p_ref++;
+ r1 = *p_ref++;
+ r2 = *p_ref++;
+ r3 = *p_ref++;
+ r4 = *p_ref++;
+ /* first pixel */
+ r5 = *p_ref++;
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres = result;
+ /* second pixel */
+ r0 = *p_ref++;
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 8);
+ /* third pixel */
+ r1 = *p_ref++;
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 16);
+ /* fourth pixel */
+ r2 = *p_ref++;
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ pkres |= (result << 24);
+
+ *p_tmp++ = pkres; /* write 4 pixel */
+ p_ref -= 5;
+ }
+ p_tmp += ((24 - blkwidth) >> 2); /* move to the next line */
+ p_ref += ref_offset; /* ref_offset = inpitch-blkwidth; */
+ }
+ }
+
+ /* perform vertical interpolation */
+ /* not word-aligned */
+ if (((uint32)in2)&0x3)
+ {
+ eCreateAlign(in2, inpitch, -2, &tmp_in[0][0], blkwidth, blkheight + 5);
+ in2 = &tmp_in[2][0];
+ inpitch = 24;
+ }
+
+ p_cur = out;
+ curr_offset = 1 - outpitch * (blkheight - 1); /* offset vertically up and one pixel right */
+ pkres = blkheight * inpitch; /* reuse it for limit */
+
+ curr_offset += 3;
+
+ for (j = 0; j < blkwidth; j += 4, in2 += 4)
+ {
+ r13 = 0;
+ p_ref = in2;
+ p_tmp8 = &(tmp_res[0][j]); /* intermediate result */
+ p_tmp8 -= 24; /* compensate for the first offset */
+ p_cur -= outpitch; /* compensate for the first offset */
+ tmp = (uint32)(p_ref + pkres); /* limit */
+ while ((uint32)p_ref < tmp) /* the loop un-rolled */
+ {
+ /* Read 1 byte at a time is too slow, too many read and pack ops, need to call CreateAlign */
+ /*p_ref8 = p_ref-(inpitch<<1); r0 = p_ref8[0]; r1 = p_ref8[2];
+ r0 |= (r1<<16); r6 = p_ref8[1]; r1 = p_ref8[3];
+ r6 |= (r1<<16); p_ref+=inpitch; */
+ r0 = *((uint32*)(p_ref - (inpitch << 1))); /* load 4 bytes */
+ p_ref += inpitch;
+ r6 = (r0 >> 8) & 0xFF00FF; /* second and fourth byte */
+ r0 &= 0xFF00FF;
+
+ /*p_ref8 = p_ref+(inpitch<<1);
+ r1 = p_ref8[0]; r7 = p_ref8[2]; r1 |= (r7<<16);
+ r7 = p_ref8[1]; r2 = p_ref8[3]; r7 |= (r2<<16);*/
+ r1 = *((uint32*)(p_ref + (inpitch << 1))); /* r1, r7, ref[3] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+
+ r0 += r1;
+ r6 += r7;
+
+ /*r2 = p_ref[0]; r8 = p_ref[2]; r2 |= (r8<<16);
+ r8 = p_ref[1]; r1 = p_ref[3]; r8 |= (r1<<16);*/
+ r2 = *((uint32*)p_ref); /* r2, r8, ref[1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ /*p_ref8 = p_ref-inpitch; r1 = p_ref8[0]; r7 = p_ref8[2];
+ r1 |= (r7<<16); r1 += r2; r7 = p_ref8[1];
+ r2 = p_ref8[3]; r7 |= (r2<<16);*/
+ r1 = *((uint32*)(p_ref - inpitch)); /* r1, r7, ref[0] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 += 20 * r1;
+ r6 += 20 * r7;
+ r0 += 0x100010;
+ r6 += 0x100010;
+
+ /*p_ref8 = p_ref-(inpitch<<1); r2 = p_ref8[0]; r8 = p_ref8[2];
+ r2 |= (r8<<16); r8 = p_ref8[1]; r1 = p_ref8[3]; r8 |= (r1<<16);*/
+ r2 = *((uint32*)(p_ref - (inpitch << 1))); /* r2, r8, ref[-1] */
+ r8 = (r2 >> 8) & 0xFF00FF;
+ r2 &= 0xFF00FF;
+
+ /*p_ref8 = p_ref+inpitch; r1 = p_ref8[0]; r7 = p_ref8[2];
+ r1 |= (r7<<16); r1 += r2; r7 = p_ref8[1];
+ r2 = p_ref8[3]; r7 |= (r2<<16);*/
+ r1 = *((uint32*)(p_ref + inpitch)); /* r1, r7, ref[2] */
+ r7 = (r1 >> 8) & 0xFF00FF;
+ r1 &= 0xFF00FF;
+ r1 += r2;
+
+ r7 += r8;
+
+ r0 -= 5 * r1;
+ r6 -= 5 * r7;
+
+ r0 >>= 5;
+ r6 >>= 5;
+ /* clip */
+ r13 |= r6;
+ r13 |= r0;
+ //CLIPPACK(r6,result)
+ /* add with horizontal results */
+ r10 = *((uint32*)(p_tmp8 += 24));
+ r9 = (r10 >> 8) & 0xFF00FF;
+ r10 &= 0xFF00FF;
+
+ r0 += r10;
+ r0 += 0x10001;
+ r0 = (r0 >> 1) & 0xFF00FF; /* mask to 8 bytes */
+
+ r6 += r9;
+ r6 += 0x10001;
+ r6 = (r6 >> 1) & 0xFF00FF; /* mask to 8 bytes */
+
+ r0 |= (r6 << 8); /* pack it back */
+ *((uint32*)(p_cur += outpitch)) = r0;
+ }
+ p_cur += curr_offset; /* offset to the next pixel */
+ if (r13 & 0xFF000700) /* this column need clipping */
+ {
+ p_cur -= 4;
+ for (i = 0; i < 4; i++)
+ {
+ p_ref = in2 + i;
+ p_tmp8 = &(tmp_res[0][j+i]); /* intermediate result */
+ p_tmp8 -= 24; /* compensate for the first offset */
+ p_cur -= outpitch; /* compensate for the first offset */
+ tmp = (uint32)(p_ref + pkres); /* limit */
+ while ((uint32)p_ref < tmp) /* the loop un-rolled */
+ {
+ r0 = *(p_ref - (inpitch << 1));
+ r1 = *(p_ref - inpitch);
+ r2 = *p_ref;
+ r3 = *(p_ref += inpitch); /* modify pointer before loading */
+ r4 = *(p_ref += inpitch);
+ /* first pixel */
+ r5 = *(p_ref += inpitch);
+ result = (r0 + r5);
+ r0 = (r1 + r4);
+ result -= (r0 * 5);//result -= r0; result -= (r0<<2);
+ r0 = (r2 + r3);
+ result += (r0 * 20);//result += (r0<<4); result += (r0<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ tmp_result = *(p_tmp8 += 24); /* modify pointer before loading */
+ result = (result + tmp_result + 1); /* no clip */
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* second pixel */
+ r0 = *(p_ref += inpitch);
+ result = (r1 + r0);
+ r1 = (r2 + r5);
+ result -= (r1 * 5);//result -= r1; result -= (r1<<2);
+ r1 = (r3 + r4);
+ result += (r1 * 20);//result += (r1<<4); result += (r1<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ tmp_result = *(p_tmp8 += 24); /* intermediate result */
+ result = (result + tmp_result + 1); /* no clip */
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* third pixel */
+ r1 = *(p_ref += inpitch);
+ result = (r2 + r1);
+ r2 = (r3 + r0);
+ result -= (r2 * 5);//result -= r2; result -= (r2<<2);
+ r2 = (r4 + r5);
+ result += (r2 * 20);//result += (r2<<4); result += (r2<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ tmp_result = *(p_tmp8 += 24); /* intermediate result */
+ result = (result + tmp_result + 1); /* no clip */
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ /* fourth pixel */
+ r2 = *(p_ref += inpitch);
+ result = (r3 + r2);
+ r3 = (r4 + r1);
+ result -= (r3 * 5);//result -= r3; result -= (r3<<2);
+ r3 = (r5 + r0);
+ result += (r3 * 20);//result += (r3<<4); result += (r3<<2);
+ result = (result + 16) >> 5;
+ CLIP_RESULT(result)
+ tmp_result = *(p_tmp8 += 24); /* intermediate result */
+ result = (result + tmp_result + 1); /* no clip */
+ result = (result >> 1);
+ *(p_cur += outpitch) = result;
+ p_ref -= (inpitch << 1); /* move back to center of the filter of the next one */
+ }
+ p_cur += (curr_offset - 3);
+ }
+ }
+ }
+
+ return ;
+}
+
+/* position G */
+void eFullPelMC(uint8 *in, int inpitch, uint8 *out, int outpitch,
+ int blkwidth, int blkheight)
+{
+ int i, j;
+ int offset_in = inpitch - blkwidth;
+ int offset_out = outpitch - blkwidth;
+ uint32 temp;
+ uint8 byte;
+
+ if (((uint32)in)&3)
+ {
+ for (j = blkheight; j > 0; j--)
+ {
+ for (i = blkwidth; i > 0; i -= 4)
+ {
+ temp = *in++;
+ byte = *in++;
+ temp |= (byte << 8);
+ byte = *in++;
+ temp |= (byte << 16);
+ byte = *in++;
+ temp |= (byte << 24);
+
+ *((uint32*)out) = temp; /* write 4 bytes */
+ out += 4;
+ }
+ out += offset_out;
+ in += offset_in;
+ }
+ }
+ else
+ {
+ for (j = blkheight; j > 0; j--)
+ {
+ for (i = blkwidth; i > 0; i -= 4)
+ {
+ temp = *((uint32*)in);
+ *((uint32*)out) = temp;
+ in += 4;
+ out += 4;
+ }
+ out += offset_out;
+ in += offset_in;
+ }
+ }
+ return ;
+}
+
+void ePadChroma(uint8 *ref, int picwidth, int picheight, int picpitch, int x_pos, int y_pos)
+{
+ int pad_height;
+ int pad_width;
+ uint8 *start;
+ uint32 word1, word2, word3;
+ int offset, j;
+
+
+ pad_height = 8 + ((y_pos & 7) ? 1 : 0);
+ pad_width = 8 + ((x_pos & 7) ? 1 : 0);
+
+ y_pos >>= 3;
+ x_pos >>= 3;
+ // pad vertical first
+ if (y_pos < 0) // need to pad up
+ {
+ if (x_pos < -8) start = ref - 8;
+ else if (x_pos + pad_width > picwidth + 7) start = ref + picwidth + 7 - pad_width;
+ else start = ref + x_pos;
+
+ /* word-align start */
+ offset = (uint32)start & 0x3;
+ if (offset) start -= offset;
+
+ word1 = *((uint32*)start);
+ word2 = *((uint32*)(start + 4));
+ word3 = *((uint32*)(start + 8));
+
+ /* pad up N rows */
+ j = -y_pos;
+ if (j > 8) j = 8;
+ while (j--)
+ {
+ *((uint32*)(start -= picpitch)) = word1;
+ *((uint32*)(start + 4)) = word2;
+ *((uint32*)(start + 8)) = word3;
+ }
+
+ }
+ else if (y_pos + pad_height >= picheight) /* pad down */
+ {
+ if (x_pos < -8) start = ref + picpitch * (picheight - 1) - 8;
+ else if (x_pos + pad_width > picwidth + 7) start = ref + picpitch * (picheight - 1) +
+ picwidth + 7 - pad_width;
+ else start = ref + picpitch * (picheight - 1) + x_pos;
+
+ /* word-align start */
+ offset = (uint32)start & 0x3;
+ if (offset) start -= offset;
+
+ word1 = *((uint32*)start);
+ word2 = *((uint32*)(start + 4));
+ word3 = *((uint32*)(start + 8));
+
+ /* pad down N rows */
+ j = y_pos + pad_height - picheight;
+ if (j > 8) j = 8;
+ while (j--)
+ {
+ *((uint32*)(start += picpitch)) = word1;
+ *((uint32*)(start + 4)) = word2;
+ *((uint32*)(start + 8)) = word3;
+ }
+ }
+
+ /* now pad horizontal */
+ if (x_pos < 0) // pad left
+ {
+ if (y_pos < -8) start = ref - (picpitch << 3);
+ else if (y_pos + pad_height > picheight + 7) start = ref + (picheight + 7 - pad_height) * picpitch;
+ else start = ref + y_pos * picpitch;
+
+ // now pad left 8 pixels for pad_height rows */
+ j = pad_height;
+ start -= picpitch;
+ while (j--)
+ {
+ word1 = *(start += picpitch);
+ word1 |= (word1 << 8);
+ word1 |= (word1 << 16);
+ *((uint32*)(start - 8)) = word1;
+ *((uint32*)(start - 4)) = word1;
+ }
+ }
+ else if (x_pos + pad_width >= picwidth) /* pad right */
+ {
+ if (y_pos < -8) start = ref - (picpitch << 3) + picwidth - 1;
+ else if (y_pos + pad_height > picheight + 7) start = ref + (picheight + 7 - pad_height) * picpitch + picwidth - 1;
+ else start = ref + y_pos * picpitch + picwidth - 1;
+
+ // now pad right 8 pixels for pad_height rows */
+ j = pad_height;
+ start -= picpitch;
+ while (j--)
+ {
+ word1 = *(start += picpitch);
+ word1 |= (word1 << 8);
+ word1 |= (word1 << 16);
+ *((uint32*)(start + 1)) = word1;
+ *((uint32*)(start + 5)) = word1;
+ }
+ }
+
+ return ;
+}
+
+
+void eChromaMotionComp(uint8 *ref, int picwidth, int picheight,
+ int x_pos, int y_pos,
+ uint8 *pred, int picpitch,
+ int blkwidth, int blkheight)
+{
+ int dx, dy;
+ int offset_dx, offset_dy;
+ int index;
+
+ ePadChroma(ref, picwidth, picheight, picpitch, x_pos, y_pos);
+
+ dx = x_pos & 7;
+ dy = y_pos & 7;
+ offset_dx = (dx + 7) >> 3;
+ offset_dy = (dy + 7) >> 3;
+ x_pos = x_pos >> 3; /* round it to full-pel resolution */
+ y_pos = y_pos >> 3;
+
+ ref += y_pos * picpitch + x_pos;
+
+ index = offset_dx + (offset_dy << 1) + ((blkwidth << 1) & 0x7);
+
+ (*(eChromaMC_SIMD[index]))(ref, picpitch , dx, dy, pred, picpitch, blkwidth, blkheight);
+ return ;
+}
+
+
+/* SIMD routines, unroll the loops in vertical direction, decreasing loops (things to be done) */
+void eChromaDiagonalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ int32 r0, r1, r2, r3, result0, result1;
+ uint8 temp[288];
+ uint8 *ref, *out;
+ int i, j;
+ int dx_8 = 8 - dx;
+ int dy_8 = 8 - dy;
+
+ /* horizontal first */
+ out = temp;
+ for (i = 0; i < blkheight + 1; i++)
+ {
+ ref = pRef;
+ r0 = ref[0];
+ for (j = 0; j < blkwidth; j += 4)
+ {
+ r0 |= (ref[2] << 16);
+ result0 = dx_8 * r0;
+
+ r1 = ref[1] | (ref[3] << 16);
+ result0 += dx * r1;
+ *(int32 *)out = result0;
+
+ result0 = dx_8 * r1;
+
+ r2 = ref[4];
+ r0 = r0 >> 16;
+ r1 = r0 | (r2 << 16);
+ result0 += dx * r1;
+ *(int32 *)(out + 16) = result0;
+
+ ref += 4;
+ out += 4;
+ r0 = r2;
+ }
+ pRef += srcPitch;
+ out += (32 - blkwidth);
+ }
+
+// pRef -= srcPitch*(blkheight+1);
+ ref = temp;
+
+ for (j = 0; j < blkwidth; j += 4)
+ {
+ r0 = *(int32 *)ref;
+ r1 = *(int32 *)(ref + 16);
+ ref += 32;
+ out = pOut;
+ for (i = 0; i < (blkheight >> 1); i++)
+ {
+ result0 = dy_8 * r0 + 0x00200020;
+ r2 = *(int32 *)ref;
+ result0 += dy * r2;
+ result0 >>= 6;
+ result0 &= 0x00FF00FF;
+ r0 = r2;
+
+ result1 = dy_8 * r1 + 0x00200020;
+ r3 = *(int32 *)(ref + 16);
+ result1 += dy * r3;
+ result1 >>= 6;
+ result1 &= 0x00FF00FF;
+ r1 = r3;
+ *(int32 *)out = result0 | (result1 << 8);
+ out += predPitch;
+ ref += 32;
+
+ result0 = dy_8 * r0 + 0x00200020;
+ r2 = *(int32 *)ref;
+ result0 += dy * r2;
+ result0 >>= 6;
+ result0 &= 0x00FF00FF;
+ r0 = r2;
+
+ result1 = dy_8 * r1 + 0x00200020;
+ r3 = *(int32 *)(ref + 16);
+ result1 += dy * r3;
+ result1 >>= 6;
+ result1 &= 0x00FF00FF;
+ r1 = r3;
+ *(int32 *)out = result0 | (result1 << 8);
+ out += predPitch;
+ ref += 32;
+ }
+ pOut += 4;
+ ref = temp + 4; /* since it can only iterate twice max */
+ }
+ return;
+}
+
+void eChromaHorizontalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(dy);
+
+ int32 r0, r1, r2, result0, result1;
+ uint8 *ref, *out;
+ int i, j;
+ int dx_8 = 8 - dx;
+
+ /* horizontal first */
+ for (i = 0; i < blkheight; i++)
+ {
+ ref = pRef;
+ out = pOut;
+
+ r0 = ref[0];
+ for (j = 0; j < blkwidth; j += 4)
+ {
+ r0 |= (ref[2] << 16);
+ result0 = dx_8 * r0 + 0x00040004;
+
+ r1 = ref[1] | (ref[3] << 16);
+ result0 += dx * r1;
+ result0 >>= 3;
+ result0 &= 0x00FF00FF;
+
+ result1 = dx_8 * r1 + 0x00040004;
+
+ r2 = ref[4];
+ r0 = r0 >> 16;
+ r1 = r0 | (r2 << 16);
+ result1 += dx * r1;
+ result1 >>= 3;
+ result1 &= 0x00FF00FF;
+
+ *(int32 *)out = result0 | (result1 << 8);
+
+ ref += 4;
+ out += 4;
+ r0 = r2;
+ }
+
+ pRef += srcPitch;
+ pOut += predPitch;
+ }
+ return;
+}
+
+void eChromaVerticalMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(dx);
+
+ int32 r0, r1, r2, r3, result0, result1;
+ int i, j;
+ uint8 *ref, *out;
+ int dy_8 = 8 - dy;
+ /* vertical first */
+ for (i = 0; i < blkwidth; i += 4)
+ {
+ ref = pRef;
+ out = pOut;
+
+ r0 = ref[0] | (ref[2] << 16);
+ r1 = ref[1] | (ref[3] << 16);
+ ref += srcPitch;
+ for (j = 0; j < blkheight; j++)
+ {
+ result0 = dy_8 * r0 + 0x00040004;
+ r2 = ref[0] | (ref[2] << 16);
+ result0 += dy * r2;
+ result0 >>= 3;
+ result0 &= 0x00FF00FF;
+ r0 = r2;
+
+ result1 = dy_8 * r1 + 0x00040004;
+ r3 = ref[1] | (ref[3] << 16);
+ result1 += dy * r3;
+ result1 >>= 3;
+ result1 &= 0x00FF00FF;
+ r1 = r3;
+ *(int32 *)out = result0 | (result1 << 8);
+ ref += srcPitch;
+ out += predPitch;
+ }
+ pOut += 4;
+ pRef += 4;
+ }
+ return;
+}
+
+void eChromaDiagonalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(blkwidth);
+
+ int32 r0, r1, temp0, temp1, result;
+ int32 temp[9];
+ int32 *out;
+ int i, r_temp;
+ int dy_8 = 8 - dy;
+
+ /* horizontal first */
+ out = temp;
+ for (i = 0; i < blkheight + 1; i++)
+ {
+ r_temp = pRef[1];
+ temp0 = (pRef[0] << 3) + dx * (r_temp - pRef[0]);
+ temp1 = (r_temp << 3) + dx * (pRef[2] - r_temp);
+ r0 = temp0 | (temp1 << 16);
+ *out++ = r0;
+ pRef += srcPitch;
+ }
+
+ pRef -= srcPitch * (blkheight + 1);
+
+ out = temp;
+
+ r0 = *out++;
+
+ for (i = 0; i < blkheight; i++)
+ {
+ result = dy_8 * r0 + 0x00200020;
+ r1 = *out++;
+ result += dy * r1;
+ result >>= 6;
+ result &= 0x00FF00FF;
+ *(int16 *)pOut = (result >> 8) | (result & 0xFF);
+ r0 = r1;
+ pOut += predPitch;
+ }
+ return;
+}
+
+void eChromaHorizontalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(dy);
+ (void)(blkwidth);
+
+ int i, temp, temp0, temp1;
+
+ /* horizontal first */
+ for (i = 0; i < blkheight; i++)
+ {
+ temp = pRef[1];
+ temp0 = ((pRef[0] << 3) + dx * (temp - pRef[0]) + 4) >> 3;
+ temp1 = ((temp << 3) + dx * (pRef[2] - temp) + 4) >> 3;
+
+ *(int16 *)pOut = temp0 | (temp1 << 8);
+ pRef += srcPitch;
+ pOut += predPitch;
+
+ }
+ return;
+}
+void eChromaVerticalMC2_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(dx);
+ (void)(blkwidth);
+
+ int32 r0, r1, result;
+ int i;
+ int dy_8 = 8 - dy;
+ r0 = pRef[0] | (pRef[1] << 16);
+ pRef += srcPitch;
+ for (i = 0; i < blkheight; i++)
+ {
+ result = dy_8 * r0 + 0x00040004;
+ r1 = pRef[0] | (pRef[1] << 16);
+ result += dy * r1;
+ result >>= 3;
+ result &= 0x00FF00FF;
+ *(int16 *)pOut = (result >> 8) | (result & 0xFF);
+ r0 = r1;
+ pRef += srcPitch;
+ pOut += predPitch;
+ }
+ return;
+}
+
+void eChromaFullMC_SIMD(uint8 *pRef, int srcPitch, int dx, int dy,
+ uint8 *pOut, int predPitch, int blkwidth, int blkheight)
+{
+ (void)(dx);
+ (void)(dy);
+
+ int i, j;
+ int offset_in = srcPitch - blkwidth;
+ int offset_out = predPitch - blkwidth;
+ uint16 temp;
+ uint8 byte;
+
+ if (((uint32)pRef)&1)
+ {
+ for (j = blkheight; j > 0; j--)
+ {
+ for (i = blkwidth; i > 0; i -= 2)
+ {
+ temp = *pRef++;
+ byte = *pRef++;
+ temp |= (byte << 8);
+ *((uint16*)pOut) = temp; /* write 2 bytes */
+ pOut += 2;
+ }
+ pOut += offset_out;
+ pRef += offset_in;
+ }
+ }
+ else
+ {
+ for (j = blkheight; j > 0; j--)
+ {
+ for (i = blkwidth; i > 0; i -= 2)
+ {
+ temp = *((uint16*)pRef);
+ *((uint16*)pOut) = temp;
+ pRef += 2;
+ pOut += 2;
+ }
+ pOut += offset_out;
+ pRef += offset_in;
+ }
+ }
+ return ;
+}
diff --git a/media/libstagefright/codecs/avc/enc/src/motion_est.cpp b/media/libstagefright/codecs/avc/enc/src/motion_est.cpp
new file mode 100644
index 0000000..f650ef9
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/motion_est.cpp
@@ -0,0 +1,1774 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+#define MIN_GOP 1 /* minimum size of GOP, 1/23/01, need to be tested */
+
+#define DEFAULT_REF_IDX 0 /* always from the first frame in the reflist */
+
+#define ALL_CAND_EQUAL 10 /* any number greater than 5 will work */
+
+
+/* from TMN 3.2 */
+#define PREF_NULL_VEC 129 /* zero vector bias */
+#define PREF_16_VEC 129 /* 1MV bias versus 4MVs*/
+#define PREF_INTRA 3024//512 /* bias for INTRA coding */
+
+const static int tab_exclude[9][9] = // [last_loc][curr_loc]
+{
+ {0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 1, 1, 1, 0, 0},
+ {0, 0, 0, 0, 1, 1, 1, 1, 1},
+ {0, 0, 0, 0, 0, 0, 1, 1, 1},
+ {0, 1, 1, 0, 0, 0, 1, 1, 1},
+ {0, 1, 1, 0, 0, 0, 0, 0, 1},
+ {0, 1, 1, 1, 1, 0, 0, 0, 1},
+ {0, 0, 1, 1, 1, 0, 0, 0, 0},
+ {0, 0, 1, 1, 1, 1, 1, 0, 0}
+}; //to decide whether to continue or compute
+
+const static int refine_next[8][2] = /* [curr_k][increment] */
+{
+ {0, 0}, {2, 0}, {1, 1}, {0, 2}, { -1, 1}, { -2, 0}, { -1, -1}, {0, -2}
+};
+
+#ifdef _SAD_STAT
+uint32 num_MB = 0;
+uint32 num_cand = 0;
+#endif
+
+/************************************************************************/
+#define TH_INTER_2 100 /* temporary for now */
+
+//#define FIXED_INTERPRED_MODE AVC_P16
+#define FIXED_REF_IDX 0
+#define FIXED_MVX 0
+#define FIXED_MVY 0
+
+// only use when AVC_P8 or AVC_P8ref0
+#define FIXED_SUBMB_MODE AVC_4x4
+/*************************************************************************/
+
+/* Initialize arrays necessary for motion search */
+AVCEnc_Status InitMotionSearchModule(AVCHandle *avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ int search_range = rateCtrl->mvRange;
+ int number_of_subpel_positions = 4 * (2 * search_range + 3);
+ int max_mv_bits, max_mvd;
+ int temp_bits = 0;
+ uint8 *mvbits;
+ int bits, imax, imin, i;
+ uint8* subpel_pred = (uint8*) encvid->subpel_pred; // all 16 sub-pel positions
+
+
+ while (number_of_subpel_positions > 0)
+ {
+ temp_bits++;
+ number_of_subpel_positions >>= 1;
+ }
+
+ max_mv_bits = 3 + 2 * temp_bits;
+ max_mvd = (1 << (max_mv_bits >> 1)) - 1;
+
+ encvid->mvbits_array = (uint8*) avcHandle->CBAVC_Malloc(encvid->avcHandle->userData,
+ sizeof(uint8) * (2 * max_mvd + 1), DEFAULT_ATTR);
+
+ if (encvid->mvbits_array == NULL)
+ {
+ return AVCENC_MEMORY_FAIL;
+ }
+
+ mvbits = encvid->mvbits = encvid->mvbits_array + max_mvd;
+
+ mvbits[0] = 1;
+ for (bits = 3; bits <= max_mv_bits; bits += 2)
+ {
+ imax = 1 << (bits >> 1);
+ imin = imax >> 1;
+
+ for (i = imin; i < imax; i++) mvbits[-i] = mvbits[i] = bits;
+ }
+
+ /* initialize half-pel search */
+ encvid->hpel_cand[0] = subpel_pred + REF_CENTER;
+ encvid->hpel_cand[1] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 1 ;
+ encvid->hpel_cand[2] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->hpel_cand[3] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 25;
+ encvid->hpel_cand[4] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 25;
+ encvid->hpel_cand[5] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 25;
+ encvid->hpel_cand[6] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->hpel_cand[7] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->hpel_cand[8] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+
+ /* For quarter-pel interpolation around best half-pel result */
+
+ encvid->bilin_base[0][0] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[0][1] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->bilin_base[0][2] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->bilin_base[0][3] = subpel_pred + REF_CENTER;
+
+
+ encvid->bilin_base[1][0] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[1][1] = subpel_pred + REF_CENTER - 24;
+ encvid->bilin_base[1][2] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[1][3] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 1;
+
+ encvid->bilin_base[2][0] = subpel_pred + REF_CENTER - 24;
+ encvid->bilin_base[2][1] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->bilin_base[2][2] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->bilin_base[2][3] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 1;
+
+ encvid->bilin_base[3][0] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->bilin_base[3][1] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 1;
+ encvid->bilin_base[3][2] = subpel_pred + REF_CENTER;
+ encvid->bilin_base[3][3] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 25;
+
+ encvid->bilin_base[4][0] = subpel_pred + REF_CENTER;
+ encvid->bilin_base[4][1] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 25;
+ encvid->bilin_base[4][2] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 25;
+ encvid->bilin_base[4][3] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 25;
+
+ encvid->bilin_base[5][0] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->bilin_base[5][1] = subpel_pred + REF_CENTER;
+ encvid->bilin_base[5][2] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->bilin_base[5][3] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 25;
+
+ encvid->bilin_base[6][0] = subpel_pred + REF_CENTER - 1;
+ encvid->bilin_base[6][1] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->bilin_base[6][2] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE + 24;
+ encvid->bilin_base[6][3] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+
+ encvid->bilin_base[7][0] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[7][1] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[7][2] = subpel_pred + REF_CENTER - 1;
+ encvid->bilin_base[7][3] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE + 24;
+
+ encvid->bilin_base[8][0] = subpel_pred + REF_CENTER - 25;
+ encvid->bilin_base[8][1] = subpel_pred + V0Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[8][2] = subpel_pred + V2Q_H0Q * SUBPEL_PRED_BLK_SIZE;
+ encvid->bilin_base[8][3] = subpel_pred + V2Q_H2Q * SUBPEL_PRED_BLK_SIZE;
+
+
+ return AVCENC_SUCCESS;
+}
+
+/* Clean-up memory */
+void CleanMotionSearchModule(AVCHandle *avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+
+ if (encvid->mvbits_array)
+ {
+ avcHandle->CBAVC_Free(avcHandle->userData, (int)(encvid->mvbits_array));
+ encvid->mvbits = NULL;
+ }
+
+ return ;
+}
+
+
+bool IntraDecisionABE(int *min_cost, uint8 *cur, int pitch, bool ave)
+{
+ int j;
+ uint8 *out;
+ int temp, SBE;
+ OsclFloat ABE;
+ bool intra = true;
+
+ SBE = 0;
+ /* top neighbor */
+ out = cur - pitch;
+ for (j = 0; j < 16; j++)
+ {
+ temp = out[j] - cur[j];
+ SBE += ((temp >= 0) ? temp : -temp);
+ }
+
+ /* left neighbor */
+ out = cur - 1;
+ out -= pitch;
+ cur -= pitch;
+ for (j = 0; j < 16; j++)
+ {
+ temp = *(out += pitch) - *(cur += pitch);
+ SBE += ((temp >= 0) ? temp : -temp);
+ }
+
+ /* compare mincost/384 and SBE/64 */
+ ABE = SBE / 32.0; //ABE = SBE/64.0; //
+ if (ABE >= *min_cost / 256.0) //if( ABE*0.8 >= min_cost/384.0) //
+ {
+ intra = false; // no possibility of intra, just use inter
+ }
+ else
+ {
+ if (ave == true)
+ {
+ *min_cost = (*min_cost + (int)(SBE * 8)) >> 1; // possibility of intra, averaging the cost
+ }
+ else
+ {
+ *min_cost = (int)(SBE * 8);
+ }
+ }
+
+ return intra;
+}
+
+/******* main function for macroblock prediction for the entire frame ***/
+/* if turns out to be IDR frame, set video->nal_unit_type to AVC_NALTYPE_IDR */
+void AVCMotionEstimation(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ int slice_type = video->slice_type;
+ AVCFrameIO *currInput = encvid->currInput;
+ AVCPictureData *refPic = video->RefPicList0[0];
+ int i, j, k;
+ int mbwidth = video->PicWidthInMbs;
+ int mbheight = video->PicHeightInMbs;
+ int totalMB = video->PicSizeInMbs;
+ int pitch = currInput->pitch;
+ AVCMacroblock *currMB, *mblock = video->mblock;
+ AVCMV *mot_mb_16x16, *mot16x16 = encvid->mot16x16;
+ // AVCMV *mot_mb_16x8, *mot_mb_8x16, *mot_mb_8x8, etc;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ uint8 *intraSearch = encvid->intraSearch;
+ uint FS_en = encvid->fullsearch_enable;
+
+ int NumIntraSearch, start_i, numLoop, incr_i;
+ int mbnum, offset;
+ uint8 *cur, *best_cand[5];
+ int totalSAD = 0; /* average SAD for rate control */
+ int type_pred;
+ int abe_cost;
+
+#ifdef HTFM
+ /***** HYPOTHESIS TESTING ********/ /* 2/28/01 */
+ int collect = 0;
+ HTFM_Stat htfm_stat;
+ double newvar[16];
+ double exp_lamda[15];
+ /*********************************/
+#endif
+ int hp_guess = 0;
+ uint32 mv_uint32;
+
+ offset = 0;
+
+ if (slice_type == AVC_I_SLICE)
+ {
+ /* cannot do I16 prediction here because it needs full decoding. */
+ for (i = 0; i < totalMB; i++)
+ {
+ encvid->min_cost[i] = 0x7FFFFFFF; /* max value for int */
+ }
+
+ memset(intraSearch, 1, sizeof(uint8)*totalMB);
+
+ encvid->firstIntraRefreshMBIndx = 0; /* reset this */
+
+ return ;
+ }
+ else // P_SLICE
+ {
+ for (i = 0; i < totalMB; i++)
+ {
+ mblock[i].mb_intra = 0;
+ }
+ memset(intraSearch, 1, sizeof(uint8)*totalMB);
+ }
+
+ if (refPic->padded == 0)
+ {
+ AVCPaddingEdge(refPic);
+ refPic->padded = 1;
+ }
+ /* Random INTRA update */
+ if (rateCtrl->intraMBRate)
+ {
+ AVCRasterIntraUpdate(encvid, mblock, totalMB, rateCtrl->intraMBRate);
+ }
+
+ encvid->sad_extra_info = NULL;
+#ifdef HTFM
+ /***** HYPOTHESIS TESTING ********/
+ InitHTFM(video, &htfm_stat, newvar, &collect);
+ /*********************************/
+#endif
+
+ if ((rateCtrl->scdEnable == 1)
+ && ((rateCtrl->frame_rate < 5.0) || (video->sliceHdr->frame_num > MIN_GOP)))
+ /* do not try to detect a new scene if low frame rate and too close to previous I-frame */
+ {
+ incr_i = 2;
+ numLoop = 2;
+ start_i = 1;
+ type_pred = 0; /* for initial candidate selection */
+ }
+ else
+ {
+ incr_i = 1;
+ numLoop = 1;
+ start_i = 0;
+ type_pred = 2;
+ }
+
+ /* First pass, loop thru half the macroblock */
+ /* determine scene change */
+ /* Second pass, for the rest of macroblocks */
+ NumIntraSearch = 0; // to be intra searched in the encoding loop.
+ while (numLoop--)
+ {
+ for (j = 0; j < mbheight; j++)
+ {
+ if (incr_i > 1)
+ start_i = (start_i == 0 ? 1 : 0) ; /* toggle 0 and 1 */
+
+ offset = pitch * (j << 4) + (start_i << 4);
+
+ mbnum = j * mbwidth + start_i;
+
+ for (i = start_i; i < mbwidth; i += incr_i)
+ {
+ video->mbNum = mbnum;
+ video->currMB = currMB = mblock + mbnum;
+ mot_mb_16x16 = mot16x16 + mbnum;
+
+ cur = currInput->YCbCr[0] + offset;
+
+ if (currMB->mb_intra == 0) /* for INTER mode */
+ {
+#if defined(HTFM)
+ HTFMPrepareCurMB_AVC(encvid, &htfm_stat, cur, pitch);
+#else
+ AVCPrepareCurMB(encvid, cur, pitch);
+#endif
+ /************************************************************/
+ /******** full-pel 1MV search **********************/
+
+ AVCMBMotionSearch(encvid, cur, best_cand, i << 4, j << 4, type_pred,
+ FS_en, &hp_guess);
+
+ abe_cost = encvid->min_cost[mbnum] = mot_mb_16x16->sad;
+
+ /* set mbMode and MVs */
+ currMB->mbMode = AVC_P16;
+ currMB->MBPartPredMode[0][0] = AVC_Pred_L0;
+ mv_uint32 = ((mot_mb_16x16->y) << 16) | ((mot_mb_16x16->x) & 0xffff);
+ for (k = 0; k < 32; k += 2)
+ {
+ currMB->mvL0[k>>1] = mv_uint32;
+ }
+
+ /* make a decision whether it should be tested for intra or not */
+ if (i != mbwidth - 1 && j != mbheight - 1 && i != 0 && j != 0)
+ {
+ if (false == IntraDecisionABE(&abe_cost, cur, pitch, true))
+ {
+ intraSearch[mbnum] = 0;
+ }
+ else
+ {
+ NumIntraSearch++;
+ rateCtrl->MADofMB[mbnum] = abe_cost;
+ }
+ }
+ else // boundary MBs, always do intra search
+ {
+ NumIntraSearch++;
+ }
+
+ totalSAD += (int) rateCtrl->MADofMB[mbnum];//mot_mb_16x16->sad;
+ }
+ else /* INTRA update, use for prediction */
+ {
+ mot_mb_16x16[0].x = mot_mb_16x16[0].y = 0;
+
+ /* reset all other MVs to zero */
+ /* mot_mb_16x8, mot_mb_8x16, mot_mb_8x8, etc. */
+ abe_cost = encvid->min_cost[mbnum] = 0x7FFFFFFF; /* max value for int */
+
+ if (i != mbwidth - 1 && j != mbheight - 1 && i != 0 && j != 0)
+ {
+ IntraDecisionABE(&abe_cost, cur, pitch, false);
+
+ rateCtrl->MADofMB[mbnum] = abe_cost;
+ totalSAD += abe_cost;
+ }
+
+ NumIntraSearch++ ;
+ /* cannot do I16 prediction here because it needs full decoding. */
+ // intraSearch[mbnum] = 1;
+
+ }
+
+ mbnum += incr_i;
+ offset += (incr_i << 4);
+
+ } /* for i */
+ } /* for j */
+
+ /* since we cannot do intra/inter decision here, the SCD has to be
+ based on other criteria such as motion vectors coherency or the SAD */
+ if (incr_i > 1 && numLoop) /* scene change on and first loop */
+ {
+ //if(NumIntraSearch > ((totalMB>>3)<<1) + (totalMB>>3)) /* 75% of 50%MBs */
+ if (NumIntraSearch*99 > (48*totalMB)) /* 20% of 50%MBs */
+ /* need to do more investigation about this threshold since the NumIntraSearch
+ only show potential intra MBs, not the actual one */
+ {
+ /* we can choose to just encode I_SLICE without IDR */
+ //video->nal_unit_type = AVC_NALTYPE_IDR;
+ video->nal_unit_type = AVC_NALTYPE_SLICE;
+ video->sliceHdr->slice_type = AVC_I_ALL_SLICE;
+ video->slice_type = AVC_I_SLICE;
+ memset(intraSearch, 1, sizeof(uint8)*totalMB);
+ i = totalMB;
+ while (i--)
+ {
+ mblock[i].mb_intra = 1;
+ encvid->min_cost[i] = 0x7FFFFFFF; /* max value for int */
+ }
+
+ rateCtrl->totalSAD = totalSAD * 2; /* SAD */
+
+ return ;
+ }
+ }
+ /******** no scene change, continue motion search **********************/
+ start_i = 0;
+ type_pred++; /* second pass */
+ }
+
+ rateCtrl->totalSAD = totalSAD; /* SAD */
+
+#ifdef HTFM
+ /***** HYPOTHESIS TESTING ********/
+ if (collect)
+ {
+ collect = 0;
+ UpdateHTFM(encvid, newvar, exp_lamda, &htfm_stat);
+ }
+ /*********************************/
+#endif
+
+ return ;
+}
+
+/*=====================================================================
+ Function: PaddingEdge
+ Date: 09/16/2000
+ Purpose: Pad edge of a Vop
+=====================================================================*/
+
+void AVCPaddingEdge(AVCPictureData *refPic)
+{
+ uint8 *src, *dst;
+ int i;
+ int pitch, width, height;
+ uint32 temp1, temp2;
+
+ width = refPic->width;
+ height = refPic->height;
+ pitch = refPic->pitch;
+
+ /* pad top */
+ src = refPic->Sl;
+
+ temp1 = *src; /* top-left corner */
+ temp2 = src[width-1]; /* top-right corner */
+ temp1 |= (temp1 << 8);
+ temp1 |= (temp1 << 16);
+ temp2 |= (temp2 << 8);
+ temp2 |= (temp2 << 16);
+
+ dst = src - (pitch << 4);
+
+ *((uint32*)(dst - 16)) = temp1;
+ *((uint32*)(dst - 12)) = temp1;
+ *((uint32*)(dst - 8)) = temp1;
+ *((uint32*)(dst - 4)) = temp1;
+
+ memcpy(dst, src, width);
+
+ *((uint32*)(dst += width)) = temp2;
+ *((uint32*)(dst + 4)) = temp2;
+ *((uint32*)(dst + 8)) = temp2;
+ *((uint32*)(dst + 12)) = temp2;
+
+ dst = dst - width - 16;
+
+ i = 15;
+ while (i--)
+ {
+ memcpy(dst + pitch, dst, pitch);
+ dst += pitch;
+ }
+
+ /* pad sides */
+ dst += (pitch + 16);
+ src = dst;
+ i = height;
+ while (i--)
+ {
+ temp1 = *src;
+ temp2 = src[width-1];
+ temp1 |= (temp1 << 8);
+ temp1 |= (temp1 << 16);
+ temp2 |= (temp2 << 8);
+ temp2 |= (temp2 << 16);
+
+ *((uint32*)(dst - 16)) = temp1;
+ *((uint32*)(dst - 12)) = temp1;
+ *((uint32*)(dst - 8)) = temp1;
+ *((uint32*)(dst - 4)) = temp1;
+
+ *((uint32*)(dst += width)) = temp2;
+ *((uint32*)(dst + 4)) = temp2;
+ *((uint32*)(dst + 8)) = temp2;
+ *((uint32*)(dst + 12)) = temp2;
+
+ src += pitch;
+ dst = src;
+ }
+
+ /* pad bottom */
+ dst -= 16;
+ i = 16;
+ while (i--)
+ {
+ memcpy(dst, dst - pitch, pitch);
+ dst += pitch;
+ }
+
+
+ return ;
+}
+
+/*===========================================================================
+ Function: AVCRasterIntraUpdate
+ Date: 2/26/01
+ Purpose: To raster-scan assign INTRA-update .
+ N macroblocks are updated (also was programmable).
+===========================================================================*/
+void AVCRasterIntraUpdate(AVCEncObject *encvid, AVCMacroblock *mblock, int totalMB, int numRefresh)
+{
+ int indx, i;
+
+ indx = encvid->firstIntraRefreshMBIndx;
+ for (i = 0; i < numRefresh && indx < totalMB; i++)
+ {
+ (mblock + indx)->mb_intra = 1;
+ encvid->intraSearch[indx++] = 1;
+ }
+
+ /* if read the end of frame, reset and loop around */
+ if (indx >= totalMB - 1)
+ {
+ indx = 0;
+ while (i < numRefresh && indx < totalMB)
+ {
+ (mblock + indx)->mb_intra = 1;
+ encvid->intraSearch[indx++] = 1;
+ i++;
+ }
+ }
+
+ encvid->firstIntraRefreshMBIndx = indx; /* update with a new value */
+
+ return ;
+}
+
+
+#ifdef HTFM
+void InitHTFM(VideoEncData *encvid, HTFM_Stat *htfm_stat, double *newvar, int *collect)
+{
+ AVCCommonObj *video = encvid->common;
+ int i;
+ int lx = video->currPic->width; // padding
+ int lx2 = lx << 1;
+ int lx3 = lx2 + lx;
+ int rx = video->currPic->pitch;
+ int rx2 = rx << 1;
+ int rx3 = rx2 + rx;
+
+ int *offset, *offset2;
+
+ /* 4/11/01, collect data every 30 frames, doesn't have to be base layer */
+ if (((int)video->sliceHdr->frame_num) % 30 == 1)
+ {
+
+ *collect = 1;
+
+ htfm_stat->countbreak = 0;
+ htfm_stat->abs_dif_mad_avg = 0;
+
+ for (i = 0; i < 16; i++)
+ {
+ newvar[i] = 0.0;
+ }
+// encvid->functionPointer->SAD_MB_PADDING = &SAD_MB_PADDING_HTFM_Collect;
+ encvid->functionPointer->SAD_Macroblock = &SAD_MB_HTFM_Collect;
+ encvid->functionPointer->SAD_MB_HalfPel[0] = NULL;
+ encvid->functionPointer->SAD_MB_HalfPel[1] = &SAD_MB_HP_HTFM_Collectxh;
+ encvid->functionPointer->SAD_MB_HalfPel[2] = &SAD_MB_HP_HTFM_Collectyh;
+ encvid->functionPointer->SAD_MB_HalfPel[3] = &SAD_MB_HP_HTFM_Collectxhyh;
+ encvid->sad_extra_info = (void*)(htfm_stat);
+ offset = htfm_stat->offsetArray;
+ offset2 = htfm_stat->offsetRef;
+ }
+ else
+ {
+// encvid->functionPointer->SAD_MB_PADDING = &SAD_MB_PADDING_HTFM;
+ encvid->functionPointer->SAD_Macroblock = &SAD_MB_HTFM;
+ encvid->functionPointer->SAD_MB_HalfPel[0] = NULL;
+ encvid->functionPointer->SAD_MB_HalfPel[1] = &SAD_MB_HP_HTFMxh;
+ encvid->functionPointer->SAD_MB_HalfPel[2] = &SAD_MB_HP_HTFMyh;
+ encvid->functionPointer->SAD_MB_HalfPel[3] = &SAD_MB_HP_HTFMxhyh;
+ encvid->sad_extra_info = (void*)(encvid->nrmlz_th);
+ offset = encvid->nrmlz_th + 16;
+ offset2 = encvid->nrmlz_th + 32;
+ }
+
+ offset[0] = 0;
+ offset[1] = lx2 + 2;
+ offset[2] = 2;
+ offset[3] = lx2;
+ offset[4] = lx + 1;
+ offset[5] = lx3 + 3;
+ offset[6] = lx + 3;
+ offset[7] = lx3 + 1;
+ offset[8] = lx;
+ offset[9] = lx3 + 2;
+ offset[10] = lx3 ;
+ offset[11] = lx + 2 ;
+ offset[12] = 1;
+ offset[13] = lx2 + 3;
+ offset[14] = lx2 + 1;
+ offset[15] = 3;
+
+ offset2[0] = 0;
+ offset2[1] = rx2 + 2;
+ offset2[2] = 2;
+ offset2[3] = rx2;
+ offset2[4] = rx + 1;
+ offset2[5] = rx3 + 3;
+ offset2[6] = rx + 3;
+ offset2[7] = rx3 + 1;
+ offset2[8] = rx;
+ offset2[9] = rx3 + 2;
+ offset2[10] = rx3 ;
+ offset2[11] = rx + 2 ;
+ offset2[12] = 1;
+ offset2[13] = rx2 + 3;
+ offset2[14] = rx2 + 1;
+ offset2[15] = 3;
+
+ return ;
+}
+
+void UpdateHTFM(AVCEncObject *encvid, double *newvar, double *exp_lamda, HTFM_Stat *htfm_stat)
+{
+ if (htfm_stat->countbreak == 0)
+ htfm_stat->countbreak = 1;
+
+ newvar[0] = (double)(htfm_stat->abs_dif_mad_avg) / (htfm_stat->countbreak * 16.);
+
+ if (newvar[0] < 0.001)
+ {
+ newvar[0] = 0.001; /* to prevent floating overflow */
+ }
+ exp_lamda[0] = 1 / (newvar[0] * 1.4142136);
+ exp_lamda[1] = exp_lamda[0] * 1.5825;
+ exp_lamda[2] = exp_lamda[0] * 2.1750;
+ exp_lamda[3] = exp_lamda[0] * 3.5065;
+ exp_lamda[4] = exp_lamda[0] * 3.1436;
+ exp_lamda[5] = exp_lamda[0] * 3.5315;
+ exp_lamda[6] = exp_lamda[0] * 3.7449;
+ exp_lamda[7] = exp_lamda[0] * 4.5854;
+ exp_lamda[8] = exp_lamda[0] * 4.6191;
+ exp_lamda[9] = exp_lamda[0] * 5.4041;
+ exp_lamda[10] = exp_lamda[0] * 6.5974;
+ exp_lamda[11] = exp_lamda[0] * 10.5341;
+ exp_lamda[12] = exp_lamda[0] * 10.0719;
+ exp_lamda[13] = exp_lamda[0] * 12.0516;
+ exp_lamda[14] = exp_lamda[0] * 15.4552;
+
+ CalcThreshold(HTFM_Pf, exp_lamda, encvid->nrmlz_th);
+ return ;
+}
+
+
+void CalcThreshold(double pf, double exp_lamda[], int nrmlz_th[])
+{
+ int i;
+ double temp[15];
+ // printf("\nLamda: ");
+
+ /* parametric PREMODELling */
+ for (i = 0; i < 15; i++)
+ {
+ // printf("%g ",exp_lamda[i]);
+ if (pf < 0.5)
+ temp[i] = 1 / exp_lamda[i] * M4VENC_LOG(2 * pf);
+ else
+ temp[i] = -1 / exp_lamda[i] * M4VENC_LOG(2 * (1 - pf));
+ }
+
+ nrmlz_th[15] = 0;
+ for (i = 0; i < 15; i++) /* scale upto no.pixels */
+ nrmlz_th[i] = (int)(temp[i] * ((i + 1) << 4) + 0.5);
+
+ return ;
+}
+
+void HTFMPrepareCurMB_AVC(AVCEncObject *encvid, HTFM_Stat *htfm_stat, uint8 *cur, int pitch)
+{
+ AVCCommonObj *video = encvid->common;
+ uint32 *htfmMB = (uint32*)(encvid->currYMB);
+ uint8 *ptr, byte;
+ int *offset;
+ int i;
+ uint32 word;
+
+ if (((int)video->sliceHdr->frame_num) % 30 == 1)
+ {
+ offset = htfm_stat->offsetArray;
+ }
+ else
+ {
+ offset = encvid->nrmlz_th + 16;
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ ptr = cur + offset[i];
+ word = ptr[0];
+ byte = ptr[4];
+ word |= (byte << 8);
+ byte = ptr[8];
+ word |= (byte << 16);
+ byte = ptr[12];
+ word |= (byte << 24);
+ *htfmMB++ = word;
+
+ word = *(ptr += (pitch << 2));
+ byte = ptr[4];
+ word |= (byte << 8);
+ byte = ptr[8];
+ word |= (byte << 16);
+ byte = ptr[12];
+ word |= (byte << 24);
+ *htfmMB++ = word;
+
+ word = *(ptr += (pitch << 2));
+ byte = ptr[4];
+ word |= (byte << 8);
+ byte = ptr[8];
+ word |= (byte << 16);
+ byte = ptr[12];
+ word |= (byte << 24);
+ *htfmMB++ = word;
+
+ word = *(ptr += (pitch << 2));
+ byte = ptr[4];
+ word |= (byte << 8);
+ byte = ptr[8];
+ word |= (byte << 16);
+ byte = ptr[12];
+ word |= (byte << 24);
+ *htfmMB++ = word;
+ }
+
+ return ;
+}
+
+
+#endif // HTFM
+
+void AVCPrepareCurMB(AVCEncObject *encvid, uint8 *cur, int pitch)
+{
+ void* tmp = (void*)(encvid->currYMB);
+ uint32 *currYMB = (uint32*) tmp;
+ int i;
+
+ cur -= pitch;
+
+ for (i = 0; i < 16; i++)
+ {
+ *currYMB++ = *((uint32*)(cur += pitch));
+ *currYMB++ = *((uint32*)(cur + 4));
+ *currYMB++ = *((uint32*)(cur + 8));
+ *currYMB++ = *((uint32*)(cur + 12));
+ }
+
+ return ;
+}
+
+#ifdef FIXED_INTERPRED_MODE
+
+/* due to the complexity of the predicted motion vector, we may not decide to skip
+a macroblock here just yet. */
+/* We will find the best motion vector and the best intra prediction mode for each block. */
+/* output are
+ currMB->NumMbPart, currMB->MbPartWidth, currMB->MbPartHeight,
+ currMB->NumSubMbPart[], currMB->SubMbPartWidth[], currMB->SubMbPartHeight,
+ currMB->MBPartPredMode[][] (L0 or L1 or BiPred)
+ currMB->RefIdx[], currMB->ref_idx_L0[],
+ currMB->mvL0[], currMB->mvL1[]
+ */
+
+AVCEnc_Status AVCMBMotionSearch(AVCEncObject *encvid, AVCMacroblock *currMB, int mbNum,
+ int num_pass)
+{
+ AVCCommonObj *video = encvid->common;
+ int mbPartIdx, subMbPartIdx;
+ int16 *mv;
+ int i;
+ int SubMbPartHeight, SubMbPartWidth, NumSubMbPart;
+
+ /* assign value to currMB->MBPartPredMode[][x],subMbMode[],NumSubMbPart[],SubMbPartWidth[],SubMbPartHeight[] */
+
+ currMB->mbMode = FIXED_INTERPRED_MODE;
+ currMB->mb_intra = 0;
+
+ if (currMB->mbMode == AVC_P16)
+ {
+ currMB->NumMbPart = 1;
+ currMB->MbPartWidth = 16;
+ currMB->MbPartHeight = 16;
+ currMB->SubMbPartHeight[0] = 16;
+ currMB->SubMbPartWidth[0] = 16;
+ currMB->NumSubMbPart[0] = 1;
+ }
+ else if (currMB->mbMode == AVC_P16x8)
+ {
+ currMB->NumMbPart = 2;
+ currMB->MbPartWidth = 16;
+ currMB->MbPartHeight = 8;
+ for (i = 0; i < 2; i++)
+ {
+ currMB->SubMbPartWidth[i] = 16;
+ currMB->SubMbPartHeight[i] = 8;
+ currMB->NumSubMbPart[i] = 1;
+ }
+ }
+ else if (currMB->mbMode == AVC_P8x16)
+ {
+ currMB->NumMbPart = 2;
+ currMB->MbPartWidth = 8;
+ currMB->MbPartHeight = 16;
+ for (i = 0; i < 2; i++)
+ {
+ currMB->SubMbPartWidth[i] = 8;
+ currMB->SubMbPartHeight[i] = 16;
+ currMB->NumSubMbPart[i] = 1;
+ }
+ }
+ else if (currMB->mbMode == AVC_P8 || currMB->mbMode == AVC_P8ref0)
+ {
+ currMB->NumMbPart = 4;
+ currMB->MbPartWidth = 8;
+ currMB->MbPartHeight = 8;
+ if (FIXED_SUBMB_MODE == AVC_8x8)
+ {
+ SubMbPartHeight = 8;
+ SubMbPartWidth = 8;
+ NumSubMbPart = 1;
+ }
+ else if (FIXED_SUBMB_MODE == AVC_8x4)
+ {
+ SubMbPartHeight = 4;
+ SubMbPartWidth = 8;
+ NumSubMbPart = 2;
+ }
+ else if (FIXED_SUBMB_MODE == AVC_4x8)
+ {
+ SubMbPartHeight = 8;
+ SubMbPartWidth = 4;
+ NumSubMbPart = 2;
+ }
+ else if (FIXED_SUBMB_MODE == AVC_4x4)
+ {
+ SubMbPartHeight = 4;
+ SubMbPartWidth = 4;
+ NumSubMbPart = 4;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ currMB->subMbMode[i] = FIXED_SUBMB_MODE;
+ currMB->SubMbPartHeight[i] = SubMbPartHeight;
+ currMB->SubMbPartWidth[i] = SubMbPartWidth;
+ currMB->NumSubMbPart[i] = NumSubMbPart;
+ }
+ }
+ else /* it's probably intra mode */
+ {
+ return AVCENC_SUCCESS;
+ }
+
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ currMB->MBPartPredMode[mbPartIdx][0] = AVC_Pred_L0;
+ currMB->ref_idx_L0[mbPartIdx] = FIXED_REF_IDX;
+ currMB->RefIdx[mbPartIdx] = video->RefPicList0[FIXED_REF_IDX]->RefIdx;
+
+ for (subMbPartIdx = 0; subMbPartIdx < 4; subMbPartIdx++)
+ {
+ mv = (int16*)(currMB->mvL0 + (mbPartIdx << 2) + subMbPartIdx);
+
+ *mv++ = FIXED_MVX;
+ *mv = FIXED_MVY;
+ }
+ }
+
+ encvid->min_cost = 0;
+
+ return AVCENC_SUCCESS;
+}
+
+#else /* perform the search */
+
+/* This option #1 search is very similar to PV's MPEG4 motion search algorithm.
+ The search is done in hierarchical manner from 16x16 MB down to smaller and smaller
+ partition. At each level, a decision can be made to stop the search if the expected
+ prediction gain is not worth the computation. The decision can also be made at the finest
+ level for more fullsearch-like behavior with the price of heavier computation. */
+void AVCMBMotionSearch(AVCEncObject *encvid, uint8 *cur, uint8 *best_cand[],
+ int i0, int j0, int type_pred, int FS_en, int *hp_guess)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCPictureData *currPic = video->currPic;
+ AVCSeqParamSet *currSPS = video->currSeqParams;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCMacroblock *currMB = video->currMB;
+ uint8 *ref, *cand, *ncand;
+ void *extra_info = encvid->sad_extra_info;
+ int mbnum = video->mbNum;
+ int width = currPic->width; /* 6/12/01, must be multiple of 16 */
+ int height = currPic->height;
+ AVCMV *mot16x16 = encvid->mot16x16;
+ int (*SAD_Macroblock)(uint8*, uint8*, int, void*) = encvid->functionPointer->SAD_Macroblock;
+
+ int range = rateCtrl->mvRange;
+
+ int lx = currPic->pitch; /* padding */
+ int i, j, imin, jmin, ilow, ihigh, jlow, jhigh;
+ int d, dmin, dn[9];
+ int k;
+ int mvx[5], mvy[5];
+ int num_can, center_again;
+ int last_loc, new_loc = 0;
+ int step, max_step = range >> 1;
+ int next;
+
+ int cmvx, cmvy; /* estimated predicted MV */
+ int lev_idx;
+ int lambda_motion = encvid->lambda_motion;
+ uint8 *mvbits = encvid->mvbits;
+ int mvshift = 2;
+ int mvcost;
+
+ int min_sad = 65535;
+
+ ref = video->RefPicList0[DEFAULT_REF_IDX]->Sl; /* origin of actual frame */
+
+ /* have to initialize these params, necessary for interprediction part */
+ currMB->NumMbPart = 1;
+ currMB->SubMbPartHeight[0] = 16;
+ currMB->SubMbPartWidth[0] = 16;
+ currMB->NumSubMbPart[0] = 1;
+ currMB->ref_idx_L0[0] = currMB->ref_idx_L0[1] =
+ currMB->ref_idx_L0[2] = currMB->ref_idx_L0[3] = DEFAULT_REF_IDX;
+ currMB->ref_idx_L1[0] = currMB->ref_idx_L1[1] =
+ currMB->ref_idx_L1[2] = currMB->ref_idx_L1[3] = DEFAULT_REF_IDX;
+ currMB->RefIdx[0] = currMB->RefIdx[1] =
+ currMB->RefIdx[2] = currMB->RefIdx[3] = video->RefPicList0[DEFAULT_REF_IDX]->RefIdx;
+
+ cur = encvid->currYMB; /* use smaller memory space for current MB */
+
+ /* find limit of the search (adjusting search range)*/
+ lev_idx = mapLev2Idx[currSPS->level_idc];
+
+ /* we can make this part dynamic based on previous statistics */
+ ilow = i0 - range;
+ if (i0 - ilow > 2047) /* clip to conform with the standard */
+ {
+ ilow = i0 - 2047;
+ }
+ if (ilow < -13) // change it from -15 to -13 because of 6-tap filter needs extra 2 lines.
+ {
+ ilow = -13;
+ }
+
+ ihigh = i0 + range - 1;
+ if (ihigh - i0 > 2047) /* clip to conform with the standard */
+ {
+ ihigh = i0 + 2047;
+ }
+ if (ihigh > width - 3)
+ {
+ ihigh = width - 3; // change from width-1 to width-3 for the same reason as above
+ }
+
+ jlow = j0 - range;
+ if (j0 - jlow > MaxVmvR[lev_idx] - 1) /* clip to conform with the standard */
+ {
+ jlow = j0 - MaxVmvR[lev_idx] + 1;
+ }
+ if (jlow < -13) // same reason as above
+ {
+ jlow = -13;
+ }
+
+ jhigh = j0 + range - 1;
+ if (jhigh - j0 > MaxVmvR[lev_idx] - 1) /* clip to conform with the standard */
+ {
+ jhigh = j0 + MaxVmvR[lev_idx] - 1;
+ }
+ if (jhigh > height - 3) // same reason as above
+ {
+ jhigh = height - 3;
+ }
+
+ /* find initial motion vector & predicted MV*/
+ AVCCandidateSelection(mvx, mvy, &num_can, i0 >> 4, j0 >> 4, encvid, type_pred, &cmvx, &cmvy);
+
+ imin = i0;
+ jmin = j0; /* needed for fullsearch */
+ ncand = ref + i0 + j0 * lx;
+
+ /* for first row of MB, fullsearch can be used */
+ if (FS_en)
+ {
+ *hp_guess = 0; /* no guess for fast half-pel */
+
+ dmin = AVCFullSearch(encvid, ref, cur, &imin, &jmin, ilow, ihigh, jlow, jhigh, cmvx, cmvy);
+
+ ncand = ref + imin + jmin * lx;
+ }
+ else
+ { /* fullsearch the top row to only upto (0,3) MB */
+ /* upto 30% complexity saving with the same complexity */
+ if (video->PrevRefFrameNum == 0 && j0 == 0 && i0 <= 64 && type_pred != 1)
+ {
+ *hp_guess = 0; /* no guess for fast half-pel */
+ dmin = AVCFullSearch(encvid, ref, cur, &imin, &jmin, ilow, ihigh, jlow, jhigh, cmvx, cmvy);
+ ncand = ref + imin + jmin * lx;
+ }
+ else
+ {
+ /************** initialize candidate **************************/
+
+ dmin = 65535;
+
+ /* check if all are equal */
+ if (num_can == ALL_CAND_EQUAL)
+ {
+ i = i0 + mvx[0];
+ j = j0 + mvy[0];
+
+ if (i >= ilow && i <= ihigh && j >= jlow && j <= jhigh)
+ {
+ cand = ref + i + j * lx;
+
+ d = (*SAD_Macroblock)(cand, cur, (dmin << 16) | lx, extra_info);
+ mvcost = MV_COST(lambda_motion, mvshift, i - i0, j - j0, cmvx, cmvy);
+ d += mvcost;
+
+ if (d < dmin)
+ {
+ dmin = d;
+ imin = i;
+ jmin = j;
+ ncand = cand;
+ min_sad = d - mvcost; // for rate control
+ }
+ }
+ }
+ else
+ {
+ /************** evaluate unique candidates **********************/
+ for (k = 0; k < num_can; k++)
+ {
+ i = i0 + mvx[k];
+ j = j0 + mvy[k];
+
+ if (i >= ilow && i <= ihigh && j >= jlow && j <= jhigh)
+ {
+ cand = ref + i + j * lx;
+ d = (*SAD_Macroblock)(cand, cur, (dmin << 16) | lx, extra_info);
+ mvcost = MV_COST(lambda_motion, mvshift, i - i0, j - j0, cmvx, cmvy);
+ d += mvcost;
+
+ if (d < dmin)
+ {
+ dmin = d;
+ imin = i;
+ jmin = j;
+ ncand = cand;
+ min_sad = d - mvcost; // for rate control
+ }
+ }
+ }
+ }
+
+ /******************* local refinement ***************************/
+ center_again = 0;
+ last_loc = new_loc = 0;
+ // ncand = ref + jmin*lx + imin; /* center of the search */
+ step = 0;
+ dn[0] = dmin;
+ while (!center_again && step <= max_step)
+ {
+
+ AVCMoveNeighborSAD(dn, last_loc);
+
+ center_again = 1;
+ i = imin;
+ j = jmin - 1;
+ cand = ref + i + j * lx;
+
+ /* starting from [0,-1] */
+ /* spiral check one step at a time*/
+ for (k = 2; k <= 8; k += 2)
+ {
+ if (!tab_exclude[last_loc][k]) /* exclude last step computation */
+ { /* not already computed */
+ if (i >= ilow && i <= ihigh && j >= jlow && j <= jhigh)
+ {
+ d = (*SAD_Macroblock)(cand, cur, (dmin << 16) | lx, extra_info);
+ mvcost = MV_COST(lambda_motion, mvshift, i - i0, j - j0, cmvx, cmvy);
+ d += mvcost;
+
+ dn[k] = d; /* keep it for half pel use */
+
+ if (d < dmin)
+ {
+ ncand = cand;
+ dmin = d;
+ imin = i;
+ jmin = j;
+ center_again = 0;
+ new_loc = k;
+ min_sad = d - mvcost; // for rate control
+ }
+ }
+ }
+ if (k == 8) /* end side search*/
+ {
+ if (!center_again)
+ {
+ k = -1; /* start diagonal search */
+ cand -= lx;
+ j--;
+ }
+ }
+ else
+ {
+ next = refine_next[k][0];
+ i += next;
+ cand += next;
+ next = refine_next[k][1];
+ j += next;
+ cand += lx * next;
+ }
+ }
+ last_loc = new_loc;
+ step ++;
+ }
+ if (!center_again)
+ AVCMoveNeighborSAD(dn, last_loc);
+
+ *hp_guess = AVCFindMin(dn);
+
+ encvid->rateCtrl->MADofMB[mbnum] = min_sad / 256.0;
+ }
+ }
+
+ mot16x16[mbnum].sad = dmin;
+ mot16x16[mbnum].x = (imin - i0) << 2;
+ mot16x16[mbnum].y = (jmin - j0) << 2;
+ best_cand[0] = ncand;
+
+ if (rateCtrl->subPelEnable) // always enable half-pel search
+ {
+ /* find half-pel resolution motion vector */
+ min_sad = AVCFindHalfPelMB(encvid, cur, mot16x16 + mbnum, best_cand[0], i0, j0, *hp_guess, cmvx, cmvy);
+
+ encvid->rateCtrl->MADofMB[mbnum] = min_sad / 256.0;
+
+
+ if (encvid->best_qpel_pos == -1)
+ {
+ ncand = encvid->hpel_cand[encvid->best_hpel_pos];
+ }
+ else
+ {
+ ncand = encvid->qpel_cand[encvid->best_qpel_pos];
+ }
+ }
+ else
+ {
+ encvid->rateCtrl->MADofMB[mbnum] = min_sad / 256.0;
+ }
+
+ /** do motion comp here for now */
+ ref = currPic->Sl + i0 + j0 * lx;
+ /* copy from the best result to current Picture */
+ for (j = 0; j < 16; j++)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ *ref++ = *ncand++;
+ }
+ ref += (lx - 16);
+ ncand += 8;
+ }
+
+ return ;
+}
+
+#endif
+
+/*===============================================================================
+ Function: AVCFullSearch
+ Date: 09/16/2000
+ Purpose: Perform full-search motion estimation over the range of search
+ region in a spiral-outward manner.
+ Input/Output: VideoEncData, current Vol, previou Vop, pointer to the left corner of
+ current VOP, current coord (also output), boundaries.
+===============================================================================*/
+int AVCFullSearch(AVCEncObject *encvid, uint8 *prev, uint8 *cur,
+ int *imin, int *jmin, int ilow, int ihigh, int jlow, int jhigh,
+ int cmvx, int cmvy)
+{
+ int range = encvid->rateCtrl->mvRange;
+ AVCPictureData *currPic = encvid->common->currPic;
+ uint8 *cand;
+ int i, j, k, l;
+ int d, dmin;
+ int i0 = *imin; /* current position */
+ int j0 = *jmin;
+ int (*SAD_Macroblock)(uint8*, uint8*, int, void*) = encvid->functionPointer->SAD_Macroblock;
+ void *extra_info = encvid->sad_extra_info;
+ int lx = currPic->pitch; /* with padding */
+
+ int offset = i0 + j0 * lx;
+
+ int lambda_motion = encvid->lambda_motion;
+ uint8 *mvbits = encvid->mvbits;
+ int mvshift = 2;
+ int mvcost;
+ int min_sad;
+
+ cand = prev + offset;
+
+ dmin = (*SAD_Macroblock)(cand, cur, (65535 << 16) | lx, (void*)extra_info);
+ mvcost = MV_COST(lambda_motion, mvshift, 0, 0, cmvx, cmvy);
+ min_sad = dmin;
+ dmin += mvcost;
+
+ /* perform spiral search */
+ for (k = 1; k <= range; k++)
+ {
+
+ i = i0 - k;
+ j = j0 - k;
+
+ cand = prev + i + j * lx;
+
+ for (l = 0; l < 8*k; l++)
+ {
+ /* no need for boundary checking again */
+ if (i >= ilow && i <= ihigh && j >= jlow && j <= jhigh)
+ {
+ d = (*SAD_Macroblock)(cand, cur, (dmin << 16) | lx, (void*)extra_info);
+ mvcost = MV_COST(lambda_motion, mvshift, i - i0, j - j0, cmvx, cmvy);
+ d += mvcost;
+
+ if (d < dmin)
+ {
+ dmin = d;
+ *imin = i;
+ *jmin = j;
+ min_sad = d - mvcost;
+ }
+ }
+
+ if (l < (k << 1))
+ {
+ i++;
+ cand++;
+ }
+ else if (l < (k << 2))
+ {
+ j++;
+ cand += lx;
+ }
+ else if (l < ((k << 2) + (k << 1)))
+ {
+ i--;
+ cand--;
+ }
+ else
+ {
+ j--;
+ cand -= lx;
+ }
+ }
+ }
+
+ encvid->rateCtrl->MADofMB[encvid->common->mbNum] = (min_sad / 256.0); // for rate control
+
+ return dmin;
+}
+
+/*===============================================================================
+ Function: AVCCandidateSelection
+ Date: 09/16/2000
+ Purpose: Fill up the list of candidate using spatio-temporal correlation
+ among neighboring blocks.
+ Input/Output: type_pred = 0: first pass, 1: second pass, or no SCD
+ Modified: , 09/23/01, get rid of redundant candidates before passing back.
+ , 09/11/07, added return for modified predicted MV, this will be
+ needed for both fast search and fullsearch.
+===============================================================================*/
+
+void AVCCandidateSelection(int *mvx, int *mvy, int *num_can, int imb, int jmb,
+ AVCEncObject *encvid, int type_pred, int *cmvx, int *cmvy)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMV *mot16x16 = encvid->mot16x16;
+ AVCMV *pmot;
+ int mbnum = video->mbNum;
+ int mbwidth = video->PicWidthInMbs;
+ int mbheight = video->PicHeightInMbs;
+ int i, j, same, num1;
+
+ /* this part is for predicted MV */
+ int pmvA_x = 0, pmvA_y = 0, pmvB_x = 0, pmvB_y = 0, pmvC_x = 0, pmvC_y = 0;
+ int availA = 0, availB = 0, availC = 0;
+
+ *num_can = 0;
+
+ if (video->PrevRefFrameNum != 0) // previous frame is an IDR frame
+ {
+ /* Spatio-Temporal Candidate (five candidates) */
+ if (type_pred == 0) /* first pass */
+ {
+ pmot = &mot16x16[mbnum]; /* same coordinate previous frame */
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ if (imb >= (mbwidth >> 1) && imb > 0) /*left neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ else if (imb + 1 < mbwidth) /*right neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+
+ if (jmb < mbheight - 1) /*bottom neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum+mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ else if (jmb > 0) /*upper neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+
+ if (imb > 0 && jmb > 0) /* upper-left neighbor current frame*/
+ {
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb > 0 && imb < mbheight - 1) /* upper right neighbor current frame*/
+ {
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ }
+ else /* second pass */
+ /* original ST1 algorithm */
+ {
+ pmot = &mot16x16[mbnum]; /* same coordinate previous frame */
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+
+ if (imb > 0) /*left neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb > 0) /*upper neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (imb < mbwidth - 1) /*right neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb < mbheight - 1) /*bottom neighbor previous frame */
+ {
+ pmot = &mot16x16[mbnum+mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ }
+
+ /* get predicted MV */
+ if (imb > 0) /* get MV from left (A) neighbor either on current or previous frame */
+ {
+ availA = 1;
+ pmot = &mot16x16[mbnum-1];
+ pmvA_x = pmot->x;
+ pmvA_y = pmot->y;
+ }
+
+ if (jmb > 0) /* get MV from top (B) neighbor either on current or previous frame */
+ {
+ availB = 1;
+ pmot = &mot16x16[mbnum-mbwidth];
+ pmvB_x = pmot->x;
+ pmvB_y = pmot->y;
+
+ availC = 1;
+
+ if (imb < mbwidth - 1) /* get MV from top-right (C) neighbor of current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ }
+ else /* get MV from top-left (D) neighbor of current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ }
+ pmvC_x = pmot->x;
+ pmvC_y = pmot->y;
+ }
+
+ }
+ else /* only Spatial Candidate (four candidates)*/
+ {
+ if (type_pred == 0) /*first pass*/
+ {
+ if (imb > 1) /* neighbor two blocks away to the left */
+ {
+ pmot = &mot16x16[mbnum-2];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (imb > 0 && jmb > 0) /* upper-left neighbor */
+ {
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb > 0 && imb < mbheight - 1) /* upper right neighbor */
+ {
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+
+ /* get predicted MV */
+ if (imb > 1) /* get MV from 2nd left (A) neighbor either of current frame */
+ {
+ availA = 1;
+ pmot = &mot16x16[mbnum-2];
+ pmvA_x = pmot->x;
+ pmvA_y = pmot->y;
+ }
+
+ if (jmb > 0 && imb > 0) /* get MV from top-left (B) neighbor of current frame */
+ {
+ availB = 1;
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ pmvB_x = pmot->x;
+ pmvB_y = pmot->y;
+ }
+
+ if (jmb > 0 && imb < mbwidth - 1)
+ {
+ availC = 1;
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ pmvC_x = pmot->x;
+ pmvC_y = pmot->y;
+ }
+ }
+//#ifdef SCENE_CHANGE_DETECTION
+ /* second pass (ST2 algorithm)*/
+ else
+ {
+ if (type_pred == 1) /* 4/7/01 */
+ {
+ if (imb > 0) /*left neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb > 0) /*upper neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (imb < mbwidth - 1) /*right neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ if (jmb < mbheight - 1) /*bottom neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum+mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ }
+ //#else
+ else /* original ST1 algorithm */
+ {
+ if (imb > 0) /*left neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+
+ if (jmb > 0) /*upper-left neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+
+ }
+ if (jmb > 0) /*upper neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+
+ if (imb < mbheight - 1) /*upper-right neighbor current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ mvx[(*num_can)] = (pmot->x) >> 2;
+ mvy[(*num_can)++] = (pmot->y) >> 2;
+ }
+ }
+ }
+
+ /* get predicted MV */
+ if (imb > 0) /* get MV from left (A) neighbor either on current or previous frame */
+ {
+ availA = 1;
+ pmot = &mot16x16[mbnum-1];
+ pmvA_x = pmot->x;
+ pmvA_y = pmot->y;
+ }
+
+ if (jmb > 0) /* get MV from top (B) neighbor either on current or previous frame */
+ {
+ availB = 1;
+ pmot = &mot16x16[mbnum-mbwidth];
+ pmvB_x = pmot->x;
+ pmvB_y = pmot->y;
+
+ availC = 1;
+
+ if (imb < mbwidth - 1) /* get MV from top-right (C) neighbor of current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth+1];
+ }
+ else /* get MV from top-left (D) neighbor of current frame */
+ {
+ pmot = &mot16x16[mbnum-mbwidth-1];
+ }
+ pmvC_x = pmot->x;
+ pmvC_y = pmot->y;
+ }
+ }
+//#endif
+ }
+
+ /* 3/23/01, remove redundant candidate (possible k-mean) */
+ num1 = *num_can;
+ *num_can = 1;
+ for (i = 1; i < num1; i++)
+ {
+ same = 0;
+ j = 0;
+ while (!same && j < *num_can)
+ {
+#if (CANDIDATE_DISTANCE==0)
+ if (mvx[i] == mvx[j] && mvy[i] == mvy[j])
+#else
+ // modified k-mean, 3/24/01, shouldn't be greater than 3
+ if (AVC_ABS(mvx[i] - mvx[j]) + AVC_ABS(mvy[i] - mvy[j]) < CANDIDATE_DISTANCE)
+#endif
+ same = 1;
+ j++;
+ }
+ if (!same)
+ {
+ mvx[*num_can] = mvx[i];
+ mvy[*num_can] = mvy[i];
+ (*num_can)++;
+ }
+ }
+
+ if (num1 == 5 && *num_can == 1)
+ *num_can = ALL_CAND_EQUAL; /* all are equal */
+
+ /* calculate predicted MV */
+
+ if (availA && !(availB || availC))
+ {
+ *cmvx = pmvA_x;
+ *cmvy = pmvA_y;
+ }
+ else
+ {
+ *cmvx = AVC_MEDIAN(pmvA_x, pmvB_x, pmvC_x);
+ *cmvy = AVC_MEDIAN(pmvA_y, pmvB_y, pmvC_y);
+ }
+
+ return ;
+}
+
+
+/*************************************************************
+ Function: AVCMoveNeighborSAD
+ Date: 3/27/01
+ Purpose: Move neighboring SAD around when center has shifted
+*************************************************************/
+
+void AVCMoveNeighborSAD(int dn[], int new_loc)
+{
+ int tmp[9];
+ tmp[0] = dn[0];
+ tmp[1] = dn[1];
+ tmp[2] = dn[2];
+ tmp[3] = dn[3];
+ tmp[4] = dn[4];
+ tmp[5] = dn[5];
+ tmp[6] = dn[6];
+ tmp[7] = dn[7];
+ tmp[8] = dn[8];
+ dn[0] = dn[1] = dn[2] = dn[3] = dn[4] = dn[5] = dn[6] = dn[7] = dn[8] = 65536;
+
+ switch (new_loc)
+ {
+ case 0:
+ break;
+ case 1:
+ dn[4] = tmp[2];
+ dn[5] = tmp[0];
+ dn[6] = tmp[8];
+ break;
+ case 2:
+ dn[4] = tmp[3];
+ dn[5] = tmp[4];
+ dn[6] = tmp[0];
+ dn[7] = tmp[8];
+ dn[8] = tmp[1];
+ break;
+ case 3:
+ dn[6] = tmp[4];
+ dn[7] = tmp[0];
+ dn[8] = tmp[2];
+ break;
+ case 4:
+ dn[1] = tmp[2];
+ dn[2] = tmp[3];
+ dn[6] = tmp[5];
+ dn[7] = tmp[6];
+ dn[8] = tmp[0];
+ break;
+ case 5:
+ dn[1] = tmp[0];
+ dn[2] = tmp[4];
+ dn[8] = tmp[6];
+ break;
+ case 6:
+ dn[1] = tmp[8];
+ dn[2] = tmp[0];
+ dn[3] = tmp[4];
+ dn[4] = tmp[5];
+ dn[8] = tmp[7];
+ break;
+ case 7:
+ dn[2] = tmp[8];
+ dn[3] = tmp[0];
+ dn[4] = tmp[6];
+ break;
+ case 8:
+ dn[2] = tmp[1];
+ dn[3] = tmp[2];
+ dn[4] = tmp[0];
+ dn[5] = tmp[6];
+ dn[6] = tmp[7];
+ break;
+ }
+ dn[0] = tmp[new_loc];
+
+ return ;
+}
+
+/* 3/28/01, find minimal of dn[9] */
+
+int AVCFindMin(int dn[])
+{
+ int min, i;
+ int dmin;
+
+ dmin = dn[1];
+ min = 1;
+ for (i = 2; i < 9; i++)
+ {
+ if (dn[i] < dmin)
+ {
+ dmin = dn[i];
+ min = i;
+ }
+ }
+
+ return min;
+}
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/rate_control.cpp b/media/libstagefright/codecs/avc/enc/src/rate_control.cpp
new file mode 100644
index 0000000..15b55fb
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/rate_control.cpp
@@ -0,0 +1,981 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+#include <math.h>
+
+/* rate control variables */
+#define RC_MAX_QUANT 51
+#define RC_MIN_QUANT 0 //cap to 10 to prevent rate fluctuation
+
+#define MAD_MIN 1 /* handle the case of devision by zero in RC */
+
+
+/* local functions */
+double QP2Qstep(int QP);
+int Qstep2QP(double Qstep);
+
+double ComputeFrameMAD(AVCCommonObj *video, AVCRateControl *rateCtrl);
+
+void targetBitCalculation(AVCEncObject *encvid, AVCCommonObj *video, AVCRateControl *rateCtrl, MultiPass *pMP);
+
+void calculateQuantizer_Multipass(AVCEncObject *encvid, AVCCommonObj *video,
+ AVCRateControl *rateCtrl, MultiPass *pMP);
+
+void updateRC_PostProc(AVCRateControl *rateCtrl, MultiPass *pMP);
+
+void AVCSaveRDSamples(MultiPass *pMP, int counter_samples);
+
+void updateRateControl(AVCRateControl *rateControl, int nal_type);
+
+int GetAvgFrameQP(AVCRateControl *rateCtrl)
+{
+ return rateCtrl->Qc;
+}
+
+AVCEnc_Status RCDetermineFrameNum(AVCEncObject *encvid, AVCRateControl *rateCtrl, uint32 modTime, uint *frameNum)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ uint32 modTimeRef = encvid->modTimeRef;
+ int32 currFrameNum ;
+ int frameInc;
+
+
+ /* check with the buffer fullness to make sure that we have enough bits to encode this frame */
+ /* we can use a threshold to guarantee minimum picture quality */
+ /**********************************/
+
+ /* for now, the default is to encode every frame, To Be Changed */
+ if (rateCtrl->first_frame)
+ {
+ encvid->modTimeRef = modTime;
+ encvid->wrapModTime = 0;
+ encvid->prevFrameNum = 0;
+ encvid->prevProcFrameNum = 0;
+
+ *frameNum = 0;
+
+ /* set frame type to IDR-frame */
+ video->nal_unit_type = AVC_NALTYPE_IDR;
+ sliceHdr->slice_type = AVC_I_ALL_SLICE;
+ video->slice_type = AVC_I_SLICE;
+
+ return AVCENC_SUCCESS;
+ }
+ else
+ {
+ if (modTime < modTimeRef) /* modTime wrapped around */
+ {
+ encvid->wrapModTime += ((uint32)0xFFFFFFFF - modTimeRef) + 1;
+ encvid->modTimeRef = modTimeRef = 0;
+ }
+ modTime += encvid->wrapModTime; /* wrapModTime is non zero after wrap-around */
+
+ currFrameNum = (int32)(((modTime - modTimeRef) * rateCtrl->frame_rate + 200) / 1000); /* add small roundings */
+
+ if (currFrameNum <= (int32)encvid->prevProcFrameNum)
+ {
+ return AVCENC_FAIL; /* this is a late frame do not encode it */
+ }
+
+ frameInc = currFrameNum - encvid->prevProcFrameNum;
+
+ if (frameInc < rateCtrl->skip_next_frame + 1)
+ {
+ return AVCENC_FAIL; /* frame skip required to maintain the target bit rate. */
+ }
+
+ RCUpdateBuffer(video, rateCtrl, frameInc - rateCtrl->skip_next_frame); /* in case more frames dropped */
+
+ *frameNum = currFrameNum;
+
+ /* This part would be similar to DetermineVopType of m4venc */
+ if ((*frameNum >= (uint)rateCtrl->idrPeriod && rateCtrl->idrPeriod > 0) || (*frameNum > video->MaxFrameNum)) /* first frame or IDR*/
+ {
+ /* set frame type to IDR-frame */
+ if (rateCtrl->idrPeriod)
+ {
+ encvid->modTimeRef += (uint32)(rateCtrl->idrPeriod * 1000 / rateCtrl->frame_rate);
+ *frameNum -= rateCtrl->idrPeriod;
+ }
+ else
+ {
+ encvid->modTimeRef += (uint32)(video->MaxFrameNum * 1000 / rateCtrl->frame_rate);
+ *frameNum -= video->MaxFrameNum;
+ }
+
+ video->nal_unit_type = AVC_NALTYPE_IDR;
+ sliceHdr->slice_type = AVC_I_ALL_SLICE;
+ video->slice_type = AVC_I_SLICE;
+ encvid->prevProcFrameNum = *frameNum;
+ }
+ else
+ {
+ video->nal_unit_type = AVC_NALTYPE_SLICE;
+ sliceHdr->slice_type = AVC_P_ALL_SLICE;
+ video->slice_type = AVC_P_SLICE;
+ encvid->prevProcFrameNum = currFrameNum;
+ }
+
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+void RCUpdateBuffer(AVCCommonObj *video, AVCRateControl *rateCtrl, int frameInc)
+{
+ int tmp;
+ MultiPass *pMP = rateCtrl->pMP;
+
+ OSCL_UNUSED_ARG(video);
+
+ if (rateCtrl->rcEnable == TRUE)
+ {
+ if (frameInc > 1)
+ {
+ tmp = rateCtrl->bitsPerFrame * (frameInc - 1);
+ rateCtrl->VBV_fullness -= tmp;
+ pMP->counter_BTsrc += 10 * (frameInc - 1);
+
+ /* Check buffer underflow */
+ if (rateCtrl->VBV_fullness < rateCtrl->low_bound)
+ {
+ rateCtrl->VBV_fullness = rateCtrl->low_bound; // -rateCtrl->Bs/2;
+ rateCtrl->TMN_W = rateCtrl->VBV_fullness - rateCtrl->low_bound;
+ pMP->counter_BTsrc = pMP->counter_BTdst + (int)((OsclFloat)(rateCtrl->Bs / 2 - rateCtrl->low_bound) / 2.0 / (pMP->target_bits_per_frame / 10));
+ }
+ }
+ }
+}
+
+
+AVCEnc_Status InitRateControlModule(AVCHandle *avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCCommonObj *video = encvid->common;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ double L1, L2, L3, bpp;
+ int qp;
+ int i, j;
+
+ rateCtrl->basicUnit = video->PicSizeInMbs;
+
+ rateCtrl->MADofMB = (double*) avcHandle->CBAVC_Malloc(encvid->avcHandle->userData,
+ video->PicSizeInMbs * sizeof(double), DEFAULT_ATTR);
+
+ if (!rateCtrl->MADofMB)
+ {
+ goto CLEANUP_RC;
+ }
+
+ if (rateCtrl->rcEnable == TRUE)
+ {
+ rateCtrl->pMP = (MultiPass*) avcHandle->CBAVC_Malloc(encvid->avcHandle->userData, sizeof(MultiPass), DEFAULT_ATTR);
+ if (!rateCtrl->pMP)
+ {
+ goto CLEANUP_RC;
+ }
+ memset(rateCtrl->pMP, 0, sizeof(MultiPass));
+ rateCtrl->pMP->encoded_frames = -1; /* forget about the very first I frame */
+
+ /* RDInfo **pRDSamples */
+ rateCtrl->pMP->pRDSamples = (RDInfo **)avcHandle->CBAVC_Malloc(encvid->avcHandle->userData, (30 * sizeof(RDInfo *)), DEFAULT_ATTR);
+ if (!rateCtrl->pMP->pRDSamples)
+ {
+ goto CLEANUP_RC;
+ }
+
+ for (i = 0; i < 30; i++)
+ {
+ rateCtrl->pMP->pRDSamples[i] = (RDInfo *)avcHandle->CBAVC_Malloc(encvid->avcHandle->userData, (32 * sizeof(RDInfo)), DEFAULT_ATTR);
+ if (!rateCtrl->pMP->pRDSamples[i])
+ {
+ goto CLEANUP_RC;
+ }
+ for (j = 0; j < 32; j++) memset(&(rateCtrl->pMP->pRDSamples[i][j]), 0, sizeof(RDInfo));
+ }
+ rateCtrl->pMP->frameRange = (int)(rateCtrl->frame_rate * 1.0); /* 1.0s time frame*/
+ rateCtrl->pMP->frameRange = AVC_MAX(rateCtrl->pMP->frameRange, 5);
+ rateCtrl->pMP->frameRange = AVC_MIN(rateCtrl->pMP->frameRange, 30);
+
+ rateCtrl->pMP->framePos = -1;
+
+
+ rateCtrl->bitsPerFrame = (int32)(rateCtrl->bitRate / rateCtrl->frame_rate);
+
+ /* BX rate control */
+ rateCtrl->skip_next_frame = 0; /* must be initialized */
+
+ rateCtrl->Bs = rateCtrl->cpbSize;
+ rateCtrl->TMN_W = 0;
+ rateCtrl->VBV_fullness = (int)(rateCtrl->Bs * 0.5); /* rateCtrl->Bs */
+ rateCtrl->encoded_frames = 0;
+
+ rateCtrl->TMN_TH = rateCtrl->bitsPerFrame;
+
+ rateCtrl->max_BitVariance_num = (int)((OsclFloat)(rateCtrl->Bs - rateCtrl->VBV_fullness) / (rateCtrl->bitsPerFrame / 10.0)) - 5;
+ if (rateCtrl->max_BitVariance_num < 0) rateCtrl->max_BitVariance_num += 5;
+
+ // Set the initial buffer fullness
+ /* According to the spec, the initial buffer fullness needs to be set to 1/3 */
+ rateCtrl->VBV_fullness = (int)(rateCtrl->Bs / 3.0 - rateCtrl->Bs / 2.0); /* the buffer range is [-Bs/2, Bs/2] */
+ rateCtrl->pMP->counter_BTsrc = (int)((rateCtrl->Bs / 2.0 - rateCtrl->Bs / 3.0) / (rateCtrl->bitsPerFrame / 10.0));
+ rateCtrl->TMN_W = (int)(rateCtrl->VBV_fullness + rateCtrl->pMP->counter_BTsrc * (rateCtrl->bitsPerFrame / 10.0));
+
+ rateCtrl->low_bound = -rateCtrl->Bs / 2;
+ rateCtrl->VBV_fullness_offset = 0;
+
+ /* Setting the bitrate and framerate */
+ rateCtrl->pMP->bitrate = rateCtrl->bitRate;
+ rateCtrl->pMP->framerate = rateCtrl->frame_rate;
+ rateCtrl->pMP->target_bits_per_frame = rateCtrl->pMP->bitrate / rateCtrl->pMP->framerate;
+
+ /*compute the initial QP*/
+ bpp = 1.0 * rateCtrl->bitRate / (rateCtrl->frame_rate * (video->PicSizeInMbs << 8));
+ if (video->PicWidthInSamplesL == 176)
+ {
+ L1 = 0.1;
+ L2 = 0.3;
+ L3 = 0.6;
+ }
+ else if (video->PicWidthInSamplesL == 352)
+ {
+ L1 = 0.2;
+ L2 = 0.6;
+ L3 = 1.2;
+ }
+ else
+ {
+ L1 = 0.6;
+ L2 = 1.4;
+ L3 = 2.4;
+ }
+
+ if (rateCtrl->initQP == 0)
+ {
+ if (bpp <= L1)
+ qp = 35;
+ else if (bpp <= L2)
+ qp = 25;
+ else if (bpp <= L3)
+ qp = 20;
+ else
+ qp = 15;
+ rateCtrl->initQP = qp;
+ }
+
+ rateCtrl->Qc = rateCtrl->initQP;
+ }
+
+ return AVCENC_SUCCESS;
+
+CLEANUP_RC:
+
+ CleanupRateControlModule(avcHandle);
+ return AVCENC_MEMORY_FAIL;
+
+}
+
+
+void CleanupRateControlModule(AVCHandle *avcHandle)
+{
+ AVCEncObject *encvid = (AVCEncObject*) avcHandle->AVCObject;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ int i;
+
+ if (rateCtrl->MADofMB)
+ {
+ avcHandle->CBAVC_Free(avcHandle->userData, (int)(rateCtrl->MADofMB));
+ }
+
+ if (rateCtrl->pMP)
+ {
+ if (rateCtrl->pMP->pRDSamples)
+ {
+ for (i = 0; i < 30; i++)
+ {
+ if (rateCtrl->pMP->pRDSamples[i])
+ {
+ avcHandle->CBAVC_Free(avcHandle->userData, (int)rateCtrl->pMP->pRDSamples[i]);
+ }
+ }
+ avcHandle->CBAVC_Free(avcHandle->userData, (int)rateCtrl->pMP->pRDSamples);
+ }
+ avcHandle->CBAVC_Free(avcHandle->userData, (int)(rateCtrl->pMP));
+ }
+
+ return ;
+}
+
+void RCInitGOP(AVCEncObject *encvid)
+{
+ /* in BX RC, there's no GOP-level RC */
+
+ OSCL_UNUSED_ARG(encvid);
+
+ return ;
+}
+
+
+void RCInitFrameQP(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCPicParamSet *picParam = video->currPicParams;
+ MultiPass *pMP = rateCtrl->pMP;
+
+ if (rateCtrl->rcEnable == TRUE)
+ {
+ /* frame layer rate control */
+ if (rateCtrl->encoded_frames == 0)
+ {
+ video->QPy = rateCtrl->Qc = rateCtrl->initQP;
+ }
+ else
+ {
+ calculateQuantizer_Multipass(encvid, video, rateCtrl, pMP);
+ video->QPy = rateCtrl->Qc;
+ }
+
+ rateCtrl->NumberofHeaderBits = 0;
+ rateCtrl->NumberofTextureBits = 0;
+ rateCtrl->numFrameBits = 0; // reset
+
+ /* update pMP->framePos */
+ if (++pMP->framePos == pMP->frameRange) pMP->framePos = 0;
+
+ if (rateCtrl->T == 0)
+ {
+ pMP->counter_BTdst = (int)(rateCtrl->frame_rate * 7.5 + 0.5); /* 0.75s time frame */
+ pMP->counter_BTdst = AVC_MIN(pMP->counter_BTdst, (int)(rateCtrl->max_BitVariance_num / 2 * 0.40)); /* 0.75s time frame may go beyond VBV buffer if we set the buffer size smaller than 0.75s */
+ pMP->counter_BTdst = AVC_MAX(pMP->counter_BTdst, (int)((rateCtrl->Bs / 2 - rateCtrl->VBV_fullness) * 0.30 / (rateCtrl->TMN_TH / 10.0) + 0.5)); /* At least 30% of VBV buffer size/2 */
+ pMP->counter_BTdst = AVC_MIN(pMP->counter_BTdst, 20); /* Limit the target to be smaller than 3C */
+
+ pMP->target_bits = rateCtrl->T = rateCtrl->TMN_TH = (int)(rateCtrl->TMN_TH * (1.0 + pMP->counter_BTdst * 0.1));
+ pMP->diff_counter = pMP->counter_BTdst;
+ }
+
+ /* collect the necessary data: target bits, actual bits, mad and QP */
+ pMP->target_bits = rateCtrl->T;
+ pMP->QP = video->QPy;
+
+ pMP->mad = (OsclFloat)rateCtrl->totalSAD / video->PicSizeInMbs; //ComputeFrameMAD(video, rateCtrl);
+ if (pMP->mad < MAD_MIN) pMP->mad = MAD_MIN; /* MAD_MIN is defined as 1 in mp4def.h */
+
+ pMP->bitrate = rateCtrl->bitRate; /* calculated in RCVopQPSetting */
+ pMP->framerate = rateCtrl->frame_rate;
+
+ /* first pass encoding */
+ pMP->nRe_Quantized = 0;
+
+ } // rcEnable
+ else
+ {
+ video->QPy = rateCtrl->initQP;
+ }
+
+// printf(" %d ",video->QPy);
+
+ if (video->CurrPicNum == 0 && encvid->outOfBandParamSet == FALSE)
+ {
+ picParam->pic_init_qs_minus26 = 0;
+ picParam->pic_init_qp_minus26 = video->QPy - 26;
+ }
+
+ // need this for motion estimation
+ encvid->lambda_mode = QP2QUANT[AVC_MAX(0, video->QPy-SHIFT_QP)];
+ encvid->lambda_motion = LAMBDA_FACTOR(encvid->lambda_mode);
+ return ;
+}
+
+/* Mad based variable bit allocation + QP calculation with a new quadratic method */
+void calculateQuantizer_Multipass(AVCEncObject *encvid, AVCCommonObj *video,
+ AVCRateControl *rateCtrl, MultiPass *pMP)
+{
+ int prev_actual_bits = 0, curr_target, /*pos=0,*/i, j;
+ OsclFloat Qstep, prev_QP = 0.625;
+
+ OsclFloat curr_mad, prev_mad, curr_RD, prev_RD, average_mad, aver_QP;
+
+ /* Mad based variable bit allocation */
+ targetBitCalculation(encvid, video, rateCtrl, pMP);
+
+ if (rateCtrl->T <= 0 || rateCtrl->totalSAD == 0)
+ {
+ if (rateCtrl->T < 0) rateCtrl->Qc = RC_MAX_QUANT;
+ return;
+ }
+
+ /* ---------------------------------------------------------------------------------------------------*/
+ /* current frame QP estimation */
+ curr_target = rateCtrl->T;
+ curr_mad = (OsclFloat)rateCtrl->totalSAD / video->PicSizeInMbs;
+ if (curr_mad < MAD_MIN) curr_mad = MAD_MIN; /* MAD_MIN is defined as 1 in mp4def.h */
+ curr_RD = (OsclFloat)curr_target / curr_mad;
+
+ if (rateCtrl->skip_next_frame == -1) // previous was skipped
+ {
+ i = pMP->framePos;
+ prev_mad = pMP->pRDSamples[i][0].mad;
+ prev_QP = pMP->pRDSamples[i][0].QP;
+ prev_actual_bits = pMP->pRDSamples[i][0].actual_bits;
+ }
+ else
+ {
+ /* Another version of search the optimal point */
+ prev_mad = 0.0;
+ i = 0;
+ while (i < pMP->frameRange && prev_mad < 0.001) /* find first one with nonzero prev_mad */
+ {
+ prev_mad = pMP->pRDSamples[i][0].mad;
+ i++;
+ }
+
+ if (i < pMP->frameRange)
+ {
+ prev_actual_bits = pMP->pRDSamples[i-1][0].actual_bits;
+
+ for (j = 0; i < pMP->frameRange; i++)
+ {
+ if (pMP->pRDSamples[i][0].mad != 0 &&
+ AVC_ABS(prev_mad - curr_mad) > AVC_ABS(pMP->pRDSamples[i][0].mad - curr_mad))
+ {
+ prev_mad = pMP->pRDSamples[i][0].mad;
+ prev_actual_bits = pMP->pRDSamples[i][0].actual_bits;
+ j = i;
+ }
+ }
+ prev_QP = QP2Qstep(pMP->pRDSamples[j][0].QP);
+
+ for (i = 1; i < pMP->samplesPerFrame[j]; i++)
+ {
+ if (AVC_ABS(prev_actual_bits - curr_target) > AVC_ABS(pMP->pRDSamples[j][i].actual_bits - curr_target))
+ {
+ prev_actual_bits = pMP->pRDSamples[j][i].actual_bits;
+ prev_QP = QP2Qstep(pMP->pRDSamples[j][i].QP);
+ }
+ }
+ }
+ }
+
+ // quadratic approximation
+ if (prev_mad > 0.001) // only when prev_mad is greater than 0, otherwise keep using the same QP
+ {
+ prev_RD = (OsclFloat)prev_actual_bits / prev_mad;
+ //rateCtrl->Qc = (Int)(prev_QP * sqrt(prev_actual_bits/curr_target) + 0.4);
+ if (prev_QP == 0.625) // added this to allow getting out of QP = 0 easily
+ {
+ Qstep = (int)(prev_RD / curr_RD + 0.5);
+ }
+ else
+ {
+ // rateCtrl->Qc =(Int)(prev_QP * M4VENC_SQRT(prev_RD/curr_RD) + 0.9);
+
+ if (prev_RD / curr_RD > 0.5 && prev_RD / curr_RD < 2.0)
+ Qstep = (int)(prev_QP * (sqrt(prev_RD / curr_RD) + prev_RD / curr_RD) / 2.0 + 0.9); /* Quadratic and linear approximation */
+ else
+ Qstep = (int)(prev_QP * (sqrt(prev_RD / curr_RD) + pow(prev_RD / curr_RD, 1.0 / 3.0)) / 2.0 + 0.9);
+ }
+ // lower bound on Qc should be a function of curr_mad
+ // When mad is already low, lower bound on Qc doesn't have to be small.
+ // Note, this doesn't work well for low complexity clip encoded at high bit rate
+ // it doesn't hit the target bit rate due to this QP lower bound.
+ /// if((curr_mad < 8) && (rateCtrl->Qc < 12)) rateCtrl->Qc = 12;
+ // else if((curr_mad < 128) && (rateCtrl->Qc < 3)) rateCtrl->Qc = 3;
+
+ rateCtrl->Qc = Qstep2QP(Qstep);
+
+ if (rateCtrl->Qc < RC_MIN_QUANT) rateCtrl->Qc = RC_MIN_QUANT;
+ if (rateCtrl->Qc > RC_MAX_QUANT) rateCtrl->Qc = RC_MAX_QUANT;
+ }
+
+ /* active bit resource protection */
+ aver_QP = (pMP->encoded_frames == 0 ? 0 : pMP->sum_QP / (OsclFloat)pMP->encoded_frames);
+ average_mad = (pMP->encoded_frames == 0 ? 0 : pMP->sum_mad / (OsclFloat)pMP->encoded_frames); /* this function is called from the scond encoded frame*/
+ if (pMP->diff_counter == 0 &&
+ ((OsclFloat)rateCtrl->Qc <= aver_QP*1.1 || curr_mad <= average_mad*1.1) &&
+ pMP->counter_BTsrc <= (pMP->counter_BTdst + (int)(pMP->framerate*1.0 + 0.5)))
+ {
+ rateCtrl->TMN_TH -= (int)(pMP->target_bits_per_frame / 10.0);
+ rateCtrl->T = rateCtrl->TMN_TH - rateCtrl->TMN_W;
+ pMP->counter_BTsrc++;
+ pMP->diff_counter--;
+ }
+
+}
+
+void targetBitCalculation(AVCEncObject *encvid, AVCCommonObj *video, AVCRateControl *rateCtrl, MultiPass *pMP)
+{
+ OSCL_UNUSED_ARG(encvid);
+ OsclFloat curr_mad;//, average_mad;
+ int diff_counter_BTsrc, diff_counter_BTdst, prev_counter_diff, curr_counter_diff, bound;
+ /* BT = Bit Transfer, for pMP->counter_BTsrc, pMP->counter_BTdst */
+
+ /* some stuff about frame dropping remained here to be done because pMP cannot be inserted into updateRateControl()*/
+ updateRC_PostProc(rateCtrl, pMP);
+
+ /* update pMP->counter_BTsrc and pMP->counter_BTdst to avoid interger overflow */
+ if (pMP->counter_BTsrc > 1000 && pMP->counter_BTdst > 1000)
+ {
+ pMP->counter_BTsrc -= 1000;
+ pMP->counter_BTdst -= 1000;
+ }
+
+ /* ---------------------------------------------------------------------------------------------------*/
+ /* target calculation */
+ curr_mad = (OsclFloat)rateCtrl->totalSAD / video->PicSizeInMbs;
+ if (curr_mad < MAD_MIN) curr_mad = MAD_MIN; /* MAD_MIN is defined as 1 in mp4def.h */
+ diff_counter_BTsrc = diff_counter_BTdst = 0;
+ pMP->diff_counter = 0;
+
+
+ /*1.calculate average mad */
+ pMP->sum_mad += curr_mad;
+ //average_mad = (pMP->encoded_frames < 1 ? curr_mad : pMP->sum_mad/(OsclFloat)(pMP->encoded_frames+1)); /* this function is called from the scond encoded frame*/
+ //pMP->aver_mad = average_mad;
+ if (pMP->encoded_frames >= 0) /* pMP->encoded_frames is set to -1 initially, so forget about the very first I frame */
+ pMP->aver_mad = (pMP->aver_mad * pMP->encoded_frames + curr_mad) / (pMP->encoded_frames + 1);
+
+ if (pMP->overlapped_win_size > 0 && pMP->encoded_frames_prev >= 0)
+ pMP->aver_mad_prev = (pMP->aver_mad_prev * pMP->encoded_frames_prev + curr_mad) / (pMP->encoded_frames_prev + 1);
+
+ /*2.average_mad, mad ==> diff_counter_BTsrc, diff_counter_BTdst */
+ if (pMP->overlapped_win_size == 0)
+ {
+ /* original verison */
+ if (curr_mad > pMP->aver_mad*1.1)
+ {
+ if (curr_mad / (pMP->aver_mad + 0.0001) > 2)
+ diff_counter_BTdst = (int)(sqrt(curr_mad / (pMP->aver_mad + 0.0001)) * 10 + 0.4) - 10;
+ //diff_counter_BTdst = (int)((sqrt(curr_mad/pMP->aver_mad)*2+curr_mad/pMP->aver_mad)/(3*0.1) + 0.4) - 10;
+ else
+ diff_counter_BTdst = (int)(curr_mad / (pMP->aver_mad + 0.0001) * 10 + 0.4) - 10;
+ }
+ else /* curr_mad <= average_mad*1.1 */
+ //diff_counter_BTsrc = 10 - (int)((sqrt(curr_mad/pMP->aver_mad) + pow(curr_mad/pMP->aver_mad, 1.0/3.0))/(2.0*0.1) + 0.4);
+ diff_counter_BTsrc = 10 - (int)(sqrt(curr_mad / (pMP->aver_mad + 0.0001)) * 10 + 0.5);
+
+ /* actively fill in the possible gap */
+ if (diff_counter_BTsrc == 0 && diff_counter_BTdst == 0 &&
+ curr_mad <= pMP->aver_mad*1.1 && pMP->counter_BTsrc < pMP->counter_BTdst)
+ diff_counter_BTsrc = 1;
+
+ }
+ else if (pMP->overlapped_win_size > 0)
+ {
+ /* transition time: use previous average mad "pMP->aver_mad_prev" instead of the current average mad "pMP->aver_mad" */
+ if (curr_mad > pMP->aver_mad_prev*1.1)
+ {
+ if (curr_mad / pMP->aver_mad_prev > 2)
+ diff_counter_BTdst = (int)(sqrt(curr_mad / (pMP->aver_mad_prev + 0.0001)) * 10 + 0.4) - 10;
+ //diff_counter_BTdst = (int)((M4VENC_SQRT(curr_mad/pMP->aver_mad_prev)*2+curr_mad/pMP->aver_mad_prev)/(3*0.1) + 0.4) - 10;
+ else
+ diff_counter_BTdst = (int)(curr_mad / (pMP->aver_mad_prev + 0.0001) * 10 + 0.4) - 10;
+ }
+ else /* curr_mad <= average_mad*1.1 */
+ //diff_counter_BTsrc = 10 - (Int)((sqrt(curr_mad/pMP->aver_mad_prev) + pow(curr_mad/pMP->aver_mad_prev, 1.0/3.0))/(2.0*0.1) + 0.4);
+ diff_counter_BTsrc = 10 - (int)(sqrt(curr_mad / (pMP->aver_mad_prev + 0.0001)) * 10 + 0.5);
+
+ /* actively fill in the possible gap */
+ if (diff_counter_BTsrc == 0 && diff_counter_BTdst == 0 &&
+ curr_mad <= pMP->aver_mad_prev*1.1 && pMP->counter_BTsrc < pMP->counter_BTdst)
+ diff_counter_BTsrc = 1;
+
+ if (--pMP->overlapped_win_size <= 0) pMP->overlapped_win_size = 0;
+ }
+
+
+ /* if difference is too much, do clipping */
+ /* First, set the upper bound for current bit allocation variance: 80% of available buffer */
+ bound = (int)((rateCtrl->Bs / 2 - rateCtrl->VBV_fullness) * 0.6 / (pMP->target_bits_per_frame / 10)); /* rateCtrl->Bs */
+ diff_counter_BTsrc = AVC_MIN(diff_counter_BTsrc, bound);
+ diff_counter_BTdst = AVC_MIN(diff_counter_BTdst, bound);
+
+ /* Second, set another upper bound for current bit allocation: 4-5*bitrate/framerate */
+ bound = 50;
+// if(video->encParams->RC_Type == CBR_LOWDELAY)
+// not necessary bound = 10; -- For Low delay */
+
+ diff_counter_BTsrc = AVC_MIN(diff_counter_BTsrc, bound);
+ diff_counter_BTdst = AVC_MIN(diff_counter_BTdst, bound);
+
+
+ /* Third, check the buffer */
+ prev_counter_diff = pMP->counter_BTdst - pMP->counter_BTsrc;
+ curr_counter_diff = prev_counter_diff + (diff_counter_BTdst - diff_counter_BTsrc);
+
+ if (AVC_ABS(prev_counter_diff) >= rateCtrl->max_BitVariance_num || AVC_ABS(curr_counter_diff) >= rateCtrl->max_BitVariance_num)
+ { //diff_counter_BTsrc = diff_counter_BTdst = 0;
+
+ if (curr_counter_diff > rateCtrl->max_BitVariance_num && diff_counter_BTdst)
+ {
+ diff_counter_BTdst = (rateCtrl->max_BitVariance_num - prev_counter_diff) + diff_counter_BTsrc;
+ if (diff_counter_BTdst < 0) diff_counter_BTdst = 0;
+ }
+
+ else if (curr_counter_diff < -rateCtrl->max_BitVariance_num && diff_counter_BTsrc)
+ {
+ diff_counter_BTsrc = diff_counter_BTdst - (-rateCtrl->max_BitVariance_num - prev_counter_diff);
+ if (diff_counter_BTsrc < 0) diff_counter_BTsrc = 0;
+ }
+ }
+
+
+ /*3.diff_counter_BTsrc, diff_counter_BTdst ==> TMN_TH */
+ rateCtrl->TMN_TH = (int)(pMP->target_bits_per_frame);
+ pMP->diff_counter = 0;
+
+ if (diff_counter_BTsrc)
+ {
+ rateCtrl->TMN_TH -= (int)(pMP->target_bits_per_frame * diff_counter_BTsrc * 0.1);
+ pMP->diff_counter = -diff_counter_BTsrc;
+ }
+ else if (diff_counter_BTdst)
+ {
+ rateCtrl->TMN_TH += (int)(pMP->target_bits_per_frame * diff_counter_BTdst * 0.1);
+ pMP->diff_counter = diff_counter_BTdst;
+ }
+
+
+ /*4.update pMP->counter_BTsrc, pMP->counter_BTdst */
+ pMP->counter_BTsrc += diff_counter_BTsrc;
+ pMP->counter_BTdst += diff_counter_BTdst;
+
+
+ /*5.target bit calculation */
+ rateCtrl->T = rateCtrl->TMN_TH - rateCtrl->TMN_W;
+
+ return ;
+}
+
+void updateRC_PostProc(AVCRateControl *rateCtrl, MultiPass *pMP)
+{
+ if (rateCtrl->skip_next_frame > 0) /* skip next frame */
+ {
+ pMP->counter_BTsrc += 10 * rateCtrl->skip_next_frame;
+
+ }
+ else if (rateCtrl->skip_next_frame == -1) /* skip current frame */
+ {
+ pMP->counter_BTdst -= pMP->diff_counter;
+ pMP->counter_BTsrc += 10;
+
+ pMP->sum_mad -= pMP->mad;
+ pMP->aver_mad = (pMP->aver_mad * pMP->encoded_frames - pMP->mad) / (pMP->encoded_frames - 1 + 0.0001);
+ pMP->sum_QP -= pMP->QP;
+ pMP->encoded_frames --;
+ }
+ /* some stuff in update VBV_fullness remains here */
+ //if(rateCtrl->VBV_fullness < -rateCtrl->Bs/2) /* rateCtrl->Bs */
+ if (rateCtrl->VBV_fullness < rateCtrl->low_bound)
+ {
+ rateCtrl->VBV_fullness = rateCtrl->low_bound; // -rateCtrl->Bs/2;
+ rateCtrl->TMN_W = rateCtrl->VBV_fullness - rateCtrl->low_bound;
+ pMP->counter_BTsrc = pMP->counter_BTdst + (int)((OsclFloat)(rateCtrl->Bs / 2 - rateCtrl->low_bound) / 2.0 / (pMP->target_bits_per_frame / 10));
+ }
+}
+
+
+void RCInitChromaQP(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMacroblock *currMB = video->currMB;
+ int q_bits;
+
+ /* we have to do the same thing for AVC_CLIP3(0,51,video->QSy) */
+
+ video->QPy_div_6 = (currMB->QPy * 43) >> 8;
+ video->QPy_mod_6 = currMB->QPy - 6 * video->QPy_div_6;
+ currMB->QPc = video->QPc = mapQPi2QPc[AVC_CLIP3(0, 51, currMB->QPy + video->currPicParams->chroma_qp_index_offset)];
+ video->QPc_div_6 = (video->QPc * 43) >> 8;
+ video->QPc_mod_6 = video->QPc - 6 * video->QPc_div_6;
+
+ /* pre-calculate this to save computation */
+ q_bits = 4 + video->QPy_div_6;
+ if (video->slice_type == AVC_I_SLICE)
+ {
+ encvid->qp_const = 682 << q_bits; // intra
+ }
+ else
+ {
+ encvid->qp_const = 342 << q_bits; // inter
+ }
+
+ q_bits = 4 + video->QPc_div_6;
+ if (video->slice_type == AVC_I_SLICE)
+ {
+ encvid->qp_const_c = 682 << q_bits; // intra
+ }
+ else
+ {
+ encvid->qp_const_c = 342 << q_bits; // inter
+ }
+
+ encvid->lambda_mode = QP2QUANT[AVC_MAX(0, currMB->QPy-SHIFT_QP)];
+ encvid->lambda_motion = LAMBDA_FACTOR(encvid->lambda_mode);
+
+ return ;
+}
+
+
+void RCInitMBQP(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCMacroblock *currMB = video->currMB;
+
+ currMB->QPy = video->QPy; /* set to previous value or picture level */
+
+ RCInitChromaQP(encvid);
+
+}
+
+void RCPostMB(AVCCommonObj *video, AVCRateControl *rateCtrl, int num_header_bits, int num_texture_bits)
+{
+ OSCL_UNUSED_ARG(video);
+ rateCtrl->numMBHeaderBits = num_header_bits;
+ rateCtrl->numMBTextureBits = num_texture_bits;
+ rateCtrl->NumberofHeaderBits += rateCtrl->numMBHeaderBits;
+ rateCtrl->NumberofTextureBits += rateCtrl->numMBTextureBits;
+}
+
+void RCRestoreQP(AVCMacroblock *currMB, AVCCommonObj *video, AVCEncObject *encvid)
+{
+ currMB->QPy = video->QPy; /* use previous QP */
+ RCInitChromaQP(encvid);
+
+ return ;
+}
+
+
+void RCCalculateMAD(AVCEncObject *encvid, AVCMacroblock *currMB, uint8 *orgL, int orgPitch)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ uint32 dmin_lx;
+
+ if (rateCtrl->rcEnable == TRUE)
+ {
+ if (currMB->mb_intra)
+ {
+ if (currMB->mbMode == AVC_I16)
+ {
+ dmin_lx = (0xFFFF << 16) | orgPitch;
+ rateCtrl->MADofMB[video->mbNum] = AVCSAD_Macroblock_C(orgL,
+ encvid->pred_i16[currMB->i16Mode], dmin_lx, NULL);
+ }
+ else /* i4 */
+ {
+ rateCtrl->MADofMB[video->mbNum] = encvid->i4_sad / 256.;
+ }
+ }
+ /* for INTER, we have already saved it with the MV search */
+ }
+
+ return ;
+}
+
+
+
+AVCEnc_Status RCUpdateFrame(AVCEncObject *encvid)
+{
+ AVCCommonObj *video = encvid->common;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ MultiPass *pMP = rateCtrl->pMP;
+ int diff_BTCounter;
+ int nal_type = video->nal_unit_type;
+
+ /* update the complexity weight of I, P, B frame */
+
+ if (rateCtrl->rcEnable == TRUE)
+ {
+ pMP->actual_bits = rateCtrl->numFrameBits;
+ pMP->mad = (OsclFloat)rateCtrl->totalSAD / video->PicSizeInMbs; //ComputeFrameMAD(video, rateCtrl);
+
+ AVCSaveRDSamples(pMP, 0);
+
+ pMP->encoded_frames++;
+
+ /* for pMP->samplesPerFrame */
+ pMP->samplesPerFrame[pMP->framePos] = 0;
+
+ pMP->sum_QP += pMP->QP;
+
+ /* update pMP->counter_BTsrc, pMP->counter_BTdst */
+ /* re-allocate the target bit again and then stop encoding */
+ diff_BTCounter = (int)((OsclFloat)(rateCtrl->TMN_TH - rateCtrl->TMN_W - pMP->actual_bits) /
+ (pMP->bitrate / (pMP->framerate + 0.0001) + 0.0001) / 0.1);
+ if (diff_BTCounter >= 0)
+ pMP->counter_BTsrc += diff_BTCounter; /* pMP->actual_bits is smaller */
+ else
+ pMP->counter_BTdst -= diff_BTCounter; /* pMP->actual_bits is bigger */
+
+ rateCtrl->TMN_TH -= (int)((OsclFloat)pMP->bitrate / (pMP->framerate + 0.0001) * (diff_BTCounter * 0.1));
+ rateCtrl->T = pMP->target_bits = rateCtrl->TMN_TH - rateCtrl->TMN_W;
+ pMP->diff_counter -= diff_BTCounter;
+
+ rateCtrl->Rc = rateCtrl->numFrameBits; /* Total Bits for current frame */
+ rateCtrl->Hc = rateCtrl->NumberofHeaderBits; /* Total Bits in Header and Motion Vector */
+
+ /* BX_RC */
+ updateRateControl(rateCtrl, nal_type);
+ if (rateCtrl->skip_next_frame == -1) // skip current frame
+ {
+ status = AVCENC_SKIPPED_PICTURE;
+ }
+ }
+
+ rateCtrl->first_frame = 0; // reset here after we encode the first frame.
+
+ return status;
+}
+
+void AVCSaveRDSamples(MultiPass *pMP, int counter_samples)
+{
+ /* for pMP->pRDSamples */
+ pMP->pRDSamples[pMP->framePos][counter_samples].QP = pMP->QP;
+ pMP->pRDSamples[pMP->framePos][counter_samples].actual_bits = pMP->actual_bits;
+ pMP->pRDSamples[pMP->framePos][counter_samples].mad = pMP->mad;
+ pMP->pRDSamples[pMP->framePos][counter_samples].R_D = (OsclFloat)pMP->actual_bits / (pMP->mad + 0.0001);
+
+ return ;
+}
+
+void updateRateControl(AVCRateControl *rateCtrl, int nal_type)
+{
+ int frame_bits;
+ MultiPass *pMP = rateCtrl->pMP;
+
+ /* BX rate contro\l */
+ frame_bits = (int)(rateCtrl->bitRate / rateCtrl->frame_rate);
+ rateCtrl->TMN_W += (rateCtrl->Rc - rateCtrl->TMN_TH);
+ rateCtrl->VBV_fullness += (rateCtrl->Rc - frame_bits); //rateCtrl->Rp);
+ //if(rateCtrl->VBV_fullness < 0) rateCtrl->VBV_fullness = -1;
+
+ rateCtrl->encoded_frames++;
+
+ /* frame dropping */
+ rateCtrl->skip_next_frame = 0;
+
+ if ((rateCtrl->VBV_fullness > rateCtrl->Bs / 2) && nal_type != AVC_NALTYPE_IDR) /* skip the current frame */ /* rateCtrl->Bs */
+ {
+ rateCtrl->TMN_W -= (rateCtrl->Rc - rateCtrl->TMN_TH);
+ rateCtrl->VBV_fullness -= rateCtrl->Rc;
+ rateCtrl->skip_next_frame = -1;
+ }
+ else if ((OsclFloat)(rateCtrl->VBV_fullness - rateCtrl->VBV_fullness_offset) > (rateCtrl->Bs / 2 - rateCtrl->VBV_fullness_offset)*0.95) /* skip next frame */
+ {
+ rateCtrl->VBV_fullness -= frame_bits; //rateCtrl->Rp;
+ rateCtrl->skip_next_frame = 1;
+ pMP->counter_BTsrc -= (int)((OsclFloat)(rateCtrl->Bs / 2 - rateCtrl->low_bound) / 2.0 / (pMP->target_bits_per_frame / 10));
+ /* BX_1, skip more than 1 frames */
+ //while(rateCtrl->VBV_fullness > rateCtrl->Bs*0.475)
+ while ((rateCtrl->VBV_fullness - rateCtrl->VBV_fullness_offset) > (rateCtrl->Bs / 2 - rateCtrl->VBV_fullness_offset)*0.95)
+ {
+ rateCtrl->VBV_fullness -= frame_bits; //rateCtrl->Rp;
+ rateCtrl->skip_next_frame++;
+ pMP->counter_BTsrc -= (int)((OsclFloat)(rateCtrl->Bs / 2 - rateCtrl->low_bound) / 2.0 / (pMP->target_bits_per_frame / 10));
+ }
+
+ /* END BX_1 */
+ }
+}
+
+
+double ComputeFrameMAD(AVCCommonObj *video, AVCRateControl *rateCtrl)
+{
+ double TotalMAD;
+ int i;
+ TotalMAD = 0.0;
+ for (i = 0; i < (int)video->PicSizeInMbs; i++)
+ TotalMAD += rateCtrl->MADofMB[i];
+ TotalMAD /= video->PicSizeInMbs;
+ return TotalMAD;
+}
+
+
+
+
+
+/* convert from QP to Qstep */
+double QP2Qstep(int QP)
+{
+ int i;
+ double Qstep;
+ static const double QP2QSTEP[6] = { 0.625, 0.6875, 0.8125, 0.875, 1.0, 1.125 };
+
+ Qstep = QP2QSTEP[QP % 6];
+ for (i = 0; i < (QP / 6); i++)
+ Qstep *= 2;
+
+ return Qstep;
+}
+
+/* convert from step size to QP */
+int Qstep2QP(double Qstep)
+{
+ int q_per = 0, q_rem = 0;
+
+ // assert( Qstep >= QP2Qstep(0) && Qstep <= QP2Qstep(51) );
+ if (Qstep < QP2Qstep(0))
+ return 0;
+ else if (Qstep > QP2Qstep(51))
+ return 51;
+
+ while (Qstep > QP2Qstep(5))
+ {
+ Qstep /= 2;
+ q_per += 1;
+ }
+
+ if (Qstep <= (0.625 + 0.6875) / 2)
+ {
+ Qstep = 0.625;
+ q_rem = 0;
+ }
+ else if (Qstep <= (0.6875 + 0.8125) / 2)
+ {
+ Qstep = 0.6875;
+ q_rem = 1;
+ }
+ else if (Qstep <= (0.8125 + 0.875) / 2)
+ {
+ Qstep = 0.8125;
+ q_rem = 2;
+ }
+ else if (Qstep <= (0.875 + 1.0) / 2)
+ {
+ Qstep = 0.875;
+ q_rem = 3;
+ }
+ else if (Qstep <= (1.0 + 1.125) / 2)
+ {
+ Qstep = 1.0;
+ q_rem = 4;
+ }
+ else
+ {
+ Qstep = 1.125;
+ q_rem = 5;
+ }
+
+ return (q_per * 6 + q_rem);
+}
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/residual.cpp b/media/libstagefright/codecs/avc/enc/src/residual.cpp
new file mode 100644
index 0000000..42eb910
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/residual.cpp
@@ -0,0 +1,389 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+AVCEnc_Status EncodeIntraPCM(AVCEncObject *encvid)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ AVCCommonObj *video = encvid->common;
+ AVCFrameIO *currInput = encvid->currInput;
+ AVCEncBitstream *stream = encvid->bitstream;
+ int x_position = (video->mb_x << 4);
+ int y_position = (video->mb_y << 4);
+ int orgPitch = currInput->pitch;
+ int offset1 = y_position * orgPitch + x_position;
+ int i, j;
+ int offset;
+ uint8 *pDst, *pSrc;
+ uint code;
+
+ ue_v(stream, 25);
+
+ i = stream->bit_left & 0x7;
+ if (i) /* not byte-aligned */
+ {
+ BitstreamWriteBits(stream, 0, i);
+ }
+
+ pSrc = currInput->YCbCr[0] + offset1;
+ pDst = video->currPic->Sl + offset1;
+ offset = video->PicWidthInSamplesL - 16;
+
+ /* at this point bitstream is byte-aligned */
+ j = 16;
+ while (j > 0)
+ {
+#if (WORD_SIZE==32)
+ for (i = 0; i < 4; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 4;
+ *((uint*)pDst) = code;
+ pDst += 4;
+ status = BitstreamWriteBits(stream, 32, code);
+ }
+#else
+ for (i = 0; i < 8; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 2;
+ *((uint*)pDst) = code;
+ pDst += 2;
+ status = BitstreamWriteBits(stream, 16, code);
+ }
+#endif
+ pDst += offset;
+ pSrc += offset;
+ j--;
+ }
+ if (status != AVCENC_SUCCESS) /* check only once per line */
+ return status;
+
+ pDst = video->currPic->Scb + ((offset1 + x_position) >> 2);
+ pSrc = currInput->YCbCr[1] + ((offset1 + x_position) >> 2);
+ offset >>= 1;
+
+ j = 8;
+ while (j > 0)
+ {
+#if (WORD_SIZE==32)
+ for (i = 0; i < 2; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 4;
+ *((uint*)pDst) = code;
+ pDst += 4;
+ status = BitstreamWriteBits(stream, 32, code);
+ }
+#else
+ for (i = 0; i < 4; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 2;
+ *((uint*)pDst) = code;
+ pDst += 2;
+ status = BitstreamWriteBits(stream, 16, code);
+ }
+#endif
+ pDst += offset;
+ pSrc += offset;
+ j--;
+ }
+
+ if (status != AVCENC_SUCCESS) /* check only once per line */
+ return status;
+
+ pDst = video->currPic->Scr + ((offset1 + x_position) >> 2);
+ pSrc = currInput->YCbCr[2] + ((offset1 + x_position) >> 2);
+
+ j = 8;
+ while (j > 0)
+ {
+#if (WORD_SIZE==32)
+ for (i = 0; i < 2; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 4;
+ *((uint*)pDst) = code;
+ pDst += 4;
+ status = BitstreamWriteBits(stream, 32, code);
+ }
+#else
+ for (i = 0; i < 4; i++)
+ {
+ code = *((uint*)pSrc);
+ pSrc += 2;
+ *((uint*)pDst) = code;
+ pDst += 2;
+ status = BitstreamWriteBits(stream, 16, code);
+ }
+#endif
+ pDst += offset;
+ pSrc += offset;
+ j--;
+ }
+
+ return status;
+}
+
+
+AVCEnc_Status enc_residual_block(AVCEncObject *encvid, AVCResidualType type, int cindx, AVCMacroblock *currMB)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ AVCCommonObj *video = encvid->common;
+ int i, maxNumCoeff, nC;
+ int cdc = 0, cac = 0;
+ int TrailingOnes;
+ AVCEncBitstream *stream = encvid->bitstream;
+ uint trailing_ones_sign_flag;
+ int zerosLeft;
+ int *level, *run;
+ int TotalCoeff;
+ const static int incVlc[] = {0, 3, 6, 12, 24, 48, 32768}; // maximum vlc = 6
+ int escape, numPrefix, sufmask, suffix, shift, sign, value, absvalue, vlcnum, level_two_or_higher;
+ int bindx = blkIdx2blkXY[cindx>>2][cindx&3] ; // raster scan index
+
+ switch (type)
+ {
+ case AVC_Luma:
+ maxNumCoeff = 16;
+ level = encvid->level[cindx];
+ run = encvid->run[cindx];
+ TotalCoeff = currMB->nz_coeff[bindx];
+ break;
+ case AVC_Intra16DC:
+ maxNumCoeff = 16;
+ level = encvid->leveldc;
+ run = encvid->rundc;
+ TotalCoeff = cindx; /* special case */
+ bindx = 0;
+ cindx = 0;
+ break;
+ case AVC_Intra16AC:
+ maxNumCoeff = 15;
+ level = encvid->level[cindx];
+ run = encvid->run[cindx];
+ TotalCoeff = currMB->nz_coeff[bindx];
+ break;
+ case AVC_ChromaDC: /* how to differentiate Cb from Cr */
+ maxNumCoeff = 4;
+ cdc = 1;
+ if (cindx >= 8)
+ {
+ level = encvid->levelcdc + 4;
+ run = encvid->runcdc + 4;
+ TotalCoeff = cindx - 8; /* special case */
+ }
+ else
+ {
+ level = encvid->levelcdc;
+ run = encvid->runcdc;
+ TotalCoeff = cindx; /* special case */
+ }
+ break;
+ case AVC_ChromaAC:
+ maxNumCoeff = 15;
+ cac = 1;
+ level = encvid->level[cindx];
+ run = encvid->run[cindx];
+ cindx -= 16;
+ bindx = 16 + blkIdx2blkXY[cindx>>2][cindx&3];
+ cindx += 16;
+ TotalCoeff = currMB->nz_coeff[bindx];
+ break;
+ default:
+ return AVCENC_FAIL;
+ }
+
+
+ /* find TrailingOnes */
+ TrailingOnes = 0;
+ zerosLeft = 0;
+ i = TotalCoeff - 1;
+ nC = 1;
+ while (i >= 0)
+ {
+ zerosLeft += run[i];
+ if (nC && (level[i] == 1 || level[i] == -1))
+ {
+ TrailingOnes++;
+ }
+ else
+ {
+ nC = 0;
+ }
+ i--;
+ }
+ if (TrailingOnes > 3)
+ {
+ TrailingOnes = 3; /* clip it */
+ }
+
+ if (!cdc)
+ {
+ if (!cac) /* not chroma */
+ {
+ nC = predict_nnz(video, bindx & 3, bindx >> 2);
+ }
+ else /* chroma ac but not chroma dc */
+ {
+ nC = predict_nnz_chroma(video, bindx & 3, bindx >> 2);
+ }
+
+ status = ce_TotalCoeffTrailingOnes(stream, TrailingOnes, TotalCoeff, nC);
+ }
+ else
+ {
+ nC = -1; /* Chroma DC level */
+ status = ce_TotalCoeffTrailingOnesChromaDC(stream, TrailingOnes, TotalCoeff);
+ }
+
+ /* This part is done quite differently in ReadCoef4x4_CAVLC() */
+ if (TotalCoeff > 0)
+ {
+
+ i = TotalCoeff - 1;
+
+ if (TrailingOnes) /* keep reading the sign of those trailing ones */
+ {
+ nC = TrailingOnes;
+ trailing_ones_sign_flag = 0;
+ while (nC)
+ {
+ trailing_ones_sign_flag <<= 1;
+ trailing_ones_sign_flag |= ((uint32)level[i--] >> 31); /* 0 or positive, 1 for negative */
+ nC--;
+ }
+
+ /* instead of writing one bit at a time, read the whole thing at once */
+ status = BitstreamWriteBits(stream, TrailingOnes, trailing_ones_sign_flag);
+ }
+
+ level_two_or_higher = 1;
+ if (TotalCoeff > 3 && TrailingOnes == 3)
+ {
+ level_two_or_higher = 0;
+ }
+
+ if (TotalCoeff > 10 && TrailingOnes < 3)
+ {
+ vlcnum = 1;
+ }
+ else
+ {
+ vlcnum = 0;
+ }
+
+ /* then do this TotalCoeff-TrailingOnes times */
+ for (i = TotalCoeff - TrailingOnes - 1; i >= 0; i--)
+ {
+ value = level[i];
+ absvalue = (value >= 0) ? value : -value;
+
+ if (level_two_or_higher)
+ {
+ if (value > 0) value--;
+ else value++;
+ level_two_or_higher = 0;
+ }
+
+ if (value >= 0)
+ {
+ sign = 0;
+ }
+ else
+ {
+ sign = 1;
+ value = -value;
+ }
+
+ if (vlcnum == 0) // VLC1
+ {
+ if (value < 8)
+ {
+ status = BitstreamWriteBits(stream, value * 2 + sign - 1, 1);
+ }
+ else if (value < 8 + 8)
+ {
+ status = BitstreamWriteBits(stream, 14 + 1 + 4, (1 << 4) | ((value - 8) << 1) | sign);
+ }
+ else
+ {
+ status = BitstreamWriteBits(stream, 14 + 2 + 12, (1 << 12) | ((value - 16) << 1) | sign) ;
+ }
+ }
+ else // VLCN
+ {
+ shift = vlcnum - 1;
+ escape = (15 << shift) + 1;
+ numPrefix = (value - 1) >> shift;
+ sufmask = ~((0xffffffff) << shift);
+ suffix = (value - 1) & sufmask;
+ if (value < escape)
+ {
+ status = BitstreamWriteBits(stream, numPrefix + vlcnum + 1, (1 << (shift + 1)) | (suffix << 1) | sign);
+ }
+ else
+ {
+ status = BitstreamWriteBits(stream, 28, (1 << 12) | ((value - escape) << 1) | sign);
+ }
+
+ }
+
+ if (absvalue > incVlc[vlcnum])
+ vlcnum++;
+
+ if (i == TotalCoeff - TrailingOnes - 1 && absvalue > 3)
+ vlcnum = 2;
+ }
+
+ if (status != AVCENC_SUCCESS) /* occasionally check the bitstream */
+ {
+ return status;
+ }
+ if (TotalCoeff < maxNumCoeff)
+ {
+ if (!cdc)
+ {
+ ce_TotalZeros(stream, zerosLeft, TotalCoeff);
+ }
+ else
+ {
+ ce_TotalZerosChromaDC(stream, zerosLeft, TotalCoeff);
+ }
+ }
+ else
+ {
+ zerosLeft = 0;
+ }
+
+ i = TotalCoeff - 1;
+ while (i > 0) /* don't do the last one */
+ {
+ if (zerosLeft > 0)
+ {
+ ce_RunBefore(stream, run[i], zerosLeft);
+ }
+
+ zerosLeft = zerosLeft - run[i];
+ i--;
+ }
+ }
+
+ return status;
+}
diff --git a/media/libstagefright/codecs/avc/enc/src/sad.cpp b/media/libstagefright/codecs/avc/enc/src/sad.cpp
new file mode 100644
index 0000000..ae7acd2
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/sad.cpp
@@ -0,0 +1,290 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+#include "sad_inline.h"
+
+#define Cached_lx 176
+
+#ifdef _SAD_STAT
+uint32 num_sad_MB = 0;
+uint32 num_sad_Blk = 0;
+uint32 num_sad_MB_call = 0;
+uint32 num_sad_Blk_call = 0;
+
+#define NUM_SAD_MB_CALL() num_sad_MB_call++
+#define NUM_SAD_MB() num_sad_MB++
+#define NUM_SAD_BLK_CALL() num_sad_Blk_call++
+#define NUM_SAD_BLK() num_sad_Blk++
+
+#else
+
+#define NUM_SAD_MB_CALL()
+#define NUM_SAD_MB()
+#define NUM_SAD_BLK_CALL()
+#define NUM_SAD_BLK()
+
+#endif
+
+
+/* consist of
+int AVCSAD_Macroblock_C(uint8 *ref,uint8 *blk,int dmin,int lx,void *extra_info)
+int AVCSAD_MB_HTFM_Collect(uint8 *ref,uint8 *blk,int dmin,int lx,void *extra_info)
+int AVCSAD_MB_HTFM(uint8 *ref,uint8 *blk,int dmin,int lx,void *extra_info)
+*/
+
+
+/*==================================================================
+ Function: SAD_Macroblock
+ Date: 09/07/2000
+ Purpose: Compute SAD 16x16 between blk and ref.
+ To do: Uniform subsampling will be inserted later!
+ Hypothesis Testing Fast Matching to be used later!
+ Changes:
+ 11/7/00: implemented MMX
+ 1/24/01: implemented SSE
+==================================================================*/
+/********** C ************/
+int AVCSAD_Macroblock_C(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info)
+{
+ (void)(extra_info);
+
+ int32 x10;
+ int dmin = (uint32)dmin_lx >> 16;
+ int lx = dmin_lx & 0xFFFF;
+
+ NUM_SAD_MB_CALL();
+
+ x10 = simd_sad_mb(ref, blk, dmin, lx);
+
+ return x10;
+}
+
+#ifdef HTFM /* HTFM with uniform subsampling implementation 2/28/01 */
+/*===============================================================
+ Function: AVCAVCSAD_MB_HTFM_Collect and AVCSAD_MB_HTFM
+ Date: 3/2/1
+ Purpose: Compute the SAD on a 16x16 block using
+ uniform subsampling and hypothesis testing fast matching
+ for early dropout. SAD_MB_HP_HTFM_Collect is to collect
+ the statistics to compute the thresholds to be used in
+ SAD_MB_HP_HTFM.
+ Input/Output:
+ Changes:
+ ===============================================================*/
+
+int AVCAVCSAD_MB_HTFM_Collect(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info)
+{
+ int i;
+ int sad = 0;
+ uint8 *p1;
+ int lx4 = (dmin_lx << 2) & 0x3FFFC;
+ uint32 cur_word;
+ int saddata[16], tmp, tmp2; /* used when collecting flag (global) is on */
+ int difmad;
+ int madstar;
+ HTFM_Stat *htfm_stat = (HTFM_Stat*) extra_info;
+ int *abs_dif_mad_avg = &(htfm_stat->abs_dif_mad_avg);
+ uint *countbreak = &(htfm_stat->countbreak);
+ int *offsetRef = htfm_stat->offsetRef;
+
+ madstar = (uint32)dmin_lx >> 20;
+
+ NUM_SAD_MB_CALL();
+
+ blk -= 4;
+ for (i = 0; i < 16; i++)
+ {
+ p1 = ref + offsetRef[i];
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ NUM_SAD_MB();
+
+ saddata[i] = sad;
+
+ if (i > 0)
+ {
+ if ((uint32)sad > ((uint32)dmin_lx >> 16))
+ {
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+ return sad;
+ }
+ }
+ }
+
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+ return sad;
+}
+
+int AVCSAD_MB_HTFM(uint8 *ref, uint8 *blk, int dmin_lx, void *extra_info)
+{
+ int sad = 0;
+ uint8 *p1;
+
+ int i;
+ int tmp, tmp2;
+ int lx4 = (dmin_lx << 2) & 0x3FFFC;
+ int sadstar = 0, madstar;
+ int *nrmlz_th = (int*) extra_info;
+ int *offsetRef = (int*) extra_info + 32;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_lx >> 20;
+
+ NUM_SAD_MB_CALL();
+
+ blk -= 4;
+ for (i = 0; i < 16; i++)
+ {
+ p1 = ref + offsetRef[i];
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = (cur_word >> 24) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[8];
+ tmp2 = (cur_word >> 16) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[4];
+ tmp2 = (cur_word >> 8) & 0xFF;
+ sad = SUB_SAD(sad, tmp, tmp2);
+ tmp = p1[0];
+ p1 += lx4;
+ tmp2 = (cur_word & 0xFF);
+ sad = SUB_SAD(sad, tmp, tmp2);
+
+ NUM_SAD_MB();
+
+ sadstar += madstar;
+ if (((uint32)sad <= ((uint32)dmin_lx >> 16)) && (sad <= (sadstar - *nrmlz_th++)))
+ ;
+ else
+ return 65536;
+ }
+
+ return sad;
+}
+#endif /* HTFM */
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/sad_halfpel.cpp b/media/libstagefright/codecs/avc/enc/src/sad_halfpel.cpp
new file mode 100644
index 0000000..faf2198
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/sad_halfpel.cpp
@@ -0,0 +1,629 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+/* contains
+int AVCHalfPel1_SAD_MB(uint8 *ref,uint8 *blk,int dmin,int width,int ih,int jh)
+int AVCHalfPel2_SAD_MB(uint8 *ref,uint8 *blk,int dmin,int width)
+int AVCHalfPel1_SAD_Blk(uint8 *ref,uint8 *blk,int dmin,int width,int ih,int jh)
+int AVCHalfPel2_SAD_Blk(uint8 *ref,uint8 *blk,int dmin,int width)
+
+int AVCSAD_MB_HalfPel_C(uint8 *ref,uint8 *blk,int dmin,int width,int rx,int xh,int yh,void *extra_info)
+int AVCSAD_MB_HP_HTFM_Collect(uint8 *ref,uint8 *blk,int dmin,int width,int rx,int xh,int yh,void *extra_info)
+int AVCSAD_MB_HP_HTFM(uint8 *ref,uint8 *blk,int dmin,int width,int rx,int xh,int yh,void *extra_info)
+int AVCSAD_Blk_HalfPel_C(uint8 *ref,uint8 *blk,int dmin,int width,int rx,int xh,int yh,void *extra_info)
+*/
+
+#include "avcenc_lib.h"
+#include "sad_halfpel_inline.h"
+
+#ifdef _SAD_STAT
+uint32 num_sad_HP_MB = 0;
+uint32 num_sad_HP_Blk = 0;
+uint32 num_sad_HP_MB_call = 0;
+uint32 num_sad_HP_Blk_call = 0;
+#define NUM_SAD_HP_MB_CALL() num_sad_HP_MB_call++
+#define NUM_SAD_HP_MB() num_sad_HP_MB++
+#define NUM_SAD_HP_BLK_CALL() num_sad_HP_Blk_call++
+#define NUM_SAD_HP_BLK() num_sad_HP_Blk++
+#else
+#define NUM_SAD_HP_MB_CALL()
+#define NUM_SAD_HP_MB()
+#define NUM_SAD_HP_BLK_CALL()
+#define NUM_SAD_HP_BLK()
+#endif
+
+
+
+/*===============================================================
+ Function: SAD_MB_HalfPel
+ Date: 09/17/2000
+ Purpose: Compute the SAD on the half-pel resolution
+ Input/Output: hmem is assumed to be a pointer to the starting
+ point of the search in the 33x33 matrix search region
+ Changes:
+ 11/7/00: implemented MMX
+ ===============================================================*/
+/*==================================================================
+ Function: AVCSAD_MB_HalfPel_C
+ Date: 04/30/2001
+ Purpose: Compute SAD 16x16 between blk and ref in halfpel
+ resolution,
+ Changes:
+ ==================================================================*/
+/* One component is half-pel */
+int AVCSAD_MB_HalfPel_Cxhyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ (void)(extra_info);
+
+ int i, j;
+ int sad = 0;
+ uint8 *kk, *p1, *p2, *p3, *p4;
+// int sumref=0;
+ int temp;
+ int rx = dmin_rx & 0xFFFF;
+
+ NUM_SAD_HP_MB_CALL();
+
+ p1 = ref;
+ p2 = ref + 1;
+ p3 = ref + rx;
+ p4 = ref + rx + 1;
+ kk = blk;
+
+ for (i = 0; i < 16; i++)
+ {
+ for (j = 0; j < 16; j++)
+ {
+
+ temp = ((p1[j] + p2[j] + p3[j] + p4[j] + 2) >> 2) - *kk++;
+ sad += AVC_ABS(temp);
+ }
+
+ NUM_SAD_HP_MB();
+
+ if (sad > (int)((uint32)dmin_rx >> 16))
+ return sad;
+
+ p1 += rx;
+ p3 += rx;
+ p2 += rx;
+ p4 += rx;
+ }
+ return sad;
+}
+
+int AVCSAD_MB_HalfPel_Cyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ (void)(extra_info);
+
+ int i, j;
+ int sad = 0;
+ uint8 *kk, *p1, *p2;
+// int sumref=0;
+ int temp;
+ int rx = dmin_rx & 0xFFFF;
+
+ NUM_SAD_HP_MB_CALL();
+
+ p1 = ref;
+ p2 = ref + rx; /* either left/right or top/bottom pixel */
+ kk = blk;
+
+ for (i = 0; i < 16; i++)
+ {
+ for (j = 0; j < 16; j++)
+ {
+
+ temp = ((p1[j] + p2[j] + 1) >> 1) - *kk++;
+ sad += AVC_ABS(temp);
+ }
+
+ NUM_SAD_HP_MB();
+
+ if (sad > (int)((uint32)dmin_rx >> 16))
+ return sad;
+ p1 += rx;
+ p2 += rx;
+ }
+ return sad;
+}
+
+int AVCSAD_MB_HalfPel_Cxh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ (void)(extra_info);
+
+ int i, j;
+ int sad = 0;
+ uint8 *kk, *p1;
+ int temp;
+ int rx = dmin_rx & 0xFFFF;
+
+ NUM_SAD_HP_MB_CALL();
+
+ p1 = ref;
+ kk = blk;
+
+ for (i = 0; i < 16; i++)
+ {
+ for (j = 0; j < 16; j++)
+ {
+
+ temp = ((p1[j] + p1[j+1] + 1) >> 1) - *kk++;
+ sad += AVC_ABS(temp);
+ }
+
+ NUM_SAD_HP_MB();
+
+ if (sad > (int)((uint32)dmin_rx >> 16))
+ return sad;
+ p1 += rx;
+ }
+ return sad;
+}
+
+#ifdef HTFM /* HTFM with uniform subsampling implementation, 2/28/01 */
+
+//Checheck here
+int AVCAVCSAD_MB_HP_HTFM_Collectxhyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0;
+ uint8 *p1, *p2;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int saddata[16]; /* used when collecting flag (global) is on */
+ int difmad, tmp, tmp2;
+ int madstar;
+ HTFM_Stat *htfm_stat = (HTFM_Stat*) extra_info;
+ int *abs_dif_mad_avg = &(htfm_stat->abs_dif_mad_avg);
+ UInt *countbreak = &(htfm_stat->countbreak);
+ int *offsetRef = htfm_stat->offsetRef;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+ p2 = p1 + rx;
+
+ j = 4;/* 4 lines */
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12] + p2[12];
+ tmp2 = p1[13] + p2[13];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 24) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8] + p2[8];
+ tmp2 = p1[9] + p2[9];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 16) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4] + p2[4];
+ tmp2 = p1[5] + p2[5];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 8) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp2 = p1[1] + p2[1];
+ tmp = p1[0] + p2[0];
+ p1 += refwx4;
+ p2 += refwx4;
+ tmp += tmp2;
+ tmp2 = (cur_word & 0xFF);
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+
+ saddata[i] = sad;
+
+ if (i > 0)
+ {
+ if (sad > ((uint32)dmin_rx >> 16))
+ {
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+ return sad;
+ }
+ }
+ }
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+
+ return sad;
+}
+
+int AVCAVCSAD_MB_HP_HTFM_Collectyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0;
+ uint8 *p1, *p2;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int saddata[16]; /* used when collecting flag (global) is on */
+ int difmad, tmp, tmp2;
+ int madstar;
+ HTFM_Stat *htfm_stat = (HTFM_Stat*) extra_info;
+ int *abs_dif_mad_avg = &(htfm_stat->abs_dif_mad_avg);
+ UInt *countbreak = &(htfm_stat->countbreak);
+ int *offsetRef = htfm_stat->offsetRef;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+ p2 = p1 + rx;
+ j = 4;
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = p2[12];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 24) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8];
+ tmp2 = p2[8];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 16) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4];
+ tmp2 = p2[4];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 8) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[0];
+ p1 += refwx4;
+ tmp2 = p2[0];
+ p2 += refwx4;
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word & 0xFF);
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+
+ saddata[i] = sad;
+
+ if (i > 0)
+ {
+ if (sad > ((uint32)dmin_rx >> 16))
+ {
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+ return sad;
+ }
+ }
+ }
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+
+ return sad;
+}
+
+int AVCAVCSAD_MB_HP_HTFM_Collectxh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0;
+ uint8 *p1;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int saddata[16]; /* used when collecting flag (global) is on */
+ int difmad, tmp, tmp2;
+ int madstar;
+ HTFM_Stat *htfm_stat = (HTFM_Stat*) extra_info;
+ int *abs_dif_mad_avg = &(htfm_stat->abs_dif_mad_avg);
+ UInt *countbreak = &(htfm_stat->countbreak);
+ int *offsetRef = htfm_stat->offsetRef;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+
+ j = 4; /* 4 lines */
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = p1[13];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 24) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8];
+ tmp2 = p1[9];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 16) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4];
+ tmp2 = p1[5];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 8) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[0];
+ tmp2 = p1[1];
+ p1 += refwx4;
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word & 0xFF);
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+
+ saddata[i] = sad;
+
+ if (i > 0)
+ {
+ if (sad > ((uint32)dmin_rx >> 16))
+ {
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+ return sad;
+ }
+ }
+ }
+ difmad = saddata[0] - ((saddata[1] + 1) >> 1);
+ (*abs_dif_mad_avg) += ((difmad > 0) ? difmad : -difmad);
+ (*countbreak)++;
+
+ return sad;
+}
+
+int AVCSAD_MB_HP_HTFMxhyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0, tmp, tmp2;
+ uint8 *p1, *p2;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int sadstar = 0, madstar;
+ int *nrmlz_th = (int*) extra_info;
+ int *offsetRef = nrmlz_th + 32;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+ p2 = p1 + rx;
+
+ j = 4; /* 4 lines */
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12] + p2[12];
+ tmp2 = p1[13] + p2[13];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 24) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8] + p2[8];
+ tmp2 = p1[9] + p2[9];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 16) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4] + p2[4];
+ tmp2 = p1[5] + p2[5];
+ tmp += tmp2;
+ tmp2 = (cur_word >> 8) & 0xFF;
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ tmp2 = p1[1] + p2[1];
+ tmp = p1[0] + p2[0];
+ p1 += refwx4;
+ p2 += refwx4;
+ tmp += tmp2;
+ tmp2 = (cur_word & 0xFF);
+ tmp += 2;
+ sad = INTERP2_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+
+ sadstar += madstar;
+ if (sad > sadstar - nrmlz_th[i] || sad > ((uint32)dmin_rx >> 16))
+ {
+ return 65536;
+ }
+ }
+
+ return sad;
+}
+
+int AVCSAD_MB_HP_HTFMyh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0, tmp, tmp2;
+ uint8 *p1, *p2;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int sadstar = 0, madstar;
+ int *nrmlz_th = (int*) extra_info;
+ int *offsetRef = nrmlz_th + 32;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+ p2 = p1 + rx;
+ j = 4;
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = p2[12];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 24) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8];
+ tmp2 = p2[8];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 16) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4];
+ tmp2 = p2[4];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 8) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[0];
+ p1 += refwx4;
+ tmp2 = p2[0];
+ p2 += refwx4;
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word & 0xFF);
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+ sadstar += madstar;
+ if (sad > sadstar - nrmlz_th[i] || sad > ((uint32)dmin_rx >> 16))
+ {
+ return 65536;
+ }
+ }
+
+ return sad;
+}
+
+int AVCSAD_MB_HP_HTFMxh(uint8 *ref, uint8 *blk, int dmin_rx, void *extra_info)
+{
+ int i, j;
+ int sad = 0, tmp, tmp2;
+ uint8 *p1;
+ int rx = dmin_rx & 0xFFFF;
+ int refwx4 = rx << 2;
+ int sadstar = 0, madstar;
+ int *nrmlz_th = (int*) extra_info;
+ int *offsetRef = nrmlz_th + 32;
+ uint32 cur_word;
+
+ madstar = (uint32)dmin_rx >> 20;
+
+ NUM_SAD_HP_MB_CALL();
+
+ blk -= 4;
+
+ for (i = 0; i < 16; i++) /* 16 stages */
+ {
+ p1 = ref + offsetRef[i];
+
+ j = 4;/* 4 lines */
+ do
+ {
+ cur_word = *((uint32*)(blk += 4));
+ tmp = p1[12];
+ tmp2 = p1[13];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 24) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[8];
+ tmp2 = p1[9];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 16) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[4];
+ tmp2 = p1[5];
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word >> 8) & 0xFF;
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ tmp = p1[0];
+ tmp2 = p1[1];
+ p1 += refwx4;
+ tmp++;
+ tmp2 += tmp;
+ tmp = (cur_word & 0xFF);
+ sad = INTERP1_SUB_SAD(sad, tmp, tmp2);;
+ }
+ while (--j);
+
+ NUM_SAD_HP_MB();
+
+ sadstar += madstar;
+ if (sad > sadstar - nrmlz_th[i] || sad > ((uint32)dmin_rx >> 16))
+ {
+ return 65536;
+ }
+ }
+
+ return sad;
+}
+
+#endif /* HTFM */
+
+
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/sad_halfpel_inline.h b/media/libstagefright/codecs/avc/enc/src/sad_halfpel_inline.h
new file mode 100644
index 0000000..3a21647
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/sad_halfpel_inline.h
@@ -0,0 +1,96 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _SAD_HALFPEL_INLINE_H_
+#define _SAD_HALFPEL_INLINE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+ __inline int32 INTERP1_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ tmp = (tmp2 >> 1) - tmp;
+ if (tmp > 0) sad += tmp;
+ else sad -= tmp;
+
+ return sad;
+ }
+
+ __inline int32 INTERP2_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ tmp = (tmp >> 2) - tmp2;
+ if (tmp > 0) sad += tmp;
+ else sad -= tmp;
+
+ return sad;
+ }
+
+#elif defined(__CC_ARM) /* only work with arm v5 */
+
+ __inline int32 INTERP1_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ __asm
+ {
+ rsbs tmp, tmp, tmp2, asr #1 ;
+ rsbmi tmp, tmp, #0 ;
+ add sad, sad, tmp ;
+ }
+
+ return sad;
+ }
+
+ __inline int32 INTERP2_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ __asm
+ {
+ rsbs tmp, tmp2, tmp, asr #2 ;
+ rsbmi tmp, tmp, #0 ;
+ add sad, sad, tmp ;
+ }
+
+ return sad;
+ }
+
+#elif defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+ __inline int32 INTERP1_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+__asm__ volatile("rsbs %1, %1, %2, asr #1\n\trsbmi %1, %1, #0\n\tadd %0, %0, %1": "=r"(sad), "=r"(tmp): "r"(tmp2));
+
+ return sad;
+ }
+
+ __inline int32 INTERP2_SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+__asm__ volatile("rsbs %1, %2, %1, asr #2\n\trsbmi %1, %1, #0\n\tadd %0, %0, %1": "=r"(sad), "=r"(tmp): "r"(tmp2));
+
+ return sad;
+ }
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_SAD_HALFPEL_INLINE_H_
+
diff --git a/media/libstagefright/codecs/avc/enc/src/sad_inline.h b/media/libstagefright/codecs/avc/enc/src/sad_inline.h
new file mode 100644
index 0000000..f39794f
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/sad_inline.h
@@ -0,0 +1,488 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _SAD_INLINE_H_
+#define _SAD_INLINE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+ __inline int32 SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ tmp = tmp - tmp2;
+ if (tmp > 0) sad += tmp;
+ else sad -= tmp;
+
+ return sad;
+ }
+
+ __inline int32 sad_4pixel(int32 src1, int32 src2, int32 mask)
+ {
+ int32 x7;
+
+ x7 = src2 ^ src1; /* check odd/even combination */
+ if ((uint32)src2 >= (uint32)src1)
+ {
+ src1 = src2 - src1; /* subs */
+ }
+ else
+ {
+ src1 = src1 - src2;
+ }
+ x7 = x7 ^ src1; /* only odd bytes need to add carry */
+ x7 = mask & ((uint32)x7 >> 1);
+ x7 = (x7 << 8) - x7;
+ src1 = src1 + (x7 >> 7); /* add 0xFF to the negative byte, add back carry */
+ src1 = src1 ^(x7 >> 7); /* take absolute value of negative byte */
+
+ return src1;
+ }
+
+#define NUMBER 3
+#define SHIFT 24
+
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 2
+#undef SHIFT
+#define SHIFT 16
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 1
+#undef SHIFT
+#define SHIFT 8
+#include "sad_mb_offset.h"
+
+
+ __inline int32 simd_sad_mb(uint8 *ref, uint8 *blk, int dmin, int lx)
+ {
+ int32 x4, x5, x6, x8, x9, x10, x11, x12, x14;
+
+ x9 = 0x80808080; /* const. */
+
+ x8 = (uint32)ref & 0x3;
+ if (x8 == 3)
+ goto SadMBOffset3;
+ if (x8 == 2)
+ goto SadMBOffset2;
+ if (x8 == 1)
+ goto SadMBOffset1;
+
+// x5 = (x4<<8)-x4; /* x5 = x4*255; */
+ x4 = x5 = 0;
+
+ x6 = 0xFFFF00FF;
+
+ ref -= lx;
+ blk -= 16;
+
+ x8 = 16;
+
+LOOP_SAD0:
+ /****** process 8 pixels ******/
+ x10 = *((uint32*)(ref += lx));
+ x11 = *((uint32*)(ref + 4));
+ x12 = *((uint32*)(blk += 16));
+ x14 = *((uint32*)(blk + 4));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****** process 8 pixels ******/
+ x10 = *((uint32*)(ref + 8));
+ x11 = *((uint32*)(ref + 12));
+ x12 = *((uint32*)(blk + 8));
+ x14 = *((uint32*)(blk + 12));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ if ((int)((uint32)x10 >> 16) <= dmin) /* compare with dmin */
+ {
+ if (--x8)
+ {
+ goto LOOP_SAD0;
+ }
+
+ }
+
+ return ((uint32)x10 >> 16);
+
+SadMBOffset3:
+
+ return sad_mb_offset3(ref, blk, lx, dmin);
+
+SadMBOffset2:
+
+ return sad_mb_offset2(ref, blk, lx, dmin);
+
+SadMBOffset1:
+
+ return sad_mb_offset1(ref, blk, lx, dmin);
+
+ }
+
+#elif defined(__CC_ARM) /* only work with arm v5 */
+
+ __inline int32 SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+ __asm
+ {
+ rsbs tmp, tmp, tmp2 ;
+ rsbmi tmp, tmp, #0 ;
+ add sad, sad, tmp ;
+ }
+
+ return sad;
+ }
+
+ __inline int32 sad_4pixel(int32 src1, int32 src2, int32 mask)
+ {
+ int32 x7;
+
+ __asm
+ {
+ EOR x7, src2, src1; /* check odd/even combination */
+ SUBS src1, src2, src1;
+ EOR x7, x7, src1;
+ AND x7, mask, x7, lsr #1;
+ ORRCC x7, x7, #0x80000000;
+ RSB x7, x7, x7, lsl #8;
+ ADD src1, src1, x7, asr #7; /* add 0xFF to the negative byte, add back carry */
+ EOR src1, src1, x7, asr #7; /* take absolute value of negative byte */
+ }
+
+ return src1;
+ }
+
+ __inline int32 sad_4pixelN(int32 src1, int32 src2, int32 mask)
+ {
+ int32 x7;
+
+ __asm
+ {
+ EOR x7, src2, src1; /* check odd/even combination */
+ ADDS src1, src2, src1;
+ EOR x7, x7, src1; /* only odd bytes need to add carry */
+ ANDS x7, mask, x7, rrx;
+ RSB x7, x7, x7, lsl #8;
+ SUB src1, src1, x7, asr #7; /* add 0xFF to the negative byte, add back carry */
+ EOR src1, src1, x7, asr #7; /* take absolute value of negative byte */
+ }
+
+ return src1;
+ }
+
+#define sum_accumulate __asm{ SBC x5, x5, x10; /* accumulate low bytes */ \
+ BIC x10, x6, x10; /* x10 & 0xFF00FF00 */ \
+ ADD x4, x4, x10,lsr #8; /* accumulate high bytes */ \
+ SBC x5, x5, x11; /* accumulate low bytes */ \
+ BIC x11, x6, x11; /* x11 & 0xFF00FF00 */ \
+ ADD x4, x4, x11,lsr #8; } /* accumulate high bytes */
+
+
+#define NUMBER 3
+#define SHIFT 24
+#define INC_X8 0x08000001
+
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 2
+#undef SHIFT
+#define SHIFT 16
+#undef INC_X8
+#define INC_X8 0x10000001
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 1
+#undef SHIFT
+#define SHIFT 8
+#undef INC_X8
+#define INC_X8 0x08000001
+#include "sad_mb_offset.h"
+
+
+ __inline int32 simd_sad_mb(uint8 *ref, uint8 *blk, int dmin, int lx)
+ {
+ int32 x4, x5, x6, x8, x9, x10, x11, x12, x14;
+
+ x9 = 0x80808080; /* const. */
+ x4 = x5 = 0;
+
+ __asm
+ {
+ MOVS x8, ref, lsl #31 ;
+ BHI SadMBOffset3;
+ BCS SadMBOffset2;
+ BMI SadMBOffset1;
+
+ MVN x6, #0xFF00;
+ }
+LOOP_SAD0:
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 12));
+ x10 = *((int32*)(ref + 8));
+ x14 = *((int32*)(blk + 12));
+ x12 = *((int32*)(blk + 8));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ __asm
+ {
+ /****** process 8 pixels ******/
+ LDR x11, [ref, #4];
+ LDR x10, [ref], lx ;
+ LDR x14, [blk, #4];
+ LDR x12, [blk], #16 ;
+ }
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ __asm
+ {
+ /****************/
+ RSBS x11, dmin, x10, lsr #16;
+ ADDLSS x8, x8, #0x10000001;
+ BLS LOOP_SAD0;
+ }
+
+ return ((uint32)x10 >> 16);
+
+SadMBOffset3:
+
+ return sad_mb_offset3(ref, blk, lx, dmin, x8);
+
+SadMBOffset2:
+
+ return sad_mb_offset2(ref, blk, lx, dmin, x8);
+
+SadMBOffset1:
+
+ return sad_mb_offset1(ref, blk, lx, dmin, x8);
+ }
+
+
+#elif defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+ __inline int32 SUB_SAD(int32 sad, int32 tmp, int32 tmp2)
+ {
+__asm__ volatile("rsbs %1, %1, %2\n\trsbmi %1, %1, #0\n\tadd %0, %0, %1": "=r"(sad): "r"(tmp), "r"(tmp2));
+ return sad;
+ }
+
+ __inline int32 sad_4pixel(int32 src1, int32 src2, int32 mask)
+ {
+ int32 x7;
+
+__asm__ volatile("EOR %1, %2, %0\n\tSUBS %0, %2, %0\n\tEOR %1, %1, %0\n\tAND %1, %3, %1, lsr #1\n\tORRCC %1, %1, #0x80000000\n\tRSB %1, %1, %1, lsl #8\n\tADD %0, %0, %1, asr #7\n\tEOR %0, %0, %1, asr #7": "=r"(src1), "=&r"(x7): "r"(src2), "r"(mask));
+
+ return src1;
+ }
+
+ __inline int32 sad_4pixelN(int32 src1, int32 src2, int32 mask)
+ {
+ int32 x7;
+
+__asm__ volatile("EOR %1, %2, %0\n\tADDS %0, %2, %0\n\tEOR %1, %1, %0\n\tANDS %1, %3, %1, rrx\n\tRSB %1, %1, %1, lsl #8\n\tSUB %0, %0, %1, asr #7\n\tEOR %0, %0, %1, asr #7": "=r"(src1), "=&r"(x7): "r"(src2), "r"(mask));
+
+ return src1;
+ }
+
+#define sum_accumulate __asm__ volatile("SBC %0, %0, %1\n\tBIC %1, %4, %1\n\tADD %2, %2, %1, lsr #8\n\tSBC %0, %0, %3\n\tBIC %3, %4, %3\n\tADD %2, %2, %3, lsr #8": "=&r" (x5), "=&r" (x10), "=&r" (x4), "=&r" (x11): "r" (x6));
+
+#define NUMBER 3
+#define SHIFT 24
+#define INC_X8 0x08000001
+
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 2
+#undef SHIFT
+#define SHIFT 16
+#undef INC_X8
+#define INC_X8 0x10000001
+#include "sad_mb_offset.h"
+
+#undef NUMBER
+#define NUMBER 1
+#undef SHIFT
+#define SHIFT 8
+#undef INC_X8
+#define INC_X8 0x08000001
+#include "sad_mb_offset.h"
+
+
+ __inline int32 simd_sad_mb(uint8 *ref, uint8 *blk, int dmin, int lx)
+ {
+ int32 x4, x5, x6, x8, x9, x10, x11, x12, x14;
+
+ x9 = 0x80808080; /* const. */
+ x4 = x5 = 0;
+
+ x8 = (uint32)ref & 0x3;
+ if (x8 == 3)
+ goto SadMBOffset3;
+ if (x8 == 2)
+ goto SadMBOffset2;
+ if (x8 == 1)
+ goto SadMBOffset1;
+
+ x8 = 16;
+///
+__asm__ volatile("MVN %0, #0xFF00": "=r"(x6));
+
+LOOP_SAD0:
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 12));
+ x10 = *((int32*)(ref + 8));
+ x14 = *((int32*)(blk + 12));
+ x12 = *((int32*)(blk + 8));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 4));
+__asm__ volatile("LDR %0, [%1], %2": "=&r"(x10), "=r"(ref): "r"(lx));
+ //x10 = *((int32*)ref); ref+=lx;
+ x14 = *((int32*)(blk + 4));
+__asm__ volatile("LDR %0, [%1], #16": "=&r"(x12), "=r"(blk));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ /****************/
+
+ if (((uint32)x10 >> 16) <= dmin) /* compare with dmin */
+ {
+ if (--x8)
+ {
+ goto LOOP_SAD0;
+ }
+
+ }
+
+ return ((uint32)x10 >> 16);
+
+SadMBOffset3:
+
+ return sad_mb_offset3(ref, blk, lx, dmin);
+
+SadMBOffset2:
+
+ return sad_mb_offset2(ref, blk, lx, dmin);
+
+SadMBOffset1:
+
+ return sad_mb_offset1(ref, blk, lx, dmin);
+ }
+
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SAD_INLINE_H_
+
diff --git a/media/libstagefright/codecs/avc/enc/src/sad_mb_offset.h b/media/libstagefright/codecs/avc/enc/src/sad_mb_offset.h
new file mode 100644
index 0000000..d5d4a42
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/sad_mb_offset.h
@@ -0,0 +1,311 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#if defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+#if (NUMBER==3)
+__inline int32 sad_mb_offset3(uint8 *ref, uint8 *blk, int lx, int dmin)
+#elif (NUMBER==2)
+__inline int32 sad_mb_offset2(uint8 *ref, uint8 *blk, int lx, int dmin)
+#elif (NUMBER==1)
+__inline int32 sad_mb_offset1(uint8 *ref, uint8 *blk, int lx, int dmin)
+#endif
+{
+ int32 x4, x5, x6, x8, x9, x10, x11, x12, x14;
+
+ // x5 = (x4<<8) - x4;
+ x4 = x5 = 0;
+ x6 = 0xFFFF00FF;
+ x9 = 0x80808080; /* const. */
+ ref -= NUMBER; /* bic ref, ref, #3 */
+ ref -= lx;
+ blk -= 16;
+ x8 = 16;
+
+#if (NUMBER==3)
+LOOP_SAD3:
+#elif (NUMBER==2)
+LOOP_SAD2:
+#elif (NUMBER==1)
+LOOP_SAD1:
+#endif
+ /****** process 8 pixels ******/
+ x10 = *((uint32*)(ref += lx)); /* D C B A */
+ x11 = *((uint32*)(ref + 4)); /* H G F E */
+ x12 = *((uint32*)(ref + 8)); /* L K J I */
+
+ x10 = ((uint32)x10 >> SHIFT); /* 0 0 0 D */
+ x10 = x10 | (x11 << (32 - SHIFT)); /* G F E D */
+ x11 = ((uint32)x11 >> SHIFT); /* 0 0 0 H */
+ x11 = x11 | (x12 << (32 - SHIFT)); /* K J I H */
+
+ x12 = *((uint32*)(blk += 16));
+ x14 = *((uint32*)(blk + 4));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****** process 8 pixels ******/
+ x10 = *((uint32*)(ref + 8)); /* D C B A */
+ x11 = *((uint32*)(ref + 12)); /* H G F E */
+ x12 = *((uint32*)(ref + 16)); /* L K J I */
+
+ x10 = ((uint32)x10 >> SHIFT); /* mvn x10, x10, lsr #24 = 0xFF 0xFF 0xFF ~D */
+ x10 = x10 | (x11 << (32 - SHIFT)); /* bic x10, x10, x11, lsl #8 = ~G ~F ~E ~D */
+ x11 = ((uint32)x11 >> SHIFT); /* 0xFF 0xFF 0xFF ~H */
+ x11 = x11 | (x12 << (32 - SHIFT)); /* ~K ~J ~I ~H */
+
+ x12 = *((uint32*)(blk + 8));
+ x14 = *((uint32*)(blk + 12));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixel(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixel(x10, x12, x9);
+
+ x5 = x5 + x10; /* accumulate low bytes */
+ x10 = x10 & (x6 << 8); /* x10 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x10 >> 8); /* accumulate high bytes */
+ x5 = x5 + x11; /* accumulate low bytes */
+ x11 = x11 & (x6 << 8); /* x11 & 0xFF00FF00 */
+ x4 = x4 + ((uint32)x11 >> 8); /* accumulate high bytes */
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ if ((int)((uint32)x10 >> 16) <= dmin) /* compare with dmin */
+ {
+ if (--x8)
+ {
+#if (NUMBER==3)
+ goto LOOP_SAD3;
+#elif (NUMBER==2)
+ goto LOOP_SAD2;
+#elif (NUMBER==1)
+ goto LOOP_SAD1;
+#endif
+ }
+
+ }
+
+ return ((uint32)x10 >> 16);
+}
+
+#elif defined(__CC_ARM) /* only work with arm v5 */
+
+#if (NUMBER==3)
+__inline int32 sad_mb_offset3(uint8 *ref, uint8 *blk, int lx, int dmin, int32 x8)
+#elif (NUMBER==2)
+__inline int32 sad_mb_offset2(uint8 *ref, uint8 *blk, int lx, int dmin, int32 x8)
+#elif (NUMBER==1)
+__inline int32 sad_mb_offset1(uint8 *ref, uint8 *blk, int lx, int dmin, int32 x8)
+#endif
+{
+ int32 x4, x5, x6, x9, x10, x11, x12, x14;
+
+ x9 = 0x80808080; /* const. */
+ x4 = x5 = 0;
+
+ __asm{
+ MVN x6, #0xff0000;
+#if (NUMBER==3)
+LOOP_SAD3:
+#elif (NUMBER==2)
+LOOP_SAD2:
+#elif (NUMBER==1)
+LOOP_SAD1:
+#endif
+ BIC ref, ref, #3;
+ }
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 12));
+ x12 = *((int32*)(ref + 16));
+ x10 = *((int32*)(ref + 8));
+ x14 = *((int32*)(blk + 12));
+
+ __asm{
+ MVN x10, x10, lsr #SHIFT;
+ BIC x10, x10, x11, lsl #(32-SHIFT);
+ MVN x11, x11, lsr #SHIFT;
+ BIC x11, x11, x12, lsl #(32-SHIFT);
+
+ LDR x12, [blk, #8];
+ }
+
+ /* process x11 & x14 */
+ x11 = sad_4pixelN(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixelN(x10, x12, x9);
+
+ sum_accumulate;
+
+ __asm{
+ /****** process 8 pixels ******/
+ LDR x11, [ref, #4];
+ LDR x12, [ref, #8];
+ LDR x10, [ref], lx ;
+ LDR x14, [blk, #4];
+
+ MVN x10, x10, lsr #SHIFT;
+ BIC x10, x10, x11, lsl #(32-SHIFT);
+ MVN x11, x11, lsr #SHIFT;
+ BIC x11, x11, x12, lsl #(32-SHIFT);
+
+ LDR x12, [blk], #16;
+ }
+
+ /* process x11 & x14 */
+ x11 = sad_4pixelN(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixelN(x10, x12, x9);
+
+ sum_accumulate;
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ __asm{
+ RSBS x11, dmin, x10, lsr #16
+ ADDLSS x8, x8, #INC_X8
+#if (NUMBER==3)
+ BLS LOOP_SAD3;
+#elif (NUMBER==2)
+BLS LOOP_SAD2;
+#elif (NUMBER==1)
+BLS LOOP_SAD1;
+#endif
+ }
+
+ return ((uint32)x10 >> 16);
+}
+
+#elif defined(__GNUC__) && defined(__arm__) /* ARM GNU COMPILER */
+
+#if (NUMBER==3)
+__inline int32 sad_mb_offset3(uint8 *ref, uint8 *blk, int lx, int dmin)
+#elif (NUMBER==2)
+__inline int32 sad_mb_offset2(uint8 *ref, uint8 *blk, int lx, int dmin)
+#elif (NUMBER==1)
+__inline int32 sad_mb_offset1(uint8 *ref, uint8 *blk, int lx, int dmin)
+#endif
+{
+ int32 x4, x5, x6, x8, x9, x10, x11, x12, x14;
+
+ x9 = 0x80808080; /* const. */
+ x4 = x5 = 0;
+ x8 = 16; //<<===========*******
+
+__asm__ volatile("MVN %0, #0xFF0000": "=r"(x6));
+
+#if (NUMBER==3)
+LOOP_SAD3:
+#elif (NUMBER==2)
+LOOP_SAD2:
+#elif (NUMBER==1)
+LOOP_SAD1:
+#endif
+__asm__ volatile("BIC %0, %0, #3": "=r"(ref));
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 12));
+ x12 = *((int32*)(ref + 16));
+ x10 = *((int32*)(ref + 8));
+ x14 = *((int32*)(blk + 12));
+
+#if (SHIFT==8)
+__asm__ volatile("MVN %0, %0, lsr #8\n\tBIC %0, %0, %1,lsl #24\n\tMVN %1, %1,lsr #8\n\tBIC %1, %1, %2,lsl #24": "=&r"(x10), "=&r"(x11): "r"(x12));
+#elif (SHIFT==16)
+__asm__ volatile("MVN %0, %0, lsr #16\n\tBIC %0, %0, %1,lsl #16\n\tMVN %1, %1,lsr #16\n\tBIC %1, %1, %2,lsl #16": "=&r"(x10), "=&r"(x11): "r"(x12));
+#elif (SHIFT==24)
+__asm__ volatile("MVN %0, %0, lsr #24\n\tBIC %0, %0, %1,lsl #8\n\tMVN %1, %1,lsr #24\n\tBIC %1, %1, %2,lsl #8": "=&r"(x10), "=&r"(x11): "r"(x12));
+#endif
+
+ x12 = *((int32*)(blk + 8));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixelN(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixelN(x10, x12, x9);
+
+ sum_accumulate;
+
+ /****** process 8 pixels ******/
+ x11 = *((int32*)(ref + 4));
+ x12 = *((int32*)(ref + 8));
+ x10 = *((int32*)ref); ref += lx;
+ x14 = *((int32*)(blk + 4));
+
+#if (SHIFT==8)
+__asm__ volatile("MVN %0, %0, lsr #8\n\tBIC %0, %0, %1,lsl #24\n\tMVN %1, %1,lsr #8\n\tBIC %1, %1, %2,lsl #24": "=&r"(x10), "=&r"(x11): "r"(x12));
+#elif (SHIFT==16)
+__asm__ volatile("MVN %0, %0, lsr #16\n\tBIC %0, %0, %1,lsl #16\n\tMVN %1, %1,lsr #16\n\tBIC %1, %1, %2,lsl #16": "=&r"(x10), "=&r"(x11): "r"(x12));
+#elif (SHIFT==24)
+__asm__ volatile("MVN %0, %0, lsr #24\n\tBIC %0, %0, %1,lsl #8\n\tMVN %1, %1,lsr #24\n\tBIC %1, %1, %2,lsl #8": "=&r"(x10), "=&r"(x11): "r"(x12));
+#endif
+__asm__ volatile("LDR %0, [%1], #16": "=&r"(x12), "=r"(blk));
+
+ /* process x11 & x14 */
+ x11 = sad_4pixelN(x11, x14, x9);
+
+ /* process x12 & x10 */
+ x10 = sad_4pixelN(x10, x12, x9);
+
+ sum_accumulate;
+
+ /****************/
+ x10 = x5 - (x4 << 8); /* extract low bytes */
+ x10 = x10 + x4; /* add with high bytes */
+ x10 = x10 + (x10 << 16); /* add with lower half word */
+
+ if (((uint32)x10 >> 16) <= (uint32)dmin) /* compare with dmin */
+ {
+ if (--x8)
+ {
+#if (NUMBER==3)
+ goto LOOP_SAD3;
+#elif (NUMBER==2)
+goto LOOP_SAD2;
+#elif (NUMBER==1)
+goto LOOP_SAD1;
+#endif
+ }
+
+ }
+
+ return ((uint32)x10 >> 16);
+}
+
+#endif
+
diff --git a/media/libstagefright/codecs/avc/enc/src/slice.cpp b/media/libstagefright/codecs/avc/enc/src/slice.cpp
new file mode 100644
index 0000000..f6d066e
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/slice.cpp
@@ -0,0 +1,1025 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+
+AVCEnc_Status AVCEncodeSlice(AVCEncObject *encvid)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ AVCCommonObj *video = encvid->common;
+ AVCPicParamSet *pps = video->currPicParams;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ AVCMacroblock *currMB ;
+ AVCEncBitstream *stream = encvid->bitstream;
+ uint slice_group_id;
+ int CurrMbAddr, slice_type;
+
+ slice_type = video->slice_type;
+
+ /* set the first mb in slice */
+ video->mbNum = CurrMbAddr = sliceHdr->first_mb_in_slice;// * (1+video->MbaffFrameFlag);
+ slice_group_id = video->MbToSliceGroupMap[CurrMbAddr];
+
+ video->mb_skip_run = 0;
+
+ /* while loop , see subclause 7.3.4 */
+ while (1)
+ {
+ video->mbNum = CurrMbAddr;
+ currMB = video->currMB = &(video->mblock[CurrMbAddr]);
+ currMB->slice_id = video->slice_id; // for deblocking
+
+ video->mb_x = CurrMbAddr % video->PicWidthInMbs;
+ video->mb_y = CurrMbAddr / video->PicWidthInMbs;
+
+ /* initialize QP for this MB here*/
+ /* calculate currMB->QPy */
+ RCInitMBQP(encvid);
+
+ /* check the availability of neighboring macroblocks */
+ InitNeighborAvailability(video, CurrMbAddr);
+
+ /* Assuming that InitNeighborAvailability has been called prior to this function */
+ video->intraAvailA = video->intraAvailB = video->intraAvailC = video->intraAvailD = 0;
+ /* this is necessary for all subsequent intra search */
+
+ if (!video->currPicParams->constrained_intra_pred_flag)
+ {
+ video->intraAvailA = video->mbAvailA;
+ video->intraAvailB = video->mbAvailB;
+ video->intraAvailC = video->mbAvailC;
+ video->intraAvailD = video->mbAvailD;
+ }
+ else
+ {
+ if (video->mbAvailA)
+ {
+ video->intraAvailA = video->mblock[video->mbAddrA].mb_intra;
+ }
+ if (video->mbAvailB)
+ {
+ video->intraAvailB = video->mblock[video->mbAddrB].mb_intra ;
+ }
+ if (video->mbAvailC)
+ {
+ video->intraAvailC = video->mblock[video->mbAddrC].mb_intra;
+ }
+ if (video->mbAvailD)
+ {
+ video->intraAvailD = video->mblock[video->mbAddrD].mb_intra;
+ }
+ }
+
+ /* encode_one_macroblock() */
+ status = EncodeMB(encvid);
+ if (status != AVCENC_SUCCESS)
+ {
+ break;
+ }
+
+ /* go to next MB */
+ CurrMbAddr++;
+
+ while ((uint)video->MbToSliceGroupMap[CurrMbAddr] != slice_group_id &&
+ (uint)CurrMbAddr < video->PicSizeInMbs)
+ {
+ CurrMbAddr++;
+ }
+
+ if ((uint)CurrMbAddr >= video->PicSizeInMbs)
+ {
+ /* end of slice, return, but before that check to see if there are other slices
+ to be encoded. */
+ encvid->currSliceGroup++;
+ if (encvid->currSliceGroup > (int)pps->num_slice_groups_minus1) /* no more slice group */
+ {
+ status = AVCENC_PICTURE_READY;
+ break;
+ }
+ else
+ {
+ /* find first_mb_num for the next slice */
+ CurrMbAddr = 0;
+ while (video->MbToSliceGroupMap[CurrMbAddr] != encvid->currSliceGroup &&
+ (uint)CurrMbAddr < video->PicSizeInMbs)
+ {
+ CurrMbAddr++;
+ }
+ if ((uint)CurrMbAddr >= video->PicSizeInMbs)
+ {
+ status = AVCENC_SLICE_EMPTY; /* error, one slice group has no MBs in it */
+ }
+
+ video->mbNum = CurrMbAddr;
+ status = AVCENC_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ if (video->mb_skip_run > 0)
+ {
+ /* write skip_run */
+ if (slice_type != AVC_I_SLICE && slice_type != AVC_SI_SLICE)
+ {
+ ue_v(stream, video->mb_skip_run);
+ video->mb_skip_run = 0;
+ }
+ else /* shouldn't happen */
+ {
+ status = AVCENC_FAIL;
+ }
+ }
+
+ return status;
+}
+
+
+AVCEnc_Status EncodeMB(AVCEncObject *encvid)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ AVCCommonObj *video = encvid->common;
+ AVCPictureData *currPic = video->currPic;
+ AVCFrameIO *currInput = encvid->currInput;
+ AVCMacroblock *currMB = video->currMB;
+ AVCMacroblock *MB_A, *MB_B;
+ AVCEncBitstream *stream = encvid->bitstream;
+ AVCRateControl *rateCtrl = encvid->rateCtrl;
+ uint8 *cur, *curL, *curCb, *curCr;
+ uint8 *orgL, *orgCb, *orgCr, *org4;
+ int CurrMbAddr = video->mbNum;
+ int picPitch = currPic->pitch;
+ int orgPitch = currInput->pitch;
+ int x_position = (video->mb_x << 4);
+ int y_position = (video->mb_y << 4);
+ int offset;
+ int b8, b4, blkidx;
+ AVCResidualType resType;
+ int slice_type;
+ int numcoeff; /* output from residual_block_cavlc */
+ int cost16, cost8;
+
+ int num_bits, start_mb_bits, start_text_bits;
+
+ slice_type = video->slice_type;
+
+ /* now, point to the reconstructed frame */
+ offset = y_position * picPitch + x_position;
+ curL = currPic->Sl + offset;
+ orgL = currInput->YCbCr[0] + offset;
+ offset = (offset + x_position) >> 2;
+ curCb = currPic->Scb + offset;
+ curCr = currPic->Scr + offset;
+ orgCb = currInput->YCbCr[1] + offset;
+ orgCr = currInput->YCbCr[2] + offset;
+
+ if (orgPitch != picPitch)
+ {
+ offset = y_position * (orgPitch - picPitch);
+ orgL += offset;
+ offset >>= 2;
+ orgCb += offset;
+ orgCr += offset;
+ }
+
+ /******* determine MB prediction mode *******/
+ if (encvid->intraSearch[CurrMbAddr])
+ {
+ MBIntraSearch(encvid, CurrMbAddr, curL, picPitch);
+ }
+ /******* This part should be determined somehow ***************/
+ if (currMB->mbMode == AVC_I_PCM)
+ {
+ /* write down mb_type and PCM data */
+ /* and copy from currInput to currPic */
+ status = EncodeIntraPCM(encvid);
+
+
+ return status;
+ }
+
+ /****** for intra prediction, pred is already done *******/
+ /****** for I4, the recon is ready and Xfrm coefs are ready to be encoded *****/
+
+ //RCCalculateMAD(encvid,currMB,orgL,orgPitch); // no need to re-calculate MAD for Intra
+ // not used since totalSAD is used instead
+
+ /* compute the prediction */
+ /* output is video->pred_block */
+ if (!currMB->mb_intra)
+ {
+ AVCMBMotionComp(encvid, video); /* perform prediction and residue calculation */
+ /* we can do the loop here and call dct_luma */
+ video->pred_pitch = picPitch;
+ currMB->CBP = 0;
+ cost16 = 0;
+ cur = curL;
+ org4 = orgL;
+
+ for (b8 = 0; b8 < 4; b8++)
+ {
+ cost8 = 0;
+
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ blkidx = blkIdx2blkXY[b8][b4];
+ video->pred_block = cur;
+ numcoeff = dct_luma(encvid, blkidx, cur, org4, &cost8);
+ currMB->nz_coeff[blkidx] = numcoeff;
+ if (numcoeff)
+ {
+ video->cbp4x4 |= (1 << blkidx);
+ currMB->CBP |= (1 << b8);
+ }
+
+ if (b4&1)
+ {
+ cur += ((picPitch << 2) - 4);
+ org4 += ((orgPitch << 2) - 4);
+ }
+ else
+ {
+ cur += 4;
+ org4 += 4;
+ }
+ }
+
+ /* move the IDCT part out of dct_luma to accommodate the check
+ for coeff_cost. */
+
+ if ((currMB->CBP&(1 << b8)) && (cost8 <= _LUMA_COEFF_COST_))
+ {
+ cost8 = 0; // reset it
+
+ currMB->CBP ^= (1 << b8);
+ blkidx = blkIdx2blkXY[b8][0];
+
+ currMB->nz_coeff[blkidx] = 0;
+ currMB->nz_coeff[blkidx+1] = 0;
+ currMB->nz_coeff[blkidx+4] = 0;
+ currMB->nz_coeff[blkidx+5] = 0;
+ }
+
+ cost16 += cost8;
+
+ if (b8&1)
+ {
+ cur -= 8;
+ org4 -= 8;
+ }
+ else
+ {
+ cur += (8 - (picPitch << 3));
+ org4 += (8 - (orgPitch << 3));
+ }
+ }
+
+ /* after the whole MB, we do another check for coeff_cost */
+ if ((currMB->CBP&0xF) && (cost16 <= _LUMA_MB_COEFF_COST_))
+ {
+ currMB->CBP = 0; // reset it to zero
+ memset(currMB->nz_coeff, 0, sizeof(uint8)*16);
+ }
+
+ // now we do IDCT
+ MBInterIdct(video, curL, currMB, picPitch);
+
+// video->pred_block = video->pred + 256;
+ }
+ else /* Intra prediction */
+ {
+ encvid->numIntraMB++;
+
+ if (currMB->mbMode == AVC_I16) /* do prediction for the whole macroblock */
+ {
+ currMB->CBP = 0;
+ /* get the prediction from encvid->pred_i16 */
+ dct_luma_16x16(encvid, curL, orgL);
+ }
+ video->pred_block = encvid->pred_ic[currMB->intra_chroma_pred_mode];
+ }
+
+ /* chrominance */
+ /* not need to do anything, the result is in encvid->pred_ic
+ chroma dct must be aware that prediction block can come from either intra or inter. */
+
+ dct_chroma(encvid, curCb, orgCb, 0);
+
+ dct_chroma(encvid, curCr, orgCr, 1);
+
+
+ /* 4.1 if there's nothing in there, video->mb_skip_run++ */
+ /* 4.2 if coded, check if there is a run of skipped MB, encodes it,
+ set video->QPyprev = currMB->QPy; */
+
+ /* 5. vlc encode */
+
+ /* check for skipped macroblock, INTER only */
+ if (!currMB->mb_intra)
+ {
+ /* decide whether this MB (for inter MB) should be skipped if there's nothing left. */
+ if (!currMB->CBP && currMB->NumMbPart == 1 && currMB->QPy == video->QPy)
+ {
+ if (currMB->MBPartPredMode[0][0] == AVC_Pred_L0 && currMB->ref_idx_L0[0] == 0)
+ {
+ MB_A = &video->mblock[video->mbAddrA];
+ MB_B = &video->mblock[video->mbAddrB];
+
+ if (!video->mbAvailA || !video->mbAvailB)
+ {
+ if (currMB->mvL0[0] == 0) /* both mv components are zeros.*/
+ {
+ currMB->mbMode = AVC_SKIP;
+ video->mvd_l0[0][0][0] = 0;
+ video->mvd_l0[0][0][1] = 0;
+ }
+ }
+ else
+ {
+ if ((MB_A->ref_idx_L0[1] == 0 && MB_A->mvL0[3] == 0) ||
+ (MB_B->ref_idx_L0[2] == 0 && MB_B->mvL0[12] == 0))
+ {
+ if (currMB->mvL0[0] == 0) /* both mv components are zeros.*/
+ {
+ currMB->mbMode = AVC_SKIP;
+ video->mvd_l0[0][0][0] = 0;
+ video->mvd_l0[0][0][1] = 0;
+ }
+ }
+ else if (video->mvd_l0[0][0][0] == 0 && video->mvd_l0[0][0][1] == 0)
+ {
+ currMB->mbMode = AVC_SKIP;
+ }
+ }
+ }
+
+ if (currMB->mbMode == AVC_SKIP)
+ {
+ video->mb_skip_run++;
+
+ /* set parameters */
+ /* not sure whether we need the followings */
+ if (slice_type == AVC_P_SLICE)
+ {
+ currMB->mbMode = AVC_SKIP;
+ currMB->MbPartWidth = currMB->MbPartHeight = 16;
+ currMB->MBPartPredMode[0][0] = AVC_Pred_L0;
+ currMB->NumMbPart = 1;
+ currMB->NumSubMbPart[0] = currMB->NumSubMbPart[1] =
+ currMB->NumSubMbPart[2] = currMB->NumSubMbPart[3] = 1;
+ currMB->SubMbPartWidth[0] = currMB->SubMbPartWidth[1] =
+ currMB->SubMbPartWidth[2] = currMB->SubMbPartWidth[3] = currMB->MbPartWidth;
+ currMB->SubMbPartHeight[0] = currMB->SubMbPartHeight[1] =
+ currMB->SubMbPartHeight[2] = currMB->SubMbPartHeight[3] = currMB->MbPartHeight;
+
+ }
+ else if (slice_type == AVC_B_SLICE)
+ {
+ currMB->mbMode = AVC_SKIP;
+ currMB->MbPartWidth = currMB->MbPartHeight = 8;
+ currMB->MBPartPredMode[0][0] = AVC_Direct;
+ currMB->NumMbPart = -1;
+ }
+
+ /* for skipped MB, always look at the first entry in RefPicList */
+ currMB->RefIdx[0] = currMB->RefIdx[1] =
+ currMB->RefIdx[2] = currMB->RefIdx[3] = video->RefPicList0[0]->RefIdx;
+
+ /* do not return yet, need to do some copies */
+ }
+ }
+ }
+ /* non-skipped MB */
+
+
+ /************* START ENTROPY CODING *************************/
+
+ start_mb_bits = 32 + (encvid->bitstream->write_pos << 3) - encvid->bitstream->bit_left;
+
+ /* encode mb_type, mb_pred, sub_mb_pred, CBP */
+ if (slice_type != AVC_I_SLICE && slice_type != AVC_SI_SLICE && currMB->mbMode != AVC_SKIP)
+ {
+ //if(!pps->entropy_coding_mode_flag) ALWAYS true
+ {
+ ue_v(stream, video->mb_skip_run);
+ video->mb_skip_run = 0;
+ }
+ }
+
+ if (currMB->mbMode != AVC_SKIP)
+ {
+ status = EncodeMBHeader(currMB, encvid);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+
+ start_text_bits = 32 + (encvid->bitstream->write_pos << 3) - encvid->bitstream->bit_left;
+
+ /**** now decoding part *******/
+ resType = AVC_Luma;
+
+ /* DC transform for luma I16 mode */
+ if (currMB->mbMode == AVC_I16)
+ {
+ /* vlc encode level/run */
+ status = enc_residual_block(encvid, AVC_Intra16DC, encvid->numcoefdc, currMB);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ resType = AVC_Intra16AC;
+ }
+
+ /* VLC encoding for luma */
+ for (b8 = 0; b8 < 4; b8++)
+ {
+ if (currMB->CBP&(1 << b8))
+ {
+ for (b4 = 0; b4 < 4; b4++)
+ {
+ /* vlc encode level/run */
+ status = enc_residual_block(encvid, resType, (b8 << 2) + b4, currMB);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+ }
+ }
+
+ /* chroma */
+ if (currMB->CBP & (3 << 4)) /* chroma DC residual present */
+ {
+ for (b8 = 0; b8 < 2; b8++) /* for iCbCr */
+ {
+ /* vlc encode level/run */
+ status = enc_residual_block(encvid, AVC_ChromaDC, encvid->numcoefcdc[b8] + (b8 << 3), currMB);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+ }
+
+ if (currMB->CBP & (2 << 4))
+ {
+ /* AC part */
+ for (b8 = 0; b8 < 2; b8++) /* for iCbCr */
+ {
+ for (b4 = 0; b4 < 4; b4++) /* for each block inside Cb or Cr */
+ {
+ /* vlc encode level/run */
+ status = enc_residual_block(encvid, AVC_ChromaAC, 16 + (b8 << 2) + b4, currMB);
+ if (status != AVCENC_SUCCESS)
+ {
+ return status;
+ }
+ }
+ }
+ }
+
+
+ num_bits = 32 + (encvid->bitstream->write_pos << 3) - encvid->bitstream->bit_left;
+
+ RCPostMB(video, rateCtrl, start_text_bits - start_mb_bits,
+ num_bits - start_text_bits);
+
+// num_bits -= start_mb_bits;
+// fprintf(fdebug,"MB #%d: %d bits\n",CurrMbAddr,num_bits);
+// fclose(fdebug);
+ return status;
+}
+
+/* copy the content from predBlock back to the reconstructed YUV frame */
+void Copy_MB(uint8 *curL, uint8 *curCb, uint8 *curCr, uint8 *predBlock, int picPitch)
+{
+ int j, offset;
+ uint32 *dst, *dst2, *src;
+
+ dst = (uint32*)curL;
+ src = (uint32*)predBlock;
+
+ offset = (picPitch - 16) >> 2;
+
+ for (j = 0; j < 16; j++)
+ {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+
+ dst += offset;
+ }
+
+ dst = (uint32*)curCb;
+ dst2 = (uint32*)curCr;
+ offset >>= 1;
+
+ for (j = 0; j < 8; j++)
+ {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst2++ = *src++;
+ *dst2++ = *src++;
+
+ dst += offset;
+ dst2 += offset;
+ }
+ return ;
+}
+
+/* encode mb_type, mb_pred, sub_mb_pred, CBP */
+/* decide whether this MB (for inter MB) should be skipped */
+AVCEnc_Status EncodeMBHeader(AVCMacroblock *currMB, AVCEncObject *encvid)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ uint mb_type;
+ AVCCommonObj *video = encvid->common;
+ AVCEncBitstream *stream = encvid->bitstream;
+
+ if (currMB->CBP > 47) /* chroma CBP is 11 */
+ {
+ currMB->CBP -= 16; /* remove the 5th bit from the right */
+ }
+
+ mb_type = InterpretMBType(currMB, video->slice_type);
+
+ status = ue_v(stream, mb_type);
+
+ if (currMB->mbMode == AVC_P8 || currMB->mbMode == AVC_P8ref0)
+ {
+ status = sub_mb_pred(video, currMB, stream);
+ }
+ else
+ {
+ status = mb_pred(video, currMB, stream) ;
+ }
+
+ if (currMB->mbMode != AVC_I16)
+ {
+ /* decode coded_block_pattern */
+ status = EncodeCBP(currMB, stream);
+ }
+
+ /* calculate currMB->mb_qp_delta = currMB->QPy - video->QPyprev */
+ if (currMB->CBP > 0 || currMB->mbMode == AVC_I16)
+ {
+ status = se_v(stream, currMB->QPy - video->QPy);
+ video->QPy = currMB->QPy; /* = (video->QPyprev + currMB->mb_qp_delta + 52)%52; */
+ // no need video->QPc = currMB->QPc;
+ }
+ else
+ {
+ if (currMB->QPy != video->QPy) // current QP is not the same as previous QP
+ {
+ /* restore these values */
+ RCRestoreQP(currMB, video, encvid);
+ }
+ }
+
+ return status;
+}
+
+
+/* inputs are mbMode, mb_intra, i16Mode, CBP, NumMbPart, MbPartWidth, MbPartHeight */
+uint InterpretMBType(AVCMacroblock *currMB, int slice_type)
+{
+ int CBP_chrom;
+ int mb_type;// part1, part2, part3;
+// const static int MapParts2Type[2][3][3]={{{4,8,12},{10,6,14},{16,18,20}},
+// {{5,9,13},{11,7,15},{17,19,21}}};
+
+ if (currMB->mb_intra)
+ {
+ if (currMB->mbMode == AVC_I4)
+ {
+ mb_type = 0;
+ }
+ else if (currMB->mbMode == AVC_I16)
+ {
+ CBP_chrom = (currMB->CBP & 0x30);
+ if (currMB->CBP&0xF)
+ {
+ currMB->CBP |= 0xF; /* either 0x0 or 0xF */
+ mb_type = 13;
+ }
+ else
+ {
+ mb_type = 1;
+ }
+ mb_type += (CBP_chrom >> 2) + currMB->i16Mode;
+ }
+ else /* if(currMB->mbMode == AVC_I_PCM) */
+ {
+ mb_type = 25;
+ }
+ }
+ else
+ { /* P-MB *//* note that the order of the enum AVCMBMode cannot be changed
+ since we use it here. */
+ mb_type = currMB->mbMode - AVC_P16;
+ }
+
+ if (slice_type == AVC_P_SLICE)
+ {
+ if (currMB->mb_intra)
+ {
+ mb_type += 5;
+ }
+ }
+ // following codes have not been tested yet, not needed.
+ /* else if(slice_type == AVC_B_SLICE)
+ {
+ if(currMB->mbMode == AVC_BDirect16)
+ {
+ mb_type = 0;
+ }
+ else if(currMB->mbMode == AVC_P16)
+ {
+ mb_type = currMB->MBPartPredMode[0][0] + 1; // 1 or 2
+ }
+ else if(currMB->mbMode == AVC_P8)
+ {
+ mb_type = 26;
+ }
+ else if(currMB->mbMode == AVC_P8ref0)
+ {
+ mb_type = 27;
+ }
+ else
+ {
+ part1 = currMB->mbMode - AVC_P16x8;
+ part2 = currMB->MBPartPredMode[0][0];
+ part3 = currMB->MBPartPredMode[1][0];
+ mb_type = MapParts2Type[part1][part2][part3];
+ }
+ }
+
+ if(slice_type == AVC_SI_SLICE)
+ {
+ mb_type++;
+ }
+ */
+ return (uint)mb_type;
+}
+
+//const static int mbPart2raster[3][4] = {{0,0,0,0},{1,1,0,0},{1,0,1,0}};
+
+/* see subclause 7.3.5.1 */
+AVCEnc_Status mb_pred(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ int mbPartIdx;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ int max_ref_idx;
+ uint code;
+
+ if (currMB->mbMode == AVC_I4 || currMB->mbMode == AVC_I16)
+ {
+ if (currMB->mbMode == AVC_I4)
+ {
+ /* perform prediction to get the actual intra 4x4 pred mode */
+ EncodeIntra4x4Mode(video, currMB, stream);
+ /* output will be in currMB->i4Mode[4][4] */
+ }
+
+ /* assume already set from MBPrediction() */
+ status = ue_v(stream, currMB->intra_chroma_pred_mode);
+ }
+ else if (currMB->MBPartPredMode[0][0] != AVC_Direct)
+ {
+
+ memset(currMB->ref_idx_L0, 0, sizeof(int16)*4);
+
+ /* see subclause 7.4.5.1 for the range of ref_idx_lX */
+ max_ref_idx = sliceHdr->num_ref_idx_l0_active_minus1;
+ /* if(video->MbaffFrameFlag && currMB->mb_field_decoding_flag)
+ max_ref_idx = 2*sliceHdr->num_ref_idx_l0_active_minus1 + 1;
+ */
+ /* decode ref index for L0 */
+ if (sliceHdr->num_ref_idx_l0_active_minus1 > 0)
+ {
+ for (mbPartIdx = 0; mbPartIdx < currMB->NumMbPart; mbPartIdx++)
+ {
+ if (/*(sliceHdr->num_ref_idx_l0_active_minus1>0 || currMB->mb_field_decoding_flag) &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L1)
+ {
+ code = currMB->ref_idx_L0[mbPartIdx];
+ status = te_v(stream, code, max_ref_idx);
+ }
+ }
+ }
+
+ /* see subclause 7.4.5.1 for the range of ref_idx_lX */
+ max_ref_idx = sliceHdr->num_ref_idx_l1_active_minus1;
+ /* if(video->MbaffFrameFlag && currMB->mb_field_decoding_flag)
+ max_ref_idx = 2*sliceHdr->num_ref_idx_l1_active_minus1 + 1;
+ */
+ /* decode ref index for L1 */
+ if (sliceHdr->num_ref_idx_l1_active_minus1 > 0)
+ {
+ for (mbPartIdx = 0; mbPartIdx < currMB->NumMbPart; mbPartIdx++)
+ {
+ if (/*(sliceHdr->num_ref_idx_l1_active_minus1>0 || currMB->mb_field_decoding_flag) &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L0)
+ {
+ status = te_v(stream, currMB->ref_idx_L1[mbPartIdx], max_ref_idx);
+ }
+ }
+ }
+
+ /* encode mvd_l0 */
+ for (mbPartIdx = 0; mbPartIdx < currMB->NumMbPart; mbPartIdx++)
+ {
+ if (currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L1)
+ {
+ status = se_v(stream, video->mvd_l0[mbPartIdx][0][0]);
+ status = se_v(stream, video->mvd_l0[mbPartIdx][0][1]);
+ }
+ }
+ /* encode mvd_l1 */
+ for (mbPartIdx = 0; mbPartIdx < currMB->NumMbPart; mbPartIdx++)
+ {
+ if (currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L0)
+ {
+ status = se_v(stream, video->mvd_l1[mbPartIdx][0][0]);
+ status = se_v(stream, video->mvd_l1[mbPartIdx][0][1]);
+ }
+ }
+ }
+
+ return status;
+}
+
+/* see subclause 7.3.5.2 */
+AVCEnc_Status sub_mb_pred(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream)
+{
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ int mbPartIdx, subMbPartIdx;
+ AVCSliceHeader *sliceHdr = video->sliceHdr;
+ uint max_ref_idx;
+ uint slice_type = video->slice_type;
+ uint sub_mb_type[4];
+
+ /* this should move somewhere else where we don't have to make this check */
+ if (currMB->mbMode == AVC_P8ref0)
+ {
+ memset(currMB->ref_idx_L0, 0, sizeof(int16)*4);
+ }
+
+ /* we have to check the values to make sure they are valid */
+ /* assign values to currMB->sub_mb_type[] */
+ if (slice_type == AVC_P_SLICE)
+ {
+ InterpretSubMBTypeP(currMB, sub_mb_type);
+ }
+ /* no need to check for B-slice
+ else if(slice_type == AVC_B_SLICE)
+ {
+ InterpretSubMBTypeB(currMB,sub_mb_type);
+ }*/
+
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ status = ue_v(stream, sub_mb_type[mbPartIdx]);
+ }
+
+ /* see subclause 7.4.5.1 for the range of ref_idx_lX */
+ max_ref_idx = sliceHdr->num_ref_idx_l0_active_minus1;
+ /* if(video->MbaffFrameFlag && currMB->mb_field_decoding_flag)
+ max_ref_idx = 2*sliceHdr->num_ref_idx_l0_active_minus1 + 1; */
+
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ if ((sliceHdr->num_ref_idx_l0_active_minus1 > 0 /*|| currMB->mb_field_decoding_flag*/) &&
+ currMB->mbMode != AVC_P8ref0 && /*currMB->subMbMode[mbPartIdx]!=AVC_BDirect8 &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L1)
+ {
+ status = te_v(stream, currMB->ref_idx_L0[mbPartIdx], max_ref_idx);
+ }
+ /* used in deblocking */
+ currMB->RefIdx[mbPartIdx] = video->RefPicList0[currMB->ref_idx_L0[mbPartIdx]]->RefIdx;
+ }
+ /* see subclause 7.4.5.1 for the range of ref_idx_lX */
+ max_ref_idx = sliceHdr->num_ref_idx_l1_active_minus1;
+ /* if(video->MbaffFrameFlag && currMB->mb_field_decoding_flag)
+ max_ref_idx = 2*sliceHdr->num_ref_idx_l1_active_minus1 + 1;*/
+
+ if (sliceHdr->num_ref_idx_l1_active_minus1 > 0)
+ {
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ if (/*(sliceHdr->num_ref_idx_l1_active_minus1>0 || currMB->mb_field_decoding_flag) &&*/
+ /*currMB->subMbMode[mbPartIdx]!=AVC_BDirect8 &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L0)
+ {
+ status = te_v(stream, currMB->ref_idx_L1[mbPartIdx], max_ref_idx);
+ }
+ }
+ }
+
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ if (/*currMB->subMbMode[mbPartIdx]!=AVC_BDirect8 &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L1)
+ {
+ for (subMbPartIdx = 0; subMbPartIdx < currMB->NumSubMbPart[mbPartIdx]; subMbPartIdx++)
+ {
+ status = se_v(stream, video->mvd_l0[mbPartIdx][subMbPartIdx][0]);
+ status = se_v(stream, video->mvd_l0[mbPartIdx][subMbPartIdx][1]);
+ }
+ }
+ }
+
+ for (mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++)
+ {
+ if (/*currMB->subMbMode[mbPartIdx]!=AVC_BDirect8 &&*/
+ currMB->MBPartPredMode[mbPartIdx][0] != AVC_Pred_L0)
+ {
+ for (subMbPartIdx = 0; subMbPartIdx < currMB->NumSubMbPart[mbPartIdx]; subMbPartIdx++)
+ {
+ status = se_v(stream, video->mvd_l1[mbPartIdx][subMbPartIdx][0]);
+ status = se_v(stream, video->mvd_l1[mbPartIdx][subMbPartIdx][1]);
+ }
+ }
+ }
+
+ return status;
+}
+
+/* input is mblock->sub_mb_type[] */
+void InterpretSubMBTypeP(AVCMacroblock *mblock, uint *sub_mb_type)
+{
+ int i;
+ /* see enum AVCMBType declaration */
+ /*const static AVCSubMBMode map2subMbMode[4] = {AVC_8x8,AVC_8x4,AVC_4x8,AVC_4x4};
+ const static int map2subPartWidth[4] = {8,8,4,4};
+ const static int map2subPartHeight[4] = {8,4,8,4};
+ const static int map2numSubPart[4] = {1,2,2,4};*/
+
+ for (i = 0; i < 4 ; i++)
+ {
+ sub_mb_type[i] = mblock->subMbMode[i] - AVC_8x8;
+ }
+
+ return ;
+}
+
+void InterpretSubMBTypeB(AVCMacroblock *mblock, uint *sub_mb_type)
+{
+ int i;
+ /* see enum AVCMBType declaration */
+ /* const static AVCSubMBMode map2subMbMode[13] = {AVC_BDirect8,AVC_8x8,AVC_8x8,
+ AVC_8x8,AVC_8x4,AVC_4x8,AVC_8x4,AVC_4x8,AVC_8x4,AVC_4x8,AVC_4x4,AVC_4x4,AVC_4x4};
+ const static int map2subPartWidth[13] = {4,8,8,8,8,4,8,4,8,4,4,4,4};
+ const static int map2subPartHeight[13] = {4,8,8,8,4,8,4,8,4,8,4,4,4};
+ const static int map2numSubPart[13] = {4,1,1,1,2,2,2,2,2,2,4,4,4};
+ const static int map2predMode[13] = {3,0,1,2,0,0,1,1,2,2,0,1,2};*/
+
+ for (i = 0; i < 4 ; i++)
+ {
+ if (mblock->subMbMode[i] == AVC_BDirect8)
+ {
+ sub_mb_type[i] = 0;
+ }
+ else if (mblock->subMbMode[i] == AVC_8x8)
+ {
+ sub_mb_type[i] = 1 + mblock->MBPartPredMode[i][0];
+ }
+ else if (mblock->subMbMode[i] == AVC_4x4)
+ {
+ sub_mb_type[i] = 10 + mblock->MBPartPredMode[i][0];
+ }
+ else
+ {
+ sub_mb_type[i] = 4 + (mblock->MBPartPredMode[i][0] << 1) + (mblock->subMbMode[i] - AVC_8x4);
+ }
+ }
+
+ return ;
+}
+
+/* see subclause 8.3.1 */
+AVCEnc_Status EncodeIntra4x4Mode(AVCCommonObj *video, AVCMacroblock *currMB, AVCEncBitstream *stream)
+{
+ int intra4x4PredModeA = 0;
+ int intra4x4PredModeB, predIntra4x4PredMode;
+ int component, SubBlock_indx, block_x, block_y;
+ int dcOnlyPredictionFlag;
+ uint flag;
+ int rem = 0;
+ int mode;
+ int bindx = 0;
+
+ for (component = 0; component < 4; component++) /* partition index */
+ {
+ block_x = ((component & 1) << 1);
+ block_y = ((component >> 1) << 1);
+
+ for (SubBlock_indx = 0; SubBlock_indx < 4; SubBlock_indx++) /* sub-partition index */
+ {
+ dcOnlyPredictionFlag = 0;
+ if (block_x > 0)
+ {
+ intra4x4PredModeA = currMB->i4Mode[(block_y << 2) + block_x - 1 ];
+ }
+ else
+ {
+ if (video->intraAvailA)
+ {
+ if (video->mblock[video->mbAddrA].mbMode == AVC_I4)
+ {
+ intra4x4PredModeA = video->mblock[video->mbAddrA].i4Mode[(block_y << 2) + 3];
+ }
+ else
+ {
+ intra4x4PredModeA = AVC_I4_DC;
+ }
+ }
+ else
+ {
+ dcOnlyPredictionFlag = 1;
+ }
+ }
+
+ if (block_y > 0)
+ {
+ intra4x4PredModeB = currMB->i4Mode[((block_y-1) << 2) + block_x];
+ }
+ else
+ {
+ if (video->intraAvailB)
+ {
+ if (video->mblock[video->mbAddrB].mbMode == AVC_I4)
+ {
+ intra4x4PredModeB = video->mblock[video->mbAddrB].i4Mode[(3 << 2) + block_x];
+ }
+ else
+ {
+ intra4x4PredModeB = AVC_I4_DC;
+ }
+ }
+ else
+ {
+ dcOnlyPredictionFlag = 1;
+ }
+ }
+
+ if (dcOnlyPredictionFlag)
+ {
+ intra4x4PredModeA = intra4x4PredModeB = AVC_I4_DC;
+ }
+
+ predIntra4x4PredMode = AVC_MIN(intra4x4PredModeA, intra4x4PredModeB);
+
+ flag = 0;
+ mode = currMB->i4Mode[(block_y<<2)+block_x];
+
+ if (mode == (AVCIntra4x4PredMode)predIntra4x4PredMode)
+ {
+ flag = 1;
+ }
+ else if (mode < predIntra4x4PredMode)
+ {
+ rem = mode;
+ }
+ else
+ {
+ rem = mode - 1;
+ }
+
+ BitstreamWrite1Bit(stream, flag);
+
+ if (!flag)
+ {
+ BitstreamWriteBits(stream, 3, rem);
+ }
+
+ bindx++;
+ block_y += (SubBlock_indx & 1) ;
+ block_x += (1 - 2 * (SubBlock_indx & 1)) ;
+ }
+ }
+
+ return AVCENC_SUCCESS;
+}
+
+
+
diff --git a/media/libstagefright/codecs/avc/enc/src/vlc_encode.cpp b/media/libstagefright/codecs/avc/enc/src/vlc_encode.cpp
new file mode 100644
index 0000000..222e709
--- /dev/null
+++ b/media/libstagefright/codecs/avc/enc/src/vlc_encode.cpp
@@ -0,0 +1,336 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 1998-2009 PacketVideo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "avcenc_lib.h"
+
+/**
+See algorithm in subclause 9.1, Table 9-1, Table 9-2. */
+AVCEnc_Status ue_v(AVCEncBitstream *bitstream, uint codeNum)
+{
+ if (AVCENC_SUCCESS != SetEGBitstring(bitstream, codeNum))
+ return AVCENC_FAIL;
+
+ return AVCENC_SUCCESS;
+}
+
+/**
+See subclause 9.1.1, Table 9-3 */
+AVCEnc_Status se_v(AVCEncBitstream *bitstream, int value)
+{
+ uint codeNum;
+ AVCEnc_Status status;
+
+ if (value <= 0)
+ {
+ codeNum = -value * 2;
+ }
+ else
+ {
+ codeNum = value * 2 - 1;
+ }
+
+ status = ue_v(bitstream, codeNum);
+
+ return status;
+}
+
+AVCEnc_Status te_v(AVCEncBitstream *bitstream, uint value, uint range)
+{
+ AVCEnc_Status status;
+
+ if (range > 1)
+ {
+ return ue_v(bitstream, value);
+ }
+ else
+ {
+ status = BitstreamWrite1Bit(bitstream, 1 - value);
+ return status;
+ }
+}
+
+/**
+See subclause 9.1, Table 9-1, 9-2. */
+// compute leadingZeros and inforbits
+//codeNum = (1<<leadingZeros)-1+infobits;
+AVCEnc_Status SetEGBitstring(AVCEncBitstream *bitstream, uint codeNum)
+{
+ AVCEnc_Status status;
+ int leadingZeros;
+ int infobits;
+
+ if (!codeNum)
+ {
+ status = BitstreamWrite1Bit(bitstream, 1);
+ return status;
+ }
+
+ /* calculate leadingZeros and infobits */
+ leadingZeros = 1;
+ while ((uint)(1 << leadingZeros) < codeNum + 2)
+ {
+ leadingZeros++;
+ }
+ leadingZeros--;
+ infobits = codeNum - (1 << leadingZeros) + 1;
+
+ status = BitstreamWriteBits(bitstream, leadingZeros, 0);
+ infobits |= (1 << leadingZeros);
+ status = BitstreamWriteBits(bitstream, leadingZeros + 1, infobits);
+ return status;
+}
+
+/* see Table 9-4 assignment of codeNum to values of coded_block_pattern. */
+const static uint8 MapCBP2code[48][2] =
+{
+ {3, 0}, {29, 2}, {30, 3}, {17, 7}, {31, 4}, {18, 8}, {37, 17}, {8, 13}, {32, 5}, {38, 18}, {19, 9}, {9, 14},
+ {20, 10}, {10, 15}, {11, 16}, {2, 11}, {16, 1}, {33, 32}, {34, 33}, {21, 36}, {35, 34}, {22, 37}, {39, 44}, {4, 40},
+ {36, 35}, {40, 45}, {23, 38}, {5, 41}, {24, 39}, {6, 42}, {7, 43}, {1, 19}, {41, 6}, {42, 24}, {43, 25}, {25, 20},
+ {44, 26}, {26, 21}, {46, 46}, {12, 28}, {45, 27}, {47, 47}, {27, 22}, {13, 29}, {28, 23}, {14, 30}, {15, 31}, {0, 12}
+};
+
+AVCEnc_Status EncodeCBP(AVCMacroblock *currMB, AVCEncBitstream *stream)
+{
+ AVCEnc_Status status;
+ uint codeNum;
+
+ if (currMB->mbMode == AVC_I4)
+ {
+ codeNum = MapCBP2code[currMB->CBP][0];
+ }
+ else
+ {
+ codeNum = MapCBP2code[currMB->CBP][1];
+ }
+
+ status = ue_v(stream, codeNum);
+
+ return status;
+}
+
+AVCEnc_Status ce_TotalCoeffTrailingOnes(AVCEncBitstream *stream, int TrailingOnes, int TotalCoeff, int nC)
+{
+ const static uint8 totCoeffTrailOne[3][4][17][2] =
+ {
+ { // 0702
+ {{1, 1}, {6, 5}, {8, 7}, {9, 7}, {10, 7}, {11, 7}, {13, 15}, {13, 11}, {13, 8}, {14, 15}, {14, 11}, {15, 15}, {15, 11}, {16, 15}, {16, 11}, {16, 7}, {16, 4}},
+ {{0, 0}, {2, 1}, {6, 4}, {8, 6}, {9, 6}, {10, 6}, {11, 6}, {13, 14}, {13, 10}, {14, 14}, {14, 10}, {15, 14}, {15, 10}, {15, 1}, {16, 14}, {16, 10}, {16, 6}},
+ {{0, 0}, {0, 0}, {3, 1}, {7, 5}, {8, 5}, {9, 5}, {10, 5}, {11, 5}, {13, 13}, {13, 9}, {14, 13}, {14, 9}, {15, 13}, {15, 9}, {16, 13}, {16, 9}, {16, 5}},
+ {{0, 0}, {0, 0}, {0, 0}, {5, 3}, {6, 3}, {7, 4}, {8, 4}, {9, 4}, {10, 4}, {11, 4}, {13, 12}, {14, 12}, {14, 8}, {15, 12}, {15, 8}, {16, 12}, {16, 8}},
+ },
+ {
+ {{2, 3}, {6, 11}, {6, 7}, {7, 7}, {8, 7}, {8, 4}, {9, 7}, {11, 15}, {11, 11}, {12, 15}, {12, 11}, {12, 8}, {13, 15}, {13, 11}, {13, 7}, {14, 9}, {14, 7}},
+ {{0, 0}, {2, 2}, {5, 7}, {6, 10}, {6, 6}, {7, 6}, {8, 6}, {9, 6}, {11, 14}, {11, 10}, {12, 14}, {12, 10}, {13, 14}, {13, 10}, {14, 11}, {14, 8}, {14, 6}},
+ {{0, 0}, {0, 0}, {3, 3}, {6, 9}, {6, 5}, {7, 5}, {8, 5}, {9, 5}, {11, 13}, {11, 9}, {12, 13}, {12, 9}, {13, 13}, {13, 9}, {13, 6}, {14, 10}, {14, 5}},
+ {{0, 0}, {0, 0}, {0, 0}, {4, 5}, {4, 4}, {5, 6}, {6, 8}, {6, 4}, {7, 4}, {9, 4}, {11, 12}, {11, 8}, {12, 12}, {13, 12}, {13, 8}, {13, 1}, {14, 4}},
+ },
+ {
+ {{4, 15}, {6, 15}, {6, 11}, {6, 8}, {7, 15}, {7, 11}, {7, 9}, {7, 8}, {8, 15}, {8, 11}, {9, 15}, {9, 11}, {9, 8}, {10, 13}, {10, 9}, {10, 5}, {10, 1}},
+ {{0, 0}, {4, 14}, {5, 15}, {5, 12}, {5, 10}, {5, 8}, {6, 14}, {6, 10}, {7, 14}, {8, 14}, {8, 10}, {9, 14}, {9, 10}, {9, 7}, {10, 12}, {10, 8}, {10, 4}},
+ {{0, 0}, {0, 0}, {4, 13}, {5, 14}, {5, 11}, {5, 9}, {6, 13}, {6, 9}, {7, 13}, {7, 10}, {8, 13}, {8, 9}, {9, 13}, {9, 9}, {10, 11}, {10, 7}, {10, 3}},
+ {{0, 0}, {0, 0}, {0, 0}, {4, 12}, {4, 11}, {4, 10}, {4, 9}, {4, 8}, {5, 13}, {6, 12}, {7, 12}, {8, 12}, {8, 8}, {9, 12}, {10, 10}, {10, 6}, {10, 2}}
+ }
+ };
+
+
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ uint code, len;
+ int vlcnum;
+
+ if (TrailingOnes > 3)
+ {
+ return AVCENC_TRAILINGONES_FAIL;
+ }
+
+ if (nC >= 8)
+ {
+ if (TotalCoeff)
+ {
+ code = ((TotalCoeff - 1) << 2) | (TrailingOnes);
+ }
+ else
+ {
+ code = 3;
+ }
+ status = BitstreamWriteBits(stream, 6, code);
+ }
+ else
+ {
+ if (nC < 2)
+ {
+ vlcnum = 0;
+ }
+ else if (nC < 4)
+ {
+ vlcnum = 1;
+ }
+ else
+ {
+ vlcnum = 2;
+ }
+
+ len = totCoeffTrailOne[vlcnum][TrailingOnes][TotalCoeff][0];
+ code = totCoeffTrailOne[vlcnum][TrailingOnes][TotalCoeff][1];
+ status = BitstreamWriteBits(stream, len, code);
+ }
+
+ return status;
+}
+
+AVCEnc_Status ce_TotalCoeffTrailingOnesChromaDC(AVCEncBitstream *stream, int TrailingOnes, int TotalCoeff)
+{
+ const static uint8 totCoeffTrailOneChrom[4][5][2] =
+ {
+ { {2, 1}, {6, 7}, {6, 4}, {6, 3}, {6, 2}},
+ { {0, 0}, {1, 1}, {6, 6}, {7, 3}, {8, 3}},
+ { {0, 0}, {0, 0}, {3, 1}, {7, 2}, {8, 2}},
+ { {0, 0}, {0, 0}, {0, 0}, {6, 5}, {7, 0}},
+ };
+
+ AVCEnc_Status status = AVCENC_SUCCESS;
+ uint code, len;
+
+ len = totCoeffTrailOneChrom[TrailingOnes][TotalCoeff][0];
+ code = totCoeffTrailOneChrom[TrailingOnes][TotalCoeff][1];
+ status = BitstreamWriteBits(stream, len, code);
+
+ return status;
+}
+
+/* see Table 9-7 and 9-8 */
+AVCEnc_Status ce_TotalZeros(AVCEncBitstream *stream, int total_zeros, int TotalCoeff)
+{
+ const static uint8 lenTotalZeros[15][16] =
+ {
+ { 1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9},
+ { 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6},
+ { 4, 3, 3, 3, 4, 4, 3, 3, 4, 5, 5, 6, 5, 6},
+ { 5, 3, 4, 4, 3, 3, 3, 4, 3, 4, 5, 5, 5},
+ { 4, 4, 4, 3, 3, 3, 3, 3, 4, 5, 4, 5},
+ { 6, 5, 3, 3, 3, 3, 3, 3, 4, 3, 6},
+ { 6, 5, 3, 3, 3, 2, 3, 4, 3, 6},
+ { 6, 4, 5, 3, 2, 2, 3, 3, 6},
+ { 6, 6, 4, 2, 2, 3, 2, 5},
+ { 5, 5, 3, 2, 2, 2, 4},
+ { 4, 4, 3, 3, 1, 3},
+ { 4, 4, 2, 1, 3},
+ { 3, 3, 1, 2},
+ { 2, 2, 1},
+ { 1, 1},
+ };
+
+ const static uint8 codTotalZeros[15][16] =
+ {
+ {1, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1},
+ {7, 6, 5, 4, 3, 5, 4, 3, 2, 3, 2, 3, 2, 1, 0},
+ {5, 7, 6, 5, 4, 3, 4, 3, 2, 3, 2, 1, 1, 0},
+ {3, 7, 5, 4, 6, 5, 4, 3, 3, 2, 2, 1, 0},
+ {5, 4, 3, 7, 6, 5, 4, 3, 2, 1, 1, 0},
+ {1, 1, 7, 6, 5, 4, 3, 2, 1, 1, 0},
+ {1, 1, 5, 4, 3, 3, 2, 1, 1, 0},
+ {1, 1, 1, 3, 3, 2, 2, 1, 0},
+ {1, 0, 1, 3, 2, 1, 1, 1, },
+ {1, 0, 1, 3, 2, 1, 1, },
+ {0, 1, 1, 2, 1, 3},
+ {0, 1, 1, 1, 1},
+ {0, 1, 1, 1},
+ {0, 1, 1},
+ {0, 1},
+ };
+ int len, code;
+ AVCEnc_Status status;
+
+ len = lenTotalZeros[TotalCoeff-1][total_zeros];
+ code = codTotalZeros[TotalCoeff-1][total_zeros];
+
+ status = BitstreamWriteBits(stream, len, code);
+
+ return status;
+}
+
+/* see Table 9-9 */
+AVCEnc_Status ce_TotalZerosChromaDC(AVCEncBitstream *stream, int total_zeros, int TotalCoeff)
+{
+ const static uint8 lenTotalZerosChromaDC[3][4] =
+ {
+ { 1, 2, 3, 3, },
+ { 1, 2, 2, 0, },
+ { 1, 1, 0, 0, },
+ };
+
+ const static uint8 codTotalZerosChromaDC[3][4] =
+ {
+ { 1, 1, 1, 0, },
+ { 1, 1, 0, 0, },
+ { 1, 0, 0, 0, },
+ };
+
+ int len, code;
+ AVCEnc_Status status;
+
+ len = lenTotalZerosChromaDC[TotalCoeff-1][total_zeros];
+ code = codTotalZerosChromaDC[TotalCoeff-1][total_zeros];
+
+ status = BitstreamWriteBits(stream, len, code);
+
+ return status;
+}
+
+/* see Table 9-10 */
+AVCEnc_Status ce_RunBefore(AVCEncBitstream *stream, int run_before, int zerosLeft)
+{
+ const static uint8 lenRunBefore[7][16] =
+ {
+ {1, 1},
+ {1, 2, 2},
+ {2, 2, 2, 2},
+ {2, 2, 2, 3, 3},
+ {2, 2, 3, 3, 3, 3},
+ {2, 3, 3, 3, 3, 3, 3},
+ {3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11},
+ };
+
+ const static uint8 codRunBefore[7][16] =
+ {
+ {1, 0},
+ {1, 1, 0},
+ {3, 2, 1, 0},
+ {3, 2, 1, 1, 0},
+ {3, 2, 3, 2, 1, 0},
+ {3, 0, 1, 3, 2, 5, 4},
+ {7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ };
+
+ int len, code;
+ AVCEnc_Status status;
+
+ if (zerosLeft <= 6)
+ {
+ len = lenRunBefore[zerosLeft-1][run_before];
+ code = codRunBefore[zerosLeft-1][run_before];
+ }
+ else
+ {
+ len = lenRunBefore[6][run_before];
+ code = codRunBefore[6][run_before];
+ }
+
+ status = BitstreamWriteBits(stream, len, code);
+
+
+ return status;
+}
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index edd8648..17771c4 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -90,7 +90,7 @@
out->setTo(baseURL);
out->append(url);
} else {
- char *slashPos = strrchr(baseURL, '/');
+ const char *slashPos = strrchr(baseURL, '/');
if (slashPos > &baseURL[6]) {
out->setTo(baseURL, slashPos - baseURL);
diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h
index f09addd..200f93c 100644
--- a/media/libstagefright/include/AACDecoder.h
+++ b/media/libstagefright/include/AACDecoder.h
@@ -25,6 +25,7 @@
namespace android {
struct MediaBufferGroup;
+struct MetaData;
struct AACDecoder : public MediaSource {
AACDecoder(const sp<MediaSource> &source);
@@ -41,6 +42,7 @@
virtual ~AACDecoder();
private:
+ sp<MetaData> mMeta;
sp<MediaSource> mSource;
bool mStarted;
@@ -50,9 +52,11 @@
void *mDecoderBuf;
int64_t mAnchorTimeUs;
int64_t mNumSamplesOutput;
+ status_t mInitCheck;
MediaBuffer *mInputBuffer;
+ status_t initCheck();
AACDecoder(const AACDecoder &);
AACDecoder &operator=(const AACDecoder &);
};
diff --git a/media/libstagefright/include/AVCEncoder.h b/media/libstagefright/include/AVCEncoder.h
new file mode 100644
index 0000000..4fe2e30
--- /dev/null
+++ b/media/libstagefright/include/AVCEncoder.h
@@ -0,0 +1,90 @@
+/*
+ * 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 AVC_ENCODER_H_
+
+#define AVC_ENCODER_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Vector.h>
+
+struct tagAVCHandle;
+struct tagAVCEncParam;
+
+namespace android {
+
+struct MediaBuffer;
+struct MediaBufferGroup;
+
+struct AVCEncoder : public MediaSource,
+ public MediaBufferObserver {
+ AVCEncoder(const sp<MediaSource> &source,
+ const sp<MetaData>& meta);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+ // Callbacks required by the encoder
+ int32_t allocOutputBuffers(unsigned int sizeInMbs, unsigned int numBuffers);
+ void unbindOutputBuffer(int32_t index);
+ int32_t bindOutputBuffer(int32_t index, uint8_t **yuv);
+
+protected:
+ virtual ~AVCEncoder();
+
+private:
+ sp<MediaSource> mSource;
+ sp<MetaData> mFormat;
+ sp<MetaData> mMeta;
+
+ int32_t mVideoWidth;
+ int32_t mVideoHeight;
+ int32_t mVideoFrameRate;
+ int32_t mVideoBitRate;
+ int32_t mVideoColorFormat;
+ int64_t mNumInputFrames;
+ status_t mInitCheck;
+ bool mStarted;
+ bool mSpsPpsHeaderReceived;
+ bool mReadyForNextFrame;
+ int32_t mIsIDRFrame; // for set kKeyIsSyncFrame
+
+ tagAVCHandle *mHandle;
+ tagAVCEncParam *mEncParams;
+ MediaBuffer *mInputBuffer;
+ uint8_t *mInputFrameData;
+ MediaBufferGroup *mGroup;
+ Vector<MediaBuffer *> mOutputBuffers;
+
+
+ status_t initCheck(const sp<MetaData>& meta);
+ void releaseOutputBuffers();
+
+ AVCEncoder(const AVCEncoder &);
+ AVCEncoder &operator=(const AVCEncoder &);
+};
+
+} // namespace android
+
+#endif // AVC_ENCODER_H_
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index e9162c0..9826990 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -116,7 +116,7 @@
path->setTo(slashPos);
}
- char *colonPos = strchr(host->c_str(), ':');
+ const char *colonPos = strchr(host->c_str(), ':');
if (colonPos != NULL) {
unsigned long x;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index ca4c55e..25b9ce2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -164,7 +164,7 @@
AString format;
getFormat(index, &format);
- char *lastSpacePos = strrchr(format.c_str(), ' ');
+ const char *lastSpacePos = strrchr(format.c_str(), ' ');
CHECK(lastSpacePos != NULL);
char *end;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 74bb798..719ebf3 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -419,7 +419,7 @@
out->setTo(baseURL);
out->append(url);
} else {
- char *slashPos = strrchr(baseURL, '/');
+ const char *slashPos = strrchr(baseURL, '/');
if (slashPos > &baseURL[6]) {
out->setTo(baseURL, slashPos - baseURL);
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index a92cea8..0559812 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -14,8 +14,8 @@
base := $(LOCAL_PATH)/../..
LOCAL_C_INCLUDES := \
- $(base)/libs/audioflinger \
- $(base)/camera/libcameraservice \
+ $(base)/services/audioflinger \
+ $(base)/services/camera/libcameraservice \
$(base)/media/libmediaplayerservice
LOCAL_MODULE:= mediaserver
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 13a6430..40adf18 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -13,6 +13,9 @@
# 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)
@@ -20,67 +23,26 @@
LOCAL_SRC_FILES:= \
MtpClient.cpp \
MtpCursor.cpp \
- MtpDatabase.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
+ MtpEventPacket.cpp \
MtpDeviceInfo.cpp \
- MtpMediaScanner.cpp \
MtpObjectInfo.cpp \
MtpPacket.cpp \
MtpProperty.cpp \
MtpRequestPacket.cpp \
MtpResponsePacket.cpp \
MtpServer.cpp \
- MtpSqliteDatabase.cpp \
MtpStorageInfo.cpp \
MtpStringBuffer.cpp \
MtpStorage.cpp \
MtpUtils.cpp \
- SqliteDatabase.cpp \
- SqliteStatement.cpp \
LOCAL_MODULE:= libmtp
-LOCAL_C_INCLUDES := external/sqlite/dist
-
LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST
include $(BUILD_STATIC_LIBRARY)
-ifneq ($(TARGET_SIMULATOR),true)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- mtptest.cpp \
-
-LOCAL_MODULE:= mtptest
-
-LOCAL_CFLAGS := -DMTP_DEVICE
-
-LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright libcutils \
- libmedia
-
-LOCAL_STATIC_LIBRARIES := libmtp
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := scantest
-LOCAL_SRC_FILES:= \
- scantest.cpp \
-
-
-LOCAL_STATIC_LIBRARIES := libmtp
-
-LOCAL_C_INCLUDES := external/sqlite/dist
-LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright libmedia
-
-LOCAL_CFLAGS := -g
-LOCAL_LDFLAGS := -g
-
-include $(BUILD_EXECUTABLE)
-
endif
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index a7e975c..6f9ea24 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -299,17 +299,28 @@
putUInt64(*values++);
}
-void MtpDataPacket::putString(const MtpStringBuffer& string)
-{
+void MtpDataPacket::putString(const MtpStringBuffer& string) {
string.writeToPacket(this);
}
-void MtpDataPacket::putString(const char* s)
-{
+void MtpDataPacket::putString(const char* s) {
MtpStringBuffer string(s);
string.writeToPacket(this);
}
+void MtpDataPacket::putString(const uint16_t* string) {
+ int count = 0;
+ for (int i = 0; i < 256; i++) {
+ if (string[i])
+ count++;
+ else
+ break;
+ }
+ putUInt8(count);
+ for (int i = 0; i < count; i++)
+ putUInt16(string[i]);
+}
+
#ifdef MTP_DEVICE
int MtpDataPacket::read(int fd) {
// first read the header
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 146ef64..759c0f9 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -79,6 +79,7 @@
void putAUInt64(const uint64_t* values, int count);
void putString(const MtpStringBuffer& string);
void putString(const char* string);
+ void putString(const uint16_t* string);
inline void putEmptyString() { putUInt16(0); }
inline void putEmptyArray() { putUInt32(0); }
diff --git a/media/mtp/MtpDatabase.cpp b/media/mtp/MtpDatabase.cpp
deleted file mode 100644
index eabd993..0000000
--- a/media/mtp/MtpDatabase.cpp
+++ /dev/null
@@ -1,81 +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.
- */
-
-#define LOG_TAG "MtpDatabase"
-
-#include "MtpDebug.h"
-#include "MtpDatabase.h"
-#include "MtpTypes.h"
-#include "mtp.h"
-
-namespace android {
-
-MtpDatabase::~MtpDatabase() {
-}
-
-uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) {
- switch (format) {
- case MTP_FORMAT_AIFF:
- case MTP_FORMAT_WAV:
- case MTP_FORMAT_MP3:
- case MTP_FORMAT_FLAC:
- case MTP_FORMAT_UNDEFINED_AUDIO:
- case MTP_FORMAT_WMA:
- case MTP_FORMAT_OGG:
- case MTP_FORMAT_AAC:
- case MTP_FORMAT_AUDIBLE:
- return kObjectHandleTableAudio;
- case MTP_FORMAT_AVI:
- case MTP_FORMAT_MPEG:
- case MTP_FORMAT_ASF:
- case MTP_FORMAT_UNDEFINED_VIDEO:
- case MTP_FORMAT_WMV:
- case MTP_FORMAT_MP4_CONTAINER:
- case MTP_FORMAT_MP2:
- case MTP_FORMAT_3GP_CONTAINER:
- return kObjectHandleTableVideo;
- case MTP_FORMAT_DEFINED:
- case MTP_FORMAT_EXIF_JPEG:
- case MTP_FORMAT_TIFF_EP:
- case MTP_FORMAT_FLASHPIX:
- case MTP_FORMAT_BMP:
- case MTP_FORMAT_CIFF:
- case MTP_FORMAT_GIF:
- case MTP_FORMAT_JFIF:
- case MTP_FORMAT_CD:
- case MTP_FORMAT_PICT:
- case MTP_FORMAT_PNG:
- case MTP_FORMAT_TIFF:
- case MTP_FORMAT_TIFF_IT:
- case MTP_FORMAT_JP2:
- case MTP_FORMAT_JPX:
- case MTP_FORMAT_WINDOWS_IMAGE_FORMAT:
- return kObjectHandleTableImage;
- case MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST:
- case MTP_FORMAT_ABSTRACT_AV_PLAYLIST:
- case MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST:
- case MTP_FORMAT_WPL_PLAYLIST:
- case MTP_FORMAT_M3U_PLAYLIST:
- case MTP_FORMAT_MPL_PLAYLIST:
- case MTP_FORMAT_ASX_PLAYLIST:
- case MTP_FORMAT_PLS_PLAYLIST:
- return kObjectHandleTablePlaylist;
- default:
- return kObjectHandleTableFile;
- }
-}
-
-} // namespace android
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 0c70d9f..7feb3dc 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -18,7 +18,6 @@
#define _MTP_DATABASE_H
#include "MtpTypes.h"
-#include "SqliteDatabase.h"
namespace android {
@@ -26,35 +25,27 @@
class MtpDatabase {
public:
- virtual ~MtpDatabase();
+ virtual ~MtpDatabase() {}
- static uint32_t getTableForFile(MtpObjectFormat format);
-
- virtual MtpObjectHandle getObjectHandle(const char* path) = 0;
- virtual MtpObjectHandle addFile(const char* path,
+ // called from SendObjectInfo to reserve a database entry for the incoming file
+ virtual MtpObjectHandle beginSendObject(const char* path,
MtpObjectFormat format,
MtpObjectHandle parent,
MtpStorageID storage,
uint64_t size,
time_t modified) = 0;
- virtual MtpObjectHandle addAudioFile(MtpObjectHandle id) = 0;
-
- virtual MtpObjectHandle addAudioFile(MtpObjectHandle id,
- const char* title,
- const char* artist,
- const char* album,
- const char* albumArtist,
- const char* genre,
- const char* composer,
- const char* mimeType,
- int track,
- int year,
- int duration) = 0;
+ // called to report success or failure of the SendObject file transfer
+ // success should signal a notification of the new object's creation,
+ // failure should remove the database entry created in beginSendObject
+ virtual void endSendObject(const char* path,
+ MtpObjectHandle handle,
+ MtpObjectFormat format,
+ bool succeeded) = 0;
virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
- MtpObjectFormat format,
- MtpObjectHandle parent) = 0;
+ MtpObjectFormat format,
+ MtpObjectHandle parent) = 0;
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
@@ -68,9 +59,6 @@
int64_t& fileLength) = 0;
virtual bool deleteFile(MtpObjectHandle handle) = 0;
- // helper for media scanner
- virtual MtpObjectHandle* getFileList(int& outCount) = 0;
-
virtual void beginTransaction() = 0;
virtual void commitTransaction() = 0;
virtual void rollbackTransaction() = 0;
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
new file mode 100644
index 0000000..089278e
--- /dev/null
+++ b/media/mtp/MtpEventPacket.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "MtpEventPacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <linux/usb/f_mtp.h>
+
+#include "MtpEventPacket.h"
+
+namespace android {
+
+MtpEventPacket::MtpEventPacket()
+ : MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(int fd) {
+ struct mtp_event event;
+
+ putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
+
+ event.data = mBuffer;
+ event.length = mPacketSize;
+ int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+ return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+ // read our buffer from the given endpoint
+int MtpEventPacket::read(struct usb_endpoint *ep) {
+ int ret = transfer(ep, mBuffer, mBufferSize);
+ if (ret >= 0)
+ mPacketSize = ret;
+ else
+ mPacketSize = 0;
+ return ret;
+}
+#endif
+
+} // namespace android
+
diff --git a/media/mtp/MtpEventPacket.h b/media/mtp/MtpEventPacket.h
new file mode 100644
index 0000000..30ae869
--- /dev/null
+++ b/media/mtp/MtpEventPacket.h
@@ -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.
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+ MtpEventPacket();
+ virtual ~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+ // write our data to the given file descriptor
+ int write(int fd);
+#endif
+
+#ifdef MTP_HOST
+ // read our buffer from the given endpoint
+ int read(struct usb_endpoint *ep);
+#endif
+
+ inline MtpEventCode getEventCode() const { return getContainerCode(); }
+ inline void setEventCode(MtpEventCode code)
+ { return setContainerCode(code); }
+};
+
+}; // namespace android
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/media/mtp/MtpMediaScanner.cpp b/media/mtp/MtpMediaScanner.cpp
deleted file mode 100644
index ac4c0cf..0000000
--- a/media/mtp/MtpMediaScanner.cpp
+++ /dev/null
@@ -1,381 +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.
- */
-
-#define LOG_TAG "MtpMediaScanner"
-
-#include "MtpDebug.h"
-#include "MtpDatabase.h"
-#include "MtpMediaScanner.h"
-#include "mtp.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-
-#include <media/mediascanner.h>
-#include <media/stagefright/StagefrightMediaScanner.h>
-
-namespace android {
-
-class MtpMediaScannerClient : public MediaScannerClient
-{
-public:
- MtpMediaScannerClient()
- {
- reset();
- }
-
- virtual ~MtpMediaScannerClient()
- {
- }
-
- // returns true if it succeeded, false if an exception occured in the Java code
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
- {
- LOGV("scanFile %s", path);
- return true;
- }
-
- // returns true if it succeeded, false if an exception occured in the Java code
- virtual bool handleStringTag(const char* name, const char* value)
- {
- int temp;
-
- if (!strcmp(name, "title")) {
- mTitle = value;
- mHasTitle = true;
- } else if (!strcmp(name, "artist")) {
- mArtist = value;
- mHasArtist = true;
- } else if (!strcmp(name, "album")) {
- mAlbum = value;
- mHasAlbum = true;
- } else if (!strcmp(name, "albumartist")) {
- mAlbumArtist = value;
- mHasAlbumArtist = true;
- } else if (!strcmp(name, "genre")) {
- // FIXME - handle numeric values here
- mGenre = value;
- mHasGenre = true;
- } else if (!strcmp(name, "composer")) {
- mComposer = value;
- mHasComposer = true;
- } else if (!strcmp(name, "tracknumber")) {
- if (sscanf(value, "%d", &temp) == 1)
- mTrack = temp;
- } else if (!strcmp(name, "discnumber")) {
- // currently unused
- } else if (!strcmp(name, "year") || !strcmp(name, "date")) {
- if (sscanf(value, "%d", &temp) == 1)
- mYear = temp;
- } else if (!strcmp(name, "duration")) {
- if (sscanf(value, "%d", &temp) == 1)
- mDuration = temp;
- } else {
- LOGV("handleStringTag %s : %s", name, value);
- }
- return true;
- }
-
- // returns true if it succeeded, false if an exception occured in the Java code
- virtual bool setMimeType(const char* mimeType)
- {
- mMimeType = mimeType;
- mHasMimeType = true;
- return true;
- }
-
- // returns true if it succeeded, false if an exception occured in the Java code
- virtual bool addNoMediaFolder(const char* path)
- {
- LOGV("addNoMediaFolder %s", path);
- return true;
- }
-
- void reset()
- {
- mHasTitle = false;
- mHasArtist = false;
- mHasAlbum = false;
- mHasAlbumArtist = false;
- mHasGenre = false;
- mHasComposer = false;
- mHasMimeType = false;
- mTrack = mYear = mDuration = 0;
- }
-
- inline const char* getTitle() const { return mHasTitle ? (const char *)mTitle : NULL; }
- inline const char* getArtist() const { return mHasArtist ? (const char *)mArtist : NULL; }
- inline const char* getAlbum() const { return mHasAlbum ? (const char *)mAlbum : NULL; }
- inline const char* getAlbumArtist() const { return mHasAlbumArtist ? (const char *)mAlbumArtist : NULL; }
- inline const char* getGenre() const { return mHasGenre ? (const char *)mGenre : NULL; }
- inline const char* getComposer() const { return mHasComposer ? (const char *)mComposer : NULL; }
- inline const char* getMimeType() const { return mHasMimeType ? (const char *)mMimeType : NULL; }
- inline int getTrack() const { return mTrack; }
- inline int getYear() const { return mYear; }
- inline int getDuration() const { return mDuration; }
-
-private:
- MtpString mTitle;
- MtpString mArtist;
- MtpString mAlbum;
- MtpString mAlbumArtist;
- MtpString mGenre;
- MtpString mComposer;
- MtpString mMimeType;
-
- bool mHasTitle;
- bool mHasArtist;
- bool mHasAlbum;
- bool mHasAlbumArtist;
- bool mHasGenre;
- bool mHasComposer;
- bool mHasMimeType;
-
- int mTrack;
- int mYear;
- int mDuration;
-};
-
-
-MtpMediaScanner::MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db)
- : mStorageID(id),
- mFilePath(filePath),
- mDatabase(db),
- mMediaScanner(NULL),
- mMediaScannerClient(NULL),
- mFileList(NULL),
- mFileCount(0)
-{
- mMediaScanner = new StagefrightMediaScanner;
- mMediaScannerClient = new MtpMediaScannerClient;
-}
-
-MtpMediaScanner::~MtpMediaScanner() {
-}
-
-bool MtpMediaScanner::scanFiles() {
- mDatabase->beginTransaction();
- mFileCount = 0;
- mFileList = mDatabase->getFileList(mFileCount);
-
- int ret = scanDirectory(mFilePath, MTP_PARENT_ROOT);
-
- for (int i = 0; i < mFileCount; i++) {
- MtpObjectHandle test = mFileList[i];
- if (! (test & kObjectHandleMarkBit)) {
- LOGV("delete missing file %08X", test);
- mDatabase->deleteFile(test);
- }
- }
-
- delete[] mFileList;
- mFileCount = 0;
- mDatabase->commitTransaction();
- return (ret == 0);
-}
-
-
-static const struct MediaFileTypeEntry
-{
- const char* extension;
- MtpObjectFormat format;
- uint32_t table;
-} sFileTypes[] =
-{
- { "MP3", MTP_FORMAT_MP3, kObjectHandleTableAudio },
- { "M4A", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "WAV", MTP_FORMAT_WAV, kObjectHandleTableAudio },
- { "AMR", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "AWB", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "WMA", MTP_FORMAT_WMA, kObjectHandleTableAudio },
- { "OGG", MTP_FORMAT_OGG, kObjectHandleTableAudio },
- { "OGA", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "AAC", MTP_FORMAT_AAC, kObjectHandleTableAudio },
- { "MID", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "MIDI", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "XMF", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "RTTTL", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "SMF", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "IMY", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "RTX", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "OTA", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
- { "MPEG", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "MP4", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "M4V", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "3GP", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "3GPP", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "3G2", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "3GPP2", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "WMV", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "ASF", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
- { "JPG", MTP_FORMAT_EXIF_JPEG, kObjectHandleTableImage },
- { "JPEG", MTP_FORMAT_EXIF_JPEG, kObjectHandleTableImage },
- { "GIF", MTP_FORMAT_GIF, kObjectHandleTableImage },
- { "PNG", MTP_FORMAT_PNG, kObjectHandleTableImage },
- { "BMP", MTP_FORMAT_BMP, kObjectHandleTableImage },
- { "WBMP", MTP_FORMAT_BMP, kObjectHandleTableImage },
- { "M3U", MTP_FORMAT_M3U_PLAYLIST, kObjectHandleTablePlaylist },
- { "PLS", MTP_FORMAT_PLS_PLAYLIST, kObjectHandleTablePlaylist },
- { "WPL", MTP_FORMAT_WPL_PLAYLIST, kObjectHandleTablePlaylist },
-};
-
-MtpObjectFormat MtpMediaScanner::getFileFormat(const char* path, uint32_t& table)
-{
- const char* extension = strrchr(path, '.');
- if (!extension)
- return MTP_FORMAT_UNDEFINED;
- extension++; // skip the dot
-
- for (unsigned i = 0; i < sizeof(sFileTypes) / sizeof(sFileTypes[0]); i++) {
- if (!strcasecmp(extension, sFileTypes[i].extension)) {
- table = sFileTypes[i].table;
- return sFileTypes[i].format;
- }
- }
- table = kObjectHandleTableFile;
- return MTP_FORMAT_UNDEFINED;
-}
-
-int MtpMediaScanner::scanDirectory(const char* path, MtpObjectHandle parent)
-{
- char buffer[PATH_MAX];
- struct dirent* entry;
-
- unsigned length = strlen(path);
- if (length > sizeof(buffer) + 2) {
- LOGE("path too long: %s", path);
- }
-
- DIR* dir = opendir(path);
- if (!dir) {
- LOGE("opendir %s failed, errno: %d", path, errno);
- return -1;
- }
-
- strncpy(buffer, path, sizeof(buffer));
- char* fileStart = buffer + length;
- // make sure we have a trailing slash
- if (fileStart[-1] != '/') {
- *(fileStart++) = '/';
- }
- int fileNameLength = sizeof(buffer) + fileStart - buffer;
-
- while ((entry = readdir(dir))) {
- const char* name = entry->d_name;
-
- // ignore "." and "..", as well as any files or directories staring with dot
- if (name[0] == '.') {
- continue;
- }
- if (strlen(name) + 1 > fileNameLength) {
- LOGE("path too long for %s", name);
- continue;
- }
- strcpy(fileStart, name);
-
- struct stat statbuf;
- memset(&statbuf, 0, sizeof(statbuf));
- stat(buffer, &statbuf);
-
- if (S_ISDIR(statbuf.st_mode)) {
- MtpObjectHandle handle = mDatabase->getObjectHandle(buffer);
- if (handle) {
- markFile(handle);
- } else {
- handle = mDatabase->addFile(buffer, MTP_FORMAT_ASSOCIATION,
- parent, mStorageID, 0, statbuf.st_mtime);
- }
- scanDirectory(buffer, handle);
- } else if (S_ISREG(statbuf.st_mode)) {
- scanFile(buffer, parent, statbuf);
- }
- }
-
- closedir(dir);
- return 0;
-}
-
-void MtpMediaScanner::scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf) {
- uint32_t table;
- MtpObjectFormat format = getFileFormat(path, table);
- // don't scan unknown file types
- if (format == MTP_FORMAT_UNDEFINED)
- return;
- MtpObjectHandle handle = mDatabase->getObjectHandle(path);
- // fixme - rescan if mod date changed
- if (handle) {
- markFile(handle);
- } else {
- mDatabase->beginTransaction();
- handle = mDatabase->addFile(path, format, parent, mStorageID,
- statbuf.st_size, statbuf.st_mtime);
- if (handle <= 0) {
- LOGE("addFile failed in MtpMediaScanner::scanFile()");
- mDatabase->rollbackTransaction();
- return;
- }
-
- if (table == kObjectHandleTableAudio) {
- mMediaScannerClient->reset();
- mMediaScanner->processFile(path, NULL, *mMediaScannerClient);
- handle = mDatabase->addAudioFile(handle,
- mMediaScannerClient->getTitle(),
- mMediaScannerClient->getArtist(),
- mMediaScannerClient->getAlbum(),
- mMediaScannerClient->getAlbumArtist(),
- mMediaScannerClient->getGenre(),
- mMediaScannerClient->getComposer(),
- mMediaScannerClient->getMimeType(),
- mMediaScannerClient->getTrack(),
- mMediaScannerClient->getYear(),
- mMediaScannerClient->getDuration());
- }
- mDatabase->commitTransaction();
- }
-}
-
-void MtpMediaScanner::markFile(MtpObjectHandle handle) {
- if (mFileList) {
- handle &= kObjectHandleIndexMask;
- // binary search for the file in mFileList
- int low = 0;
- int high = mFileCount;
- int index;
-
- while (low < high) {
- index = (low + high) >> 1;
- MtpObjectHandle test = (mFileList[index] & kObjectHandleIndexMask);
- if (handle < test)
- high = index; // item is less than index
- else if (handle > test)
- low = index + 1; // item is greater than index
- else {
- mFileList[index] |= kObjectHandleMarkBit;
- return;
- }
- }
- LOGE("file %d not found in mFileList", handle);
- }
-}
-
-} // namespace android
diff --git a/media/mtp/MtpMediaScanner.h b/media/mtp/MtpMediaScanner.h
deleted file mode 100644
index 53d5063..0000000
--- a/media/mtp/MtpMediaScanner.h
+++ /dev/null
@@ -1,56 +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.
- */
-
-#ifndef _MTP_MEDIA_SCANNER_H
-#define _MTP_MEDIA_SCANNER_H
-
-struct stat;
-
-namespace android {
-
-class MtpDatabase;
-class SqliteStatement;
-class MediaScanner;
-class MtpMediaScannerClient;
-
-class MtpMediaScanner {
-private:
- MtpStorageID mStorageID;
- const char* mFilePath;
- MtpDatabase* mDatabase;
- MediaScanner* mMediaScanner;
- MtpMediaScannerClient* mMediaScannerClient;
-
- // for garbage collecting missing files
- MtpObjectHandle* mFileList;
- int mFileCount;
-
-public:
- MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db);
- virtual ~MtpMediaScanner();
-
- bool scanFiles();
-
-private:
- MtpObjectFormat getFileFormat(const char* path, uint32_t& table);
- int scanDirectory(const char* path, MtpObjectHandle parent);
- void scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf);
- void markFile(MtpObjectHandle handle);
-};
-
-}; // namespace android
-
-#endif // _MTP_MEDIA_SCANNER_H
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index b9eeec5..163c05b 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -25,13 +25,13 @@
#include <cutils/properties.h>
#include "MtpDebug.h"
+#include "MtpDatabase.h"
#include "MtpProperty.h"
#include "MtpServer.h"
-#include "MtpSqliteDatabase.h"
#include "MtpStorage.h"
#include "MtpStringBuffer.h"
-#include "f_mtp.h"
+#include <linux/usb/f_mtp.h>
namespace android {
@@ -73,6 +73,11 @@
// MTP_OPERATION_SKIP,
};
+static const MtpEventCode kSupportedEventCodes[] = {
+ MTP_EVENT_OBJECT_ADDED,
+ MTP_EVENT_OBJECT_REMOVED,
+};
+
static const MtpObjectProperty kSupportedObjectProperties[] = {
MTP_PROPERTY_STORAGE_ID,
MTP_PROPERTY_OBJECT_FORMAT,
@@ -113,22 +118,19 @@
// MTP_FORMAT_PLS_PLAYLIST,
};
-MtpServer::MtpServer(int fd, const char* databasePath,
+MtpServer::MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm)
: mFD(fd),
- mDatabasePath(databasePath),
- mDatabase(NULL),
+ mDatabase(database),
mFileGroup(fileGroup),
mFilePermission(filePerm),
mDirectoryPermission(directoryPerm),
mSessionID(0),
mSessionOpen(false),
mSendObjectHandle(kInvalidObjectHandle),
+ mSendObjectFormat(0),
mSendObjectFileSize(0)
{
- mDatabase = new MtpSqliteDatabase();
- mDatabase->open(databasePath, true);
-
initObjectProperties();
}
@@ -151,13 +153,6 @@
return NULL;
}
-void MtpServer::scanStorage() {
- for (int i = 0; i < mStorages.size(); i++) {
- MtpStorage* storage = mStorages[i];
- storage->scanFiles();
- }
-}
-
void MtpServer::run() {
int fd = mFD;
@@ -249,6 +244,24 @@
return NULL;
}
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+ LOGD("sendObjectAdded %d\n", handle);
+ mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
+ mEvent.setTransactionID(mRequest.getTransactionID());
+ mEvent.setParameter(1, handle);
+ int ret = mEvent.write(mFD);
+ LOGD("mEvent.write returned %d\n", ret);
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+ LOGD("sendObjectRemoved %d\n", handle);
+ mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
+ mEvent.setTransactionID(mRequest.getTransactionID());
+ mEvent.setParameter(1, handle);
+ int ret = mEvent.write(mFD);
+ LOGD("mEvent.write returned %d\n", ret);
+}
+
void MtpServer::initObjectProperties() {
mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
@@ -336,7 +349,8 @@
mData.putUInt16(0); //Functional Mode
mData.putAUInt16(kSupportedOperationCodes,
sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
- mData.putEmptyArray(); // Events Supported
+ mData.putAUInt16(kSupportedEventCodes,
+ sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
mData.putEmptyArray(); // Device Properties Supported
mData.putEmptyArray(); // Capture Formats
mData.putAUInt16(kSupportedPlaybackFormats,
@@ -427,6 +441,8 @@
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
// 0x00000000 for all objects?
+ if (parent == 0xFFFFFFFF)
+ parent = 0;
MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
mData.putAUInt32(handles);
@@ -488,9 +504,10 @@
return MTP_RESPONSE_INVALID_STORAGE_ID;
// special case the root
- if (parent == MTP_PARENT_ROOT)
+ if (parent == MTP_PARENT_ROOT) {
path = storage->getPath();
- else {
+ parent = 0;
+ } else {
int64_t dummy;
if (!mDatabase->getObjectFilePath(parent, path, dummy))
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
@@ -527,15 +544,12 @@
path += (const char *)name;
mDatabase->beginTransaction();
- MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID,
- mSendObjectFileSize, modifiedTime);
+ MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
+ format, parent, storageID, mSendObjectFileSize, modifiedTime);
if (handle == kInvalidObjectHandle) {
mDatabase->rollbackTransaction();
return MTP_RESPONSE_GENERAL_ERROR;
}
- uint32_t table = MtpDatabase::getTableForFile(format);
- if (table == kObjectHandleTableAudio)
- handle = mDatabase->addAudioFile(handle);
mDatabase->commitTransaction();
if (format == MTP_FORMAT_ASSOCIATION) {
@@ -549,23 +563,29 @@
mSendObjectFilePath = path;
// save the handle for the SendObject call, which should follow
mSendObjectHandle = handle;
+ mSendObjectFormat = format;
}
mResponse.setParameter(1, storageID);
- mResponse.setParameter(2, parent);
+ mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
mResponse.setParameter(3, handle);
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doSendObject() {
+ MtpResponseCode result = MTP_RESPONSE_OK;
+ mode_t mask;
+ int ret;
+
if (mSendObjectHandle == kInvalidObjectHandle) {
LOGE("Expected SendObjectInfo before SendObject");
- return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
+ result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
+ goto done;
}
// read the header
- int ret = mData.readDataHeader(mFD);
+ ret = mData.readDataHeader(mFD);
// FIXME - check for errors here.
// reset so we don't attempt to send this back
@@ -574,11 +594,12 @@
mtp_file_range mfr;
mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
if (mfr.fd < 0) {
- return MTP_RESPONSE_GENERAL_ERROR;
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ goto done;
}
fchown(mfr.fd, getuid(), mFileGroup);
// set permissions
- mode_t mask = umask(0);
+ mask = umask(0);
fchmod(mfr.fd, mFilePermission);
umask(mask);
@@ -589,18 +610,22 @@
ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
close(mfr.fd);
- // FIXME - we need to delete mSendObjectHandle from the database if this fails.
LOGV("MTP_RECEIVE_FILE returned %d", ret);
- mSendObjectHandle = kInvalidObjectHandle;
if (ret < 0) {
unlink(mSendObjectFilePath);
if (errno == ECANCELED)
- return MTP_RESPONSE_TRANSACTION_CANCELLED;
+ result = MTP_RESPONSE_TRANSACTION_CANCELLED;
else
- return MTP_RESPONSE_GENERAL_ERROR;
+ result = MTP_RESPONSE_GENERAL_ERROR;
}
- return MTP_RESPONSE_OK;
+
+done:
+ mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
+ result == MTP_RESPONSE_OK);
+ mSendObjectHandle = kInvalidObjectHandle;
+ mSendObjectFormat = 0;
+ return result;
}
MtpResponseCode MtpServer::doDeleteObject() {
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 25635af..aff973a 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -20,15 +20,16 @@
#include "MtpRequestPacket.h"
#include "MtpDataPacket.h"
#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
#include "mtp.h"
#include "MtpUtils.h"
namespace android {
-class MtpStorage;
-class MtpSqliteDatabase;
+class MtpDatabase;
class MtpProperty;
+class MtpStorage;
class MtpServer {
@@ -36,10 +37,7 @@
// file descriptor for MTP kernel driver
int mFD;
- // path to our sqlite3 database
- const char* mDatabasePath;
-
- MtpSqliteDatabase* mDatabase;
+ MtpDatabase* mDatabase;
// group to own new files and folders
int mFileGroup;
@@ -55,6 +53,7 @@
MtpRequestPacket mRequest;
MtpDataPacket mData;
MtpResponsePacket mResponse;
+ MtpEventPacket mEvent;
MtpStorageList mStorages;
@@ -63,23 +62,26 @@
// handle for new object, set by SendObjectInfo and used by SendObject
MtpObjectHandle mSendObjectHandle;
+ MtpObjectFormat mSendObjectFormat;
MtpString mSendObjectFilePath;
size_t mSendObjectFileSize;
public:
- MtpServer(int fd, const char* databasePath,
+ MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm);
virtual ~MtpServer();
void addStorage(const char* filePath);
inline void addStorage(MtpStorage* storage) { mStorages.push(storage); }
MtpStorage* getStorage(MtpStorageID id);
- void scanStorage();
void run();
MtpProperty* getObjectProperty(MtpPropertyCode propCode);
MtpProperty* getDeviceProperty(MtpPropertyCode propCode);
+ void sendObjectAdded(MtpObjectHandle handle);
+ void sendObjectRemoved(MtpObjectHandle handle);
+
private:
void initObjectProperties();
diff --git a/media/mtp/MtpSqliteDatabase.cpp b/media/mtp/MtpSqliteDatabase.cpp
deleted file mode 100644
index fa3bdfe..0000000
--- a/media/mtp/MtpSqliteDatabase.cpp
+++ /dev/null
@@ -1,564 +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.
- */
-
-#define LOG_TAG "MtpSqliteDatabase"
-
-#include "MtpDebug.h"
-#include "MtpSqliteDatabase.h"
-#include "MtpDataPacket.h"
-#include "MtpUtils.h"
-#include "SqliteDatabase.h"
-#include "SqliteStatement.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sqlite3.h>
-
-namespace android {
-
-#define FILE_ID_COLUMN 1
-#define FILE_PATH_COLUMN 2
-#define FILE_FORMAT_COLUMN 3
-#define FILE_PARENT_COLUMN 4
-#define FILE_STORAGE_COLUMN 5
-#define FILE_SIZE_COLUMN 6
-#define FILE_MODIFIED_COLUMN 7
-
-#define AUDIO_ID_COLUMN 1
-#define AUDIO_TITLE_COLUMN 2
-#define AUDIO_ARTIST_COLUMN 3
-#define AUDIO_ALBUM_COLUMN 4
-#define AUDIO_ALBUM_ARTIST_COLUMN 5
-#define AUDIO_GENRE_COLUMN 6
-#define AUDIO_COMPOSER_COLUMN 7
-#define AUDIO_TRACK_NUMBER_COLUMN 8
-#define AUDIO_YEAR_COLUMN 9
-#define AUDIO_DURATION_COLUMN 10
-#define AUDIO_USE_COUNT_COLUMN 11
-#define AUDIO_SAMPLE_RATE_COLUMN 12
-#define AUDIO_NUM_CHANNELS_COLUMN 13
-#define AUDIO_AUDIO_WAVE_CODEC_COLUMN 14
-#define AUDIO_AUDIO_BIT_RATE_COLUMN 15
-
-#define FILE_TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \
- "_id INTEGER PRIMARY KEY," \
- "path TEXT," \
- "format INTEGER," \
- "parent INTEGER," \
- "storage INTEGER," \
- "size INTEGER," \
- "date_modified INTEGER" \
- ");"
-
-#define AUDIO_TABLE_CREATE "CREATE TABLE IF NOT EXISTS audio (" \
- "id INTEGER PRIMARY KEY," \
- "title TEXT," \
- "artist TEXT," \
- "album TEXT," \
- "album_artist TEXT," \
- "genre TEXT," \
- "composer TEXT," \
- "track_number INTEGER," \
- "year INTEGER," \
- "duration INTEGER," \
- "use_count INTEGER," \
- "sample_rate INTEGER," \
- "num_channels INTEGER," \
- "audio_wave_codec TEXT," \
- "audio_bit_rate INTEGER" \
- ");"
-
-#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);"
-
-#define FILE_ID_QUERY "SELECT _id,format FROM files WHERE path = ?;"
-#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?"
-
-#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_modified FROM files WHERE _id = ?;"
-#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?);"
-#define FILE_DELETE "DELETE FROM files WHERE _id = ?;"
-
-#define AUDIO_INSERT "INSERT INTO audio VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"
-#define AUDIO_DELETE "DELETE FROM audio WHERE id = ?;"
-
-struct PropertyTableEntry {
- MtpObjectProperty property;
- int type;
- const char* columnName;
-};
-
-static const PropertyTableEntry kPropertyTable[] = {
- { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" },
- { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" },
- { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" },
- { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" },
- { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" },
- { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" },
-};
-
-static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) {
- int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
- const PropertyTableEntry* entry = kPropertyTable;
- for (int i = 0; i < count; i++, entry++) {
- if (entry->property == property) {
- type = entry->type;
- columnName = entry->columnName;
- return true;
- }
- }
- return false;
-}
-
-MtpSqliteDatabase::MtpSqliteDatabase()
- : mDatabase(NULL),
- mFileIdQuery(NULL),
- mFilePathQuery(NULL),
- mObjectInfoQuery(NULL),
- mFileInserter(NULL),
- mFileDeleter(NULL),
- mAudioInserter(NULL),
- mAudioDeleter(NULL)
-{
-}
-
-MtpSqliteDatabase::~MtpSqliteDatabase() {
- delete mDatabase;
- delete mFileIdQuery;
- delete mFilePathQuery;
- delete mObjectInfoQuery;
- delete mFileInserter;
- delete mFileDeleter;
- delete mAudioInserter;
- delete mAudioDeleter;
-}
-
-bool MtpSqliteDatabase::open(const char* path, bool create) {
- mDatabase = new SqliteDatabase;
-
- if (!mDatabase->open(path, create))
- goto fail;
-
- // create tables and indices if necessary
- if (!mDatabase->exec(FILE_TABLE_CREATE)) {
- LOGE("could not create file table");
- goto fail;
- }
- if (!mDatabase->exec(PATH_INDEX_CREATE)) {
- LOGE("could not path index on file table");
- goto fail;
- }
- if (!mDatabase->exec(AUDIO_TABLE_CREATE)) {
- LOGE("could not create file table");
- goto fail;
- }
-
- if (!mFileIdQuery) {
- mFileIdQuery = new SqliteStatement(mDatabase);
- if (!mFileIdQuery->prepare(FILE_ID_QUERY)) {
- LOGE("could not compile FILE_ID_QUERY");
- goto fail;
- }
- }
- if (!mFilePathQuery) {
- mFilePathQuery = new SqliteStatement(mDatabase);
- if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
- LOGE("could not compile FILE_PATH_QUERY");
- goto fail;
- }
- }
- if (!mObjectInfoQuery) {
- mObjectInfoQuery = new SqliteStatement(mDatabase);
- if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
- LOGE("could not compile GET_OBJECT_INFO_QUERY");
- goto fail;
- }
- }
- if (!mFileInserter) {
- mFileInserter = new SqliteStatement(mDatabase);
- if (!mFileInserter->prepare(FILE_INSERT)) {
- LOGE("could not compile FILE_INSERT\n");
- goto fail;
- }
- }
- if (!mFileDeleter) {
- mFileDeleter = new SqliteStatement(mDatabase);
- if (!mFileDeleter->prepare(FILE_DELETE)) {
- LOGE("could not compile FILE_DELETE\n");
- goto fail;
- }
- }
- if (!mAudioInserter) {
- mAudioInserter = new SqliteStatement(mDatabase);
- if (!mAudioInserter->prepare(AUDIO_INSERT)) {
- LOGE("could not compile AUDIO_INSERT\n");
- goto fail;
- }
- }
- if (!mAudioDeleter) {
- mAudioDeleter = new SqliteStatement(mDatabase);
- if (!mAudioDeleter->prepare(AUDIO_DELETE)) {
- LOGE("could not compile AUDIO_DELETE\n");
- goto fail;
- }
- }
-
- return true;
-
-fail:
- delete mDatabase;
- delete mFileIdQuery;
- delete mFilePathQuery;
- delete mObjectInfoQuery;
- delete mFileInserter;
- delete mFileDeleter;
- delete mAudioInserter;
- delete mAudioDeleter;
- mDatabase = NULL;
- mFileIdQuery = NULL;
- mFilePathQuery = NULL;
- mObjectInfoQuery = NULL;
- mFileInserter = NULL;
- mFileDeleter = NULL;
- mAudioInserter = NULL;
- mAudioDeleter = NULL;
- return false;
-}
-
-void MtpSqliteDatabase::close() {
- if (mDatabase) {
- mDatabase->close();
- mDatabase = NULL;
- }
-}
-
-MtpObjectHandle MtpSqliteDatabase::getObjectHandle(const char* path) {
- mFileIdQuery->reset();
- mFileIdQuery->bind(1, path);
- if (mFileIdQuery->step()) {
- int row = mFileIdQuery->getColumnInt(0);
- if (row > 0) {
- MtpObjectFormat format = mFileIdQuery->getColumnInt(1);
- row |= getTableForFile(format);
- return row;
- }
- }
-
- return 0;
-}
-
-MtpObjectHandle MtpSqliteDatabase::addFile(const char* path,
- MtpObjectFormat format,
- MtpObjectHandle parent,
- MtpStorageID storage,
- uint64_t size,
- time_t modified) {
- mFileInserter->bind(FILE_PATH_COLUMN, path);
- mFileInserter->bind(FILE_FORMAT_COLUMN, format);
- mFileInserter->bind(FILE_PARENT_COLUMN, parent);
- mFileInserter->bind(FILE_STORAGE_COLUMN, storage);
- mFileInserter->bind(FILE_SIZE_COLUMN, size);
- mFileInserter->bind(FILE_MODIFIED_COLUMN, modified);
- mFileInserter->step();
- mFileInserter->reset();
- int result = mDatabase->lastInsertedRow();
- return (result <= 0 ? kInvalidObjectHandle : result);
-}
-
-MtpObjectHandle MtpSqliteDatabase::addAudioFile(MtpObjectHandle handle) {
- mAudioInserter->bind(AUDIO_ID_COLUMN, handle);
- mAudioInserter->step();
- mAudioInserter->reset();
- int result = mDatabase->lastInsertedRow();
- handle |= kObjectHandleTableAudio;
- return (result > 0 ? handle : kInvalidObjectHandle);
-}
-
-MtpObjectHandle MtpSqliteDatabase::addAudioFile(MtpObjectHandle handle,
- const char* title,
- const char* artist,
- const char* album,
- const char* albumArtist,
- const char* genre,
- const char* composer,
- const char* mimeType,
- int track,
- int year,
- int duration) {
- mAudioInserter->bind(AUDIO_ID_COLUMN, handle);
- if (title) mAudioInserter->bind(AUDIO_TITLE_COLUMN, title);
- if (artist) mAudioInserter->bind(AUDIO_ARTIST_COLUMN, artist);
- if (album) mAudioInserter->bind(AUDIO_ALBUM_COLUMN, album);
- if (albumArtist) mAudioInserter->bind(AUDIO_ALBUM_ARTIST_COLUMN, albumArtist);
- if (genre) mAudioInserter->bind(AUDIO_GENRE_COLUMN, genre);
- if (composer) mAudioInserter->bind(AUDIO_COMPOSER_COLUMN, composer);
- if (track) mAudioInserter->bind(AUDIO_TRACK_NUMBER_COLUMN, track);
- if (year) mAudioInserter->bind(AUDIO_YEAR_COLUMN, year);
- if (duration) mAudioInserter->bind(AUDIO_DURATION_COLUMN, duration);
- mAudioInserter->step();
- mAudioInserter->reset();
- int result = mDatabase->lastInsertedRow();
- if (result <= 0)
- return kInvalidObjectHandle;
- result |= kObjectHandleTableAudio;
- return result;
-}
-
-MtpObjectHandleList* MtpSqliteDatabase::getObjectList(MtpStorageID storageID,
- MtpObjectFormat format,
- MtpObjectHandle parent) {
- bool whereStorage = (storageID != 0xFFFFFFFF);
- bool whereFormat = (format != 0);
- bool whereParent = (parent != 0);
- char intBuffer[20];
-
- MtpString query("SELECT _id,format FROM files");
- if (whereStorage || whereFormat || whereParent)
- query += " WHERE";
- if (whereStorage) {
- snprintf(intBuffer, sizeof(intBuffer), "%d", storageID);
- query += " storage = ";
- query += intBuffer;
- }
- if (whereFormat) {
- snprintf(intBuffer, sizeof(intBuffer), "%d", format);
- if (whereStorage)
- query += " AND";
- query += " format = ";
- query += intBuffer;
- }
- if (whereParent) {
- if (parent != MTP_PARENT_ROOT)
- parent &= kObjectHandleIndexMask;
- snprintf(intBuffer, sizeof(intBuffer), "%d", parent);
- if (whereStorage || whereFormat)
- query += " AND";
- query += " parent = ";
- query += intBuffer;
- }
- query += ";";
-
- SqliteStatement stmt(mDatabase);
- LOGV("%s", (const char *)query);
- stmt.prepare(query);
-
- MtpObjectHandleList* list = new MtpObjectHandleList();
- while (!stmt.isDone()) {
- if (stmt.step()) {
- int index = stmt.getColumnInt(0);
- LOGV("stmt.getColumnInt returned %d", index);
- if (index > 0) {
- MtpObjectFormat format = stmt.getColumnInt(1);
- index |= getTableForFile(format);
- list->push(index);
- }
- }
- }
- LOGV("list size: %d", list->size());
- return list;
-}
-
-
-MtpResponseCode MtpSqliteDatabase::getObjectProperty(MtpObjectHandle handle,
- MtpObjectProperty property,
- MtpDataPacket& packet) {
- int type;
- const char* columnName;
- char intBuffer[20];
-
- if (handle != MTP_PARENT_ROOT)
- handle &= kObjectHandleIndexMask;
-
- if (!getPropertyInfo(property, type, columnName))
- return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
- snprintf(intBuffer, sizeof(intBuffer), "%d", handle);
-
- MtpString query("SELECT ");
- query += columnName;
- query += " FROM files WHERE _id = ";
- query += intBuffer;
- query += ";";
-
- SqliteStatement stmt(mDatabase);
- LOGV("%s", (const char *)query);
- stmt.prepare(query);
-
- if (!stmt.step())
- return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
-
- switch (type) {
- case MTP_TYPE_INT8:
- packet.putInt8(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_UINT8:
- packet.putUInt8(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_INT16:
- packet.putInt16(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_UINT16:
- packet.putUInt16(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_INT32:
- packet.putInt32(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_UINT32:
- packet.putUInt32(stmt.getColumnInt(0));
- break;
- case MTP_TYPE_INT64:
- packet.putInt64(stmt.getColumnInt64(0));
- break;
- case MTP_TYPE_UINT64:
- packet.putUInt64(stmt.getColumnInt64(0));
- break;
- case MTP_TYPE_STR:
- packet.putString(stmt.getColumnString(0));
- break;
- default:
- LOGE("unsupported object type\n");
- return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
- }
- return MTP_RESPONSE_OK;
-}
-
-MtpResponseCode MtpSqliteDatabase::getObjectInfo(MtpObjectHandle handle,
- MtpDataPacket& packet) {
- char date[20];
-
- if (handle != MTP_PARENT_ROOT)
- handle &= kObjectHandleIndexMask;
-
- mObjectInfoQuery->reset();
- mObjectInfoQuery->bind(1, handle);
- if (!mObjectInfoQuery->step())
- return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
-
- MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0);
- MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1);
- MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2);
- // extract name from path. do we want a separate database entry for this?
- const char* name = mObjectInfoQuery->getColumnString(3);
- const char* lastSlash = strrchr(name, '/');
- if (lastSlash)
- name = lastSlash + 1;
- int64_t size = mObjectInfoQuery->getColumnInt64(4);
- time_t modified = mObjectInfoQuery->getColumnInt(5);
- int associationType = (format == MTP_FORMAT_ASSOCIATION ?
- MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
- MTP_ASSOCIATION_TYPE_UNDEFINED);
-
- LOGV("storageID: %d, format: %d, parent: %d", storageID, format, parent);
-
- packet.putUInt32(storageID);
- packet.putUInt16(format);
- packet.putUInt16(0); // protection status
- packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
- packet.putUInt16(0); // thumb format
- packet.putUInt32(0); // thumb compressed size
- packet.putUInt32(0); // thumb pix width
- packet.putUInt32(0); // thumb pix height
- packet.putUInt32(0); // image pix width
- packet.putUInt32(0); // image pix height
- packet.putUInt32(0); // image bit depth
- packet.putUInt32(parent);
- packet.putUInt16(associationType);
- packet.putUInt32(0); // association desc
- packet.putUInt32(0); // sequence number
- packet.putString(name); // file name
- packet.putEmptyString();
- formatDateTime(modified, date, sizeof(date));
- packet.putString(date); // date modified
- packet.putEmptyString(); // keywords
-
- return MTP_RESPONSE_OK;
-}
-
-bool MtpSqliteDatabase::getObjectFilePath(MtpObjectHandle handle,
- MtpString& filePath,
- int64_t& fileLength) {
- if (handle != MTP_PARENT_ROOT)
- handle &= kObjectHandleIndexMask;
- mFilePathQuery->reset();
- mFilePathQuery->bind(1, handle);
- if (!mFilePathQuery->step())
- return false;
-
- const char* path = mFilePathQuery->getColumnString(0);
- if (!path)
- return false;
- filePath = path;
- fileLength = mFilePathQuery->getColumnInt64(1);
- return true;
-}
-
-bool MtpSqliteDatabase::deleteFile(MtpObjectHandle handle) {
- uint32_t table = handle & kObjectHandleTableMask;
- handle &= kObjectHandleIndexMask;
- mFileDeleter->bind(1, handle);
- mFileDeleter->step();
- mFileDeleter->reset();
- if (table == kObjectHandleTableAudio) {
- mAudioDeleter->bind(1, handle);
- mAudioDeleter->step();
- mAudioDeleter->reset();
- }
-
- return true;
-}
-
-MtpObjectHandle* MtpSqliteDatabase::getFileList(int& outCount) {
- MtpObjectHandle* result = NULL;
- int count = 0;
- SqliteStatement stmt(mDatabase);
- stmt.prepare("SELECT count(*) FROM files;");
-
- MtpObjectHandleList* list = new MtpObjectHandleList();
- if (stmt.step())
- count = stmt.getColumnInt(0);
-
- if (count > 0) {
- result = new MtpObjectHandle[count];
- memset(result, 0, count * sizeof(*result));
- SqliteStatement stmt2(mDatabase);
- stmt2.prepare("SELECT _id,format FROM files;");
-
- for (int i = 0; i < count; i++) {
- if (!stmt2.step()) {
- LOGW("getFileList ended early");
- count = i;
- break;
- }
- MtpObjectHandle handle = stmt2.getColumnInt(0);
- MtpObjectFormat format = stmt2.getColumnInt(1);
- handle |= getTableForFile(format);
- result[i] = handle;
- }
- }
- outCount = count;
- return result;
-}
-
-void MtpSqliteDatabase::beginTransaction() {
- mDatabase->beginTransaction();
-}
-
-void MtpSqliteDatabase::commitTransaction() {
- mDatabase->commitTransaction();
-}
-
-void MtpSqliteDatabase::rollbackTransaction() {
- mDatabase->rollbackTransaction();
-}
-
-} // namespace android
diff --git a/media/mtp/MtpSqliteDatabase.h b/media/mtp/MtpSqliteDatabase.h
deleted file mode 100644
index 74626a8..0000000
--- a/media/mtp/MtpSqliteDatabase.h
+++ /dev/null
@@ -1,96 +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.
- */
-
-#ifndef _MTP_SQLITE_DATABASE_H
-#define _MTP_SQLITE_DATABASE_H
-
-#include "MtpTypes.h"
-#include "MtpDatabase.h"
-
-class SqliteDatabase;
-
-namespace android {
-
-class MtpDataPacket;
-class SqliteStatement;
-
-class MtpSqliteDatabase : public MtpDatabase {
-private:
- SqliteDatabase* mDatabase;
- SqliteStatement* mFileIdQuery;
- SqliteStatement* mFilePathQuery;
- SqliteStatement* mObjectInfoQuery;
- SqliteStatement* mFileInserter;
- SqliteStatement* mFileDeleter;
- SqliteStatement* mAudioInserter;
- SqliteStatement* mAudioDeleter;
-
-public:
- MtpSqliteDatabase();
- virtual ~MtpSqliteDatabase();
-
- bool open(const char* path, bool create);
- void close();
-
- virtual MtpObjectHandle getObjectHandle(const char* path);
- virtual MtpObjectHandle addFile(const char* path,
- MtpObjectFormat format,
- MtpObjectHandle parent,
- MtpStorageID storage,
- uint64_t size,
- time_t modified);
-
- virtual MtpObjectHandle addAudioFile(MtpObjectHandle id);
-
- virtual MtpObjectHandle addAudioFile(MtpObjectHandle id,
- const char* title,
- const char* artist,
- const char* album,
- const char* albumArtist,
- const char* genre,
- const char* composer,
- const char* mimeType,
- int track,
- int year,
- int duration);
-
- virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
- MtpObjectFormat format,
- MtpObjectHandle parent);
-
- virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
- MtpObjectProperty property,
- MtpDataPacket& packet);
-
- virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
- MtpDataPacket& packet);
-
- virtual bool getObjectFilePath(MtpObjectHandle handle,
- MtpString& filePath,
- int64_t& fileLength);
- virtual bool deleteFile(MtpObjectHandle handle);
-
- // helper for media scanner
- virtual MtpObjectHandle* getFileList(int& outCount);
-
- virtual void beginTransaction();
- virtual void commitTransaction();
- virtual void rollbackTransaction();
-};
-
-}; // namespace android
-
-#endif // _MTP_SQLITE_DATABASE_H
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
index 7e89a90..eccf186 100644
--- a/media/mtp/MtpStorage.cpp
+++ b/media/mtp/MtpStorage.cpp
@@ -19,7 +19,6 @@
#include "MtpDebug.h"
#include "MtpDatabase.h"
#include "MtpStorage.h"
-#include "MtpMediaScanner.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -78,9 +77,4 @@
return "Device Storage";
}
-bool MtpStorage::scanFiles() {
- MtpMediaScanner scanner(mStorageID, mFilePath, mDatabase);
- return scanner.scanFiles();
-}
-
} // namespace android
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
index 6097272..b13b926 100644
--- a/media/mtp/MtpStorage.h
+++ b/media/mtp/MtpStorage.h
@@ -22,7 +22,6 @@
namespace android {
class MtpDatabase;
-class SqliteStatement;
class MtpStorage {
@@ -44,8 +43,6 @@
uint64_t getFreeSpace();
const char* getDescription() const;
inline const char* getPath() const { return mFilePath; }
-
- bool scanFiles();
};
}; // namespace android
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index b7c79b2..2a895a7 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -28,6 +28,7 @@
typedef uint16_t MtpOperationCode;
typedef uint16_t MtpResponseCode;
+typedef uint16_t MtpEventCode;
typedef uint32_t MtpSessionID;
typedef uint32_t MtpStorageID;
typedef uint32_t MtpTransactionID;
@@ -60,16 +61,6 @@
#define MTP_PARENT_ROOT 0xFFFFFFFF // parent is root of the storage
#define kInvalidObjectHandle 0xFFFFFFFF
-// MtpObjectHandle bits and masks
-#define kObjectHandleMarkBit 0x80000000 // used for mark & sweep by MtpMediaScanner
-#define kObjectHandleTableMask 0x70000000 // mask for object table
-#define kObjectHandleTableFile 0x00000000 // object is only in the file table
-#define kObjectHandleTableAudio 0x10000000 // object is in the audio table
-#define kObjectHandleTableVideo 0x20000000 // object is in the video table
-#define kObjectHandleTableImage 0x30000000 // object is in the images table
-#define kObjectHandleTablePlaylist 0x40000000 // object is in the playlist table
-#define kObjectHandleIndexMask 0x0FFFFFFF // mask for object index in file table
-
class MtpStorage;
class MtpDevice;
class MtpProperty;
diff --git a/media/mtp/SqliteDatabase.cpp b/media/mtp/SqliteDatabase.cpp
deleted file mode 100644
index 1de3a41..0000000
--- a/media/mtp/SqliteDatabase.cpp
+++ /dev/null
@@ -1,88 +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.
- */
-
-#define LOG_TAG "SqliteDatabase"
-
-#include "MtpDebug.h"
-#include "SqliteDatabase.h"
-#include "SqliteStatement.h"
-
-#include <stdio.h>
-#include <sqlite3.h>
-
-namespace android {
-
-SqliteDatabase::SqliteDatabase()
- : mDatabaseHandle(NULL)
-{
-}
-
-SqliteDatabase::~SqliteDatabase() {
- close();
-}
-
-bool SqliteDatabase::open(const char* path, bool create) {
- int flags = SQLITE_OPEN_READWRITE;
- if (create) flags |= SQLITE_OPEN_CREATE;
- // SQLITE_OPEN_NOMUTEX?
- int ret = sqlite3_open_v2(path, &mDatabaseHandle, flags, NULL);
- if (ret) {
- LOGE("could not open database\n");
- return false;
- }
- return true;
-}
-
-void SqliteDatabase::close() {
- if (mDatabaseHandle) {
- sqlite3_close(mDatabaseHandle);
- mDatabaseHandle = NULL;
- }
-}
-
-bool SqliteDatabase::exec(const char* sql) {
- return (sqlite3_exec(mDatabaseHandle, sql, NULL, NULL, NULL) == 0);
-}
-
-int SqliteDatabase::lastInsertedRow() {
- return sqlite3_last_insert_rowid(mDatabaseHandle);
-}
-
-void SqliteDatabase::beginTransaction() {
- exec("BEGIN TRANSACTION");
-}
-
-void SqliteDatabase::commitTransaction() {
- exec("COMMIT TRANSACTION");
-}
-
-void SqliteDatabase::rollbackTransaction() {
- exec("ROLLBACK TRANSACTION");
-}
-
-int SqliteDatabase::getVersion() {
- SqliteStatement stmt(this);
- stmt.prepare("PRAGMA user_version;");
- stmt.step();
- return stmt.getColumnInt(0);
-}
-void SqliteDatabase::setVersion(int version) {
- char buffer[40];
- snprintf(buffer, sizeof(buffer), "PRAGMA user_version = %d", version);
- exec(buffer);
-}
-
-} // namespace android
diff --git a/media/mtp/SqliteDatabase.h b/media/mtp/SqliteDatabase.h
deleted file mode 100644
index 7d008f9..0000000
--- a/media/mtp/SqliteDatabase.h
+++ /dev/null
@@ -1,50 +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.
- */
-
-#ifndef _SQLITE_DATABASE_H
-#define _SQLITE_DATABASE_H
-
-typedef struct sqlite3 sqlite3;
-
-namespace android {
-
-class SqliteDatabase {
-private:
- sqlite3* mDatabaseHandle;
-
-public:
- SqliteDatabase();
- virtual ~SqliteDatabase();
-
- bool open(const char* path, bool create);
- void close();
-
- bool exec(const char* sql);
- int lastInsertedRow();
-
- void beginTransaction();
- void commitTransaction();
- void rollbackTransaction();
-
- int getVersion();
- void setVersion(int version);
-
- inline sqlite3* getDatabaseHandle() const { return mDatabaseHandle; }
-};
-
-}; // namespace android
-
-#endif // _SQLITE_DATABASE_H
diff --git a/media/mtp/SqliteStatement.cpp b/media/mtp/SqliteStatement.cpp
deleted file mode 100644
index adef7ae..0000000
--- a/media/mtp/SqliteStatement.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SqliteStatement"
-
-#include "SqliteStatement.h"
-#include "SqliteDatabase.h"
-
-#include <stdio.h>
-#include <sqlite3.h>
-
-namespace android {
-
-SqliteStatement::SqliteStatement(SqliteDatabase* db)
- : mDatabaseHandle(db->getDatabaseHandle()),
- mStatement(NULL),
- mDone(false)
-{
-}
-
-SqliteStatement::~SqliteStatement() {
- finalize();
-}
-
-bool SqliteStatement::prepare(const char* sql) {
- return (sqlite3_prepare_v2(mDatabaseHandle, sql, -1, &mStatement, NULL) == 0);
-}
-
-bool SqliteStatement::step() {
- int ret = sqlite3_step(mStatement);
- if (ret == SQLITE_DONE) {
- mDone = true;
- return true;
- }
- return (ret == SQLITE_OK || ret == SQLITE_ROW);
-}
-
-void SqliteStatement::reset() {
- sqlite3_reset(mStatement);
- mDone = false;
-}
-
-void SqliteStatement::finalize() {
- if (mStatement) {
- sqlite3_finalize(mStatement);
- mStatement = NULL;
- }
-}
-
-void SqliteStatement::bind(int column, int value) {
- sqlite3_bind_int(mStatement, column, value);
-}
-
-void SqliteStatement::bind(int column, const char* value) {
- sqlite3_bind_text(mStatement, column, value, -1, SQLITE_TRANSIENT);
-}
-
-int SqliteStatement::getColumnInt(int column) {
- return sqlite3_column_int(mStatement, column);
-}
-
-int64_t SqliteStatement::getColumnInt64(int column) {
- return sqlite3_column_int64(mStatement, column);
-}
-
-const char* SqliteStatement::getColumnString(int column) {
- return (const char *)sqlite3_column_text(mStatement, column);
-}
-
-} // namespace android
diff --git a/media/mtp/SqliteStatement.h b/media/mtp/SqliteStatement.h
deleted file mode 100644
index c0ebfff..0000000
--- a/media/mtp/SqliteStatement.h
+++ /dev/null
@@ -1,56 +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.
- */
-
-#ifndef _SQLITE_STATEMENT_H
-#define _SQLITE_STATEMENT_H
-
-#include <stdint.h>
-
-typedef struct sqlite3 sqlite3;
-typedef struct sqlite3_stmt sqlite3_stmt;
-
-namespace android {
-
-class SqliteDatabase;
-
-class SqliteStatement {
-private:
- sqlite3* mDatabaseHandle;
- sqlite3_stmt* mStatement;
- bool mDone;
-
-public:
- SqliteStatement(SqliteDatabase* db);
- virtual ~SqliteStatement();
-
- bool prepare(const char* sql);
- bool step();
- void reset();
- void finalize();
-
- void bind(int column, int value);
- void bind(int column, const char* value);
-
- int getColumnInt(int column);
- int64_t getColumnInt64(int column);
- const char* getColumnString(int column);
-
- inline bool isDone() const { return mDone; }
-};
-
-}; // namespace android
-
-#endif // _SQLITE_STATEMENT_H
diff --git a/media/mtp/f_mtp.h b/media/mtp/f_mtp.h
deleted file mode 100644
index c1c9aef..0000000
--- a/media/mtp/f_mtp.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Gadget Function Driver for MTP
- *
- * Copyright (C) 2010 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __LINUX_USB_F_MTP_H
-#define __LINUX_USB_F_MTP_H
-
-/* Constants for MTP_SET_INTERFACE_MODE */
-#define MTP_INTERFACE_MODE_MTP 0
-#define MTP_INTERFACE_MODE_PTP 1
-
-
-struct mtp_file_range {
- /* file descriptor for file to transfer */
- int fd;
- /* offset in file for start of transfer */
- loff_t offset;
- /* number of bytes to transfer */
- size_t length;
-};
-
-/* Sends the specified file range to the host */
-#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range)
-/* Receives data from the host and writes it to a file.
- * The file is created if it does not exist.
- */
-#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range)
-/* Sets the driver mode to either MTP or PTP */
-#define MTP_SET_INTERFACE_MODE _IOW('M', 2, int)
-
-#endif /* __LINUX_USB_F_MTP_H */
diff --git a/media/mtp/mtptest.cpp b/media/mtp/mtptest.cpp
deleted file mode 100644
index af0f77f..0000000
--- a/media/mtp/mtptest.cpp
+++ /dev/null
@@ -1,88 +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.
- */
-
-#define LOG_TAG "mtp_usb"
-
-#include "MtpDebug.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include "MtpServer.h"
-#include "MtpStorage.h"
-#include "f_mtp.h"
-#include "private/android_filesystem_config.h"
-
-using namespace android;
-
-static bool enable_usb_function(const char* name, bool enable) {
- char path[PATH_MAX];
-
- snprintf(path, sizeof(path), "/sys/class/usb_composite/%s/enable", name);
- int fd = open(path, O_RDWR);
- if (fd < 0) {
- fprintf(stderr, "could not open %s in enable_usb_function\n", path);
- return false;
- }
- write(fd, enable ? "1" : "0", 2);
- close(fd);
- return true;
-}
-
-int main(int argc, char* argv[]) {
- bool usePTP = false;
- const char* storagePath = "/sdcard";
-
- for (int i = 1; i < argc; i++) {
- const char* arg = argv[i];
- if (!strcmp(arg, "-p"))
- usePTP = true;
- else if (arg[0] == '/')
- storagePath = arg;
- }
-
- int fd = open("/dev/mtp_usb", O_RDWR);
- printf("open returned %d\n", fd);
- if (fd < 0) {
- fprintf(stderr, "could not open MTP driver\n");
- return -1;
- }
-
- if (usePTP) {
- // set driver mode to PTP
- int ret = ioctl(fd, MTP_SET_INTERFACE_MODE, MTP_INTERFACE_MODE_PTP);
- if (ret) {
- fprintf(stderr, "MTP_SET_INTERFACE_MODE failed\n");
- return -1;
- }
- }
-
- // disable UMS and enable MTP USB functions
- enable_usb_function("usb_mass_storage", false);
- enable_usb_function("mtp", true);
-
- MtpServer server(fd, "/data/data/mtp/mtp.db", AID_SDCARD_RW, 0664, 0775);
- server.addStorage(storagePath);
- server.scanStorage();
- server.run();
-
- close(fd);
- return 0;
-}
-
diff --git a/media/mtp/scantest.cpp b/media/mtp/scantest.cpp
deleted file mode 100644
index 3702a5d..0000000
--- a/media/mtp/scantest.cpp
+++ /dev/null
@@ -1,38 +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.
- */
-
-#include <stdio.h>
-
-#include "MtpSqliteDatabase.h"
-#include "MtpMediaScanner.h"
-
-using namespace android;
-
-int main(int argc, char* argv[]) {
- if (argc != 2) {
- fprintf(stderr, "usage: %s <storage path>\n", argv[0]);
- return -1;
- }
-
- MtpSqliteDatabase* database = new MtpSqliteDatabase();
- database->open("scantest.db", true);
-
- MtpMediaScanner scanner(1, argv[1], database);
- scanner.scanFiles();
- database->close();
-
- return 0;
-}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 376c64f..2fe0679 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -6,17 +6,20 @@
# our source files
#
LOCAL_SRC_FILES:= \
- activity.cpp \
input.cpp \
looper.cpp \
- native_window.cpp
+ native_activity.cpp \
+ native_window.cpp \
+ sensor.cpp
LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
libcutils \
libutils \
libbinder \
- libui
+ libui \
+ libgui \
+ libsurfaceflinger_client \
+ libandroid_runtime
LOCAL_C_INCLUDES += \
frameworks/base/native/include \
diff --git a/native/android/activity.cpp b/native/android/activity.cpp
deleted file mode 100644
index e69de29..0000000
--- a/native/android/activity.cpp
+++ /dev/null
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 015a1ce..a4dde51 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -22,6 +22,8 @@
#include <ui/InputTransport.h>
#include <utils/PollLoop.h>
+#include <android_runtime/android_app_NativeActivity.h>
+
#include <poll.h>
using android::InputEvent;
@@ -186,66 +188,22 @@
}
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
- ALooper_callbackFunc callback, void* data) {
- queue->setPollLoop(static_cast<android::PollLoop*>(looper));
- ALooper_setCallback(looper, queue->getConsumer().getChannel()->getReceivePipeFd(),
- POLLIN, callback, data);
+ ALooper_callbackFunc* callback, void* data) {
+ queue->attachLooper(looper, callback, data);
}
void AInputQueue_detachLooper(AInputQueue* queue) {
- queue->getPollLoop()->removeCallback(
- queue->getConsumer().getChannel()->getReceivePipeFd());
+ queue->detachLooper();
}
int AInputQueue_hasEvents(AInputQueue* queue) {
- struct pollfd pfd;
-
- pfd.fd = queue->getConsumer().getChannel()->getReceivePipeFd();
- pfd.events = POLLIN;
- pfd.revents = 0;
-
- int nfd = poll(&pfd, 1, 0);
- if (nfd <= 0) return nfd;
- return pfd.revents == POLLIN ? 1 : -1;
+ return queue->hasEvents();
}
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) {
- *outEvent = NULL;
-
- int32_t res = queue->getConsumer().receiveDispatchSignal();
- if (res != android::OK) {
- LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
- queue->getConsumer().getChannel()->getName().string(), res);
- return -1;
- }
-
- InputEvent* myEvent = NULL;
- res = queue->consume(&myEvent);
- if (res != android::OK) {
- LOGW("channel '%s' ~ Failed to consume input event. status=%d",
- queue->getConsumer().getChannel()->getName().string(), res);
- queue->getConsumer().sendFinishedSignal();
- return -1;
- }
-
- *outEvent = myEvent;
- return 0;
+ return queue->getEvent(outEvent);
}
-void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event,
- int handled) {
- if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY
- && ((KeyEvent*)event)->hasDefaultAction()) {
- // The app didn't handle this, but it may have a default action
- // associated with it. We need to hand this back to Java to be
- // executed.
- queue->doDefaultKey((KeyEvent*)event);
- return;
- }
-
- int32_t res = queue->getConsumer().sendFinishedSignal();
- if (res != android::OK) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- queue->getConsumer().getChannel()->getName().string(), res);
- }
+void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
+ queue->finishEvent(event, handled != 0);
}
diff --git a/native/android/looper.cpp b/native/android/looper.cpp
index 6e78bbd..1564c47 100644
--- a/native/android/looper.cpp
+++ b/native/android/looper.cpp
@@ -27,22 +27,41 @@
return PollLoop::getForThread().get();
}
-ALooper* ALooper_prepare() {
+ALooper* ALooper_prepare(int32_t opts) {
+ bool allowFds = (opts&ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) != 0;
sp<PollLoop> loop = PollLoop::getForThread();
if (loop == NULL) {
- loop = new PollLoop();
+ loop = new PollLoop(allowFds);
PollLoop::setForThread(loop);
}
+ if (loop->getAllowNonCallbacks() != allowFds) {
+ LOGW("ALooper_prepare again with different ALOOPER_PREPARE_ALLOW_NON_CALLBACKS");
+ }
return loop.get();
}
-int32_t ALooper_pollOnce(int timeoutMillis) {
+int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData) {
sp<PollLoop> loop = PollLoop::getForThread();
if (loop == NULL) {
LOGW("ALooper_pollOnce: No looper for this thread!");
return -1;
}
- return loop->pollOnce(timeoutMillis) ? 1 : 0;
+ return loop->pollOnce(timeoutMillis, outEvents, outData);
+}
+
+int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData) {
+ sp<PollLoop> loop = PollLoop::getForThread();
+ if (loop == NULL) {
+ LOGW("ALooper_pollOnce: No looper for this thread!");
+ return -1;
+ }
+
+ int32_t result;
+ while ((result = loop->pollOnce(timeoutMillis, outEvents, outData)) == ALOOPER_POLL_CALLBACK) {
+ ;
+ }
+
+ return result;
}
void ALooper_acquire(ALooper* looper) {
@@ -53,11 +72,11 @@
static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire);
}
-void ALooper_setCallback(ALooper* looper, int fd, int events,
+void ALooper_addFd(ALooper* looper, int fd, int events,
ALooper_callbackFunc* callback, void* data) {
static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data);
}
-int32_t ALooper_removeCallback(ALooper* looper, int fd) {
+int32_t ALooper_removeFd(ALooper* looper, int fd) {
return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0;
}
diff --git a/native/android/native_activity.cpp b/native/android/native_activity.cpp
new file mode 100644
index 0000000..0c6823a
--- /dev/null
+++ b/native/android/native_activity.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "native_activity"
+#include <utils/Log.h>
+
+#include <android_runtime/android_app_NativeActivity.h>
+
+using namespace android;
+
+void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format) {
+ android_NativeActivity_setWindowFormat(activity, format);
+}
+
+void ANativeActivity_setWindowFlags(ANativeActivity* activity,
+ uint32_t addFlags, uint32_t removeFlags) {
+ android_NativeActivity_setWindowFlags(activity, addFlags, addFlags|removeFlags);
+}
+
+void ANativeActivity_showSoftInput(ANativeActivity* activity, uint32_t flags) {
+ android_NativeActivity_showSoftInput(activity, flags);
+}
+
+void ANativeActivity_hideSoftInput(ANativeActivity* activity, uint32_t flags) {
+ android_NativeActivity_hideSoftInput(activity, flags);
+}
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
index 448cbfc..bada078 100644
--- a/native/android/native_window.cpp
+++ b/native/android/native_window.cpp
@@ -17,10 +17,27 @@
#define LOG_TAG "Surface"
#include <utils/Log.h>
-#include <android/native_window.h>
+#include <android/native_window_jni.h>
#include <surfaceflinger/Surface.h>
+#include <android_runtime/android_view_Surface.h>
-using android::Surface;
+using namespace android;
+
+ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
+ sp<ANativeWindow> win = android_Surface_getNativeWindow(env, surface);
+ if (win != NULL) {
+ win->incStrong((void*)ANativeWindow_acquire);
+ }
+ return win.get();
+}
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+ window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+ window->decStrong((void*)ANativeWindow_acquire);
+}
static int32_t getWindowProp(ANativeWindow* window, int what) {
int value;
@@ -41,7 +58,40 @@
}
int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
- int32_t height, int32_t format) {
- native_window_set_buffers_geometry(window, width, height, format);
+ int32_t height) {
+ native_window_set_buffers_geometry(window, width, height, 0);
return 0;
}
+
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds) {
+ Region dirtyRegion;
+ Region* dirtyParam = NULL;
+ if (inOutDirtyBounds != NULL) {
+ dirtyRegion.set(*(Rect*)inOutDirtyBounds);
+ dirtyParam = &dirtyRegion;
+ }
+
+ Surface::SurfaceInfo info;
+ status_t res = static_cast<Surface*>(window)->lock(&info, dirtyParam);
+ if (res != OK) {
+ return -1;
+ }
+
+ outBuffer->width = (int32_t)info.w;
+ outBuffer->height = (int32_t)info.h;
+ outBuffer->stride = (int32_t)info.s;
+ outBuffer->format = (int32_t)info.format;
+ outBuffer->bits = info.bits;
+
+ if (inOutDirtyBounds != NULL) {
+ *inOutDirtyBounds = dirtyRegion.getBounds();
+ }
+
+ return 0;
+}
+
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
+ status_t res = static_cast<Surface*>(window)->unlockAndPost();
+ return res == android::OK ? 0 : -1;
+}
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
new file mode 100644
index 0000000..7a3907e
--- /dev/null
+++ b/native/android/sensor.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "sensor"
+#include <utils/Log.h>
+
+#include <android/looper.h>
+#include <android/sensor.h>
+
+#include <utils/RefBase.h>
+#include <utils/PollLoop.h>
+#include <utils/Timers.h>
+
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
+
+#include <poll.h>
+
+using android::sp;
+using android::Sensor;
+using android::SensorManager;
+using android::SensorEventQueue;
+using android::String8;
+
+/*****************************************************************************/
+
+ASensorManager* ASensorManager_getInstance()
+{
+ return &SensorManager::getInstance();
+}
+
+int ASensorManager_getSensorList(ASensorManager* manager, ASensor** list)
+{
+ Sensor* l;
+ int c = static_cast<SensorManager*>(manager)->getSensorList(&l);
+ if (list) {
+ *list = l;
+ }
+ return c;
+}
+
+ASensor* ASensorManager_getDefaultSensor(ASensorManager* manager, int type)
+{
+ return static_cast<SensorManager*>(manager)->getDefaultSensor(type);
+}
+
+ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
+ ALooper* looper, ALooper_callbackFunc* callback, void* data)
+{
+ sp<SensorEventQueue> queue =
+ static_cast<SensorManager*>(manager)->createEventQueue();
+ if (queue != 0) {
+ ALooper_addFd(looper, queue->getFd(), POLLIN, callback, data);
+ queue->looper = looper;
+ queue->incStrong(manager);
+ }
+ return static_cast<ASensorEventQueue*>(queue.get());
+}
+
+int ASensorManager_destroyEventQueue(ASensorManager* manager,
+ ASensorEventQueue* inQueue)
+{
+ sp<SensorEventQueue> queue = static_cast<SensorEventQueue*>(inQueue);
+ ALooper_removeFd(queue->looper, queue->getFd());
+ queue->decStrong(manager);
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor* sensor)
+{
+ return static_cast<SensorEventQueue*>(queue)->enableSensor(
+ static_cast<Sensor*>(sensor));
+}
+
+int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor)
+{
+ return static_cast<SensorEventQueue*>(queue)->disableSensor(
+ static_cast<Sensor*>(sensor));
+}
+
+int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor* sensor,
+ int32_t usec)
+{
+ return static_cast<SensorEventQueue*>(queue)->setEventRate(
+ static_cast<Sensor*>(sensor), us2ns(usec));
+}
+
+int ASensorEventQueue_hasEvents(ASensorEventQueue* queue)
+{
+ struct pollfd pfd;
+ pfd.fd = static_cast<SensorEventQueue*>(queue)->getFd();
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ int nfd = poll(&pfd, 1, 0);
+
+ if (nfd < 0)
+ return -errno;
+
+ if (pfd.revents != POLLIN)
+ return -1;
+
+ return (nfd == 0) ? 0 : 1;
+}
+
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
+ ASensorEvent* events, size_t count)
+{
+ return static_cast<SensorEventQueue*>(queue)->read(events, count);
+}
+
+
+/*****************************************************************************/
+
+const char* ASensor_getName(ASensor* sensor)
+{
+ return static_cast<Sensor*>(sensor)->getName().string();
+}
+
+const char* ASensor_getVendor(ASensor* sensor)
+{
+ return static_cast<Sensor*>(sensor)->getVendor().string();
+}
+
+int ASensor_getType(ASensor* sensor)
+{
+ return static_cast<Sensor*>(sensor)->getType();
+}
+
+float ASensor_getResolution(ASensor* sensor)
+{
+ return static_cast<Sensor*>(sensor)->getResolution();
+}
+
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 75be85a..25dd68e 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -68,7 +68,10 @@
INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
/* The input device is a directional pad. */
- INPUT_DEVICE_CLASS_DPAD = 0x00000020
+ INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+
+ /* The input device is a gamepad (implies keyboard). */
+ INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040
};
/*
@@ -534,10 +537,11 @@
typedef struct AInputQueue AInputQueue;
/*
- * Add this input queue to a looper for processing.
+ * Add this input queue to a looper for processing. See
+ * ALooper_addFd() for information on the callback and data params.
*/
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
- ALooper_callbackFunc callback, void* data);
+ ALooper_callbackFunc* callback, void* data);
/*
* Remove the input queue from the looper it is currently attached to.
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index 36855c5..496eccc0 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -41,114 +41,122 @@
/*
* Key codes.
- *
- * XXX: The declarations in <ui/KeycodeLabel.h> should be updated to use these instead.
- * We should probably move this into android/keycodes.h and add some new API for
- * getting labels so that we can remove the other tables also in KeycodeLabel.h.
*/
enum {
- KEYCODE_UNKNOWN = 0,
- KEYCODE_SOFT_LEFT = 1,
- KEYCODE_SOFT_RIGHT = 2,
- KEYCODE_HOME = 3,
- KEYCODE_BACK = 4,
- KEYCODE_CALL = 5,
- KEYCODE_ENDCALL = 6,
- KEYCODE_0 = 7,
- KEYCODE_1 = 8,
- KEYCODE_2 = 9,
- KEYCODE_3 = 10,
- KEYCODE_4 = 11,
- KEYCODE_5 = 12,
- KEYCODE_6 = 13,
- KEYCODE_7 = 14,
- KEYCODE_8 = 15,
- KEYCODE_9 = 16,
- KEYCODE_STAR = 17,
- KEYCODE_POUND = 18,
- KEYCODE_DPAD_UP = 19,
- KEYCODE_DPAD_DOWN = 20,
- KEYCODE_DPAD_LEFT = 21,
- KEYCODE_DPAD_RIGHT = 22,
- KEYCODE_DPAD_CENTER = 23,
- KEYCODE_VOLUME_UP = 24,
- KEYCODE_VOLUME_DOWN = 25,
- KEYCODE_POWER = 26,
- KEYCODE_CAMERA = 27,
- KEYCODE_CLEAR = 28,
- KEYCODE_A = 29,
- KEYCODE_B = 30,
- KEYCODE_C = 31,
- KEYCODE_D = 32,
- KEYCODE_E = 33,
- KEYCODE_F = 34,
- KEYCODE_G = 35,
- KEYCODE_H = 36,
- KEYCODE_I = 37,
- KEYCODE_J = 38,
- KEYCODE_K = 39,
- KEYCODE_L = 40,
- KEYCODE_M = 41,
- KEYCODE_N = 42,
- KEYCODE_O = 43,
- KEYCODE_P = 44,
- KEYCODE_Q = 45,
- KEYCODE_R = 46,
- KEYCODE_S = 47,
- KEYCODE_T = 48,
- KEYCODE_U = 49,
- KEYCODE_V = 50,
- KEYCODE_W = 51,
- KEYCODE_X = 52,
- KEYCODE_Y = 53,
- KEYCODE_Z = 54,
- KEYCODE_COMMA = 55,
- KEYCODE_PERIOD = 56,
- KEYCODE_ALT_LEFT = 57,
- KEYCODE_ALT_RIGHT = 58,
- KEYCODE_SHIFT_LEFT = 59,
- KEYCODE_SHIFT_RIGHT = 60,
- KEYCODE_TAB = 61,
- KEYCODE_SPACE = 62,
- KEYCODE_SYM = 63,
- KEYCODE_EXPLORER = 64,
- KEYCODE_ENVELOPE = 65,
- KEYCODE_ENTER = 66,
- KEYCODE_DEL = 67,
- KEYCODE_GRAVE = 68,
- KEYCODE_MINUS = 69,
- KEYCODE_EQUALS = 70,
- KEYCODE_LEFT_BRACKET = 71,
- KEYCODE_RIGHT_BRACKET = 72,
- KEYCODE_BACKSLASH = 73,
- KEYCODE_SEMICOLON = 74,
- KEYCODE_APOSTROPHE = 75,
- KEYCODE_SLASH = 76,
- KEYCODE_AT = 77,
- KEYCODE_NUM = 78,
- KEYCODE_HEADSETHOOK = 79,
- KEYCODE_FOCUS = 80, // *Camera* focus
- KEYCODE_PLUS = 81,
- KEYCODE_MENU = 82,
- KEYCODE_NOTIFICATION = 83,
- KEYCODE_SEARCH = 84,
- KEYCODE_MEDIA_PLAY_PAUSE= 85,
- KEYCODE_MEDIA_STOP = 86,
- KEYCODE_MEDIA_NEXT = 87,
- KEYCODE_MEDIA_PREVIOUS = 88,
- KEYCODE_MEDIA_REWIND = 89,
- KEYCODE_MEDIA_FAST_FORWARD = 90,
- KEYCODE_MUTE = 91,
- KEYCODE_PAGE_UP = 92,
- KEYCODE_PAGE_DOWN = 93
+ AKEYCODE_UNKNOWN = 0,
+ AKEYCODE_SOFT_LEFT = 1,
+ AKEYCODE_SOFT_RIGHT = 2,
+ AKEYCODE_HOME = 3,
+ AKEYCODE_BACK = 4,
+ AKEYCODE_CALL = 5,
+ AKEYCODE_ENDCALL = 6,
+ AKEYCODE_0 = 7,
+ AKEYCODE_1 = 8,
+ AKEYCODE_2 = 9,
+ AKEYCODE_3 = 10,
+ AKEYCODE_4 = 11,
+ AKEYCODE_5 = 12,
+ AKEYCODE_6 = 13,
+ AKEYCODE_7 = 14,
+ AKEYCODE_8 = 15,
+ AKEYCODE_9 = 16,
+ AKEYCODE_STAR = 17,
+ AKEYCODE_POUND = 18,
+ AKEYCODE_DPAD_UP = 19,
+ AKEYCODE_DPAD_DOWN = 20,
+ AKEYCODE_DPAD_LEFT = 21,
+ AKEYCODE_DPAD_RIGHT = 22,
+ AKEYCODE_DPAD_CENTER = 23,
+ AKEYCODE_VOLUME_UP = 24,
+ AKEYCODE_VOLUME_DOWN = 25,
+ AKEYCODE_POWER = 26,
+ AKEYCODE_CAMERA = 27,
+ AKEYCODE_CLEAR = 28,
+ AKEYCODE_A = 29,
+ AKEYCODE_B = 30,
+ AKEYCODE_C = 31,
+ AKEYCODE_D = 32,
+ AKEYCODE_E = 33,
+ AKEYCODE_F = 34,
+ AKEYCODE_G = 35,
+ AKEYCODE_H = 36,
+ AKEYCODE_I = 37,
+ AKEYCODE_J = 38,
+ AKEYCODE_K = 39,
+ AKEYCODE_L = 40,
+ AKEYCODE_M = 41,
+ AKEYCODE_N = 42,
+ AKEYCODE_O = 43,
+ AKEYCODE_P = 44,
+ AKEYCODE_Q = 45,
+ AKEYCODE_R = 46,
+ AKEYCODE_S = 47,
+ AKEYCODE_T = 48,
+ AKEYCODE_U = 49,
+ AKEYCODE_V = 50,
+ AKEYCODE_W = 51,
+ AKEYCODE_X = 52,
+ AKEYCODE_Y = 53,
+ AKEYCODE_Z = 54,
+ AKEYCODE_COMMA = 55,
+ AKEYCODE_PERIOD = 56,
+ AKEYCODE_ALT_LEFT = 57,
+ AKEYCODE_ALT_RIGHT = 58,
+ AKEYCODE_SHIFT_LEFT = 59,
+ AKEYCODE_SHIFT_RIGHT = 60,
+ AKEYCODE_TAB = 61,
+ AKEYCODE_SPACE = 62,
+ AKEYCODE_SYM = 63,
+ AKEYCODE_EXPLORER = 64,
+ AKEYCODE_ENVELOPE = 65,
+ AKEYCODE_ENTER = 66,
+ AKEYCODE_DEL = 67,
+ AKEYCODE_GRAVE = 68,
+ AKEYCODE_MINUS = 69,
+ AKEYCODE_EQUALS = 70,
+ AKEYCODE_LEFT_BRACKET = 71,
+ AKEYCODE_RIGHT_BRACKET = 72,
+ AKEYCODE_BACKSLASH = 73,
+ AKEYCODE_SEMICOLON = 74,
+ AKEYCODE_APOSTROPHE = 75,
+ AKEYCODE_SLASH = 76,
+ AKEYCODE_AT = 77,
+ AKEYCODE_NUM = 78,
+ AKEYCODE_HEADSETHOOK = 79,
+ AKEYCODE_FOCUS = 80, // *Camera* focus
+ AKEYCODE_PLUS = 81,
+ AKEYCODE_MENU = 82,
+ AKEYCODE_NOTIFICATION = 83,
+ AKEYCODE_SEARCH = 84,
+ AKEYCODE_MEDIA_PLAY_PAUSE= 85,
+ AKEYCODE_MEDIA_STOP = 86,
+ AKEYCODE_MEDIA_NEXT = 87,
+ AKEYCODE_MEDIA_PREVIOUS = 88,
+ AKEYCODE_MEDIA_REWIND = 89,
+ AKEYCODE_MEDIA_FAST_FORWARD = 90,
+ AKEYCODE_MUTE = 91,
+ AKEYCODE_PAGE_UP = 92,
+ AKEYCODE_PAGE_DOWN = 93,
+ AKEYCODE_PICTSYMBOLS = 94,
+ AKEYCODE_SWITCH_CHARSET = 95,
+ AKEYCODE_BUTTON_A = 96,
+ AKEYCODE_BUTTON_B = 97,
+ AKEYCODE_BUTTON_C = 98,
+ AKEYCODE_BUTTON_X = 99,
+ AKEYCODE_BUTTON_Y = 100,
+ AKEYCODE_BUTTON_Z = 101,
+ AKEYCODE_BUTTON_L1 = 102,
+ AKEYCODE_BUTTON_R1 = 103,
+ AKEYCODE_BUTTON_L2 = 104,
+ AKEYCODE_BUTTON_R2 = 105,
+ AKEYCODE_BUTTON_THUMBL = 106,
+ AKEYCODE_BUTTON_THUMBR = 107,
+ AKEYCODE_BUTTON_START = 108,
+ AKEYCODE_BUTTON_SELECT = 109,
+ AKEYCODE_BUTTON_MODE = 110,
- /* NOTE: If you add a new keycode here you must also add it to:
- * native/include/android/keycodes.h
- * frameworks/base/include/ui/KeycodeLabels.h
- * frameworks/base/core/java/android/view/KeyEvent.java
- * tools/puppet_master/PuppetMaster.nav_keys.py
- * frameworks/base/core/res/res/values/attrs.xml
- */
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
};
#ifdef __cplusplus
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
index 90a8983..2917216 100644
--- a/native/include/android/looper.h
+++ b/native/include/android/looper.h
@@ -24,25 +24,151 @@
extern "C" {
#endif
+/**
+ * ALooper
+ *
+ * A looper is the state tracking an event loop for a thread.
+ * Loopers do not define event structures or other such things; rather
+ * they are a lower-level facility to attach one or more discrete objects
+ * listening for an event. An "event" here is simply data available on
+ * a file descriptor: each attached object has an associated file descriptor,
+ * and waiting for "events" means (internally) polling on all of these file
+ * descriptors until one or more of them have data available.
+ *
+ * A thread can have only one ALooper associated with it.
+ */
struct ALooper;
typedef struct ALooper ALooper;
+/**
+ * For callback-based event loops, this is the prototype of the function
+ * that is called. It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically POLLIN), and
+ * the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
typedef int ALooper_callbackFunc(int fd, int events, void* data);
+/**
+ * Return the ALooper associated with the calling thread, or NULL if
+ * there is not one.
+ */
ALooper* ALooper_forThread();
-ALooper* ALooper_prepare();
+enum {
+ /**
+ * Option for ALooper_prepare: this ALooper will accept calls to
+ * ALooper_addFd() that do not have a callback (that is provide NULL
+ * for the callback). In this case the caller of ALooper_pollOnce()
+ * or ALooper_pollAll() MUST check the return from these functions to
+ * discover when data is available on such fds and process it.
+ */
+ ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0
+};
-int32_t ALooper_pollOnce(int timeoutMillis);
+/**
+ * Prepare an ALooper associated with the calling thread, and return it.
+ * If the thread already has an ALooper, it is returned. Otherwise, a new
+ * one is created, associated with the thread, and returned.
+ *
+ * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0.
+ */
+ALooper* ALooper_prepare(int32_t opts);
+enum {
+ /**
+ * Result from ALooper_pollOnce() and ALooper_pollAll(): one or
+ * more callbacks were executed.
+ */
+ ALOOPER_POLL_CALLBACK = -1,
+
+ /**
+ * Result from ALooper_pollOnce() and ALooper_pollAll(): the
+ * timeout expired.
+ */
+ ALOOPER_POLL_TIMEOUT = -2,
+
+ /**
+ * Result from ALooper_pollOnce() and ALooper_pollAll(): an error
+ * occurred.
+ */
+ ALOOPER_POLL_ERROR = -3,
+};
+
+/**
+ * Wait for events to be available, with optional timeout in milliseconds.
+ * Invokes callbacks for all file descriptors on which an event occurred.
+ *
+ * If the timeout is zero, returns immediately without blocking.
+ * If the timeout is negative, waits indefinitely until an event appears.
+ *
+ * Returns ALOOPER_POLL_CALLBACK if a callback was invoked.
+ *
+ * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns ALOPER_POLL_ERROR if an error occurred.
+ *
+ * Returns a value >= 0 containing a file descriptor if it has data
+ * and it has no callback function (requiring the caller here to handle it).
+ * In this (and only this) case outEvents and outData will contain the poll
+ * events and data associated with the fd.
+ *
+ * This method does not return until it has finished invoking the appropriate callbacks
+ * for all file descriptors that were signalled.
+ */
+int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData);
+
+/**
+ * Like ALooper_pollOnce(), but performs all pending callbacks until all
+ * data has been consumed or a file descriptor is available with no callback.
+ * This function will never return ALOOPER_POLL_CALLBACK.
+ */
+int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData);
+
+/**
+ * Acquire a reference on the given ALooper object. This prevents the object
+ * from being deleted until the reference is removed. This is only needed
+ * to safely hand an ALooper from one thread to another.
+ */
void ALooper_acquire(ALooper* looper);
+/**
+ * Remove a reference that was previously acquired with ALooper_acquire().
+ */
void ALooper_release(ALooper* looper);
-void ALooper_setCallback(ALooper* looper, int fd, int events,
+/**
+ * Add a new file descriptor to be polled by the looper. If the same file
+ * descriptor was previously added, it is replaced.
+ *
+ * "fd" is the file descriptor to be added.
+ * "events" are the poll events to wake up on. Typically this is POLLIN.
+ * "callback" is the function to call when there is an event on the file
+ * descriptor.
+ * "id" is an identifier to associated with this file descriptor, or 0.
+ * "data" is a private data pointer to supply to the callback.
+ *
+ * There are two main uses of this function:
+ *
+ * (1) If "callback" is non-NULL, then
+ * this function will be called when there is data on the file descriptor. It
+ * should execute any events it has pending, appropriately reading from the
+ * file descriptor.
+ *
+ * (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce
+ * when it has data available, requiring the caller to take care of processing
+ * it.
+ */
+void ALooper_addFd(ALooper* looper, int fd, int events,
ALooper_callbackFunc* callback, void* data);
-int32_t ALooper_removeCallback(ALooper* looper, int fd);
+/**
+ * Remove a previously added file descriptor from the looper.
+ */
+int32_t ALooper_removeFd(ALooper* looper, int fd);
#ifdef __cplusplus
};
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index a31c5af..ea6f05f 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -147,6 +147,21 @@
void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window);
/**
+ * The drawing window for this native activity has been resized. You should
+ * retrieve the new size from the window and ensure that your rendering in
+ * it now matches.
+ */
+ void (*onNativeWindowResized)(ANativeActivity* activity, ANativeWindow* window);
+
+ /**
+ * The drawing window for this native activity needs to be redrawn. To avoid
+ * transient artifacts during screen changes (such resizing after rotation),
+ * applications should not return from this function until they have finished
+ * drawing their window in its current state.
+ */
+ void (*onNativeWindowRedrawNeeded)(ANativeActivity* activity, ANativeWindow* window);
+
+ /**
* The drawing window for this native activity is going to be destroyed.
* You MUST ensure that you do not touch the window object after returning
* from this function: in the common case of drawing to the window from
@@ -170,6 +185,11 @@
void (*onInputQueueDestroyed)(ANativeActivity* activity, AInputQueue* queue);
/**
+ * The rectangle in the window in which content should be placed has changed.
+ */
+ void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect);
+
+ /**
* 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.
@@ -192,6 +212,33 @@
*/
extern ANativeActivity_createFunc ANativeActivity_onCreate;
+void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format);
+
+void ANativeActivity_setWindowFlags(ANativeActivity* activity,
+ uint32_t addFlags, uint32_t removeFlags);
+
+/**
+ * Flags for ANativeActivity_showSoftInput; see the Java InputMethodManager
+ * API for documentation.
+ */
+enum {
+ ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 0x0001,
+ ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED = 0x0002,
+};
+
+void ANativeActivity_showSoftInput(ANativeActivity* activity, uint32_t flags);
+
+/**
+ * Flags for ANativeActivity_hideSoftInput; see the Java InputMethodManager
+ * API for documentation.
+ */
+enum {
+ ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 0x0001,
+ ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 0x0002,
+};
+
+void ANativeActivity_hideSoftInput(ANativeActivity* activity, uint32_t flags);
+
#ifdef __cplusplus
};
#endif
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 678ba3d..7599d7e 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-
#ifndef ANDROID_NATIVE_WINDOW_H
#define ANDROID_NATIVE_WINDOW_H
+#include <android/rect.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -34,6 +35,27 @@
struct ANativeWindow;
typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow_Buffer {
+ int32_t width;
+ int32_t height;
+ int32_t stride;
+ int32_t format;
+ void* bits;
+
+ uint32_t reserved[6];
+} ANativeWindow_Buffer;
+
+/**
+ * Acquire a reference on the given ANativeWindow object. This prevents the object
+ * from being deleted until the reference is removed.
+ */
+void ANativeWindow_acquire(ANativeWindow* window);
+
+/**
+ * Remove a reference that was previously acquired with ANativeWindow_acquire().
+ */
+void ANativeWindow_release(ANativeWindow* window);
+
/*
* Return the current width in pixels of the window surface. Returns a
* negative value on error.
@@ -60,13 +82,22 @@
* window's physical size, then it buffer will be scaled to match that size
* when compositing it to the screen.
*
- * The format may be one of the window format constants above.
- *
- * For all of these parameters, if 0 is supplied than the window's base
+ * For all of these parameters, if 0 is supplied then the window's base
* value will come back in force.
*/
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
- int32_t height, int32_t format);
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height);
+
+/**
+ * Lock the window's next drawing surface for writing.
+ */
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds);
+
+/**
+ * Unlock the window's drawing surface after previously locking it,
+ * posting the new buffer to the display.
+ */
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
#ifdef __cplusplus
};
diff --git a/native/include/android/native_window_jni.h b/native/include/android/native_window_jni.h
new file mode 100644
index 0000000..b9e72ef
--- /dev/null
+++ b/native/include/android/native_window_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_NATIVE_WINDOW_JNI_H
+#define ANDROID_NATIVE_WINDOW_JNI_H
+
+#include <android/native_window.h>
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Return the ANativeWindow associated with a Java Surface object,
+ * for interacting with it through native code. This acquires a reference
+ * on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
+ * when done with it so that it doesn't leak.
+ */
+ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/native/include/android/rect.h b/native/include/android/rect.h
new file mode 100644
index 0000000..3e81f53
--- /dev/null
+++ b/native/include/android/rect.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_RECT_H
+#define ANDROID_RECT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ARect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} ARect;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_RECT_H
diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h
new file mode 100644
index 0000000..4291d3e
--- /dev/null
+++ b/native/include/android/sensor.h
@@ -0,0 +1,235 @@
+/*
+ * 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_SENSOR_H
+#define ANDROID_SENSOR_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Structures and functions to receive and process sensor events in
+ * native code.
+ *
+ */
+
+#include <sys/types.h>
+
+#include <android/looper.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Sensor types
+ * (keep in sync with hardware/sensor.h)
+ */
+
+enum {
+ ASENSOR_TYPE_ACCELEROMETER = 1,
+ ASENSOR_TYPE_MAGNETIC_FIELD = 2,
+ ASENSOR_TYPE_GYROSCOPE = 4,
+ ASENSOR_TYPE_LIGHT = 5,
+ ASENSOR_TYPE_PROXIMITY = 8
+};
+
+/*
+ * Sensor accuracy measure
+ */
+enum {
+ ASENSOR_STATUS_UNRELIABLE = 0,
+ ASENSOR_STATUS_ACCURACY_LOW = 1,
+ ASENSOR_STATUS_ACCURACY_MEDIUM = 2,
+ ASENSOR_STATUS_ACCURACY_HIGH = 3
+};
+
+/*
+ * A few useful constants
+ */
+
+/* Earth's gravity in m/s^2 */
+#define ASENSOR_STANDARD_GRAVITY (9.80665f)
+/* Maximum magnetic field on Earth's surface in uT */
+#define ASENSOR_MAGNETIC_FIELD_EARTH_MAX (60.0f)
+/* Minimum magnetic field on Earth's surface in uT*/
+#define ASENSOR_MAGNETIC_FIELD_EARTH_MIN (30.0f)
+
+/*
+ * A sensor event.
+ */
+
+typedef struct ASensorVector {
+ union {
+ float v[3];
+ struct {
+ float x;
+ float y;
+ float z;
+ };
+ };
+ int8_t status;
+ uint8_t reserved[3];
+} ASensorVector;
+
+typedef struct ASensorEvent {
+ int sensor;
+ int32_t reserved0;
+ union {
+ float data[16];
+ ASensorVector acceleration;
+ ASensorVector magnetic;
+ float temperature;
+ float distance;
+ float light;
+ };
+ int64_t timestamp;
+ int32_t reserved1[4];
+} ASensorEvent;
+
+
+struct ASensorManager;
+typedef struct ASensorManager ASensorManager;
+
+struct ASensorEventQueue;
+typedef struct ASensorEventQueue ASensorEventQueue;
+
+struct ASensor;
+typedef struct ASensor ASensor;
+
+/*****************************************************************************/
+
+/*
+ * Get a reference to the sensor manager. ASensorManager is a singleton.
+ *
+ * Example:
+ *
+ * ASensorManager* sensorManager = ASensorManager_getInstance();
+ *
+ */
+ASensorManager* ASensorManager_getInstance();
+
+
+/*
+ * Returns the list of available sensors.
+ */
+int ASensorManager_getSensorList(ASensorManager* manager, ASensor** list);
+
+/*
+ * Returns the default sensor for the given type, or NULL if no sensor
+ * of that type exist.
+ */
+ASensor* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
+
+/*
+ * Creates a new sensor event queue and associate it with a looper.
+ */
+ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
+ ALooper* looper, ALooper_callbackFunc* callback, void* data);
+
+/*
+ * Destroys the event queue and free all resources associated to it.
+ */
+int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue);
+
+
+/*****************************************************************************/
+
+/*
+ * Enable the selected sensor. Returns a negative error code on failure.
+ */
+int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor* sensor);
+
+/*
+ * Disable the selected sensor. Returns a negative error code on failure.
+ */
+int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor* sensor);
+
+/*
+ * Sets the delivery rate of events in microseconds for the given sensor.
+ * Note that this is a hint only, generally event will arrive at a higher
+ * rate.
+ * Returns a negative error code on failure.
+ */
+int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor* sensor, int32_t usec);
+
+/*
+ * Returns true if there are one or more events available in the
+ * sensor queue. Returns 1 if the queue has events; 0 if
+ * it does not have events; and a negative value if there is an error.
+ */
+int ASensorEventQueue_hasEvents(ASensorEventQueue* queue);
+
+/*
+ * Returns the next available events from the queue. Returns a negative
+ * value if no events are available or an error has occurred, otherwise
+ * the number of events returned.
+ *
+ * Examples:
+ * ASensorEvent event;
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
+ *
+ * ASensorEvent eventBuffer[8];
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
+ *
+ */
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
+ ASensorEvent* events, size_t count);
+
+
+/*****************************************************************************/
+
+/*
+ * Returns this sensor's name (non localized)
+ */
+const char* ASensor_getName(ASensor* sensor);
+
+/*
+ * Returns this sensor's vendor's name (non localized)
+ */
+const char* ASensor_getVendor(ASensor* sensor);
+
+/*
+ * Return this sensor's type
+ */
+int ASensor_getType(ASensor* sensor);
+
+/*
+ * Returns this sensors's resolution
+ */
+float ASensor_getResolution(ASensor* sensor);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_SENSOR_H
diff --git a/native/include/android/tts.h b/native/include/android/tts.h
index e5c99f7..fb15108 100644
--- a/native/include/android/tts.h
+++ b/native/include/android/tts.h
@@ -87,6 +87,11 @@
*/
extern android_tts_engine_t *android_getTtsEngine();
+/* Including the old version for legacy support (Froyo compatibility).
+ * This should return the same thing as android_getTtsEngine.
+ */
+extern "C" android_tts_engine_t *getTtsEngine();
+
// A callback type used to notify the framework of new synthetized
// audio samples, status will be SYNTH_DONE for the last sample of
// the last request, of SYNTH_PENDING otherwise.
diff --git a/native/include/android/window.h b/native/include/android/window.h
new file mode 100644
index 0000000..2ab192b
--- /dev/null
+++ b/native/include/android/window.h
@@ -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.
+ */
+
+
+#ifndef ANDROID_WINDOW_H
+#define ANDROID_WINDOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Window flags, as per the Java API at android.view.WindowManager.LayoutParams.
+ */
+enum {
+ AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ AWINDOW_FLAG_DIM_BEHIND = 0x00000002,
+ AWINDOW_FLAG_BLUR_BEHIND = 0x00000004,
+ AWINDOW_FLAG_NOT_FOCUSABLE = 0x00000008,
+ AWINDOW_FLAG_NOT_TOUCHABLE = 0x00000010,
+ AWINDOW_FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ AWINDOW_FLAG_KEEP_SCREEN_ON = 0x00000080,
+ AWINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ AWINDOW_FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ AWINDOW_FLAG_FULLSCREEN = 0x00000400,
+ AWINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ AWINDOW_FLAG_DITHER = 0x00001000,
+ AWINDOW_FLAG_SECURE = 0x00002000,
+ AWINDOW_FLAG_SCALED = 0x00004000,
+ AWINDOW_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ AWINDOW_FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ AWINDOW_FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ AWINDOW_FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ AWINDOW_FLAG_SHOW_WALLPAPER = 0x00100000,
+ AWINDOW_FLAG_TURN_SCREEN_ON = 0x00200000,
+ AWINDOW_FLAG_DISMISS_KEYGUARD = 0x00400000,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_WINDOW_H
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 2ff231d..41207f7 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -222,6 +222,10 @@
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
+ // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+ // this statement if back-porting to 2.2 or older:
+ // holder.setFormat(PixelFormat.RGB_565);
+ //
// setType is not needed for SDK 2.0 or newer. Uncomment this
// statement if back-porting this code to older SDKs.
// holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
@@ -1103,7 +1107,6 @@
mRenderer = renderer;
}
-
@Override
public void run() {
setName("GLThread " + getId());
@@ -1154,6 +1157,7 @@
boolean sizeChanged = false;
boolean wantRenderNotification = false;
boolean doRenderNotification = false;
+ boolean askedToReleaseEglContext = false;
int w = 0;
int h = 0;
Runnable event = null;
@@ -1179,6 +1183,17 @@
}
}
+ // Do we need to give up the EGL context?
+ if (mShouldReleaseEglContext) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ mShouldReleaseEglContext = false;
+ askedToReleaseEglContext = true;
+ }
+
// Have we lost the EGL context?
if (lostEglContext) {
stopEglSurfaceLocked();
@@ -1228,6 +1243,9 @@
}
if (doRenderNotification) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "sending render notification tid=" + getId());
+ }
wantRenderNotification = false;
doRenderNotification = false;
mRenderComplete = true;
@@ -1235,22 +1253,24 @@
}
// Ready to draw?
- if ((!mPaused) && mHasSurface
- && (mWidth > 0) && (mHeight > 0)
- && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
+ if (readyToDraw()) {
// If we don't have an EGL context, try to acquire one.
- if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglContextLocked(this)) {
- try {
- mEglHelper.start();
- } catch (RuntimeException t) {
- sGLThreadManager.releaseEglContextLocked(this);
- throw t;
- }
- mHaveEglContext = true;
- createEglContext = true;
+ if (! mHaveEglContext) {
+ if (askedToReleaseEglContext) {
+ askedToReleaseEglContext = false;
+ } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ try {
+ mEglHelper.start();
+ } catch (RuntimeException t) {
+ sGLThreadManager.releaseEglContextLocked(this);
+ throw t;
+ }
+ mHaveEglContext = true;
+ createEglContext = true;
- sGLThreadManager.notifyAll();
+ sGLThreadManager.notifyAll();
+ }
}
if (mHaveEglContext && !mHaveEglSurface) {
@@ -1265,6 +1285,9 @@
w = mWidth;
h = mHeight;
wantRenderNotification = true;
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticing that we want render notification tid=" + getId());
+ }
if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
// We keep mRequestRender true so that we draw twice after the size changes.
@@ -1284,7 +1307,16 @@
// By design, this is the only place in a GLThread thread where we wait().
if (LOG_THREADS) {
- Log.i("GLThread", "waiting tid=" + getId());
+ Log.i("GLThread", "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mWaitingForSurface: " + mWaitingForSurface
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRequestRender: " + mRequestRender
+ + " mRenderMode: " + mRenderMode);
}
sGLThreadManager.wait();
}
@@ -1326,7 +1358,7 @@
}
if (LOG_RENDERER_DRAW_FRAME) {
- Log.w("GLThread", "onDrawFrame");
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
}
mRenderer.onDrawFrame(gl);
if (!mEglHelper.swap()) {
@@ -1352,6 +1384,16 @@
}
}
+ public boolean ableToDraw() {
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
+ }
+
+ private boolean readyToDraw() {
+ return (!mPaused) && mHasSurface
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+ }
+
public void setRenderMode(int renderMode) {
if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
throw new IllegalArgumentException("renderMode");
@@ -1461,9 +1503,10 @@
sGLThreadManager.notifyAll();
// Wait for thread to react to resize and render a frame
- while (! mExited && !mPaused && !mRenderComplete ) {
+ while (! mExited && !mPaused && !mRenderComplete
+ && (mGLThread != null && mGLThread.ableToDraw())) {
if (LOG_SURFACE) {
- Log.i("Main thread", "onWindowResize waiting for render complete.");
+ Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + mGLThread.getId());
}
try {
sGLThreadManager.wait();
@@ -1490,6 +1533,11 @@
}
}
+ public void requestReleaseEglContextLocked() {
+ mShouldReleaseEglContext = true;
+ sGLThreadManager.notifyAll();
+ }
+
/**
* Queue an "event" to be run on the GL rendering thread.
* @param r the runnable to be run on the GL rendering thread.
@@ -1514,6 +1562,7 @@
private boolean mWaitingForSurface;
private boolean mHaveEglContext;
private boolean mHaveEglSurface;
+ private boolean mShouldReleaseEglContext;
private int mWidth;
private int mHeight;
private int mRenderMode;
@@ -1598,6 +1647,13 @@
if (mMultipleGLESContextsAllowed) {
return true;
}
+ // Notify the owning thread that it should release the context.
+ // TODO: implement a fairness policy. Currently
+ // if the owning thread is drawing continuously it will just
+ // reacquire the EGL context.
+ if (mEglOwner != null) {
+ mEglOwner.requestReleaseEglContextLocked();
+ }
return false;
}
diff --git a/opengl/tests/gl_perf/Android.mk b/opengl/tests/gl_perf/Android.mk
new file mode 100644
index 0000000..37647ca
--- /dev/null
+++ b/opengl/tests/gl_perf/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ gl2_perf.cpp \
+ filltest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv2 \
+ libui
+
+LOCAL_MODULE:= test-opengl-gl2_perf
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl_perf/filltest.cpp b/opengl/tests/gl_perf/filltest.cpp
new file mode 100644
index 0000000..eb398ec
--- /dev/null
+++ b/opengl/tests/gl_perf/filltest.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <string.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+#include <EGL/egl.h>
+
+
+using namespace android;
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ fprintf(stderr, "Could not compile shader %d:\n%s\n",
+ shaderType, buf);
+ free(buf);
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+ return shader;
+}
+
+enum {
+ A_POS,
+ A_COLOR,
+ A_TEX0,
+ A_TEX1
+};
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+ if (!vertexShader) {
+ return 0;
+ }
+
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+ if (!pixelShader) {
+ return 0;
+ }
+
+ GLuint program = glCreateProgram();
+ if (program) {
+ glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader v");
+ glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader p");
+
+ glBindAttribLocation(program, A_POS, "a_pos");
+ glBindAttribLocation(program, A_COLOR, "a_color");
+ glBindAttribLocation(program, A_TEX0, "a_tex0");
+ glBindAttribLocation(program, A_TEX1, "a_tex1");
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ printf("Could not link program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ checkGlError("createProgram");
+ glUseProgram(program);
+ return program;
+}
+
+uint64_t getTime() {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+uint64_t gTime;
+void startTimer() {
+ gTime = getTime();
+}
+
+void endTimer(const char *str, int w, int h, double dc, int count) {
+ uint64_t t2 = getTime();
+ double delta = ((double)(t2 - gTime)) / 1000000000;
+ double pixels = dc * (w * h) * count;
+ double mpps = pixels / delta / 1000000;
+ double dc60 = pixels / delta / (w * h) / 60;
+
+ printf("%s, %f, %f\n", str, mpps, dc60);
+}
+
+static const char gVertexShader[] =
+ "attribute vec4 a_pos;\n"
+ "attribute vec4 a_color;\n"
+ "attribute vec2 a_tex0;\n"
+ "attribute vec2 a_tex1;\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_tex0;\n"
+ "varying vec2 v_tex1;\n"
+
+ "void main() {\n"
+ " v_color = a_color;\n"
+ " v_tex0 = a_tex0;\n"
+ " v_tex1 = a_tex1;\n"
+ " gl_Position = a_pos;\n"
+ "}\n";
+
+static const char gShaderPrefix[] =
+ "precision mediump float;\n"
+ "uniform vec4 u_color;\n"
+ "uniform vec4 u_0;\n"
+ "uniform vec4 u_1;\n"
+ "uniform vec4 u_2;\n"
+ "uniform vec4 u_3;\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_tex0;\n"
+ "varying vec2 v_tex1;\n"
+ "uniform sampler2D u_tex0;\n"
+ "uniform sampler2D u_tex1;\n"
+ "void main() {\n";
+
+static const char gShaderPostfix[] =
+ " gl_FragColor = c;\n"
+ "}\n";
+
+
+static char * append(char *d, const char *s) {
+ size_t len = strlen(s);
+ memcpy(d, s, len);
+ return d + len;
+}
+
+static char * genShader(
+ bool useVarColor,
+ int texCount,
+ bool modulateFirstTex,
+ int extraMath)
+{
+ char *str = (char *)calloc(16 * 1024, 1);
+ char *tmp = append(str, gShaderPrefix);
+
+ if (modulateFirstTex || !texCount) {
+ if (useVarColor) {
+ tmp = append(tmp, " vec4 c = v_color;\n");
+ } else {
+ tmp = append(tmp, " vec4 c = u_color;\n");
+ }
+ } else {
+ tmp = append(tmp, " vec4 c = texture2D(u_tex0, v_tex0);\n");
+ }
+
+ if (modulateFirstTex && texCount) {
+ tmp = append(tmp, " c *= texture2D(u_tex0, v_tex0);\n");
+ }
+ if (texCount > 1) {
+ tmp = append(tmp, " c *= texture2D(u_tex1, v_tex1);\n");
+ }
+
+ if (extraMath > 0) {
+ tmp = append(tmp, " c *= u_0;\n");
+ }
+ if (extraMath > 1) {
+ tmp = append(tmp, " c *= u_1;\n");
+ }
+ if (extraMath > 2) {
+ tmp = append(tmp, " c *= u_2;\n");
+ }
+ if (extraMath > 3) {
+ tmp = append(tmp, " c *= u_3;\n");
+ }
+
+
+ tmp = append(tmp, gShaderPostfix);
+ tmp[0] = 0;
+
+ //printf("%s", str);
+ return str;
+}
+
+static void setupVA() {
+ static const float vtx[] = {
+ -2.0f,-1.0f,
+ 1.0f,-1.0f,
+ -2.0f, 1.0f,
+ 1.0f, 1.0f };
+ static const float color[] = {
+ 1.0f,0.0f,1.0f,1.0f,
+ 0.0f,0.0f,1.0f,1.0f,
+ 1.0f,1.0f,0.0f,1.0f,
+ 1.0f,1.0f,1.0f,1.0f };
+ static const float tex0[] = {
+ 0.0f,0.0f,
+ 1.0f,0.0f,
+ 1.0f,1.0f,
+ 0.0f,1.0f };
+ static const float tex1[] = {
+ 1.0f,0.0f,
+ 1.0f,1.0f,
+ 0.0f,1.0f,
+ 0.0f,0.0f };
+
+ glEnableVertexAttribArray(A_POS);
+ glEnableVertexAttribArray(A_COLOR);
+ glEnableVertexAttribArray(A_TEX0);
+ glEnableVertexAttribArray(A_TEX1);
+
+ glVertexAttribPointer(A_POS, 2, GL_FLOAT, false, 8, vtx);
+ glVertexAttribPointer(A_COLOR, 4, GL_FLOAT, false, 16, color);
+ glVertexAttribPointer(A_TEX0, 2, GL_FLOAT, false, 8, tex0);
+ glVertexAttribPointer(A_TEX1, 2, GL_FLOAT, false, 8, tex1);
+}
+
+//////////////////////////
+
+void ptSwap();
+
+static void doLoop(uint32_t w, uint32_t h, const char *str) {
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ ptSwap();
+ glFinish();
+
+ startTimer();
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ for (int ct=0; ct < 100; ct++) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+ ptSwap();
+ glFinish();
+ endTimer(str, w, h, 1, 100);
+}
+
+static void doSingleTest(uint32_t w, uint32_t h,
+ bool useVarColor,
+ int texCount,
+ bool modulateFirstTex,
+ int extraMath,
+ int tex0, int tex1) {
+ char *pgmTxt = genShader(useVarColor, texCount, modulateFirstTex, extraMath);
+ int pgm = createProgram(gVertexShader, pgmTxt);
+ if (!pgm) {
+ printf("error running test\n");
+ return;
+ }
+ int loc = glGetUniformLocation(pgm, "u_tex0");
+ //printf("loc = %i \n", loc);
+ if (loc >= 0) glUniform1i(loc, 0);
+ loc = glGetUniformLocation(pgm, "u_tex1");
+ if (loc >= 0) glUniform1i(loc, 1);
+
+ loc = glGetUniformLocation(pgm, "u_color");
+ if (loc >= 0) glUniform4f(loc, 1.f, 0.4f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_0");
+ if (loc >= 0) glUniform4f(loc, 1.f, 0.4f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_1");
+ if (loc >= 0) glUniform4f(loc, 0.7f, 0.8f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_2");
+ if (loc >= 0) glUniform4f(loc, 0.9f, 0.6f, 0.7f, 1.0f);
+
+ loc = glGetUniformLocation(pgm, "u_3");
+ if (loc >= 0) glUniform4f(loc, 0.88f, 0.2f, 0.4f, 0.2f);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, tex1);
+ glActiveTexture(GL_TEXTURE0);
+
+ char str2[1024];
+
+ glBlendFunc(GL_ONE, GL_ONE);
+ glDisable(GL_BLEND);
+ sprintf(str2, "%i, %i, %i, %i, %i, 0",
+ useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+ doLoop(w, h, str2);
+
+ glEnable(GL_BLEND);
+ sprintf(str2, "%i, %i, %i, %i, %i, 1",
+ useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+ doLoop(w, h, str2);
+}
+
+void genTextures() {
+ uint32_t *m = (uint32_t *)malloc(1024*1024*4);
+ for (int y=0; y < 1024; y++){
+ for (int x=0; x < 1024; x++){
+ m[y*1024 + x] = 0xff0000ff | ((x & 0xff) << 8) | (y << 16);
+ }
+ }
+ glBindTexture(GL_TEXTURE_2D, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ for (int y=0; y < 16; y++){
+ for (int x=0; x < 16; x++){
+ m[y*16 + x] = 0xff0000ff | (x<<12) | (y<<20);
+ }
+ }
+ glBindTexture(GL_TEXTURE_2D, 2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+}
+
+bool doTest(uint32_t w, uint32_t h) {
+ setupVA();
+ genTextures();
+
+ printf("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+
+ for (int texCount = 0; texCount < 3; texCount++) {
+ for (int extraMath = 0; extraMath < 5; extraMath++) {
+
+ doSingleTest(w, h, false, texCount, false, extraMath, 1, 1);
+ doSingleTest(w, h, true, texCount, false, extraMath, 1, 1);
+ if (texCount) {
+ doSingleTest(w, h, false, texCount, true, extraMath, 1, 1);
+ doSingleTest(w, h, true, texCount, true, extraMath, 1, 1);
+
+ doSingleTest(w, h, false, texCount, false, extraMath, 2, 2);
+ doSingleTest(w, h, true, texCount, false, extraMath, 2, 2);
+ doSingleTest(w, h, false, texCount, true, extraMath, 2, 2);
+ doSingleTest(w, h, true, texCount, true, extraMath, 2, 2);
+ }
+ }
+ }
+
+ exit(0);
+ return true;
+}
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
new file mode 100644
index 0000000..9dfcf1c
--- /dev/null
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+ if (returnVal != EGL_TRUE) {
+ fprintf(stderr, "%s() returned %d\n", op, returnVal);
+ }
+
+ for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+ = eglGetError()) {
+ fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+ error);
+ }
+}
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+bool doTest(uint32_t w, uint32_t h);
+
+static EGLDisplay dpy;
+static EGLSurface surface;
+
+int main(int argc, char** argv) {
+ EGLBoolean returnValue;
+ EGLConfig myConfig = {0};
+
+ EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLint s_configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE };
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLint w, h;
+
+
+ checkEglError("<init>");
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ checkEglError("eglGetDisplay");
+ if (dpy == EGL_NO_DISPLAY) {
+ printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+ return 0;
+ }
+
+ returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+ checkEglError("eglInitialize", returnValue);
+ if (returnValue != EGL_TRUE) {
+ printf("eglInitialize failed\n");
+ return 0;
+ }
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+ returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+ if (returnValue) {
+ printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+ return 0;
+ }
+
+ checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+ surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+ checkEglError("eglCreateWindowSurface");
+ if (surface == EGL_NO_SURFACE) {
+ printf("gelCreateWindowSurface failed.\n");
+ return 0;
+ }
+
+ context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+ checkEglError("eglCreateContext");
+ if (context == EGL_NO_CONTEXT) {
+ printf("eglCreateContext failed\n");
+ return 0;
+ }
+ returnValue = eglMakeCurrent(dpy, surface, surface, context);
+ checkEglError("eglMakeCurrent", returnValue);
+ if (returnValue != EGL_TRUE) {
+ return 0;
+ }
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ checkEglError("eglQuerySurface");
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ checkEglError("eglQuerySurface");
+ GLint dim = w < h ? w : h;
+
+ glViewport(0, 0, w, h);
+
+ for (;;) {
+ doTest(w, h);
+ eglSwapBuffers(dpy, surface);
+ checkEglError("eglSwapBuffers");
+ }
+
+ return 0;
+}
+
+void ptSwap() {
+ eglSwapBuffers(dpy, surface);
+}
+
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cd4f96d..7395233 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -49,6 +49,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
/**
@@ -67,11 +68,29 @@
private Context mContext;
+ private static final HashSet<String> mValidTables = new HashSet<String>();
+
+ static {
+ mValidTables.add("system");
+ mValidTables.add("secure");
+ mValidTables.add("bluetooth_devices");
+ mValidTables.add("bookmarks");
+
+ // These are old.
+ mValidTables.add("favorites");
+ mValidTables.add("gservices");
+ mValidTables.add("old_favorites");
+ }
+
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
+ public static boolean isValidTable(String name) {
+ return mValidTables.contains(name);
+ }
+
private void createSecureTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE secure (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1019fa8..6a5290e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -83,6 +83,9 @@
SqlArguments(Uri url, String where, String[] args) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = where;
this.args = args;
} else if (url.getPathSegments().size() != 2) {
@@ -91,6 +94,9 @@
throw new UnsupportedOperationException("WHERE clause not supported: " + url);
} else {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
if ("system".equals(this.table) || "secure".equals(this.table)) {
this.where = Settings.NameValueTable.NAME + "=?";
this.args = new String[] { url.getPathSegments().get(1) };
@@ -105,6 +111,9 @@
SqlArguments(Uri url) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = null;
this.args = null;
} else {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 75045d7..6d77a3a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -12,7 +12,12 @@
android:icon="@drawable/ic_launcher_settings">
<service
- android:name=".statusbar.StatusBarService"
+ android:name=".statusbar.PhoneStatusBarService"
+ android:exported="false"
+ />
+
+ <service
+ android:name=".statusbar.tablet.TabletStatusBarService"
android:exported="false"
/>
diff --git a/packages/SystemUI/res/drawable/notification_dragger.png b/packages/SystemUI/res/drawable/notification_dragger.png
new file mode 100644
index 0000000..fad1f32
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_dragger.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
new file mode 100644
index 0000000..1e93bee
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:background="@drawable/status_bar_background"
+ android:orientation="vertical"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+
+ <RelativeLayout android:id="@+id/icons"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+<!--
+ <LinearLayout android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:paddingRight="6dip"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"/>
+-->
+
+ <com.android.systemui.statusbar.tablet.NotificationIconArea
+ android:id="@+id/notificationIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="10dp"
+ android:paddingLeft="6dip"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ />
+
+ <RelativeLayout android:id="@+id/icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ >
+
+ <com.android.systemui.statusbar.Clock
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:singleLine="true"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ />
+ </RelativeLayout>
+
+ <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
+ android:layout_width="wrap_content"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_toLeftOf="@+id/menu"
+ android:layout_marginRight="10dp"
+ android:src="@drawable/status_bar_back"
+ systemui:keyCode="4"
+ />
+ <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
+ android:layout_width="wrap_content"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_toLeftOf="@+id/home"
+ android:src="@drawable/status_bar_menu"
+ android:layout_marginRight="10dp"
+ systemui:keyCode="82"
+ />
+ <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
+ android:layout_width="wrap_content"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="10dp"
+ android:src="@drawable/status_bar_home"
+ systemui:keyCode="3"
+ />
+
+ </RelativeLayout>
+<!--
+
+ <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"
+ />
+-->
+</FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4667149..2f1b36e8 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -60,33 +60,6 @@
android:textStyle="bold"
android:gravity="center_vertical|left"
/>
-
- <LinearLayout android:id="@+id/buttons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="6dip"
- android:orientation="horizontal" >
-
- <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/status_bar_height"
- android:src="@drawable/status_bar_back"
- systemui:keyCode="4"
- />
- <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/status_bar_height"
- android:src="@drawable/status_bar_menu"
- systemui:keyCode="82"
- />
- <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
- android:layout_width="wrap_content"
- android:layout_height="@*android:dimen/status_bar_height"
- android:src="@drawable/status_bar_home"
- systemui:keyCode="3"
- />
- </LinearLayout>
-
</LinearLayout>
<LinearLayout android:id="@+id/ticker"
diff --git a/packages/SystemUI/res/layout/unused.xml b/packages/SystemUI/res/layout/unused.xml
deleted file mode 100644
index 05a7d7d..0000000
--- a/packages/SystemUI/res/layout/unused.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<Unused
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/buttons">
-</Unused>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
index f45caf51..0f6723e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
@@ -23,7 +23,7 @@
public class CloseDragHandle extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
public CloseDragHandle(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f9347b1..2c0af65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -32,7 +32,7 @@
* coalescing these calls so they don't stack up. For the calls
* are coalesced, note that they are all idempotent.
*/
-class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub {
private static final String TAG = "StatusBar.CommandQueue";
private static final int MSG_MASK = 0xffff0000;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
index 3d85f27..a2d4b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
@@ -27,7 +27,7 @@
public class ExpandedView extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
int mPrevHeight = -1;
public ExpandedView(Context context, AttributeSet attrs) {
@@ -53,7 +53,7 @@
//Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight
// + " new=" + height);
mPrevHeight = height;
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
new file mode 100644
index 0000000..eff9a1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -0,0 +1,1552 @@
+/*
+ * 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;
+
+import android.app.Service;
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.StatusBarPolicy;
+
+
+
+public class PhoneStatusBarService extends StatusBarService {
+ static final String TAG = "PhoneStatusBarService";
+ static final boolean SPEW = false;
+
+ public static final String ACTION_STATUSBAR_START
+ = "com.android.internal.policy.statusbar.START";
+
+ static final int EXPANDED_LEAVE_ALONE = -10000;
+ static final int EXPANDED_FULL_OPEN = -10001;
+
+ private static final int MSG_ANIMATE = 1000;
+ private static final int MSG_ANIMATE_REVEAL = 1001;
+ private static final int MSG_SHOW_INTRUDER = 1002;
+ private static final int MSG_HIDE_INTRUDER = 1003;
+
+ // will likely move to a resource or other tunable param at some point
+ private static final int INTRUDER_ALERT_DECAY_MS = 10000;
+
+ StatusBarPolicy mIconPolicy;
+
+ int mIconSize;
+ Display mDisplay;
+
+ StatusBarView mStatusBarView;
+ int mPixelFormat;
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+
+ // icons
+ LinearLayout mIcons;
+ IconMerger mNotificationIcons;
+ LinearLayout mStatusIcons;
+
+ // expanded notifications
+ Dialog mExpandedDialog;
+ ExpandedView mExpandedView;
+ WindowManager.LayoutParams mExpandedParams;
+ ScrollView mScrollView;
+ View mNotificationLinearLayout;
+ View mExpandedContents;
+ // top bar
+ TextView mNoNotificationsTitle;
+ TextView mClearButton;
+ // drag bar
+ CloseDragHandle mCloseView;
+ // ongoing
+ NotificationData mOngoing = new NotificationData();
+ TextView mOngoingTitle;
+ LinearLayout mOngoingItems;
+ // latest
+ NotificationData mLatest = new NotificationData();
+ TextView mLatestTitle;
+ LinearLayout mLatestItems;
+ // position
+ int[] mPositionTmp = new int[2];
+ boolean mExpanded;
+ boolean mExpandedVisible;
+
+ // the date view
+ DateView mDateView;
+
+ // for immersive activities
+ private View mIntruderAlertView;
+
+ // the tracker view
+ TrackingView mTrackingView;
+ WindowManager.LayoutParams mTrackingParams;
+ int mTrackingPosition; // the position of the top of the tracking view.
+ private boolean mPanelSlightlyVisible;
+
+ // ticker
+ private Ticker mTicker;
+ private View mTickerView;
+ private boolean mTicking;
+
+ // Tracking finger for opening/closing.
+ int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+ boolean mTracking;
+ VelocityTracker mVelocityTracker;
+
+ static final int ANIM_FRAME_DURATION = (1000/60);
+
+ boolean mAnimating;
+ long mCurAnimationTime;
+ float mDisplayHeight;
+ float mAnimY;
+ float mAnimVel;
+ float mAnimAccel;
+ long mAnimLastTime;
+ boolean mAnimatingReveal = false;
+ int mViewDelta;
+ int[] mAbsPos = new int[2];
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
+ private class ExpandedDialog extends Dialog {
+ ExpandedDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!down) {
+ animateCollapse();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+
+ super.onCreate();
+
+ addIntruderView();
+
+ // Lastly, call to the icon policy to install/update all the icons.
+ mIconPolicy = new StatusBarPolicy(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ // we're never destroyed
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+ protected View makeStatusBarView() {
+ final Context context = this;
+
+ Resources res = context.getResources();
+
+ mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+ ExpandedView expanded = (ExpandedView)View.inflate(context,
+ R.layout.status_bar_expanded, null);
+ expanded.mService = this;
+
+ mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
+ mIntruderAlertView.setVisibility(View.GONE);
+ mIntruderAlertView.setClickable(true);
+
+ StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+ sb.mService = this;
+
+ // figure out which pixel-format to use for the status bar.
+ mPixelFormat = PixelFormat.TRANSLUCENT;
+ Drawable bg = sb.getBackground();
+ if (bg != null) {
+ mPixelFormat = bg.getOpacity();
+ }
+
+ mStatusBarView = sb;
+ mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+ mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+ mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+ mTickerView = sb.findViewById(R.id.ticker);
+ mDateView = (DateView)sb.findViewById(R.id.date);
+
+ mExpandedDialog = new ExpandedDialog(context);
+ mExpandedView = expanded;
+ mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+ mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+ mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+ mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+ mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+ mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+ mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+ mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+ mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+ mOngoingTitle.setVisibility(View.GONE);
+ mLatestTitle.setVisibility(View.GONE);
+
+ mTicker = new MyTicker(context, sb);
+
+ TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+ tickerView.mTicker = mTicker;
+
+ mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
+ mTrackingView.mService = this;
+ mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+ mCloseView.mService = this;
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ // the more notifications icon
+ StatusBarIconView moreView = new StatusBarIconView(this, "more");
+ moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
+ mNotificationIcons.addMoreView(moreView,
+ new LinearLayout.LayoutParams(mIconSize, mIconSize));
+
+ // set the inital view visibility
+ setAreThereNotifications();
+ mDateView.setVisibility(View.INVISIBLE);
+
+ // receive broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mBroadcastReceiver, filter);
+
+ return sb;
+ }
+
+ protected int getStatusBarGravity() {
+ return Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ }
+
+ private void addIntruderView() {
+ final Resources res = getResources();
+ final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ 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_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.y += height * 1.5; // FIXME
+ lp.setTitle("IntruderAlert");
+ lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert;
+
+ WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
+ }
+
+ public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+ if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " icon=" + icon);
+ StatusBarIconView view = new StatusBarIconView(this, slot);
+ view.set(icon);
+ mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+ }
+
+ public void updateIcon(String slot, int index, int viewIndex,
+ StatusBarIcon old, StatusBarIcon icon) {
+ if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " old=" + old + " icon=" + icon);
+ StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
+ view.set(icon);
+ }
+
+ public void removeIcon(String slot, int index, int viewIndex) {
+ if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+ mStatusIcons.removeViewAt(viewIndex);
+ }
+
+ public void addNotification(IBinder key, StatusBarNotification notification) {
+ StatusBarIconView iconView = addNotificationViews(key, notification);
+ if (iconView == null) return;
+
+ boolean immersive = false;
+ try {
+ immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+ Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+ } catch (RemoteException ex) {
+ }
+ if (immersive) {
+ if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
+ Slog.d(TAG, "Presenting high-priority notification in immersive activity");
+ // @@@ special new transient ticker mode
+ // 1. Populate mIntruderAlertView
+
+ ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+ TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
+ alertIcon.setImageDrawable(StatusBarIconView.getIcon(
+ alertIcon.getContext(),
+ iconView.getStatusBarIcon()));
+ alertText.setText(notification.notification.tickerText);
+
+ View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
+ button.setOnClickListener(
+ new Launcher(notification.notification.contentIntent,
+ notification.pkg, notification.tag, notification.id));
+
+ // 2. Animate mIntruderAlertView in
+ mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+
+ // 3. Set alarm to age the notification off (TODO)
+ mHandler.removeMessages(MSG_HIDE_INTRUDER);
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
+ }
+ } else if (notification.notification.fullScreenIntent != null) {
+ // not immersive & a full-screen alert should be shown
+ Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+ + " sending fullScreenIntent");
+ try {
+ notification.notification.fullScreenIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
+ } else {
+ // usual case: status bar visible & not immersive
+
+ // show the ticker
+ tick(notification);
+ }
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+
+ NotificationData oldList;
+ int oldIndex = mOngoing.findEntry(key);
+ if (oldIndex >= 0) {
+ oldList = mOngoing;
+ } else {
+ oldIndex = mLatest.findEntry(key);
+ if (oldIndex < 0) {
+ Slog.w(TAG, "updateNotification for unknown key: " + key);
+ return;
+ }
+ oldList = mLatest;
+ }
+ final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
+ final StatusBarNotification oldNotification = oldEntry.notification;
+ final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+ final RemoteViews contentView = notification.notification.contentView;
+
+ if (false) {
+ Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " expanded=" + oldEntry.expanded
+ + " contentView=" + oldContentView);
+ Slog.d(TAG, "new notification: when=" + notification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " contentView=" + contentView);
+ }
+
+ // Can we just reapply the RemoteViews in place? If when didn't change, the order
+ // didn't change.
+ if (notification.notification.when == oldNotification.notification.when
+ && notification.isOngoing() == oldNotification.isOngoing()
+ && oldEntry.expanded != null
+ && contentView != null && oldContentView != null
+ && contentView.getPackage() != null
+ && oldContentView.getPackage() != null
+ && oldContentView.getPackage().equals(contentView.getPackage())
+ && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+ if (SPEW) Slog.d(TAG, "reusing notification");
+ oldEntry.notification = notification;
+ try {
+ // Reapply the RemoteViews
+ contentView.reapply(this, oldEntry.content);
+ // update the contentIntent
+ final PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ }
+ // Update the icon.
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+ notification.notification.icon, notification.notification.iconLevel,
+ notification.notification.number);
+ if (!oldEntry.icon.set(ic)) {
+ handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+ return;
+ }
+ }
+ catch (RuntimeException e) {
+ // It failed to add cleanly. Log, and remove the view from the panel.
+ Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+ } else {
+ if (SPEW) Slog.d(TAG, "not reusing notification");
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+
+ // Restart the ticker if it's still running
+ tick(notification);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void removeNotification(IBinder key) {
+ if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
+ StatusBarNotification old = removeNotificationViews(key);
+
+ if (old != null) {
+ // Cancel the ticker if it's still running
+ mTicker.removeEntry(old);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+ }
+
+ private int chooseIconIndex(boolean isOngoing, int viewIndex) {
+ final int latestSize = mLatest.size();
+ if (isOngoing) {
+ return latestSize + (mOngoing.size() - viewIndex);
+ } else {
+ return latestSize - viewIndex;
+ }
+ }
+
+ View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+ Notification n = notification.notification;
+ RemoteViews remoteViews = n.contentView;
+ if (remoteViews == null) {
+ return null;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = n.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+ notification.tag, notification.id));
+ }
+
+ View expanded = null;
+ Exception exception = null;
+ try {
+ expanded = remoteViews.apply(this, content);
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expanded == null) {
+ String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return null;
+ } else {
+ content.addView(expanded);
+ row.setDrawingCacheEnabled(true);
+ }
+
+ return new View[] { row, content, expanded };
+ }
+
+ StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+ NotificationData list;
+ ViewGroup parent;
+ final boolean isOngoing = notification.isOngoing();
+ if (isOngoing) {
+ list = mOngoing;
+ parent = mOngoingItems;
+ } else {
+ list = mLatest;
+ parent = mLatestItems;
+ }
+ // Construct the expanded view.
+ final View[] views = makeNotificationView(notification, parent);
+ if (views == null) {
+ handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ + notification);
+ return null;
+ }
+ final View row = views[0];
+ final View content = views[1];
+ final View expanded = views[2];
+ // Construct the icon.
+ final StatusBarIconView iconView = new StatusBarIconView(this,
+ notification.pkg + "/0x" + Integer.toHexString(notification.id));
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
+ notification.notification.iconLevel, notification.notification.number);
+ if (!iconView.set(ic)) {
+ handleNotificationError(key, notification, "Coulding create icon: " + ic);
+ return null;
+ }
+ // Add the expanded view.
+ final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
+ parent.addView(row, viewIndex);
+ // Add the icon.
+ final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
+ mNotificationIcons.addView(iconView, iconIndex,
+ new LinearLayout.LayoutParams(mIconSize, mIconSize));
+ return iconView;
+ }
+
+ StatusBarNotification removeNotificationViews(IBinder key) {
+ NotificationData.Entry entry = mOngoing.remove(key);
+ if (entry == null) {
+ entry = mLatest.remove(key);
+ if (entry == null) {
+ Slog.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ }
+ // Remove the expanded view.
+ ((ViewGroup)entry.row.getParent()).removeView(entry.row);
+ // Remove the icon.
+ ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+
+ return entry.notification;
+ }
+
+ private void setAreThereNotifications() {
+ boolean ongoing = mOngoing.hasVisibleItems();
+ boolean latest = mLatest.hasVisibleItems();
+
+ // (no ongoing notifications are clearable)
+ if (mLatest.hasClearableItems()) {
+ mClearButton.setVisibility(View.VISIBLE);
+ } else {
+ mClearButton.setVisibility(View.INVISIBLE);
+ }
+
+ mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+ mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+ if (ongoing || latest) {
+ mNoNotificationsTitle.setVisibility(View.GONE);
+ } else {
+ mNoNotificationsTitle.setVisibility(View.VISIBLE);
+ }
+ }
+
+
+ /**
+ * State is one or more of the DISABLE constants from StatusBarManager.
+ */
+ public void disable(int state) {
+ 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) {
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mTicker.halt();
+ }
+ }
+ }
+
+ /**
+ * All changes to the status bar and notifications funnel through here and are batched.
+ */
+ private class H extends Handler {
+ public void handleMessage(Message m) {
+ switch (m.what) {
+ case MSG_ANIMATE:
+ doAnimation();
+ break;
+ case MSG_ANIMATE_REVEAL:
+ doRevealAnimation();
+ break;
+ case MSG_SHOW_INTRUDER:
+ setIntruderAlertVisibility(true);
+ break;
+ case MSG_HIDE_INTRUDER:
+ setIntruderAlertVisibility(false);
+ break;
+ }
+ }
+ }
+
+ View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Because 'v' is a ViewGroup, all its children will be (un)selected
+ // too, which allows marqueeing to work.
+ v.setSelected(hasFocus);
+ }
+ };
+
+ private void makeExpandedVisible() {
+ if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = true;
+ visibilityChanged(true);
+
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mExpandedView.requestFocus(View.FOCUS_FORWARD);
+ mTrackingView.setVisibility(View.VISIBLE);
+
+ if (!mTicking) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+
+ public void animateExpand() {
+ if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ prepareTracking(0, true);
+ performFling(0, 2000.0f, true);
+ }
+
+ public void animateCollapse() {
+ if (SPEW) {
+ Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible
+ + " mExpanded=" + mExpanded
+ + " mAnimating=" + mAnimating
+ + " mAnimY=" + mAnimY
+ + " mAnimVel=" + mAnimVel);
+ }
+
+ if (!mExpandedVisible) {
+ return;
+ }
+
+ int y;
+ if (mAnimating) {
+ y = (int)mAnimY;
+ } else {
+ y = mDisplay.getHeight()-1;
+ }
+ // Let the fling think that we're open so it goes in the right direction
+ // and doesn't try to re-open the windowshade.
+ mExpanded = true;
+ prepareTracking(y, false);
+ performFling(y, -2000.0f, true);
+ }
+
+ void performExpand() {
+ if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ mExpanded = true;
+ makeExpandedVisible();
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+ if (false) postStartTracing();
+ }
+
+ void performCollapse() {
+ if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = false;
+ visibilityChanged(false);
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mTrackingView.setVisibility(View.GONE);
+
+ if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+ if (!mExpanded) {
+ return;
+ }
+ mExpanded = false;
+ }
+
+ void doAnimation() {
+ if (mAnimating) {
+ if (SPEW) Slog.d(TAG, "doAnimation");
+ if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+ incrementAnim();
+ if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
+ if (mAnimY >= mDisplay.getHeight()-1) {
+ if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+ mAnimating = false;
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+ performExpand();
+ }
+ else if (mAnimY < mStatusBarView.getHeight()) {
+ if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+ mAnimating = false;
+ updateExpandedViewPos(0);
+ performCollapse();
+ }
+ else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ }
+ }
+ }
+
+ void stopTracking() {
+ mTracking = false;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ void incrementAnim() {
+ long now = SystemClock.uptimeMillis();
+ float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
+ final float y = mAnimY;
+ final float v = mAnimVel; // px/s
+ final float a = mAnimAccel; // px/s/s
+ mAnimY = y + (v*t) + (0.5f*a*t*t); // px
+ mAnimVel = v + (a*t); // px/s
+ mAnimLastTime = now; // ms
+ //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+ // + " mAnimAccel=" + mAnimAccel);
+ }
+
+ void doRevealAnimation() {
+ final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+ if (mAnimatingReveal && mAnimating && mAnimY < h) {
+ incrementAnim();
+ if (mAnimY >= h) {
+ mAnimY = h;
+ updateExpandedViewPos((int)mAnimY);
+ } else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ }
+ }
+ }
+
+ void prepareTracking(int y, boolean opening) {
+ mTracking = true;
+ mVelocityTracker = VelocityTracker.obtain();
+ if (opening) {
+ mAnimAccel = 2000.0f;
+ mAnimVel = 200;
+ mAnimY = mStatusBarView.getHeight();
+ updateExpandedViewPos((int)mAnimY);
+ mAnimating = true;
+ mAnimatingReveal = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ makeExpandedVisible();
+ } else {
+ // it's open, close it?
+ if (mAnimating) {
+ mAnimating = false;
+ mHandler.removeMessages(MSG_ANIMATE);
+ }
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ }
+
+ void performFling(int y, float vel, boolean always) {
+ mAnimatingReveal = false;
+ mDisplayHeight = mDisplay.getHeight();
+
+ mAnimY = y;
+ mAnimVel = vel;
+
+ //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+ if (mExpanded) {
+ if (!always && (
+ vel > 200.0f
+ || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+ // We are expanded, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the expanded position.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are expanded and are now going to animate away.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ } else {
+ if (always || (
+ vel > 200.0f
+ || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+ // We are collapsed, and they moved enough to allow us to
+ // expand. Animate in the notifications.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are collapsed, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the collapsed position.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ }
+ //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+ // + " mAnimAccel=" + mAnimAccel);
+
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ stopTracking();
+ }
+
+ boolean interceptTouchEvent(MotionEvent event) {
+ if (SPEW) {
+ Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ + mDisabled);
+ }
+
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return false;
+ }
+
+ final int statusBarSize = mStatusBarView.getHeight();
+ final int hitSize = statusBarSize*2;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int y = (int)event.getRawY();
+
+ if (!mExpanded) {
+ mViewDelta = statusBarSize - y;
+ } else {
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+ }
+ if ((!mExpanded && y < hitSize) ||
+ (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+ // We drop events at the edge of the screen to make the windowshade come
+ // down by accident less, especially when pushing open a device with a keyboard
+ // that rotates (like g1 and droid)
+ int x = (int)event.getRawX();
+ final int edgeBorder = mEdgeBorder;
+ if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+ prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+ mVelocityTracker.addMovement(event);
+ }
+ }
+ } else if (mTracking) {
+ mVelocityTracker.addMovement(event);
+ final int minY = statusBarSize + mCloseView.getHeight();
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ int y = (int)event.getRawY();
+ if (mAnimatingReveal && y < minY) {
+ // nothing
+ } else {
+ mAnimatingReveal = false;
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ float yVel = mVelocityTracker.getYVelocity();
+ boolean negative = yVel < 0;
+
+ float xVel = mVelocityTracker.getXVelocity();
+ if (xVel < 0) {
+ xVel = -xVel;
+ }
+ if (xVel > 150.0f) {
+ xVel = 150.0f; // limit how much we care about the x axis
+ }
+
+ float vel = (float)Math.hypot(yVel, xVel);
+ if (negative) {
+ vel = -vel;
+ }
+
+ performFling((int)event.getRawY(), vel, false);
+ }
+
+ }
+ return false;
+ }
+
+ private class Launcher implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ Launcher(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ if (mIntent != null) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(PhoneStatusBarService.this, 0, overlay);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+ }
+
+ try {
+ mBarService.onNotificationClick(mPkg, mTag, mId);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+
+ // close the shade if it was open
+ animateCollapse();
+
+ // If this click was on the intruder alert, hide that instead
+ mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ }
+ }
+
+ 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))) {
+ mTicker.addEntry(n);
+ }
+ }
+ }
+
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
+ void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+ removeNotification(key);
+ try {
+ mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
+ private class MyTicker extends Ticker {
+ MyTicker(Context context, StatusBarView sb) {
+ super(context, sb);
+ }
+
+ @Override
+ void tickerStarting() {
+ mTicking = true;
+ mIcons.setVisibility(View.GONE);
+ mTickerView.setVisibility(View.VISIBLE);
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+ if (mExpandedVisible) {
+ setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+ }
+ }
+
+ @Override
+ void tickerDone() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+ }
+ }
+
+ void tickerHalting() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ }
+
+ Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+ public void onAnimationEnd(Animation animation) {
+ mTicking = false;
+ }
+ public void onAnimationRepeat(Animation animation) {
+ }
+ public void onAnimationStart(Animation animation) {
+ }
+ };
+
+ private Animation loadAnim(int id, Animation.AnimationListener listener) {
+ Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+ if (listener != null) {
+ anim.setAnimationListener(listener);
+ }
+ return anim;
+ }
+
+ public String viewInfo(View v) {
+ return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ + " " + v.getWidth() + "x" + v.getHeight() + ")";
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ pw.println("Current Status Bar state:");
+ pw.println(" mExpanded=" + mExpanded
+ + ", mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mTicking=" + mTicking);
+ pw.println(" mTracking=" + mTracking);
+ pw.println(" mAnimating=" + mAnimating
+ + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+ + ", mAnimAccel=" + mAnimAccel);
+ pw.println(" mCurAnimationTime=" + mCurAnimationTime
+ + " mAnimLastTime=" + mAnimLastTime);
+ pw.println(" mDisplayHeight=" + mDisplayHeight
+ + " mAnimatingReveal=" + mAnimatingReveal
+ + " mViewDelta=" + mViewDelta);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ pw.println(" mExpandedParams: " + mExpandedParams);
+ pw.println(" mExpandedView: " + viewInfo(mExpandedView));
+ pw.println(" mExpandedDialog: " + mExpandedDialog);
+ pw.println(" mTrackingParams: " + mTrackingParams);
+ pw.println(" mTrackingView: " + viewInfo(mTrackingView));
+ pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
+ pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
+ pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
+ pw.println(" mLatestItems: " + viewInfo(mLatestItems));
+ pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+ pw.println(" mCloseView: " + viewInfo(mCloseView));
+ pw.println(" mTickerView: " + viewInfo(mTickerView));
+ pw.println(" mScrollView: " + viewInfo(mScrollView)
+ + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+ pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+ }
+ /*
+ synchronized (mNotificationData) {
+ int N = mNotificationData.ongoingCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getOngoing(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ N = mNotificationData.latestCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getLatest(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ }
+ */
+
+ if (false) {
+ pw.println("see the logcat for a dump of the views we have created.");
+ // must happen on ui thread
+ mHandler.post(new Runnable() {
+ public void run() {
+ mStatusBarView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mStatusBarView.getWidth() + "x"
+ + mStatusBarView.getHeight());
+ mStatusBarView.debug();
+
+ mExpandedView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mExpandedView.getWidth() + "x"
+ + mExpandedView.getHeight());
+ mExpandedView.debug();
+
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mTrackingView.getWidth() + "x"
+ + mTrackingView.getHeight());
+ mTrackingView.debug();
+ }
+ });
+ }
+ }
+
+ void onBarViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Tracking View --------------
+ pixelFormat = PixelFormat.RGBX_8888;
+ bg = mTrackingView.getBackground();
+ if (bg != null) {
+ pixelFormat = bg.getOpacity();
+ }
+
+ lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ pixelFormat);
+// lp.token = mStatusBarView.getWindowToken();
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("TrackingView");
+ lp.y = mTrackingPosition;
+ mTrackingParams = lp;
+
+ WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+ }
+
+ void onTrackingViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Expanded View --------------
+ pixelFormat = PixelFormat.TRANSLUCENT;
+
+ final int disph = mDisplay.getHeight();
+ lp = mExpandedDialog.getWindow().getAttributes();
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ lp.height = getExpandedHeight();
+ lp.x = 0;
+ mTrackingPosition = lp.y = -disph; // sufficiently large negative
+ lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+ lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_DITHER
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ lp.format = pixelFormat;
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("StatusBarExpanded");
+ mExpandedDialog.getWindow().setAttributes(lp);
+ mExpandedDialog.getWindow().setFormat(pixelFormat);
+ mExpandedParams = lp;
+
+ mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ mExpandedDialog.setContentView(mExpandedView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mExpandedDialog.getWindow().setBackgroundDrawable(null);
+ mExpandedDialog.show();
+ FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+ }
+
+ void setDateViewVisibility(boolean visible, int anim) {
+ mDateView.setUpdates(visible);
+ mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ mDateView.startAnimation(loadAnim(anim, null));
+ }
+
+ void setNotificationIconVisibility(boolean visible, int anim) {
+ int old = mNotificationIcons.getVisibility();
+ int v = visible ? View.VISIBLE : View.INVISIBLE;
+ if (old != v) {
+ mNotificationIcons.setVisibility(v);
+ mNotificationIcons.startAnimation(loadAnim(anim, null));
+ }
+ }
+
+ void updateExpandedViewPos(int expandedPosition) {
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition);
+ }
+
+ int h = mStatusBarView.getHeight();
+ int disph = mDisplay.getHeight();
+
+ // If the expanded view is not visible, make sure they're still off screen.
+ // Maybe the view was resized.
+ if (!mExpandedVisible) {
+ if (mTrackingView != null) {
+ mTrackingPosition = -disph;
+ if (mTrackingParams != null) {
+ mTrackingParams.y = mTrackingPosition;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+ }
+ }
+ if (mExpandedParams != null) {
+ mExpandedParams.y = -disph;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ return;
+ }
+
+ // tracking view...
+ int pos;
+ if (expandedPosition == EXPANDED_FULL_OPEN) {
+ pos = h;
+ }
+ else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+ pos = mTrackingPosition;
+ }
+ else {
+ if (expandedPosition <= disph) {
+ pos = expandedPosition;
+ } else {
+ pos = disph;
+ }
+ pos -= disph-h;
+ }
+ mTrackingPosition = mTrackingParams.y = pos;
+ mTrackingParams.height = disph-h;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+ if (mExpandedParams != null) {
+ mCloseView.getLocationInWindow(mPositionTmp);
+ final int closePos = mPositionTmp[1];
+
+ mExpandedContents.getLocationInWindow(mPositionTmp);
+ final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+ mExpandedParams.y = pos + mTrackingView.getHeight()
+ - (mTrackingParams.height-closePos) - contentsBottom;
+ int max = h;
+ if (mExpandedParams.y > max) {
+ mExpandedParams.y = max;
+ }
+ int min = mTrackingPosition;
+ if (mExpandedParams.y < min) {
+ mExpandedParams.y = min;
+ }
+
+ boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+ if (!visible) {
+ // if the contents aren't visible, move the expanded view way off screen
+ // because the window itself extends below the content view.
+ mExpandedParams.y = -disph;
+ }
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+
+ // As long as this isn't just a repositioning that's not supposed to affect
+ // the user's perception of what's showing, call to say that the visibility
+ // has changed. (Otherwise, someone else will call to do that).
+ if (expandedPosition != EXPANDED_LEAVE_ALONE) {
+ if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
+ visibilityChanged(visible);
+ }
+ }
+
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition
+ + " mExpandedParams.y=" + mExpandedParams.y
+ + " mExpandedParams.height=" + mExpandedParams.height);
+ }
+ }
+
+ int getExpandedHeight() {
+ return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+ }
+
+ void updateExpandedHeight() {
+ if (mExpandedView != null) {
+ mExpandedParams.height = getExpandedHeight();
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ }
+
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ void visibilityChanged(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ try {
+ mBarService.onPanelRevealed();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ }
+
+ 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) {
+ mNotificationIcons.setVisibility(View.INVISIBLE);
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mTicker.halt();
+ }
+ }
+ }
+
+ private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ mBarService.onClearAllNotifications();
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ animateCollapse();
+ }
+ };
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ //collapse();
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ }
+ };
+
+ private void setIntruderAlertVisibility(boolean vis) {
+ mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Reload some of our resources when the configuration changes.
+ *
+ * We don't reload everything when the configuration changes -- we probably
+ * should, but getting that smooth is tough. Someday we'll fix that. In the
+ * meantime, just update the things that we know change.
+ */
+ void updateResources() {
+ Resources res = getResources();
+
+ mClearButton.setText(getText(R.string.status_bar_clear_all_button));
+ mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
+ mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
+ mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ if (false) Slog.v(TAG, "updateResources");
+ }
+
+ //
+ // tracing
+ //
+
+ void postStartTracing() {
+ mHandler.postDelayed(mStartTracing, 3000);
+ }
+
+ void vibrate() {
+ android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+ vib.vibrate(250);
+ }
+
+ Runnable mStartTracing = new Runnable() {
+ public void run() {
+ vibrate();
+ SystemClock.sleep(250);
+ Slog.d(TAG, "startTracing");
+ android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+ mHandler.postDelayed(mStopTracing, 10000);
+ }
+ };
+
+ Runnable mStopTracing = new Runnable() {
+ public void run() {
+ android.os.Debug.stopMethodTracing();
+ Slog.d(TAG, "stopTracing");
+ vibrate();
+ }
+ };
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index 0d02447..a64c3e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -17,194 +17,53 @@
package com.android.systemui.statusbar;
import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+import java.util.ArrayList;
+
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarNotification;
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.Log;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.StatusBarPolicy;
-
-
-public class StatusBarService extends Service implements CommandQueue.Callbacks {
+public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
static final String TAG = "StatusBarService";
- static final boolean SPEW = false;
- public static final String ACTION_STATUSBAR_START
- = "com.android.internal.policy.statusbar.START";
+ protected CommandQueue mCommandQueue;
+ protected IStatusBarService mBarService;
- static final int EXPANDED_LEAVE_ALONE = -10000;
- static final int EXPANDED_FULL_OPEN = -10001;
+ // Up-call methods
+ protected abstract View makeStatusBarView();
+ protected abstract int getStatusBarGravity();
- private static final int MSG_ANIMATE = 1000;
- private static final int MSG_ANIMATE_REVEAL = 1001;
- private static final int MSG_SHOW_INTRUDER = 1002;
- private static final int MSG_HIDE_INTRUDER = 1003;
-
- // will likely move to a resource or other tunable param at some point
- private static final int INTRUDER_ALERT_DECAY_MS = 10000;
-
- static final int POSITION_TOP = 0;
- static final int POSITION_BOTTOM = 1;
-
- StatusBarPolicy mIconPolicy;
-
- CommandQueue mCommandQueue;
- IStatusBarService mBarService;
-
- int mIconSize;
- Display mDisplay;
- int mPosition;
-
- StatusBarView mStatusBarView;
- int mPixelFormat;
- H mHandler = new H();
- Object mQueueLock = new Object();
-
- // icons
- LinearLayout mIcons;
- IconMerger mNotificationIcons;
- LinearLayout mStatusIcons;
-
- // expanded notifications
- Dialog mExpandedDialog;
- ExpandedView mExpandedView;
- WindowManager.LayoutParams mExpandedParams;
- ScrollView mScrollView;
- View mNotificationLinearLayout;
- View mExpandedContents;
- // top bar
- TextView mNoNotificationsTitle;
- TextView mClearButton;
- // drag bar
- CloseDragHandle mCloseView;
- // ongoing
- NotificationData mOngoing = new NotificationData();
- TextView mOngoingTitle;
- LinearLayout mOngoingItems;
- // latest
- NotificationData mLatest = new NotificationData();
- TextView mLatestTitle;
- LinearLayout mLatestItems;
- // position
- int[] mPositionTmp = new int[2];
- boolean mExpanded;
- boolean mExpandedVisible;
-
- // the date view
- DateView mDateView;
-
- // the tracker view
- TrackingView mTrackingView;
- WindowManager.LayoutParams mTrackingParams;
- int mTrackingPosition; // the position of the top of the tracking view.
- private boolean mPanelSlightlyVisible;
-
- // ticker
- private Ticker mTicker;
- private View mTickerView;
- private boolean mTicking;
-
- // Tracking finger for opening/closing.
- int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
- boolean mTracking;
- VelocityTracker mVelocityTracker;
-
- static final int ANIM_FRAME_DURATION = (1000/60);
-
- boolean mAnimating;
- long mCurAnimationTime;
- float mDisplayHeight;
- float mAnimY;
- float mAnimVel;
- float mAnimAccel;
- long mAnimLastTime;
- boolean mAnimatingReveal = false;
- int mViewDelta;
- int[] mAbsPos = new int[2];
-
- // for disabling the status bar
- int mDisabled = 0;
-
- private class ExpandedDialog extends Dialog {
- ExpandedDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- animateCollapse();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
+ /**
+ * Nobody binds to us.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
}
@Override
public void onCreate() {
// First set up our views and stuff.
- final Resources res = getResources();
- mPosition = res.getInteger(R.integer.config_status_bar_position);
- mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
- makeStatusBarView(this);
+ View sb = makeStatusBarView();
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
@@ -242,1379 +101,22 @@
}
// Put up the view
- addStatusBarView();
-
- // Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy = new StatusBarPolicy(this);
- }
-
- @Override
- public void onDestroy() {
- // we're never destroyed
- }
-
- // for immersive activities
- private View mIntruderAlertView;
-
- /**
- * Nobody binds to us.
- */
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- // ================================================================================
- // Constructing the view
- // ================================================================================
- private void makeStatusBarView(Context context) {
- Resources res = context.getResources();
-
- mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
-
- ExpandedView expanded = (ExpandedView)View.inflate(context,
- R.layout.status_bar_expanded, null);
- expanded.mService = this;
-
- mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
- mIntruderAlertView.setVisibility(View.GONE);
- mIntruderAlertView.setClickable(true);
-
- StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
- sb.mService = this;
-
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
- mStatusBarView = sb;
- mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
- mIcons = (LinearLayout)sb.findViewById(R.id.icons);
- mTickerView = sb.findViewById(R.id.ticker);
- mDateView = (DateView)sb.findViewById(R.id.date);
-
- mExpandedDialog = new ExpandedDialog(context);
- mExpandedView = expanded;
- mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
- mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
- mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
- mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
- mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
- mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
- mOngoingTitle.setVisibility(View.GONE);
- mLatestTitle.setVisibility(View.GONE);
-
- mTicker = new MyTicker(context, sb);
-
- TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
-
- mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
- mTrackingView.mService = this;
- mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
- mCloseView.mService = this;
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- // the more notifications icon
- StatusBarIconView moreView = new StatusBarIconView(this, "more");
- moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
- mNotificationIcons.addMoreView(moreView,
- new LinearLayout.LayoutParams(mIconSize, mIconSize));
-
- // set the inital view visibility
- setAreThereNotifications();
- mDateView.setVisibility(View.INVISIBLE);
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- protected void addStatusBarView() {
- Resources res = getResources();
+ final Resources res = getResources();
final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- final StatusBarView view = mStatusBarView;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
PixelFormat.RGBX_8888);
- if (mPosition == POSITION_TOP) {
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- } else {
- lp.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
- }
+ lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
// TODO lp.windowAnimations = R.style.Animation_StatusBar;
+ WindowManagerImpl.getDefault().addView(sb, lp);
- WindowManagerImpl.getDefault().addView(view, lp);
-
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- 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_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- PixelFormat.TRANSLUCENT);
- if (mPosition == POSITION_TOP) {
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- } else {
- lp.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
- }
- lp.y += height * 1.5; // FIXME
- lp.setTitle("IntruderAlert");
- lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert;
-
- WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
+ Slog.d(TAG, "Added status bar view w/ gravity 0x" + Integer.toHexString(lp.gravity));
}
-
- public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
- if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
- + " icon=" + icon);
- StatusBarIconView view = new StatusBarIconView(this, slot);
- view.set(icon);
- mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
- }
-
- public void updateIcon(String slot, int index, int viewIndex,
- StatusBarIcon old, StatusBarIcon icon) {
- if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
- + " old=" + old + " icon=" + icon);
- StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
- view.set(icon);
- }
-
- public void removeIcon(String slot, int index, int viewIndex) {
- if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
- mStatusIcons.removeViewAt(viewIndex);
- }
-
- public void addNotification(IBinder key, StatusBarNotification notification) {
- StatusBarIconView iconView = addNotificationViews(key, notification);
- if (iconView == null) return;
-
- boolean immersive = false;
- try {
- immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
- Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
- } catch (RemoteException ex) {
- }
- if (immersive) {
- if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
- Slog.d(TAG, "Presenting high-priority notification in immersive activity");
- // @@@ special new transient ticker mode
- // 1. Populate mIntruderAlertView
-
- ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
- TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
- alertIcon.setImageDrawable(StatusBarIconView.getIcon(
- alertIcon.getContext(),
- iconView.getStatusBarIcon()));
- alertText.setText(notification.notification.tickerText);
-
- View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
- button.setOnClickListener(
- new Launcher(notification.notification.contentIntent,
- notification.pkg, notification.tag, notification.id));
-
- // 2. Animate mIntruderAlertView in
- mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
-
- // 3. Set alarm to age the notification off (TODO)
- mHandler.removeMessages(MSG_HIDE_INTRUDER);
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
- }
- } else if (notification.notification.fullScreenIntent != null) {
- // not immersive & a full-screen alert should be shown
- Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
- + " sending fullScreenIntent");
- try {
- notification.notification.fullScreenIntent.send();
- } catch (PendingIntent.CanceledException e) {
- }
- } else {
- // usual case: status bar visible & not immersive
-
- // show the ticker
- tick(notification);
- }
-
- // Recalculate the position of the sliding windows and the titles.
- setAreThereNotifications();
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
-
- public void updateNotification(IBinder key, StatusBarNotification notification) {
- Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
-
- NotificationData oldList;
- int oldIndex = mOngoing.findEntry(key);
- if (oldIndex >= 0) {
- oldList = mOngoing;
- } else {
- oldIndex = mLatest.findEntry(key);
- if (oldIndex < 0) {
- Slog.w(TAG, "updateNotification for unknown key: " + key);
- return;
- }
- oldList = mLatest;
- }
- final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
- final StatusBarNotification oldNotification = oldEntry.notification;
- final RemoteViews oldContentView = oldNotification.notification.contentView;
-
- final RemoteViews contentView = notification.notification.contentView;
-
- if (false) {
- Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
- + " ongoing=" + oldNotification.isOngoing()
- + " expanded=" + oldEntry.expanded
- + " contentView=" + oldContentView);
- Slog.d(TAG, "new notification: when=" + notification.notification.when
- + " ongoing=" + oldNotification.isOngoing()
- + " contentView=" + contentView);
- }
-
- // Can we just reapply the RemoteViews in place? If when didn't change, the order
- // didn't change.
- if (notification.notification.when == oldNotification.notification.when
- && notification.isOngoing() == oldNotification.isOngoing()
- && oldEntry.expanded != null
- && contentView != null && oldContentView != null
- && contentView.getPackage() != null
- && oldContentView.getPackage() != null
- && oldContentView.getPackage().equals(contentView.getPackage())
- && oldContentView.getLayoutId() == contentView.getLayoutId()) {
- if (SPEW) Slog.d(TAG, "reusing notification");
- oldEntry.notification = notification;
- try {
- // Reapply the RemoteViews
- contentView.reapply(this, oldEntry.content);
- // update the contentIntent
- final PendingIntent contentIntent = notification.notification.contentIntent;
- if (contentIntent != null) {
- oldEntry.content.setOnClickListener(new Launcher(contentIntent,
- notification.pkg, notification.tag, notification.id));
- }
- // Update the icon.
- final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
- notification.notification.icon, notification.notification.iconLevel,
- notification.notification.number);
- if (!oldEntry.icon.set(ic)) {
- handleNotificationError(key, notification, "Couldn't update icon: " + ic);
- return;
- }
- }
- catch (RuntimeException e) {
- // It failed to add cleanly. Log, and remove the view from the panel.
- Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
- removeNotificationViews(key);
- addNotificationViews(key, notification);
- }
- } else {
- if (SPEW) Slog.d(TAG, "not reusing notification");
- removeNotificationViews(key);
- addNotificationViews(key, notification);
- }
-
- // Restart the ticker if it's still running
- tick(notification);
-
- // Recalculate the position of the sliding windows and the titles.
- setAreThereNotifications();
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
-
- public void removeNotification(IBinder key) {
- if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
- StatusBarNotification old = removeNotificationViews(key);
-
- if (old != null) {
- // Cancel the ticker if it's still running
- mTicker.removeEntry(old);
-
- // Recalculate the position of the sliding windows and the titles.
- setAreThereNotifications();
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
- }
-
- private int chooseIconIndex(boolean isOngoing, int viewIndex) {
- final int latestSize = mLatest.size();
- if (isOngoing) {
- return latestSize + (mOngoing.size() - viewIndex);
- } else {
- return latestSize - viewIndex;
- }
- }
-
- View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
- Notification n = notification.notification;
- RemoteViews remoteViews = n.contentView;
- if (remoteViews == null) {
- return null;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
- notification.tag, notification.id));
- }
-
- View expanded = null;
- Exception exception = null;
- try {
- expanded = remoteViews.apply(this, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return null;
- } else {
- content.addView(expanded);
- row.setDrawingCacheEnabled(true);
- }
-
- return new View[] { row, content, expanded };
- }
-
- StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
- NotificationData list;
- ViewGroup parent;
- final boolean isOngoing = notification.isOngoing();
- if (isOngoing) {
- list = mOngoing;
- parent = mOngoingItems;
- } else {
- list = mLatest;
- parent = mLatestItems;
- }
- // Construct the expanded view.
- final View[] views = makeNotificationView(notification, parent);
- if (views == null) {
- handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
- + notification);
- return null;
- }
- final View row = views[0];
- final View content = views[1];
- final View expanded = views[2];
- // Construct the icon.
- final StatusBarIconView iconView = new StatusBarIconView(this,
- notification.pkg + "/0x" + Integer.toHexString(notification.id));
- final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
- notification.notification.iconLevel, notification.notification.number);
- if (!iconView.set(ic)) {
- handleNotificationError(key, notification, "Coulding create icon: " + ic);
- return null;
- }
- // Add the expanded view.
- final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
- parent.addView(row, viewIndex);
- // Add the icon.
- final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
- mNotificationIcons.addView(iconView, iconIndex,
- new LinearLayout.LayoutParams(mIconSize, mIconSize));
- return iconView;
- }
-
- StatusBarNotification removeNotificationViews(IBinder key) {
- NotificationData.Entry entry = mOngoing.remove(key);
- if (entry == null) {
- entry = mLatest.remove(key);
- if (entry == null) {
- Slog.w(TAG, "removeNotification for unknown key: " + key);
- return null;
- }
- }
- // Remove the expanded view.
- ((ViewGroup)entry.row.getParent()).removeView(entry.row);
- // Remove the icon.
- ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
-
- return entry.notification;
- }
-
- private void setAreThereNotifications() {
- boolean ongoing = mOngoing.hasVisibleItems();
- boolean latest = mLatest.hasVisibleItems();
-
- // (no ongoing notifications are clearable)
- if (mLatest.hasClearableItems()) {
- mClearButton.setVisibility(View.VISIBLE);
- } else {
- mClearButton.setVisibility(View.INVISIBLE);
- }
-
- mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
- mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
- if (ongoing || latest) {
- mNoNotificationsTitle.setVisibility(View.GONE);
- } else {
- mNoNotificationsTitle.setVisibility(View.VISIBLE);
- }
- }
-
-
- /**
- * State is one or more of the DISABLE constants from StatusBarManager.
- */
- public void disable(int state) {
- 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) {
- mTicker.halt();
- } else {
- setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
- }
- } else {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- if (!mExpandedVisible) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
- mTicker.halt();
- }
- }
- }
-
- /**
- * All changes to the status bar and notifications funnel through here and are batched.
- */
- private class H extends Handler {
- public void handleMessage(Message m) {
- switch (m.what) {
- case MSG_ANIMATE:
- doAnimation();
- break;
- case MSG_ANIMATE_REVEAL:
- doRevealAnimation();
- break;
- case MSG_SHOW_INTRUDER:
- setIntruderAlertVisibility(true);
- break;
- case MSG_HIDE_INTRUDER:
- setIntruderAlertVisibility(false);
- break;
- }
- }
- }
-
- View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- // Because 'v' is a ViewGroup, all its children will be (un)selected
- // too, which allows marqueeing to work.
- v.setSelected(hasFocus);
- }
- };
-
- private void makeExpandedVisible() {
- if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (mExpandedVisible) {
- return;
- }
- mExpandedVisible = true;
- visibilityChanged(true);
-
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mExpandedView.requestFocus(View.FOCUS_FORWARD);
- mTrackingView.setVisibility(View.VISIBLE);
-
- if (!mTicking) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
-
- public void animateExpand() {
- if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- prepareTracking(0, true);
- performFling(0, 2000.0f, true);
- }
-
- public void animateCollapse() {
- if (SPEW) {
- Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible
- + " mExpanded=" + mExpanded
- + " mAnimating=" + mAnimating
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel);
- }
-
- if (!mExpandedVisible) {
- return;
- }
-
- int y;
- if (mAnimating) {
- y = (int)mAnimY;
- } else {
- y = mDisplay.getHeight()-1;
- }
- // Let the fling think that we're open so it goes in the right direction
- // and doesn't try to re-open the windowshade.
- mExpanded = true;
- prepareTracking(y, false);
- performFling(y, -2000.0f, true);
- }
-
- void performExpand() {
- if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- mExpanded = true;
- makeExpandedVisible();
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
- if (false) postStartTracing();
- }
-
- void performCollapse() {
- if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible) {
- return;
- }
- mExpandedVisible = false;
- visibilityChanged(false);
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mTrackingView.setVisibility(View.GONE);
-
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-
- if (!mExpanded) {
- return;
- }
- mExpanded = false;
- }
-
- void doAnimation() {
- if (mAnimating) {
- if (SPEW) Slog.d(TAG, "doAnimation");
- if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
- incrementAnim();
- if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
- if (mAnimY >= mDisplay.getHeight()-1) {
- if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
- mAnimating = false;
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
- performExpand();
- }
- else if (mAnimY < mStatusBarView.getHeight()) {
- if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
- mAnimating = false;
- updateExpandedViewPos(0);
- performCollapse();
- }
- else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- }
- }
- }
-
- void stopTracking() {
- mTracking = false;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- void incrementAnim() {
- long now = SystemClock.uptimeMillis();
- float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTime = now; // ms
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void doRevealAnimation() {
- final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
- if (mAnimatingReveal && mAnimating && mAnimY < h) {
- incrementAnim();
- if (mAnimY >= h) {
- mAnimY = h;
- updateExpandedViewPos((int)mAnimY);
- } else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- }
- }
- }
-
- void prepareTracking(int y, boolean opening) {
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = 2000.0f;
- mAnimVel = 200;
- mAnimY = mStatusBarView.getHeight();
- updateExpandedViewPos((int)mAnimY);
- mAnimating = true;
- mAnimatingReveal = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- makeExpandedVisible();
- } else {
- // it's open, close it?
- if (mAnimating) {
- mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
- }
- updateExpandedViewPos(y + mViewDelta);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- mAnimatingReveal = false;
- mDisplayHeight = mDisplay.getHeight();
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > 200.0f
- || (y > (mDisplayHeight-25) && vel > -200.0f))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > 200.0f
- || (y > (mDisplayHeight/2) && vel > -200.0f))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- stopTracking();
- }
-
- boolean interceptTouchEvent(MotionEvent event) {
- if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
- + mDisabled);
- }
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
- final int statusBarSize = mStatusBarView.getHeight();
- final int hitSize = statusBarSize*2;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int y = (int)event.getRawY();
-
- if (!mExpanded) {
- mViewDelta = statusBarSize - y;
- } else {
- mTrackingView.getLocationOnScreen(mAbsPos);
- mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
- }
- if ((!mExpanded && y < hitSize) ||
- (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
- // We drop events at the edge of the screen to make the windowshade come
- // down by accident less, especially when pushing open a device with a keyboard
- // that rotates (like g1 and droid)
- int x = (int)event.getRawX();
- final int edgeBorder = mEdgeBorder;
- if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
- prepareTracking(y, !mExpanded);// opening if we're not already fully visible
- mVelocityTracker.addMovement(event);
- }
- }
- } else if (mTracking) {
- mVelocityTracker.addMovement(event);
- final int minY = statusBarSize + mCloseView.getHeight();
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- int y = (int)event.getRawY();
- if (mAnimatingReveal && y < minY) {
- // nothing
- } else {
- mAnimatingReveal = false;
- updateExpandedViewPos(y + mViewDelta);
- }
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > 150.0f) {
- xVel = 150.0f; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- performFling((int)event.getRawY(), vel, false);
- }
-
- }
- return false;
- }
-
- private class Launcher implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- Launcher(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- if (mIntent != null) {
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(StatusBarService.this, 0, overlay);
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
- }
-
- try {
- mBarService.onNotificationClick(mPkg, mTag, mId);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
-
- // close the shade if it was open
- animateCollapse();
-
- // If this click was on the intruder alert, hide that instead
- mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
- }
- }
-
- 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))) {
- mTicker.addEntry(n);
- }
- }
- }
-
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
- removeNotification(key);
- try {
- mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
- Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
- public void onAnimationEnd(Animation animation) {
- mTicking = false;
- }
- public void onAnimationRepeat(Animation animation) {
- }
- public void onAnimationStart(Animation animation) {
- }
- };
-
- private Animation loadAnim(int id, Animation.AnimationListener listener) {
- Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
- if (listener != null) {
- anim.setAnimationListener(listener);
- }
- return anim;
- }
-
- public String viewInfo(View v) {
- return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + " " + v.getWidth() + "x" + v.getHeight() + ")";
- }
-
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump StatusBar from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpanded=" + mExpanded
- + ", mExpandedVisible=" + mExpandedVisible);
- pw.println(" mTicking=" + mTicking);
- pw.println(" mTracking=" + mTracking);
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mCurAnimationTime=" + mCurAnimationTime
- + " mAnimLastTime=" + mAnimLastTime);
- pw.println(" mDisplayHeight=" + mDisplayHeight
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
- pw.println(" mDisplayHeight=" + mDisplayHeight);
- pw.println(" mExpandedParams: " + mExpandedParams);
- pw.println(" mExpandedView: " + viewInfo(mExpandedView));
- pw.println(" mExpandedDialog: " + mExpandedDialog);
- pw.println(" mTrackingParams: " + mTrackingParams);
- pw.println(" mTrackingView: " + viewInfo(mTrackingView));
- pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
- pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
- pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
- pw.println(" mLatestItems: " + viewInfo(mLatestItems));
- pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
- pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
- pw.println(" mScrollView: " + viewInfo(mScrollView)
- + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
- pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
- }
- /*
- synchronized (mNotificationData) {
- int N = mNotificationData.ongoingCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getOngoing(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- N = mNotificationData.latestCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getLatest(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- }
- */
-
- if (false) {
- pw.println("see the logcat for a dump of the views we have created.");
- // must happen on ui thread
- mHandler.post(new Runnable() {
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + mStatusBarView.getHeight());
- mStatusBarView.debug();
-
- mExpandedView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mExpandedView.getWidth() + "x"
- + mExpandedView.getHeight());
- mExpandedView.debug();
-
- mTrackingView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mTrackingView.getWidth() + "x"
- + mTrackingView.getHeight());
- mTrackingView.debug();
- }
- });
- }
- }
-
- void onBarViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Tracking View --------------
- pixelFormat = PixelFormat.RGBX_8888;
- bg = mTrackingView.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- pixelFormat);
-// lp.token = mStatusBarView.getWindowToken();
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("TrackingView");
- lp.y = mTrackingPosition;
- mTrackingParams = lp;
-
- WindowManagerImpl.getDefault().addView(mTrackingView, lp);
- }
-
- void onTrackingViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Expanded View --------------
- pixelFormat = PixelFormat.TRANSLUCENT;
-
- final int disph = mDisplay.getHeight();
- lp = mExpandedDialog.getWindow().getAttributes();
- lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = getExpandedHeight();
- lp.x = 0;
- mTrackingPosition = lp.y = -disph; // sufficiently large negative
- lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
- lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_DITHER
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.format = pixelFormat;
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBarExpanded");
- mExpandedDialog.getWindow().setAttributes(lp);
- mExpandedDialog.getWindow().setFormat(pixelFormat);
- mExpandedParams = lp;
-
- mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- mExpandedDialog.setContentView(mExpandedView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mExpandedDialog.getWindow().setBackgroundDrawable(null);
- mExpandedDialog.show();
- FrameLayout hack = (FrameLayout)mExpandedView.getParent();
- }
-
- void setDateViewVisibility(boolean visible, int anim) {
- mDateView.setUpdates(visible);
- mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- mDateView.startAnimation(loadAnim(anim, null));
- }
-
- void setNotificationIconVisibility(boolean visible, int anim) {
- int old = mNotificationIcons.getVisibility();
- int v = visible ? View.VISIBLE : View.INVISIBLE;
- if (old != v) {
- mNotificationIcons.setVisibility(v);
- mNotificationIcons.startAnimation(loadAnim(anim, null));
- }
- }
-
- void updateExpandedViewPos(int expandedPosition) {
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition);
- }
-
- int h = mStatusBarView.getHeight();
- int disph = mDisplay.getHeight();
-
- // If the expanded view is not visible, make sure they're still off screen.
- // Maybe the view was resized.
- if (!mExpandedVisible) {
- if (mTrackingView != null) {
- mTrackingPosition = -disph;
- if (mTrackingParams != null) {
- mTrackingParams.y = mTrackingPosition;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
- }
- }
- if (mExpandedParams != null) {
- mExpandedParams.y = -disph;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- return;
- }
-
- // tracking view...
- int pos;
- if (expandedPosition == EXPANDED_FULL_OPEN) {
- pos = h;
- }
- else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
- pos = mTrackingPosition;
- }
- else {
- if (expandedPosition <= disph) {
- pos = expandedPosition;
- } else {
- pos = disph;
- }
- pos -= disph-h;
- }
- mTrackingPosition = mTrackingParams.y = pos;
- mTrackingParams.height = disph-h;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
- if (mExpandedParams != null) {
- mCloseView.getLocationInWindow(mPositionTmp);
- final int closePos = mPositionTmp[1];
-
- mExpandedContents.getLocationInWindow(mPositionTmp);
- final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
- mExpandedParams.y = pos + mTrackingView.getHeight()
- - (mTrackingParams.height-closePos) - contentsBottom;
- int max = h;
- if (mExpandedParams.y > max) {
- mExpandedParams.y = max;
- }
- int min = mTrackingPosition;
- if (mExpandedParams.y < min) {
- mExpandedParams.y = min;
- }
-
- boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
- if (!visible) {
- // if the contents aren't visible, move the expanded view way off screen
- // because the window itself extends below the content view.
- mExpandedParams.y = -disph;
- }
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-
- // As long as this isn't just a repositioning that's not supposed to affect
- // the user's perception of what's showing, call to say that the visibility
- // has changed. (Otherwise, someone else will call to do that).
- if (expandedPosition != EXPANDED_LEAVE_ALONE) {
- if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
- visibilityChanged(visible);
- }
- }
-
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition
- + " mExpandedParams.y=" + mExpandedParams.y
- + " mExpandedParams.height=" + mExpandedParams.height);
- }
- }
-
- int getExpandedHeight() {
- return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
- }
-
- void updateExpandedHeight() {
- if (mExpandedView != null) {
- mExpandedParams.height = getExpandedHeight();
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- }
-
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- void visibilityChanged(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- try {
- mBarService.onPanelRevealed();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
- }
-
- 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) {
- mNotificationIcons.setVisibility(View.INVISIBLE);
- mTicker.halt();
- } else {
- setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
- }
- } else {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- if (!mExpandedVisible) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
- }
- }
- }
-
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- try {
- mBarService.onClearAllNotifications();
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- animateCollapse();
- }
- };
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- //collapse();
- }
- else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- updateResources();
- }
- }
- };
-
- private void setIntruderAlertVisibility(boolean vis) {
- mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Reload some of our resources when the configuration changes.
- *
- * We don't reload everything when the configuration changes -- we probably
- * should, but getting that smooth is tough. Someday we'll fix that. In the
- * meantime, just update the things that we know change.
- */
- void updateResources() {
- Resources res = getResources();
-
- mClearButton.setText(getText(R.string.status_bar_clear_all_button));
- mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
- mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
- mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- if (false) Slog.v(TAG, "updateResources");
- }
-
- //
- // tracing
- //
-
- void postStartTracing() {
- mHandler.postDelayed(mStartTracing, 3000);
- }
-
- void vibrate() {
- android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
- vib.vibrate(250);
- }
-
- Runnable mStartTracing = new Runnable() {
- public void run() {
- vibrate();
- SystemClock.sleep(250);
- Slog.d(TAG, "startTracing");
- android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
- mHandler.postDelayed(mStopTracing, 10000);
- }
- };
-
- Runnable mStopTracing = new Runnable() {
- public void run() {
- android.os.Debug.stopMethodTracing();
- Slog.d(TAG, "stopTracing");
- vibrate();
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
index 57452af..20fc41f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
@@ -35,13 +35,12 @@
static final int DIM_ANIM_TIME = 400;
- StatusBarService mService;
+ PhoneStatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
ViewGroup mNotificationIcons;
ViewGroup mStatusIcons;
View mDate;
- View mButtonArea;
FixedSizeDrawable mBackground;
boolean mNightMode = false;
@@ -65,8 +64,6 @@
mBackground = new FixedSizeDrawable(mDate.getBackground());
mBackground.setFixedBounds(0, 0, 0, 0);
mDate.setBackgroundDrawable(mBackground);
-
- mButtonArea = findViewById(R.id.buttons);
}
@Override
@@ -101,7 +98,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
@Override
@@ -132,10 +129,6 @@
mDate.layout(mDate.getLeft(), mDate.getTop(), newDateRight, mDate.getBottom());
mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t));
-
- if (mButtonArea != null) {
- mButtonArea.getHitRect(mButtonBounds);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
index 9108eee..c59eb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
@@ -26,7 +26,7 @@
public class TrackingView extends LinearLayout {
final Display mDisplay;
- StatusBarService mService;
+ PhoneStatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
new file mode 100644
index 0000000..7835d42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.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.systemui.statusbar.tablet;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+
+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 = new MoreView(context);
+ addView(mMoreView, new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ mIconLayout = new IconLayout(context);
+ addView(mIconLayout, new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ mDraggerView = new DraggerView(context);
+ addView(mDraggerView, new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ }
+
+ class MoreView extends ImageView {
+ public MoreView(Context context) {
+ super(context);
+ setImageResource(R.drawable.stat_notify_more);
+ }
+ }
+
+ class IconLayout extends LinearLayout {
+ public IconLayout(Context context) {
+ super(context);
+ }
+ }
+
+ class DraggerView extends ImageView {
+ public DraggerView(Context context) {
+ super(context);
+ setImageResource(R.drawable.notification_dragger);
+ }
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
new file mode 100644
index 0000000..24d3c39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -0,0 +1,102 @@
+/*
+ * 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.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.statusbar.*;
+import com.android.systemui.R;
+
+public class TabletStatusBarService extends StatusBarService {
+
+ View mStatusBarView;
+ NotificationIconArea mNotificationIconArea;
+
+ int mIconSize;
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ protected View makeStatusBarView() {
+ Resources res = getResources();
+
+ mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+ final View sb = View.inflate(this, R.layout.status_bar, null);
+ mStatusBarView = sb;
+
+ // the more notifications icon
+ mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
+
+ return sb;
+ }
+
+ protected int getStatusBarGravity() {
+ return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
+ }
+
+ public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+ // TODO
+ }
+
+ public void updateIcon(String slot, int index, int viewIndex,
+ StatusBarIcon old, StatusBarIcon icon) {
+ // TODO
+ }
+
+ public void removeIcon(String slot, int index, int viewIndex) {
+ // TODO
+ }
+
+ public void addNotification(IBinder key, StatusBarNotification notification) {
+ // TODO
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ // TODO
+ }
+
+ public void removeNotification(IBinder key) {
+ // TODO
+ }
+
+ public void disable(int state) {
+ // TODO
+ }
+
+ public void animateExpand() {
+ // TODO
+ }
+
+ public void animateCollapse() {
+ // TODO
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index f8abc5a..b9e915a4 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -244,27 +244,12 @@
intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final boolean adbOn = 1 == Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.ADB_ENABLED,
- 0);
-
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
setUsbStorageNotification(
com.android.internal.R.string.usb_storage_notification_title,
com.android.internal.R.string.usb_storage_notification_message,
com.android.internal.R.drawable.stat_sys_data_usb,
false, true, pi);
-
- if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
- // We assume that developers don't want to enable UMS every
- // time they attach a device to a USB host. The average user,
- // however, is looking to charge the phone (in which case this
- // is harmless) or transfer files (in which case this coaches
- // the user about how to complete that task and saves several
- // steps).
- mContext.startActivity(intent);
- }
} else {
setUsbStorageNotification(0, 0, 0, false, false, null);
}
@@ -313,6 +298,23 @@
}
mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+ final boolean adbOn = 1 == Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.ADB_ENABLED,
+ 0);
+
+ if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
+ // Pop up a full-screen alert to coach the user through enabling UMS. The average
+ // user has attached the device to USB either to charge the phone (in which case
+ // this is harmless) or transfer files, and in the latter case this alert saves
+ // several steps (as well as subtly indicates that you shouldn't mix UMS with other
+ // activities on the device).
+ //
+ // If ADB is enabled, however, we suppress this dialog (under the assumption that a
+ // developer (a) knows how to enable UMS, and (b) is probably using USB to install
+ // builds or use adb commands.
+ mUsbStorageNotification.fullScreenIntent = pi;
+ }
}
final int notificationId = mUsbStorageNotification.icon;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 5c56d3c..070d1e8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -27,6 +27,7 @@
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuView;
import com.android.internal.view.menu.SubMenuBuilder;
import com.android.internal.widget.ActionBarView;
@@ -77,9 +78,12 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.ListPopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
+import java.lang.ref.WeakReference;
+
/**
* Android-specific Window.
* <p>
@@ -96,7 +100,7 @@
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
*/
- ContextMenuCallback mContextMenuCallback = new ContextMenuCallback(FEATURE_CONTEXT_MENU);
+ DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@@ -105,7 +109,7 @@
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
- SurfaceHolder.Callback mTakeSurfaceCallback;
+ SurfaceHolder.Callback2 mTakeSurfaceCallback;
BaseSurfaceHolder mSurfaceHolder;
InputQueue.Callback mTakeInputQueueCallback;
@@ -251,7 +255,7 @@
}
@Override
- public void takeSurface(SurfaceHolder.Callback callback) {
+ public void takeSurface(SurfaceHolder.Callback2 callback) {
mTakeSurfaceCallback = callback;
}
@@ -280,7 +284,7 @@
if (mTitleView != null) {
mTitleView.setText(title);
} else if (mActionBar != null) {
- mActionBar.setTitle(title);
+ mActionBar.setWindowTitle(title);
}
mTitle = title;
}
@@ -808,8 +812,20 @@
return true;
}
- // The window manager will give us a valid window token
- new MenuDialogHelper(subMenu).show(null);
+ final Menu parentMenu = subMenu.getRootMenu();
+ final PanelFeatureState panel = findMenuPanel(parentMenu);
+
+ /*
+ * Use the panel open state to determine whether this is coming from an open panel
+ * or an action button. If it's an open panel we want to use MenuDialogHelper.
+ * If it's closed we want to grab the relevant view and create a popup anchored to it.
+ */
+ if (panel.isOpen) {
+ // The window manager will give us a valid window token
+ new MenuDialogHelper(subMenu).show(null);
+ } else {
+ new MenuPopupHelper(getContext(), subMenu).show();
+ }
return true;
}
@@ -2069,7 +2085,7 @@
}
}
- public android.view.SurfaceHolder.Callback willYouTakeTheSurface() {
+ public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
return mFeatureId < 0 ? mTakeSurfaceCallback : null;
}
@@ -2318,7 +2334,7 @@
} else {
mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
if (mActionBar != null && mActionBar.getTitle() == null) {
- mActionBar.setTitle(mTitle);
+ mActionBar.setWindowTitle(mTitle);
}
}
}
@@ -2797,11 +2813,11 @@
* <li> Calls back to the callback's onMenuItemSelected when an item is
* selected.
*/
- private final class ContextMenuCallback implements MenuBuilder.Callback {
+ private final class DialogMenuCallback implements MenuBuilder.Callback {
private int mFeatureId;
private MenuDialogHelper mSubMenuHelper;
- public ContextMenuCallback(int featureId) {
+ public DialogMenuCallback(int featureId) {
mFeatureId = featureId;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 43bd26d..767f38d 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -60,7 +60,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowOrientationListener;
-import android.view.RawInputEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
@@ -153,8 +152,6 @@
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
-
- static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f;
// Debugging: set this to have the system act like there is no hard keyboard.
static final boolean KEYBOARD_ALWAYS_HIDDEN = false;
@@ -164,6 +161,10 @@
static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+ // Useful scan codes.
+ private static final int SW_LID = 0x00;
+ private static final int BTN_MOUSE = 0x110;
+
final Object mLock = new Object();
Context mContext;
@@ -690,7 +691,7 @@
void readLidState() {
try {
- int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID);
+ int sw = mWindowManager.getSwitchState(SW_LID);
if (sw >= 0) {
mLidOpen = sw == 0;
}
@@ -727,19 +728,6 @@
: Configuration.KEYBOARDHIDDEN_YES;
}
- public boolean isCheekPressedAgainstScreen(MotionEvent ev) {
- if(ev.getSize() > SLIDE_TOUCH_EVENT_SIZE_LIMIT) {
- return true;
- }
- int size = ev.getHistorySize();
- for(int i = 0; i < size; i++) {
- if(ev.getHistoricalSize(i) > SLIDE_TOUCH_EVENT_SIZE_LIMIT) {
- return true;
- }
- }
- return false;
- }
-
public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
if (mPointerLocationView == null) {
return;
@@ -1034,19 +1022,22 @@
};
/** {@inheritDoc} */
- public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down,
- int repeatCount, int flags) {
- boolean keyguardOn = keyguardOn();
+ @Override
+ public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
+ int keyCode, int metaState, int repeatCount, int policyFlags) {
+ final boolean keyguardOn = keyguardOn();
+ final boolean down = (action == KeyEvent.ACTION_DOWN);
+ final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0);
if (false) {
- Log.d(TAG, "interceptKeyTi code=" + code + " down=" + down + " repeatCount="
+ Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
+ repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
}
// Clear a pending HOME longpress if the user releases Home
// TODO: This could probably be inside the next bit of logic, but that code
// turned out to be a bit fragile so I'm doing it here explicitly, for now.
- if ((code == KeyEvent.KEYCODE_HOME) && !down) {
+ if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) {
mHandler.removeCallbacks(mHomeLongPress);
}
@@ -1056,11 +1047,11 @@
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
- if (code == KeyEvent.KEYCODE_HOME) {
+ if (keyCode == KeyEvent.KEYCODE_HOME) {
if (!down) {
mHomePressed = false;
- if ((flags&KeyEvent.FLAG_CANCELED) == 0) {
+ if (! canceled) {
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallScreen at this point,
// and his ONLY options are to answer or reject the call.)
@@ -1094,7 +1085,7 @@
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
// timeout.
- if (code == KeyEvent.KEYCODE_HOME) {
+ if (keyCode == KeyEvent.KEYCODE_HOME) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
@@ -1122,17 +1113,17 @@
mHomePressed = true;
}
return true;
- } else if (code == KeyEvent.KEYCODE_MENU) {
+ } else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
if (down && repeatCount == 0) {
- if (mEnableShiftMenuBugReports && (metaKeys & chordBug) == chordBug) {
+ if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
mContext.sendOrderedBroadcast(intent, null);
return true;
} else if (SHOW_PROCESSES_ON_ALT_MENU &&
- (metaKeys & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
+ (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
Intent service = new Intent();
service.setClassName(mContext, "com.android.server.LoadAverageService");
ContentResolver res = mContext.getContentResolver();
@@ -1148,7 +1139,7 @@
return true;
}
}
- } else if (code == KeyEvent.KEYCODE_SEARCH) {
+ } else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
mSearchKeyPressed = true;
@@ -1167,7 +1158,7 @@
// Shortcuts are invoked through Search+key, so intercept those here
if (mSearchKeyPressed) {
if (down && repeatCount == 0 && !keyguardOn) {
- Intent shortcutIntent = mShortcutManager.getIntent(code, metaKeys);
+ Intent shortcutIntent = mShortcutManager.getIntent(keyCode, metaState);
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(shortcutIntent);
@@ -1613,42 +1604,6 @@
}
/** {@inheritDoc} */
- public boolean preprocessInputEventTq(RawInputEvent event) {
- switch (event.type) {
- case RawInputEvent.EV_SW:
- if (event.keycode == RawInputEvent.SW_LID) {
- // lid changed state
- mLidOpen = event.value == 0;
- boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen);
- updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
- if (awakeNow) {
- // If the lid opening and we don't have to keep the
- // keyguard up, then we can turn on the screen
- // immediately.
- mKeyguardMediator.pokeWakelock();
- } else if (keyguardIsShowingTq()) {
- if (mLidOpen) {
- // If we are opening the lid and not hiding the
- // keyguard, then we need to have it turn on the
- // screen once it is shown.
- mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
- KeyEvent.KEYCODE_POWER);
- }
- } else {
- // Light up the keyboard if we are sliding up.
- if (mLidOpen) {
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
- LocalPowerManager.BUTTON_EVENT);
- } else {
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
- LocalPowerManager.OTHER_EVENT);
- }
- }
- }
- }
- return false;
- }
-
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
// lid changed state
mLidOpen = lidOpen;
@@ -1679,26 +1634,6 @@
}
}
-
- /** {@inheritDoc} */
- public boolean isAppSwitchKeyTqTiLwLi(int keycode) {
- return keycode == KeyEvent.KEYCODE_HOME
- || keycode == KeyEvent.KEYCODE_ENDCALL;
- }
-
- /** {@inheritDoc} */
- public boolean isMovementKeyTi(int keycode) {
- switch (keycode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return true;
- }
- return false;
- }
-
-
/**
* @return Whether a telephone call is in progress right now.
*/
@@ -1769,60 +1704,63 @@
}
/** {@inheritDoc} */
- public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) {
+ @Override
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
int result = ACTION_PASS_TO_USER;
- final boolean isWakeKey = isWakeKeyTq(event);
+
+ final boolean isWakeKey = (policyFlags
+ & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+
// If screen is off then we treat the case where the keyguard is open but hidden
// the same as if it were open and in front.
// This will prevent any keys other than the power button from waking the screen
// when the keyguard is hidden by another activity.
- final boolean keyguardActive = (screenIsOn ?
+ final boolean keyguardActive = (isScreenOn ?
mKeyguardMediator.isShowingAndNotHidden() :
mKeyguardMediator.isShowing());
if (false) {
- Log.d(TAG, "interceptKeyTq event=" + event + " keycode=" + event.keycode
- + " screenIsOn=" + screenIsOn + " keyguardActive=" + keyguardActive);
+ Log.d(TAG, "interceptKeyTq keycode=" + keyCode
+ + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
}
if (keyguardActive) {
- if (screenIsOn) {
+ if (isScreenOn) {
// when the screen is on, always give the event to the keyguard
result |= ACTION_PASS_TO_USER;
} else {
// otherwise, don't pass it to the user
result &= ~ACTION_PASS_TO_USER;
- final boolean isKeyDown =
- (event.type == RawInputEvent.EV_KEY) && (event.value != 0);
- if (isWakeKey && isKeyDown) {
+ if (isWakeKey && down) {
// tell the mediator about a wake key, it may decide to
// turn on the screen depending on whether the key is
// appropriate.
- if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)
- && (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
- || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
+ if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode)
+ && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
// when keyguard is showing and screen off, we need
// to handle the volume key for calls and music here
if (isInCall()) {
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
+ handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
} else if (isMusicActive()) {
- handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode);
+ handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
}
}
}
}
- } else if (!screenIsOn) {
+ } else if (!isScreenOn) {
// If we are in-call with screen off and keyguard is not showing,
// then handle the volume key ourselves.
// This is necessary because the phone app will disable the keyguard
// when the proximity sensor is in use.
- if (isInCall() && event.type == RawInputEvent.EV_KEY &&
- (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
- || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
+ if (isInCall() &&
+ (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
result &= ~ACTION_PASS_TO_USER;
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
+ handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
}
if (isWakeKey) {
// a wake key has a sole purpose of waking the device; don't pass
@@ -1832,156 +1770,151 @@
}
}
- int type = event.type;
- int code = event.keycode;
- boolean down = event.value != 0;
-
- if (type == RawInputEvent.EV_KEY) {
- if (code == KeyEvent.KEYCODE_ENDCALL
- || code == KeyEvent.KEYCODE_POWER) {
- if (down) {
- boolean handled = false;
- boolean hungUp = false;
- // key repeats are generated by the window manager, and we don't see them
- // here, so unless the driver is doing something it shouldn't be, we know
- // this is the real press event.
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- try {
- if (code == KeyEvent.KEYCODE_ENDCALL) {
+ if (keyCode == KeyEvent.KEYCODE_ENDCALL
+ || keyCode == KeyEvent.KEYCODE_POWER) {
+ if (down) {
+ boolean handled = false;
+ boolean hungUp = false;
+ // key repeats are generated by the window manager, and we don't see them
+ // here, so unless the driver is doing something it shouldn't be, we know
+ // this is the real press event.
+ ITelephony phoneServ = getPhoneInterface();
+ if (phoneServ != null) {
+ try {
+ if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
+ handled = hungUp = phoneServ.endCall();
+ } else if (keyCode == KeyEvent.KEYCODE_POWER) {
+ if (phoneServ.isRinging()) {
+ // Pressing Power while there's a ringing incoming
+ // call should silence the ringer.
+ phoneServ.silenceRinger();
+ handled = true;
+ } else if (phoneServ.isOffhook() &&
+ ((mIncallPowerBehavior
+ & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
+ != 0)) {
+ // Otherwise, if "Power button ends call" is enabled,
+ // the Power button will hang up any current active call.
handled = hungUp = phoneServ.endCall();
- } else if (code == KeyEvent.KEYCODE_POWER) {
- if (phoneServ.isRinging()) {
- // Pressing Power while there's a ringing incoming
- // call should silence the ringer.
- phoneServ.silenceRinger();
- handled = true;
- } else if (phoneServ.isOffhook() &&
- ((mIncallPowerBehavior
- & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
- != 0)) {
- // Otherwise, if "Power button ends call" is enabled,
- // the Power button will hang up any current active call.
- handled = hungUp = phoneServ.endCall();
- }
}
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException" + ex);
}
- } else {
- Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
- }
-
- if (!screenIsOn
- || (handled && code != KeyEvent.KEYCODE_POWER)
- || (handled && hungUp && code == KeyEvent.KEYCODE_POWER)) {
- mShouldTurnOffOnKeyUp = false;
- } else {
- // only try to turn off the screen if we didn't already hang up
- mShouldTurnOffOnKeyUp = true;
- mHandler.postDelayed(mPowerLongPress,
- ViewConfiguration.getGlobalActionKeyTimeout());
- result &= ~ACTION_PASS_TO_USER;
+ } catch (RemoteException ex) {
+ Log.w(TAG, "ITelephony threw RemoteException" + ex);
}
} else {
- mHandler.removeCallbacks(mPowerLongPress);
- if (mShouldTurnOffOnKeyUp) {
- mShouldTurnOffOnKeyUp = false;
- boolean gohome, sleeps;
- if (code == KeyEvent.KEYCODE_ENDCALL) {
- gohome = (mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
- sleeps = (mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
- } else {
- gohome = false;
- sleeps = true;
- }
- if (keyguardActive
- || (sleeps && !gohome)
- || (gohome && !goHome() && sleeps)) {
- // they must already be on the keyguad or home screen,
- // go to sleep instead
- Log.d(TAG, "I'm tired mEndcallBehavior=0x"
- + Integer.toHexString(mEndcallBehavior));
- result &= ~ACTION_POKE_USER_ACTIVITY;
- result |= ACTION_GO_TO_SLEEP;
- }
- result &= ~ACTION_PASS_TO_USER;
- }
+ Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
}
- } else if (isMediaKey(code)) {
- // This key needs to be handled even if the screen is off.
- // If others need to be handled while it's off, this is a reasonable
- // pattern to follow.
- if ((result & ACTION_PASS_TO_USER) == 0) {
- // Only do this if we would otherwise not pass it to the user. In that
- // case, the PhoneWindow class will do the same thing, except it will
- // only do it if the showing app doesn't process the key on its own.
- KeyEvent keyEvent = new KeyEvent(event.when, event.when,
- down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
- code, 0);
- mBroadcastWakeLock.acquire();
- mHandler.post(new PassHeadsetKey(keyEvent));
+
+ if (!isScreenOn
+ || (handled && keyCode != KeyEvent.KEYCODE_POWER)
+ || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) {
+ mShouldTurnOffOnKeyUp = false;
+ } else {
+ // only try to turn off the screen if we didn't already hang up
+ mShouldTurnOffOnKeyUp = true;
+ mHandler.postDelayed(mPowerLongPress,
+ ViewConfiguration.getGlobalActionKeyTimeout());
+ result &= ~ACTION_PASS_TO_USER;
}
- } else if (code == KeyEvent.KEYCODE_CALL) {
- // If an incoming call is ringing, answer it!
- // (We handle this key here, rather than in the InCallScreen, to make
- // sure we'll respond to the key even if the InCallScreen hasn't come to
- // the foreground yet.)
-
- // We answer the call on the DOWN event, to agree with
- // the "fallback" behavior in the InCallScreen.
- if (down) {
- try {
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- if (phoneServ.isRinging()) {
- Log.i(TAG, "interceptKeyTq:"
- + " CALL key-down while ringing: Answer the call!");
- phoneServ.answerRingingCall();
-
- // And *don't* pass this key thru to the current activity
- // (which is presumably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- }
- } else {
- Log.w(TAG, "CALL button: Unable to find ITelephony interface");
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
+ } else {
+ mHandler.removeCallbacks(mPowerLongPress);
+ if (mShouldTurnOffOnKeyUp) {
+ mShouldTurnOffOnKeyUp = false;
+ boolean gohome, sleeps;
+ if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
+ gohome = (mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
+ sleeps = (mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
+ } else {
+ gohome = false;
+ sleeps = true;
}
+ if (keyguardActive
+ || (sleeps && !gohome)
+ || (gohome && !goHome() && sleeps)) {
+ // they must already be on the keyguad or home screen,
+ // go to sleep instead
+ Log.d(TAG, "I'm tired mEndcallBehavior=0x"
+ + Integer.toHexString(mEndcallBehavior));
+ result &= ~ACTION_POKE_USER_ACTIVITY;
+ result |= ACTION_GO_TO_SLEEP;
+ }
+ result &= ~ACTION_PASS_TO_USER;
}
- } else if ((code == KeyEvent.KEYCODE_VOLUME_UP)
- || (code == KeyEvent.KEYCODE_VOLUME_DOWN)) {
- // If an incoming call is ringing, either VOLUME key means
- // "silence ringer". We handle these keys here, rather than
- // in the InCallScreen, to make sure we'll respond to them
- // even if the InCallScreen hasn't come to the foreground yet.
+ }
+ } else if (isMediaKey(keyCode)) {
+ // This key needs to be handled even if the screen is off.
+ // If others need to be handled while it's off, this is a reasonable
+ // pattern to follow.
+ if ((result & ACTION_PASS_TO_USER) == 0) {
+ // Only do this if we would otherwise not pass it to the user. In that
+ // case, the PhoneWindow class will do the same thing, except it will
+ // only do it if the showing app doesn't process the key on its own.
+ long when = whenNanos / 1000000;
+ KeyEvent keyEvent = new KeyEvent(when, when,
+ down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
+ keyCode, 0);
+ mBroadcastWakeLock.acquire();
+ mHandler.post(new PassHeadsetKey(keyEvent));
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_CALL) {
+ // If an incoming call is ringing, answer it!
+ // (We handle this key here, rather than in the InCallScreen, to make
+ // sure we'll respond to the key even if the InCallScreen hasn't come to
+ // the foreground yet.)
- // Look for the DOWN event here, to agree with the "fallback"
- // behavior in the InCallScreen.
- if (down) {
- try {
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- if (phoneServ.isRinging()) {
- Log.i(TAG, "interceptKeyTq:"
- + " VOLUME key-down while ringing: Silence ringer!");
- // Silence the ringer. (It's safe to call this
- // even if the ringer has already been silenced.)
- phoneServ.silenceRinger();
+ // We answer the call on the DOWN event, to agree with
+ // the "fallback" behavior in the InCallScreen.
+ if (down) {
+ try {
+ ITelephony phoneServ = getPhoneInterface();
+ if (phoneServ != null) {
+ if (phoneServ.isRinging()) {
+ Log.i(TAG, "interceptKeyTq:"
+ + " CALL key-down while ringing: Answer the call!");
+ phoneServ.answerRingingCall();
- // And *don't* pass this key thru to the current activity
- // (which is probably the InCallScreen.)
- result &= ~ACTION_PASS_TO_USER;
- }
- } else {
- Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
+ // And *don't* pass this key thru to the current activity
+ // (which is presumably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
}
- } catch (RemoteException ex) {
- Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
+ } else {
+ Log.w(TAG, "CALL button: Unable to find ITelephony interface");
}
+ } catch (RemoteException ex) {
+ Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
+ }
+ }
+ } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP)
+ || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+ // If an incoming call is ringing, either VOLUME key means
+ // "silence ringer". We handle these keys here, rather than
+ // in the InCallScreen, to make sure we'll respond to them
+ // even if the InCallScreen hasn't come to the foreground yet.
+
+ // Look for the DOWN event here, to agree with the "fallback"
+ // behavior in the InCallScreen.
+ if (down) {
+ try {
+ ITelephony phoneServ = getPhoneInterface();
+ if (phoneServ != null) {
+ if (phoneServ.isRinging()) {
+ Log.i(TAG, "interceptKeyTq:"
+ + " VOLUME key-down while ringing: Silence ringer!");
+ // Silence the ringer. (It's safe to call this
+ // even if the ringer has already been silenced.)
+ phoneServ.silenceRinger();
+
+ // And *don't* pass this key thru to the current activity
+ // (which is probably the InCallScreen.)
+ result &= ~ACTION_PASS_TO_USER;
+ }
+ } else {
+ Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
+ }
+ } catch (RemoteException ex) {
+ Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
}
}
}
@@ -2031,35 +1964,6 @@
};
/** {@inheritDoc} */
- public boolean isWakeRelMovementTq(int device, int classes,
- RawInputEvent event) {
- // if it's tagged with one of the wake bits, it wakes up the device
- return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
- }
-
- /** {@inheritDoc} */
- public boolean isWakeAbsMovementTq(int device, int classes,
- RawInputEvent event) {
- // if it's tagged with one of the wake bits, it wakes up the device
- return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
- }
-
- /**
- * Given the current state of the world, should this key wake up the device?
- */
- protected boolean isWakeKeyTq(RawInputEvent event) {
- // There are not key maps for trackball devices, but we'd still
- // like to have pressing it wake the device up, so force it here.
- int keycode = event.keycode;
- int flags = event.flags;
- if (keycode == RawInputEvent.BTN_MOUSE) {
- flags |= WindowManagerPolicy.FLAG_WAKE;
- }
- return (flags
- & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
- }
-
- /** {@inheritDoc} */
public void screenTurnedOff(int why) {
EventLog.writeEvent(70000, 0);
mKeyguardMediator.onScreenTurnedOff(why);
@@ -2172,7 +2076,7 @@
int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
int sState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_S);
int dpadState = mWindowManager.getDPadKeycodeState(KeyEvent.KEYCODE_DPAD_CENTER);
- int trackballState = mWindowManager.getTrackballScancodeState(RawInputEvent.BTN_MOUSE);
+ int trackballState = mWindowManager.getTrackballScancodeState(BTN_MOUSE);
mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0;
performHapticFeedbackLw(null, mSafeMode
? HapticFeedbackConstants.SAFE_MODE_ENABLED
@@ -2420,13 +2324,6 @@
return true;
}
- public void keyFeedbackFromInput(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && (event.getFlags()&KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
- performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
- }
- }
-
public void screenOnStoppedLw() {
if (!mKeyguardMediator.isShowingAndNotHidden() && mPowerManager.isScreenOn()) {
long curTime = SystemClock.uptimeMillis();
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
similarity index 100%
rename from libs/audioflinger/A2dpAudioInterface.cpp
rename to services/audioflinger/A2dpAudioInterface.cpp
diff --git a/libs/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
similarity index 100%
rename from libs/audioflinger/A2dpAudioInterface.h
rename to services/audioflinger/A2dpAudioInterface.h
diff --git a/libs/audioflinger/Android.mk b/services/audioflinger/Android.mk
similarity index 100%
rename from libs/audioflinger/Android.mk
rename to services/audioflinger/Android.mk
diff --git a/libs/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h
similarity index 100%
rename from libs/audioflinger/AudioBufferProvider.h
rename to services/audioflinger/AudioBufferProvider.h
diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp
similarity index 100%
rename from libs/audioflinger/AudioDumpInterface.cpp
rename to services/audioflinger/AudioDumpInterface.cpp
diff --git a/libs/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h
similarity index 100%
rename from libs/audioflinger/AudioDumpInterface.h
rename to services/audioflinger/AudioDumpInterface.h
diff --git a/libs/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
similarity index 98%
rename from libs/audioflinger/AudioFlinger.cpp
rename to services/audioflinger/AudioFlinger.cpp
index e6f46ce..771d885 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -17,8 +17,7 @@
#define LOG_TAG "AudioFlinger"
-//
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#include <math.h>
#include <signal.h>
@@ -5085,15 +5084,53 @@
}
}
+void AudioFlinger::EffectModule::updateState() {
+ Mutex::Autolock _l(mLock);
+
+ switch (mState) {
+ case RESTART:
+ reset_l();
+ // FALL THROUGH
+
+ case STARTING:
+ // clear auxiliary effect input buffer for next accumulation
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ memset(mConfig.inputCfg.buffer.raw,
+ 0,
+ mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ }
+ start_l();
+ mState = ACTIVE;
+ break;
+ case STOPPING:
+ stop_l();
+ mDisableWaitCnt = mMaxDisableWaitCnt;
+ mState = STOPPED;
+ break;
+ case STOPPED:
+ // mDisableWaitCnt is forced to 1 by process() when the engine indicates the end of the
+ // turn off sequence.
+ if (--mDisableWaitCnt == 0) {
+ reset_l();
+ mState = IDLE;
+ }
+ break;
+ default: //IDLE , ACTIVE
+ break;
+ }
+}
+
void AudioFlinger::EffectModule::process()
{
Mutex::Autolock _l(mLock);
- if (mEffectInterface == NULL || mConfig.inputCfg.buffer.raw == NULL || mConfig.outputCfg.buffer.raw == NULL) {
+ if (mEffectInterface == NULL ||
+ mConfig.inputCfg.buffer.raw == NULL ||
+ mConfig.outputCfg.buffer.raw == NULL) {
return;
}
- if (mState != IDLE) {
+ if (mState == ACTIVE || mState == STOPPING || mState == STOPPED) {
// 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,
@@ -5101,33 +5138,15 @@
mConfig.inputCfg.buffer.frameCount);
}
- // TODO: handle effects with buffer provider
- if (mState != ACTIVE) {
- switch (mState) {
- case RESET:
- reset_l();
- mState = STARTING;
- // clear auxiliary effect input buffer for next accumulation
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
- }
- return;
- case STARTING:
- start_l();
- mState = ACTIVE;
- break;
- case STOPPING:
- mState = STOPPED;
- break;
- case STOPPED:
- stop_l();
- mState = IDLE;
- return;
- }
- }
-
// do the actual processing in the effect engine
- (*mEffectInterface)->process(mEffectInterface, &mConfig.inputCfg.buffer, &mConfig.outputCfg.buffer);
+ int ret = (*mEffectInterface)->process(mEffectInterface,
+ &mConfig.inputCfg.buffer,
+ &mConfig.outputCfg.buffer);
+
+ // force transition to IDLE state when engine is ready
+ if (mState == STOPPED && ret == -ENODATA) {
+ mDisableWaitCnt = 1;
+ }
// clear auxiliary effect input buffer for next accumulation
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
@@ -5216,6 +5235,10 @@
if (status == 0) {
status = cmdStatus;
}
+
+ mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
+ (1000 * mConfig.outputCfg.buffer.frameCount);
+
return status;
}
@@ -5292,21 +5315,19 @@
switch (mState) {
// going from disabled to enabled
case IDLE:
- mState = RESET;
+ mState = STARTING;
+ break;
+ case STOPPED:
+ mState = RESTART;
break;
case STOPPING:
mState = ACTIVE;
break;
- case STOPPED:
- mState = STARTING;
- break;
// going from enabled to disabled
- case RESET:
- mState = IDLE;
- break;
+ case RESTART:
case STARTING:
- mState = STOPPED;
+ mState = IDLE;
break;
case ACTIVE:
mState = STOPPING;
@@ -5325,7 +5346,7 @@
bool AudioFlinger::EffectModule::isEnabled()
{
switch (mState) {
- case RESET:
+ case RESTART:
case STARTING:
case ACTIVE:
return true;
@@ -5772,6 +5793,9 @@
for (size_t i = 0; i < size; i++) {
mEffects[i]->process();
}
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->updateState();
+ }
// if no track is active, input buffer must be cleared here as the mixer process
// will not do it
if (mSessionId > 0 && activeTracks() == 0) {
@@ -6044,11 +6068,4 @@
return BnAudioFlinger::onTransact(code, data, reply, flags);
}
-// ----------------------------------------------------------------------------
-
-void AudioFlinger::instantiate() {
- defaultServiceManager()->addService(
- String16("media.audio_flinger"), new AudioFlinger());
-}
-
}; // namespace android
diff --git a/libs/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
similarity index 98%
rename from libs/audioflinger/AudioFlinger.h
rename to services/audioflinger/AudioFlinger.h
index ec3d7f1..7013d76 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -31,10 +31,12 @@
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
-#include <binder/MemoryDealer.h>
#include <utils/SortedVector.h>
#include <utils/Vector.h>
+#include <binder/BinderService.h>
+#include <binder/MemoryDealer.h>
+
#include <hardware_legacy/AudioHardwareInterface.h>
#include "AudioBufferProvider.h"
@@ -58,10 +60,13 @@
static const nsecs_t kStandbyTimeInNsecs = seconds(3);
-class AudioFlinger : public BnAudioFlinger
+class AudioFlinger :
+ public BinderService<AudioFlinger>,
+ public BnAudioFlinger
{
+ friend class BinderService<AudioFlinger>;
public:
- static void instantiate();
+ static char const* getServiceName() { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -905,7 +910,7 @@
enum effect_state {
IDLE,
- RESET,
+ RESTART,
STARTING,
ACTIVE,
STOPPING,
@@ -914,6 +919,7 @@
int id() { return mId; }
void process();
+ void updateState();
status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
void reset_l();
@@ -948,6 +954,9 @@
protected:
+ // Maximum time allocated to effect engines to complete the turn off sequence
+ static const uint32_t MAX_DISABLE_TIME_MS = 10000;
+
EffectModule(const EffectModule&);
EffectModule& operator = (const EffectModule&);
@@ -973,6 +982,9 @@
status_t mStatus; // initialization status
uint32_t mState; // current activation state (effect_state)
Vector< wp<EffectHandle> > mHandles; // list of client handles
+ uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
+ // sending disable command.
+ uint32_t mDisableWaitCnt; // current process() calls count during disable period.
};
// The EffectHandle class implements the IEffect interface. It provides resources
diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/services/audioflinger/AudioHardwareGeneric.cpp
similarity index 100%
rename from libs/audioflinger/AudioHardwareGeneric.cpp
rename to services/audioflinger/AudioHardwareGeneric.cpp
diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/services/audioflinger/AudioHardwareGeneric.h
similarity index 100%
rename from libs/audioflinger/AudioHardwareGeneric.h
rename to services/audioflinger/AudioHardwareGeneric.h
diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp
similarity index 100%
rename from libs/audioflinger/AudioHardwareInterface.cpp
rename to services/audioflinger/AudioHardwareInterface.cpp
diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/services/audioflinger/AudioHardwareStub.cpp
similarity index 100%
rename from libs/audioflinger/AudioHardwareStub.cpp
rename to services/audioflinger/AudioHardwareStub.cpp
diff --git a/libs/audioflinger/AudioHardwareStub.h b/services/audioflinger/AudioHardwareStub.h
similarity index 100%
rename from libs/audioflinger/AudioHardwareStub.h
rename to services/audioflinger/AudioHardwareStub.h
diff --git a/libs/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
similarity index 100%
rename from libs/audioflinger/AudioMixer.cpp
rename to services/audioflinger/AudioMixer.cpp
diff --git a/libs/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
similarity index 100%
rename from libs/audioflinger/AudioMixer.h
rename to services/audioflinger/AudioMixer.h
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
similarity index 100%
rename from libs/audioflinger/AudioPolicyManagerBase.cpp
rename to services/audioflinger/AudioPolicyManagerBase.cpp
diff --git a/libs/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
similarity index 100%
rename from libs/audioflinger/AudioPolicyService.cpp
rename to services/audioflinger/AudioPolicyService.cpp
diff --git a/libs/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
similarity index 100%
rename from libs/audioflinger/AudioPolicyService.h
rename to services/audioflinger/AudioPolicyService.h
diff --git a/libs/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
similarity index 100%
rename from libs/audioflinger/AudioResampler.cpp
rename to services/audioflinger/AudioResampler.cpp
diff --git a/libs/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
similarity index 100%
rename from libs/audioflinger/AudioResampler.h
rename to services/audioflinger/AudioResampler.h
diff --git a/libs/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
similarity index 100%
rename from libs/audioflinger/AudioResamplerCubic.cpp
rename to services/audioflinger/AudioResamplerCubic.cpp
diff --git a/libs/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
similarity index 100%
rename from libs/audioflinger/AudioResamplerCubic.h
rename to services/audioflinger/AudioResamplerCubic.h
diff --git a/libs/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
similarity index 100%
rename from libs/audioflinger/AudioResamplerSinc.cpp
rename to services/audioflinger/AudioResamplerSinc.cpp
diff --git a/libs/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
similarity index 100%
rename from libs/audioflinger/AudioResamplerSinc.h
rename to services/audioflinger/AudioResamplerSinc.h
diff --git a/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
similarity index 100%
rename from camera/libcameraservice/Android.mk
rename to services/camera/libcameraservice/Android.mk
diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
similarity index 100%
rename from camera/libcameraservice/CameraHardwareStub.cpp
rename to services/camera/libcameraservice/CameraHardwareStub.cpp
diff --git a/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
similarity index 100%
rename from camera/libcameraservice/CameraHardwareStub.h
rename to services/camera/libcameraservice/CameraHardwareStub.h
diff --git a/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
similarity index 99%
rename from camera/libcameraservice/CameraService.cpp
rename to services/camera/libcameraservice/CameraService.cpp
index 10668a4..c786f94 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -192,11 +192,6 @@
return mClient[cameraId].promote();
}
-void CameraService::instantiate() {
- defaultServiceManager()->addService(String16("media.camera"),
- new CameraService());
-}
-
status_t CameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
// Permission checks
diff --git a/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
similarity index 97%
rename from camera/libcameraservice/CameraService.h
rename to services/camera/libcameraservice/CameraService.h
index 8193e77..b0b2d7a 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -19,6 +19,8 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+#include <binder/BinderService.h>
+
#include <camera/ICameraService.h>
#include <camera/CameraHardwareInterface.h>
@@ -30,11 +32,14 @@
class MemoryHeapBase;
class MediaPlayer;
-class CameraService: public BnCameraService
+class CameraService :
+ public BinderService<CameraService>,
+ public BnCameraService
{
class Client;
+ friend class BinderService<CameraService>;
public:
- static void instantiate();
+ static char const* getServiceName() { return "media.camera"; }
CameraService();
virtual ~CameraService();
diff --git a/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h
similarity index 100%
rename from camera/libcameraservice/CannedJpeg.h
rename to services/camera/libcameraservice/CannedJpeg.h
diff --git a/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp
similarity index 100%
rename from camera/libcameraservice/FakeCamera.cpp
rename to services/camera/libcameraservice/FakeCamera.cpp
diff --git a/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h
similarity index 100%
rename from camera/libcameraservice/FakeCamera.h
rename to services/camera/libcameraservice/FakeCamera.h
diff --git a/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk
similarity index 100%
rename from camera/tests/CameraServiceTest/Android.mk
rename to services/camera/tests/CameraServiceTest/Android.mk
diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
similarity index 100%
rename from camera/tests/CameraServiceTest/CameraServiceTest.cpp
rename to services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9ff7de6..9c504fe 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@
import android.net.IConnectivityManager;
import android.net.MobileDataStateTracker;
import android.net.NetworkInfo;
+import android.net.NetworkProperties;
import android.net.NetworkStateTracker;
import android.net.wifi.WifiStateTracker;
import android.net.NetworkUtils;
@@ -51,7 +52,10 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -741,6 +745,7 @@
* specified host is to be routed
* @param hostAddress the IP address of the host to which the route is
* desired
+ * todo - deprecate (only v4!)
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -757,7 +762,11 @@
}
return false;
}
- return addHostRoute(tracker, hostAddress);
+ try {
+ InetAddress addr = InetAddress.getByAddress(NetworkUtils.v4IntToArray(hostAddress));
+ return addHostRoute(tracker, addr);
+ } catch (UnknownHostException e) {}
+ return false;
}
/**
@@ -765,22 +774,25 @@
* host via the mobile data network.
* @param hostAddress the IP address of the host to which the route is desired,
* in network byte order.
+ * TODO - deprecate
* @return {@code true} on success, {@code false} on failure
*/
- private boolean addHostRoute(NetworkStateTracker nt, int hostAddress) {
+ private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
return false;
}
- String interfaceName = nt.getInterfaceName();
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) return false;
+ String interfaceName = p.getInterfaceName();
if (DBG) {
- Slog.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
- "(" + interfaceName + ")");
+ Slog.d(TAG, "Requested host route to " + hostAddress + "(" + interfaceName + ")");
}
- if (interfaceName != null && hostAddress != -1) {
+ if (interfaceName != null) {
return NetworkUtils.addHostRoute(interfaceName, hostAddress) == 0;
} else {
+ if (DBG) Slog.e(TAG, "addHostRoute failed due to null interface name");
return false;
}
}
@@ -1251,21 +1263,20 @@
}
private void addPrivateDnsRoutes(NetworkStateTracker nt) {
- String interfaceName = nt.getInterfaceName();
boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
if (DBG) {
Slog.d(TAG, "addPrivateDnsRoutes for " + nt +
"(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet);
}
- String[] dnsList = getNameServerList(nt.getDnsPropNames());
if (interfaceName != null && !privateDnsRouteSet) {
- for (String addrString : dnsList) {
- int addr = NetworkUtils.lookupHost(addrString);
- if (addr != -1 && addr != 0) {
- if (DBG) Slog.d(TAG, " adding "+addrString+" ("+addr+")");
- NetworkUtils.addHostRoute(interfaceName, addr);
- }
+ Collection<InetAddress> dnsList = p.getDnses();
+ for (InetAddress dns : dnsList) {
+ if (DBG) Slog.d(TAG, " adding " + dns);
+ NetworkUtils.addHostRoute(interfaceName, dns);
}
nt.privateDnsRouteSet(true);
}
@@ -1274,7 +1285,9 @@
private void removePrivateDnsRoutes(NetworkStateTracker nt) {
// TODO - we should do this explicitly but the NetUtils api doesnt
// support this yet - must remove all. No worse than before
- String interfaceName = nt.getInterfaceName();
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
if (interfaceName != null && privateDnsRouteSet) {
if (DBG) {
@@ -1286,61 +1299,42 @@
}
}
- /**
- * Return the IP addresses of the DNS servers available for this
- * network interface.
- * @param propertyNames the names of the system properties whose values
- * give the IP addresses. Properties with no values are skipped.
- * @return an array of {@code String}s containing the IP addresses
- * of the DNS servers, in dot-notation. This may have fewer
- * non-null entries than the list of names passed in, since
- * some of the passed-in names may have empty values.
- */
- String[] getNameServerList(String[] propertyNames) {
- String[] dnsAddresses = new String[propertyNames.length];
- int i, j;
-
- for (i = 0, j = 0; i < propertyNames.length; i++) {
- String value = SystemProperties.get(propertyNames[i]);
- // The GSM layer sometimes sets a bogus DNS server address of
- // 0.0.0.0
- if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) {
- dnsAddresses[j++] = value;
- }
- }
- return dnsAddresses;
- }
private void addDefaultRoute(NetworkStateTracker nt) {
- String interfaceName = nt.getInterfaceName();
- int defaultGatewayAddr = nt.getDefaultGatewayAddr();
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
+ InetAddress defaultGatewayAddr = p.getGateway();
boolean defaultRouteSet = nt.isDefaultRouteSet();
- NetworkInfo networkInfo = nt.getNetworkInfo();
- if ((interfaceName != null) && (defaultGatewayAddr != 0) &&
- defaultRouteSet == false) {
- if (DBG) {
+ if ((interfaceName != null) && (defaultGatewayAddr != null ) &&
+ (defaultRouteSet == false)) {
+ boolean error = (NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr) < 0);
+
+ if (DBG && !error) {
+ NetworkInfo networkInfo = nt.getNetworkInfo();
Slog.d(TAG, "addDefaultRoute for " + networkInfo.getTypeName() +
" (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr);
}
- NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr);
- nt.defaultRouteSet(true);
+ nt.defaultRouteSet(!error);
}
}
public void removeDefaultRoute(NetworkStateTracker nt) {
- String interfaceName = nt.getInterfaceName();
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
boolean defaultRouteSet = nt.isDefaultRouteSet();
- NetworkInfo networkInfo = nt.getNetworkInfo();
if (interfaceName != null && defaultRouteSet == true) {
- if (DBG) {
+ boolean error = (NetworkUtils.removeDefaultRoute(interfaceName) < 0);
+ if (DBG && !error) {
+ NetworkInfo networkInfo = nt.getNetworkInfo();
Slog.d(TAG, "removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
interfaceName + ")");
}
- NetworkUtils.removeDefaultRoute(interfaceName);
- nt.defaultRouteSet(false);
+ nt.defaultRouteSet(error);
}
}
@@ -1430,12 +1424,14 @@
NetworkStateTracker nt = mNetTrackers[i];
if (nt.getNetworkInfo().isConnected() &&
!nt.isTeardownRequested()) {
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) continue;
List pids = mNetRequestersPids[i];
for (int j=0; j<pids.size(); j++) {
Integer pid = (Integer)pids.get(j);
if (pid.intValue() == myPid) {
- String[] dnsList = getNameServerList(nt.getDnsPropNames());
- writePidDns(dnsList, myPid);
+ Collection<InetAddress> dnses = p.getDnses();
+ writePidDns(dnses, myPid);
if (doBump) {
bumpDns();
}
@@ -1457,12 +1453,10 @@
}
}
- private void writePidDns(String[] dnsList, int pid) {
+ private void writePidDns(Collection <InetAddress> dnses, int pid) {
int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
- SystemProperties.set("net.dns" + j++ + "." + pid, dns);
- }
+ for (InetAddress dns : dnses) {
+ SystemProperties.set("net.dns" + j++ + "." + pid, dns.getHostAddress());
}
}
@@ -1488,17 +1482,17 @@
NetworkStateTracker nt = mNetTrackers[netType];
if (nt != null && nt.getNetworkInfo().isConnected() &&
!nt.isTeardownRequested()) {
- String[] dnsList = getNameServerList(nt.getDnsPropNames());
+ NetworkProperties p = nt.getNetworkProperties();
+ if (p == null) continue;
+ Collection<InetAddress> dnses = p.getDnses();
if (mNetAttributes[netType].isDefault()) {
int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
- if (DBG) {
- Slog.d(TAG, "adding dns " + dns + " for " +
- nt.getNetworkInfo().getTypeName());
- }
- SystemProperties.set("net.dns" + j++, dns);
+ for (InetAddress dns : dnses) {
+ if (DBG) {
+ Slog.d(TAG, "adding dns " + dns + " for " +
+ nt.getNetworkInfo().getTypeName());
}
+ SystemProperties.set("net.dns" + j++, dns.getHostAddress());
}
for (int k=j ; k<mNumDnsEntries; k++) {
if (DBG) Slog.d(TAG, "erasing net.dns" + k);
@@ -1510,7 +1504,7 @@
List pids = mNetRequestersPids[netType];
for (int y=0; y< pids.size(); y++) {
Integer pid = (Integer)pids.get(y);
- writePidDns(dnsList, pid.intValue());
+ writePidDns(dnses, pid.intValue());
}
}
}
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
deleted file mode 100644
index 414b69f..0000000
--- a/services/java/com/android/server/InputDevice.java
+++ /dev/null
@@ -1,1025 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.util.Slog;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-
-public class InputDevice {
- static final boolean DEBUG_POINTERS = false;
- static final boolean DEBUG_HACKS = false;
-
- /** Amount that trackball needs to move in order to generate a key event. */
- static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
- /** Maximum number of pointers we will track and report. */
- static final int MAX_POINTERS = 10;
-
- /**
- * Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value.
- */
- private static final int JUMPY_EPSILON_DIVISOR = 212;
-
- /** Number of jumpy points to drop for touchscreens that need it. */
- private static final int JUMPY_TRANSITION_DROPS = 3;
- private static final int JUMPY_DROP_LIMIT = 3;
-
- final int id;
- final int classes;
- final String name;
- final AbsoluteInfo absX;
- final AbsoluteInfo absY;
- final AbsoluteInfo absPressure;
- final AbsoluteInfo absSize;
-
- long mKeyDownTime = 0;
- int mMetaKeysState = 0;
-
- // For use by KeyInputQueue for keeping track of the current touch
- // data in the old non-multi-touch protocol.
- final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2];
-
- final MotionState mAbs = new MotionState(0, 0);
- final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD);
-
- static class MotionState {
- int xPrecision;
- int yPrecision;
- float xMoveScale;
- float yMoveScale;
- MotionEvent currentMove = null;
- boolean changed = false;
- boolean everChanged = false;
- long mDownTime = 0;
-
- // The currently assigned pointer IDs, corresponding to the last data.
- int[] mPointerIds = new int[MAX_POINTERS];
-
- // This is the last generated pointer data, ordered to match
- // mPointerIds.
- boolean mSkipLastPointers;
- int mLastNumPointers = 0;
- final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is the next set of pointer data being generated. It is not
- // in any known order, and will be propagated in to mLastData
- // as part of mapping it to the appropriate pointer IDs.
- // Note that we have one extra sample of data here, to help clients
- // avoid doing bounds checking.
- int mNextNumPointers = 0;
- final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- + MotionEvent.NUM_SAMPLE_DATA];
-
- // Used to determine whether we dropped bad data, to avoid doing
- // it repeatedly.
- final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
-
- // Used to count the number of jumpy points dropped.
- private int mJumpyPointsDropped = 0;
-
- // Used to perform averaging of reported coordinates, to smooth
- // the data and filter out transients during a release.
- static final int HISTORY_SIZE = 5;
- int[] mHistoryDataStart = new int[MAX_POINTERS];
- int[] mHistoryDataEnd = new int[MAX_POINTERS];
- final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- * HISTORY_SIZE];
- final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // Temporary data structures for doing the pointer ID mapping.
- final int[] mLast2Next = new int[MAX_POINTERS];
- final int[] mNext2Last = new int[MAX_POINTERS];
- final long[] mNext2LastDistance = new long[MAX_POINTERS];
-
- // Temporary data structure for generating the final motion data.
- final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is not used here, but can be used by callers for state tracking.
- int mAddingPointerOffset = 0;
- final boolean[] mDown = new boolean[MAX_POINTERS];
-
- void dumpIntArray(PrintWriter pw, int[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i]);
- }
- pw.print("]");
- }
-
- void dumpBooleanArray(PrintWriter pw, boolean[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i] ? "true" : "false");
- }
- pw.print("]");
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision);
- pw.print(" yPrecision="); pw.println(yPrecision);
- pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale);
- pw.print(" yMoveScale="); pw.println(yMoveScale);
- if (currentMove != null) {
- pw.print(prefix); pw.print("currentMove="); pw.println(currentMove);
- }
- if (changed || mDownTime != 0) {
- pw.print(prefix); pw.print("changed="); pw.print(changed);
- pw.print(" mDownTime="); pw.println(mDownTime);
- }
- pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds);
- pw.println("");
- if (mSkipLastPointers || mLastNumPointers != 0) {
- pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers);
- pw.print(" mLastNumPointers="); pw.println(mLastNumPointers);
- pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData);
- pw.println("");
- }
- if (mNextNumPointers != 0) {
- pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers);
- pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData);
- pw.println("");
- }
- pw.print(prefix); pw.print("mDroppedBadPoint=");
- dumpBooleanArray(pw, mDroppedBadPoint); pw.println("");
- pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset);
- pw.print(prefix); pw.print("mDown=");
- dumpBooleanArray(pw, mDown); pw.println("");
- }
-
- MotionState(int mx, int my) {
- xPrecision = mx;
- yPrecision = my;
- xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
- yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
- for (int i=0; i<MAX_POINTERS; i++) {
- mPointerIds[i] = i;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it.
- */
- void dropBadPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (mNextNumPointers != mLastNumPointers) {
- return;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16;
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (int i=mNextNumPointers-1; i>=0; i--) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- //final int x = mNextData[ioff + MotionEvent.SAMPLE_X];
- final int y = mNextData[ioff + MotionEvent.SAMPLE_Y];
- if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y);
- boolean dropped = false;
- if (!mDroppedBadPoint[i] && mLastNumPointers > 0) {
- dropped = true;
- int closestDy = -1;
- int closestY = -1;
- // We will drop this new point if it is sufficiently
- // far away from -all- last points.
- for (int j=mLastNumPointers-1; j>=0; j--) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- //int dx = x - mLastData[joff + MotionEvent.SAMPLE_X];
- int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y];
- //if (dx < 0) dx = -dx;
- if (dy < 0) dy = -dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j
- + ": y=" + mLastData[joff] + " dy=" + dy);
- if (dy < maxDy) {
- dropped = false;
- break;
- } else if (closestDy < 0 || dy < closestDy) {
- closestDy = dy;
- closestY = mLastData[joff + MotionEvent.SAMPLE_Y];
- }
- }
- if (dropped) {
- dropped = true;
- Slog.i("InputDevice", "Dropping bad point #" + i
- + ": newY=" + y + " closestDy=" + closestDy
- + " maxDy=" + maxDy);
- mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY;
- break;
- }
- }
- mDroppedBadPoint[i] = dropped;
- }
- }
-
- void dropJumpyPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR;
-
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
- final int[] nextData = mNextData;
- final int[] lastData = mLastData;
-
- if (nextNumPointers != mLastNumPointers) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Different pointer count " + lastNumPointers +
- " -> " + nextNumPointers);
- for (int i = 0; i < nextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
-
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- if (lastNumPointers == 1 && nextNumPointers == 2
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- mNextNumPointers = 1;
- mJumpyPointsDropped++;
- } else if (lastNumPointers == 2 && nextNumPointers == 1
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- // The event when we go from 2 -> 1 tends to be messed up too
- System.arraycopy(lastData, 0, nextData, 0,
- lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
- mNextNumPointers = lastNumPointers;
- mJumpyPointsDropped++;
-
- if (DEBUG_HACKS) {
- for (int i = 0; i < mNextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " replaced (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
- } else {
- mJumpyPointsDropped = 0;
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Transition - drop limit reset");
- }
- }
- return;
- }
-
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (nextNumPointers < 2) {
- return;
- }
-
- int badPointerIndex = -1;
- int badPointerReplaceXWith = 0;
- int badPointerReplaceYWith = 0;
- int badPointerDistance = Integer.MIN_VALUE;
- for (int i = nextNumPointers - 1; i >= 0; i--) {
- boolean dropx = false;
- boolean dropy = false;
-
- // Limit how many times a jumpy point can get dropped.
- if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- final int x = nextData[ioff + MotionEvent.SAMPLE_X];
- final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
- }
-
- // Check if a touch point is too close to another's coordinates
- for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
- if (j == i) {
- continue;
- }
-
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
- final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
-
- dropx = Math.abs(x - xOther) <= jumpyEpsilon;
- dropy = Math.abs(y - yOther) <= jumpyEpsilon;
- }
-
- if (dropx) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(yreplace - y);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lasty = lastData[joff + MotionEvent.SAMPLE_Y];
- int currDist = Math.abs(lasty - y);
- if (currDist < distance) {
- xreplace = lastData[joff + MotionEvent.SAMPLE_X];
- yreplace = lasty;
- distance = currDist;
- }
- }
-
- int badXDelta = Math.abs(xreplace - x);
- if (badXDelta > badPointerDistance) {
- badPointerDistance = badXDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- } else if (dropy) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(xreplace - x);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lastx = lastData[joff + MotionEvent.SAMPLE_X];
- int currDist = Math.abs(lastx - x);
- if (currDist < distance) {
- xreplace = lastx;
- yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
- distance = currDist;
- }
- }
-
- int badYDelta = Math.abs(yreplace - y);
- if (badYDelta > badPointerDistance) {
- badPointerDistance = badYDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- }
- }
- }
- if (badPointerIndex >= 0) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
- " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
- ")");
- }
-
- final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
- nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
- nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
- mJumpyPointsDropped++;
- } else {
- mJumpyPointsDropped = 0;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications.
- */
- int[] generateAveragedData(int upOrDownPointer, int lastNumPointers,
- int nextNumPointers) {
- final int numPointers = mLastNumPointers;
- final int[] rawData = mLastData;
- if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers
- + " numPointers=" + numPointers);
- for (int i=0; i<numPointers; i++) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- // We keep the average data in offsets based on the pointer
- // ID, so we don't need to move it around as fingers are
- // pressed and released.
- final int p = mPointerIds[i];
- final int poff = p * MotionEvent.NUM_SAMPLE_DATA * HISTORY_SIZE;
- if (i == upOrDownPointer && lastNumPointers != nextNumPointers) {
- if (lastNumPointers < nextNumPointers) {
- // This pointer is going down. Clear its history
- // and start fresh.
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- // The pointer is going up. Just fall through to
- // recompute the last averaged point (and don't add
- // it as a new point to include in the average).
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- }
- } else {
- int end = mHistoryDataEnd[i];
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int oldX = mHistoryData[eoff + MotionEvent.SAMPLE_X];
- int oldY = mHistoryData[eoff + MotionEvent.SAMPLE_Y];
- int newX = rawData[ioff + MotionEvent.SAMPLE_X];
- int newY = rawData[ioff + MotionEvent.SAMPLE_Y];
- int dx = newX-oldX;
- int dy = newY-oldY;
- int delta = dx*dx + dy*dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta);
- if (delta >= (75*75)) {
- // Magic number, if moving farther than this, turn
- // off filtering to avoid lag in response.
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- end++;
- if (end >= HISTORY_SIZE) {
- end -= HISTORY_SIZE;
- }
- mHistoryDataEnd[i] = end;
- int noff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- mHistoryData[noff + MotionEvent.SAMPLE_X] = newX;
- mHistoryData[noff + MotionEvent.SAMPLE_Y] = newY;
- mHistoryData[noff + MotionEvent.SAMPLE_PRESSURE]
- = rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- int start = mHistoryDataStart[i];
- if (end == start) {
- start++;
- if (start >= HISTORY_SIZE) {
- start -= HISTORY_SIZE;
- }
- mHistoryDataStart[i] = start;
- }
- }
- }
-
- // Now compute the average.
- int start = mHistoryDataStart[i];
- int end = mHistoryDataEnd[i];
- int x=0, y=0;
- int totalPressure = 0;
- while (start != end) {
- int soff = poff + (start*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[soff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[soff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[soff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- start++;
- if (start >= HISTORY_SIZE) start = 0;
- }
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[eoff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[eoff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[eoff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- x /= totalPressure;
- y /= totalPressure;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure
- + " weight: (" + x + "," + y + ")");
- mAveragedData[ioff + MotionEvent.SAMPLE_X] = x;
- mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y;
- mAveragedData[ioff + MotionEvent.SAMPLE_PRESSURE] =
- rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- mAveragedData[ioff + MotionEvent.SAMPLE_SIZE] =
- rawData[ioff + MotionEvent.SAMPLE_SIZE];
- }
- return mAveragedData;
- }
-
- private boolean assignPointer(int nextIndex, boolean allowOverlap) {
- final int lastNumPointers = mLastNumPointers;
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- final int[] last2Next = mLast2Next;
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex="
- + nextIndex + " dataOff=" + id);
- final int x1 = nextData[id + MotionEvent.SAMPLE_X];
- final int y1 = nextData[id + MotionEvent.SAMPLE_Y];
-
- long bestDistance = -1;
- int bestIndex = -1;
- for (int j=0; j<lastNumPointers; j++) {
- // If we are not allowing multiple new points to be assigned
- // to the same old pointer, then skip this one if it is already
- // detected as a conflict (-2).
- if (!allowOverlap && last2Next[j] < -1) {
- continue;
- }
- final int jd = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1;
- final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1;
- final long distance = xd*(long)xd + yd*(long)yd;
- if (bestDistance == -1 || distance < bestDistance) {
- bestDistance = distance;
- bestIndex = j;
- }
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex
- + " best old index=" + bestIndex + " (distance="
- + bestDistance + ")");
- next2Last[nextIndex] = bestIndex;
- next2LastDistance[nextIndex] = bestDistance;
-
- if (bestIndex < 0) {
- return true;
- }
-
- if (last2Next[bestIndex] == -1) {
- last2Next[bestIndex] = nextIndex;
- return false;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex
- + " has multiple best new pointers!");
-
- last2Next[bestIndex] = -2;
- return true;
- }
-
- private int updatePointerIdentifiers() {
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
-
- if (nextNumPointers == 1 && lastNumPointers == 1) {
- System.arraycopy(nextData, 0, lastData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- return -1;
- }
-
- // Clear our old state.
- final int[] last2Next = mLast2Next;
- for (int i=0; i<lastNumPointers; i++) {
- last2Next[i] = -1;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Update pointers: lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers);
-
- // Figure out the closes new points to the previous points.
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- boolean conflicts = false;
- for (int i=0; i<nextNumPointers; i++) {
- conflicts |= assignPointer(i, true);
- }
-
- // Resolve ambiguities in pointer mappings, when two or more
- // new pointer locations find their best previous location is
- // the same.
- if (conflicts) {
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts");
-
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] != -2) {
- continue;
- }
-
- // Note that this algorithm is far from perfect. Ideally
- // we should do something like the one described at
- // http://portal.acm.org/citation.cfm?id=997856
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Resolving last index #" + i);
-
- int numFound;
- do {
- numFound = 0;
- long worstDistance = 0;
- int worstJ = -1;
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] != i) {
- continue;
- }
- numFound++;
- if (worstDistance < next2LastDistance[j]) {
- worstDistance = next2LastDistance[j];
- worstJ = j;
- }
- }
-
- if (worstJ >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Worst new pointer: " + worstJ
- + " (distance=" + worstDistance + ")");
- if (assignPointer(worstJ, false)) {
- // In this case there is no last pointer
- // remaining for this new one!
- next2Last[worstJ] = -1;
- }
- }
- } while (numFound > 2);
- }
- }
-
- int retIndex = -1;
-
- if (lastNumPointers < nextNumPointers) {
- // We have one or more new pointers that are down. Create a
- // new pointer identifier for one of them.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer");
- int nextId = 0;
- int i=0;
- while (i < lastNumPointers) {
- if (mPointerIds[i] > nextId) {
- // Found a hole, insert the pointer here.
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Inserting new pointer at hole " + i);
- System.arraycopy(mPointerIds, i, mPointerIds,
- i+1, lastNumPointers-i);
- System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(next2Last, i, next2Last,
- i+1, lastNumPointers-i);
- break;
- }
- i++;
- nextId++;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "New pointer id " + nextId + " at index " + i);
-
- mLastNumPointers++;
- retIndex = i;
- mPointerIds[i] = nextId;
-
- // And assign this identifier to the first new pointer.
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] < 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Assigning new id to new pointer index " + j);
- next2Last[j] = i;
- break;
- }
- }
- }
-
- // Propagate all of the current data into the appropriate
- // location in the old data to match the pointer ID that was
- // assigned to it.
- for (int i=0; i<nextNumPointers; i++) {
- int lastIndex = next2Last[i];
- if (lastIndex >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Copying next pointer index " + i
- + " to last index " + lastIndex);
- System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA,
- MotionEvent.NUM_SAMPLE_DATA);
- }
- }
-
- if (lastNumPointers > nextNumPointers) {
- // One or more pointers has gone up. Find the first one,
- // and adjust accordingly.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer");
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] == -1) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Removing old pointer at index " + i);
- retIndex = i;
- break;
- }
- }
- }
-
- return retIndex;
- }
-
- void removeOldPointer(int index) {
- final int lastNumPointers = mLastNumPointers;
- if (index >= 0 && index < lastNumPointers) {
- System.arraycopy(mPointerIds, index+1, mPointerIds,
- index, lastNumPointers-index-1);
- System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA,
- mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA);
- mLastNumPointers--;
- }
- }
-
- MotionEvent generateAbsMotion(InputDevice device, long curTime,
- long curTimeNano, Display display, int orientation,
- int metaState) {
-
- if (mSkipLastPointers) {
- mSkipLastPointers = false;
- mLastNumPointers = 0;
- }
-
- if (mNextNumPointers <= 0 && mLastNumPointers <= 0) {
- return null;
- }
-
- final int lastNumPointers = mLastNumPointers;
- final int nextNumPointers = mNextNumPointers;
- if (mNextNumPointers > MAX_POINTERS) {
- Slog.w("InputDevice", "Number of pointers " + mNextNumPointers
- + " exceeded maximum of " + MAX_POINTERS);
- mNextNumPointers = MAX_POINTERS;
- }
-
- int upOrDownPointer = updatePointerIdentifiers();
-
- final float[] reportData = mReportData;
- final int[] rawData;
- if (KeyInputQueue.BAD_TOUCH_HACK) {
- rawData = generateAveragedData(upOrDownPointer, lastNumPointers,
- nextNumPointers);
- } else {
- rawData = mLastData;
- }
-
- final int numPointers = mLastNumPointers;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing "
- + numPointers + " pointers (going from " + lastNumPointers
- + " to " + nextNumPointers + ")");
-
- for (int i=0; i<numPointers; i++) {
- final int pos = i * MotionEvent.NUM_SAMPLE_DATA;
- reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X];
- reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y];
- reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE];
- reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE];
- }
-
- int action;
- int edgeFlags = 0;
- if (nextNumPointers != lastNumPointers) {
- if (nextNumPointers > lastNumPointers) {
- if (lastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else {
- action = MotionEvent.ACTION_POINTER_DOWN
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- if (numPointers == 1) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_POINTER_UP
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- }
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- final int dispW = display.getWidth()-1;
- final int dispH = display.getHeight()-1;
- int w = dispW;
- int h = dispH;
- if (orientation == Surface.ROTATION_90
- || orientation == Surface.ROTATION_270) {
- int tmp = w;
- w = h;
- h = tmp;
- }
-
- final AbsoluteInfo absX = device.absX;
- final AbsoluteInfo absY = device.absY;
- final AbsoluteInfo absPressure = device.absPressure;
- final AbsoluteInfo absSize = device.absSize;
- for (int i=0; i<numPointers; i++) {
- final int j = i * MotionEvent.NUM_SAMPLE_DATA;
-
- if (absX != null) {
- reportData[j + MotionEvent.SAMPLE_X] =
- ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)
- / absX.range) * w;
- }
- if (absY != null) {
- reportData[j + MotionEvent.SAMPLE_Y] =
- ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)
- / absY.range) * h;
- }
- if (absPressure != null) {
- reportData[j + MotionEvent.SAMPLE_PRESSURE] =
- ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
- / (float)absPressure.range);
- }
- if (absSize != null) {
- reportData[j + MotionEvent.SAMPLE_SIZE] =
- ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
- / (float)absSize.range);
- }
-
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = w-temp;
- break;
- }
- case Surface.ROTATION_180: {
- reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
- }
-
- // We only consider the first pointer when computing the edge
- // flags, since they are global to the event.
- if (action == MotionEvent.ACTION_DOWN) {
- if (reportData[MotionEvent.SAMPLE_X] <= 0) {
- edgeFlags |= MotionEvent.EDGE_LEFT;
- } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) {
- edgeFlags |= MotionEvent.EDGE_RIGHT;
- }
- if (reportData[MotionEvent.SAMPLE_Y] <= 0) {
- edgeFlags |= MotionEvent.EDGE_TOP;
- } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) {
- edgeFlags |= MotionEvent.EDGE_BOTTOM;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + reportData[MotionEvent.SAMPLE_X]
- + " y=" + reportData[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, reportData, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, numPointers, mPointerIds, reportData,
- metaState, xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
-
- if (nextNumPointers < lastNumPointers) {
- removeOldPointer(upOrDownPointer);
- }
-
- return me;
- }
-
- boolean hasMore() {
- return mLastNumPointers != mNextNumPointers;
- }
-
- void finish() {
- mNextNumPointers = mAddingPointerOffset = 0;
- mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
- }
-
- MotionEvent generateRelMotion(InputDevice device, long curTime,
- long curTimeNano, int orientation, int metaState) {
-
- final float[] scaled = mReportData;
-
- // For now we only support 1 pointer with relative motions.
- scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
- scaled[MotionEvent.SAMPLE_SIZE] = 0;
- int edgeFlags = 0;
-
- int action;
- if (mNextNumPointers != mLastNumPointers) {
- mNextData[MotionEvent.SAMPLE_X] =
- mNextData[MotionEvent.SAMPLE_Y] = 0;
- if (mNextNumPointers > 0 && mLastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else if (mNextNumPointers == 0) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
- mLastNumPointers = mNextNumPointers;
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
- scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = -temp;
- break;
- }
- case Surface.ROTATION_180: {
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + scaled[MotionEvent.SAMPLE_X]
- + " y=" + scaled[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, scaled, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, 1, mPointerIds, scaled, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
- return me;
- }
- }
-
- static class AbsoluteInfo {
- int minValue;
- int maxValue;
- int range;
- int flat;
- int fuzz;
-
- final void dump(PrintWriter pw) {
- pw.print("minValue="); pw.print(minValue);
- pw.print(" maxValue="); pw.print(maxValue);
- pw.print(" range="); pw.print(range);
- pw.print(" flat="); pw.print(flat);
- pw.print(" fuzz="); pw.print(fuzz);
- }
- };
-
- InputDevice(int _id, int _classes, String _name,
- AbsoluteInfo _absX, AbsoluteInfo _absY,
- AbsoluteInfo _absPressure, AbsoluteInfo _absSize) {
- id = _id;
- classes = _classes;
- name = _name;
- absX = _absX;
- absY = _absY;
- absPressure = _absPressure;
- absSize = _absSize;
- }
-};
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 2ba2914..cdae27c 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -56,7 +56,6 @@
private final Callbacks mCallbacks;
private final Context mContext;
private final WindowManagerService mWindowManagerService;
- private final WindowManagerPolicy mWindowManagerPolicy;
private final PowerManager mPowerManager;
private final PowerManagerService mPowerManagerService;
@@ -103,12 +102,10 @@
public InputManager(Context context,
WindowManagerService windowManagerService,
- WindowManagerPolicy windowManagerPolicy,
PowerManager powerManager,
PowerManagerService powerManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
- this.mWindowManagerPolicy = windowManagerPolicy;
this.mPowerManager = powerManager;
this.mPowerManagerService = powerManagerService;
@@ -325,23 +322,8 @@
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
@SuppressWarnings("unused")
- public boolean isScreenOn() {
- return mPowerManagerService.isScreenOn();
- }
-
- @SuppressWarnings("unused")
- public boolean isScreenBright() {
- return mPowerManagerService.isScreenBright();
- }
-
- @SuppressWarnings("unused")
- public void virtualKeyFeedback(long whenNanos, int deviceId, int action, int flags,
- int keyCode, int scanCode, int metaState, long downTimeNanos) {
- KeyEvent keyEvent = new KeyEvent(downTimeNanos / 1000000,
- whenNanos / 1000000, action, keyCode, 0, metaState, scanCode, deviceId,
- flags);
-
- mWindowManagerService.virtualKeyFeedback(keyEvent);
+ public void virtualKeyDownFeedback() {
+ mWindowManagerService.mInputMonitor.virtualKeyDownFeedback();
}
@SuppressWarnings("unused")
@@ -356,7 +338,7 @@
@SuppressWarnings("unused")
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
- mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
+ mWindowManagerService.mInputMonitor.notifyLidSwitchChanged(whenNanos, lidOpen);
}
@SuppressWarnings("unused")
@@ -380,17 +362,17 @@
}
@SuppressWarnings("unused")
- public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
- int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
- return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(deviceId, type,
- scanCode, keyCode, policyFlags, value, whenNanos, isScreenOn);
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
+ whenNanos, keyCode, down, policyFlags, isScreenOn);
}
@SuppressWarnings("unused")
- public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
- int metaState, boolean down, int repeatCount, int policyFlags) {
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
+ int flags, int keyCode, int metaState, int repeatCount, int policyFlags) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
- keyCode, metaState, down, repeatCount, policyFlags);
+ action, flags, keyCode, metaState, repeatCount, policyFlags);
}
@SuppressWarnings("unused")
@@ -401,18 +383,6 @@
}
@SuppressWarnings("unused")
- public void goToSleep(long whenNanos) {
- long when = whenNanos / 1000000;
- mPowerManager.goToSleep(when);
- }
-
- @SuppressWarnings("unused")
- public void pokeUserActivity(long eventTimeNanos, int eventType) {
- long eventTime = eventTimeNanos / 1000000;
- mPowerManagerService.userActivity(eventTime, false, eventType, false);
- }
-
- @SuppressWarnings("unused")
public void notifyAppSwitchComing() {
mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index f6e3441..36b3a5e 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -980,7 +980,7 @@
void setInputMethodLocked(String id) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
- throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+ throw new IllegalArgumentException("Unknown id: " + id);
}
if (id.equals(mCurMethodId)) {
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
deleted file mode 100644
index f62c7ee..0000000
--- a/services/java/com/android/server/KeyInputQueue.java
+++ /dev/null
@@ -1,1388 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Environment;
-import android.os.LatencyTimer;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RawInputEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public abstract class KeyInputQueue {
- static final String TAG = "KeyInputQueue";
-
- static final boolean DEBUG = false;
- static final boolean DEBUG_VIRTUAL_KEYS = false;
- static final boolean DEBUG_POINTERS = false;
-
- /**
- * Turn on some hacks we have to improve the touch interaction with a
- * certain device whose screen currently is not all that good.
- */
- static boolean BAD_TOUCH_HACK = false;
-
- /**
- * Turn on some hacks to improve touch interaction with another device
- * where touch coordinate data can get corrupted.
- */
- static boolean JUMPY_TOUCH_HACK = false;
-
- private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
-
- final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
- final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
- final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
- final HapticFeedbackCallback mHapticFeedbackCallback;
-
- int mGlobalMetaState = 0;
- boolean mHaveGlobalMetaState = false;
-
- final QueuedEvent mFirst;
- final QueuedEvent mLast;
- QueuedEvent mCache;
- int mCacheCount;
-
- Display mDisplay = null;
- int mDisplayWidth;
- int mDisplayHeight;
-
- int mOrientation = Surface.ROTATION_0;
- int[] mKeyRotationMap = null;
-
- VirtualKey mPressedVirtualKey = null;
-
- PowerManager.WakeLock mWakeLock;
-
- static final int[] KEY_90_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- static final int[] KEY_180_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
- };
-
- static final int[] KEY_270_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- public static final int FILTER_REMOVE = 0;
- public static final int FILTER_KEEP = 1;
- public static final int FILTER_ABORT = -1;
-
- private static final boolean MEASURE_LATENCY = false;
- private LatencyTimer lt;
-
- public interface FilterCallback {
- int filterEvent(QueuedEvent ev);
- }
-
- public interface HapticFeedbackCallback {
- void virtualKeyFeedback(KeyEvent event);
- }
-
- static class QueuedEvent {
- InputDevice inputDevice;
- long whenNano;
- int flags; // From the raw event
- int classType; // One of the class constants in InputEvent
- Object event;
- boolean inQueue;
-
- void copyFrom(QueuedEvent that) {
- this.inputDevice = that.inputDevice;
- this.whenNano = that.whenNano;
- this.flags = that.flags;
- this.classType = that.classType;
- this.event = that.event;
- }
-
- @Override
- public String toString() {
- return "QueuedEvent{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + event + "}";
- }
-
- // not copied
- QueuedEvent prev;
- QueuedEvent next;
- }
-
- /**
- * A key that exists as a part of the touch-screen, outside of the normal
- * display area of the screen.
- */
- static class VirtualKey {
- int scancode;
- int centerx;
- int centery;
- int width;
- int height;
-
- int hitLeft;
- int hitTop;
- int hitRight;
- int hitBottom;
-
- InputDevice lastDevice;
- int lastKeycode;
-
- boolean checkHit(int x, int y) {
- return (x >= hitLeft && x <= hitRight
- && y >= hitTop && y <= hitBottom);
- }
-
- void computeHitRect(InputDevice dev, int dw, int dh) {
- if (dev == lastDevice) {
- return;
- }
-
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
- + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
-
- lastDevice = dev;
-
- int minx = dev.absX.minValue;
- int maxx = dev.absX.maxValue;
-
- int halfw = width/2;
- int left = centerx - halfw;
- int right = centerx + halfw;
- hitLeft = minx + ((left*maxx-minx)/dw);
- hitRight = minx + ((right*maxx-minx)/dw);
-
- int miny = dev.absY.minValue;
- int maxy = dev.absY.maxValue;
-
- int halfh = height/2;
- int top = centery - halfh;
- int bottom = centery + halfh;
- hitTop = miny + ((top*maxy-miny)/dh);
- hitBottom = miny + ((bottom*maxy-miny)/dh);
- }
- }
-
- private void readVirtualKeys(String deviceName) {
- try {
- FileInputStream fis = new FileInputStream(
- "/sys/board_properties/virtualkeys." + deviceName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr, 2048);
- String str = br.readLine();
- if (str != null) {
- String[] it = str.split(":");
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
- final int N = it.length-6;
- for (int i=0; i<=N; i+=6) {
- if (!"0x01".equals(it[i])) {
- Slog.w(TAG, "Unknown virtual key type at elem #" + i
- + ": " + it[i]);
- continue;
- }
- try {
- VirtualKey sb = new VirtualKey();
- sb.scancode = Integer.parseInt(it[i+1]);
- sb.centerx = Integer.parseInt(it[i+2]);
- sb.centery = Integer.parseInt(it[i+3]);
- sb.width = Integer.parseInt(it[i+4]);
- sb.height = Integer.parseInt(it[i+5]);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
- + sb.scancode + ": center=" + sb.centerx + ","
- + sb.centery + " size=" + sb.width + "x"
- + sb.height);
- mVirtualKeys.add(sb);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Bad number at region " + i + " in: "
- + str, e);
- }
- }
- }
- br.close();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No virtual keys found");
- } catch (IOException e) {
- Slog.w(TAG, "Error reading virtual keys", e);
- }
- }
-
- private void readExcludedDevices() {
- // Read partner-provided list of excluded input devices
- XmlPullParser parser = null;
- // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
- File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
- try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (!"device".equals(parser.getName())) {
- break;
- }
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
- addExcludedDevice(name);
- }
- }
- } catch (FileNotFoundException e) {
- // It's ok if the file does not exist.
- } catch (Exception e) {
- Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
- } finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
- }
- }
-
- KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
- if (MEASURE_LATENCY) {
- lt = new LatencyTimer(100, 1000);
- }
-
- Resources r = context.getResources();
- BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
-
- JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
-
- mHapticFeedbackCallback = hapticFeedbackCallback;
-
- if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
- readExcludedDevices();
- }
-
- PowerManager pm = (PowerManager)context.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "KeyInputQueue");
- mWakeLock.setReferenceCounted(false);
-
- mFirst = new QueuedEvent();
- mLast = new QueuedEvent();
- mFirst.next = mLast;
- mLast.prev = mFirst;
-
- if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
- mThread.start();
- }
- }
-
- public void setDisplay(Display display) {
- mDisplay = display;
-
- // We assume at this point that the display dimensions reflect the
- // natural, unrotated display. We will perform hit tests for soft
- // buttons based on that display.
- mDisplayWidth = display.getWidth();
- mDisplayHeight = display.getHeight();
- }
-
- public void getInputConfiguration(Configuration config) {
- synchronized (mFirst) {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
- config.navigation = Configuration.NAVIGATION_NONAV;
-
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice d = mDevices.valueAt(i);
- if (d != null) {
- if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- config.touchscreen
- = Configuration.TOUCHSCREEN_FINGER;
- //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
- }
- if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
- config.keyboard
- = Configuration.KEYBOARD_QWERTY;
- //Slog.i("foo", "***** HAVE QWERTY!");
- }
- if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- config.navigation
- = Configuration.NAVIGATION_TRACKBALL;
- //Slog.i("foo", "***** HAVE TRACKBALL!");
- } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
- config.navigation
- = Configuration.NAVIGATION_DPAD;
- //Slog.i("foo", "***** HAVE DPAD!");
- }
- }
- }
- }
- }
-
- public int getScancodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(code);
- }
- }
-
- public int getScancodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(deviceId, code);
- }
- }
-
- public int getTrackballScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getKeycodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(code);
- }
- }
-
- public int getKeycodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(deviceId, code);
- }
- }
-
- public int getTrackballKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public static native String getDeviceName(int deviceId);
- public static native int getDeviceClasses(int deviceId);
- public static native void addExcludedDevice(String deviceName);
- public static native boolean getAbsoluteInfo(int deviceId, int axis,
- InputDevice.AbsoluteInfo outInfo);
- public static native int getSwitchState(int sw);
- public static native int getSwitchState(int deviceId, int sw);
- public static native int nativeGetScancodeState(int code);
- public static native int nativeGetScancodeState(int deviceId, int code);
- public static native int nativeGetKeycodeState(int code);
- public static native int nativeGetKeycodeState(int deviceId, int code);
- public static native int scancodeToKeycode(int deviceId, int scancode);
- public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
-
- public static KeyEvent newKeyEvent(InputDevice device, long downTime,
- long eventTime, boolean down, int keycode, int repeatCount,
- int scancode, int flags) {
- return new KeyEvent(
- downTime, eventTime,
- down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
- keycode, repeatCount,
- device != null ? device.mMetaKeysState : 0,
- device != null ? device.id : -1, scancode,
- flags | KeyEvent.FLAG_FROM_SYSTEM);
- }
-
- Thread mThread = new Thread("InputDeviceReader") {
- public void run() {
- if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- RawInputEvent ev = new RawInputEvent();
- while (true) {
- try {
- InputDevice di;
-
- // block, doesn't release the monitor
- readEvent(ev);
-
- boolean send = false;
- boolean configChanged = false;
-
- if (false) {
- Slog.i(TAG, "Input event: dev=0x"
- + Integer.toHexString(ev.deviceId)
- + " type=0x" + Integer.toHexString(ev.type)
- + " scancode=" + ev.scancode
- + " keycode=" + ev.keycode
- + " value=" + ev.value);
- }
-
- if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
- synchronized (mFirst) {
- di = newInputDevice(ev.deviceId);
- if (di.classes != 0) {
- // If this device is some kind of input class,
- // we care about it.
- mDevices.put(ev.deviceId, di);
- if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- readVirtualKeys(di.name);
- }
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else {
- // We won't do anything with this device.
- mIgnoredDevices.put(ev.deviceId, di);
- Slog.i(TAG, "Ignoring non-input device: id=0x"
- + Integer.toHexString(di.id)
- + ", name=" + di.name);
- }
- }
- } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
- synchronized (mFirst) {
- if (false) {
- Slog.i(TAG, "Device removed: id=0x"
- + Integer.toHexString(ev.deviceId));
- }
- di = mDevices.get(ev.deviceId);
- if (di != null) {
- mDevices.delete(ev.deviceId);
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
- mIgnoredDevices.remove(ev.deviceId);
- } else {
- Slog.w(TAG, "Removing bad device id: "
- + Integer.toHexString(ev.deviceId));
- continue;
- }
- }
- } else {
- di = getInputDevice(ev.deviceId);
- if (di == null) {
- // This may be some junk from an ignored device.
- continue;
- }
-
- // first crack at it
- send = preprocessEvent(di, ev);
-
- if (ev.type == RawInputEvent.EV_KEY) {
- di.mMetaKeysState = makeMetaState(ev.keycode,
- ev.value != 0, di.mMetaKeysState);
- mHaveGlobalMetaState = false;
- }
- }
-
- if (configChanged) {
- synchronized (mFirst) {
- addLocked(di, System.nanoTime(), 0,
- RawInputEvent.CLASS_CONFIGURATION_CHANGED,
- null);
- }
- }
-
- if (!send) {
- continue;
- }
-
- synchronized (mFirst) {
- // NOTE: The event timebase absolutely must be the same
- // timebase as SystemClock.uptimeMillis().
- //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
- final long curTime = SystemClock.uptimeMillis();
- final long curTimeNano = System.nanoTime();
- //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
-
- final int classes = di.classes;
- final int type = ev.type;
- final int scancode = ev.scancode;
- send = false;
-
- // Is it a key event?
- if (type == RawInputEvent.EV_KEY &&
- (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
- (scancode < RawInputEvent.BTN_FIRST ||
- scancode > RawInputEvent.BTN_LAST)) {
- boolean down;
- if (ev.value != 0) {
- down = true;
- di.mKeyDownTime = curTime;
- } else {
- down = false;
- }
- int keycode = rotateKeyCodeLocked(ev.keycode);
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mKeyDownTime, curTime, down,
- keycode, 0, scancode,
- ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
- ? KeyEvent.FLAG_WOKE_HERE : 0));
-
- } else if (ev.type == RawInputEvent.EV_KEY) {
- // Single touch protocol: touch going down or up.
- if (ev.scancode == RawInputEvent.BTN_TOUCH &&
- (classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- di.mAbs.changed = true;
- di.mAbs.mDown[0] = ev.value != 0;
-
- // Trackball (mouse) protocol: press down or up.
- } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- di.mRel.changed = true;
- di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
- send = true;
- }
-
- // Process position events from multitouch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_X] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " X:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_Y] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " Y:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process position events from single touch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- if (ev.scancode == RawInputEvent.ABS_X) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_Y) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process movement events from trackball (mouse) protocol.
- } else if (ev.type == RawInputEvent.EV_REL &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- // Add this relative movement into our totals.
- if (ev.scancode == RawInputEvent.REL_X) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
- } else if (ev.scancode == RawInputEvent.REL_Y) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
- }
- }
-
- // Handle multitouch protocol sync: tells us that the
- // driver has returned all data for -one- of the pointers
- // that is currently down.
- if (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_MT_REPORT
- && di.mAbs != null) {
- di.mAbs.changed = true;
- if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
- // If the value is <= 0, the pointer is not
- // down, so keep it in the count.
-
- if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] != 0) {
- final int num = di.mAbs.mNextNumPointers+1;
- di.mAbs.mNextNumPointers = num;
- if (DEBUG_POINTERS) Slog.v(TAG,
- "MT_REPORT: now have " + num + " pointers");
- final int newOffset = (num <= InputDevice.MAX_POINTERS)
- ? (num * MotionEvent.NUM_SAMPLE_DATA)
- : (InputDevice.MAX_POINTERS *
- MotionEvent.NUM_SAMPLE_DATA);
- di.mAbs.mAddingPointerOffset = newOffset;
- di.mAbs.mNextData[newOffset
- + MotionEvent.SAMPLE_PRESSURE] = 0;
- } else {
- if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
- }
- }
-
- // Handle general event sync: all data for the current
- // event update has been delivered.
- } else if (send || (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_REPORT)) {
- if (mDisplay != null) {
- if (!mHaveGlobalMetaState) {
- computeGlobalMetaStateLocked();
- }
-
- MotionEvent me;
-
- InputDevice.MotionState ms = di.mAbs;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- ms.mNextNumPointers = 0;
- if (ms.mDown[0]) {
- System.arraycopy(di.curTouchVals, 0,
- ms.mNextData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- ms.mNextNumPointers++;
- }
- }
-
- if (BAD_TOUCH_HACK) {
- ms.dropBadPoint(di);
- }
- if (JUMPY_TOUCH_HACK) {
- ms.dropJumpyPoint(di);
- }
-
- boolean doMotion = !monitorVirtualKey(di,
- ev, curTime, curTimeNano);
-
- if (doMotion && ms.mNextNumPointers > 0
- && (ms.mLastNumPointers == 0
- || ms.mSkipLastPointers)) {
- doMotion = !generateVirtualKeyDown(di,
- ev, curTime, curTimeNano);
- }
-
- if (doMotion) {
- // XXX Need to be able to generate
- // multiple events here, for example
- // if two fingers change up/down state
- // at the same time.
- do {
- me = ms.generateAbsMotion(di, curTime,
- curTimeNano, mDisplay,
- mOrientation, mGlobalMetaState);
- if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i(TAG, "Enqueueing: " + me);
- }
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
- }
- } while (ms.hasMore());
- } else {
- // We are consuming movement in the
- // virtual key area... but still
- // propagate this to the previous
- // data for comparisons.
- int num = ms.mNextNumPointers;
- if (num > InputDevice.MAX_POINTERS) {
- num = InputDevice.MAX_POINTERS;
- }
- System.arraycopy(ms.mNextData, 0,
- ms.mLastData, 0,
- num * MotionEvent.NUM_SAMPLE_DATA);
- ms.mLastNumPointers = num;
- ms.mSkipLastPointers = true;
- }
-
- ms.finish();
- }
-
- ms = di.mRel;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- me = ms.generateRelMotion(di, curTime,
- curTimeNano,
- mOrientation, mGlobalMetaState);
- if (false) Slog.v(TAG, "Relative: x="
- + di.mRel.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
- }
- }
- }
- }
- }
-
- } catch (RuntimeException exc) {
- Slog.e(TAG, "InputReaderThread uncaught exception", exc);
- }
- }
- }
- };
-
- private boolean isInsideDisplay(InputDevice dev) {
- final InputDevice.AbsoluteInfo absx = dev.absX;
- final InputDevice.AbsoluteInfo absy = dev.absY;
- final InputDevice.MotionState absm = dev.mAbs;
- if (absx == null || absy == null || absm == null) {
- return true;
- }
-
- if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
- && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
- + absm.mNextData[MotionEvent.SAMPLE_X]
- + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
- + ") inside of display");
- return true;
- }
-
- return false;
- }
-
- private VirtualKey findVirtualKey(InputDevice dev) {
- final int N = mVirtualKeys.size();
- if (N <= 0) {
- return null;
- }
-
- final InputDevice.MotionState absm = dev.mAbs;
- for (int i=0; i<N; i++) {
- VirtualKey sb = mVirtualKeys.get(i);
- sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
- + sb.scancode + " - (" + sb.hitLeft
- + "," + sb.hitTop + ")-(" + sb.hitRight + ","
- + sb.hitBottom + ")");
- if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
- absm.mNextData[MotionEvent.SAMPLE_Y])) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
- return sb;
- }
- }
-
- return null;
- }
-
- private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- if (isInsideDisplay(di)) {
- // Didn't consume event.
- return false;
- }
-
-
- VirtualKey vk = findVirtualKey(di);
- if (vk != null) {
- final InputDevice.MotionState ms = di.mAbs;
- mPressedVirtualKey = vk;
- vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
- ms.mLastNumPointers = ms.mNextNumPointers;
- di.mKeyDownTime = curTime;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
- "Generate key down for: " + vk.scancode
- + " (keycode=" + vk.lastKeycode + ")");
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- }
-
- // We always consume the event, even if we didn't
- // generate a key event. There are two reasons for
- // this: to avoid spurious touches when holding
- // the edges of the device near the touchscreen,
- // and to avoid reporting events if there are virtual
- // keys on the touchscreen outside of the display
- // area.
- // Note that for all of this we are only looking at the
- // first pointer, since what we are handling here is the
- // first pointer going down, and this is the coordinate
- // that will be used to dispatch the event.
- if (false) {
- final InputDevice.AbsoluteInfo absx = di.absX;
- final InputDevice.AbsoluteInfo absy = di.absY;
- final InputDevice.MotionState absm = di.mAbs;
- Slog.v(TAG, "Rejecting ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
- + absx.minValue + "," + absy.minValue
- + ")-(" + absx.maxValue + ","
- + absx.maxValue + ")");
- }
- return true;
- }
-
- private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk == null) {
- return false;
- }
-
- final InputDevice.MotionState ms = di.mAbs;
- if (ms.mNextNumPointers <= 0) {
- mPressedVirtualKey = null;
- ms.mLastNumPointers = 0;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- return true;
-
- } else if (isInsideDisplay(di)) {
- // Whoops the pointer has moved into
- // the display area! Cancel the
- // virtual key and start a pointer
- // motion.
- mPressedVirtualKey = null;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- ms.mLastNumPointers = 0;
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a new meta state for the given keys and old state.
- */
- private static final int makeMetaState(int keycode, boolean down, int old) {
- int mask;
- switch (keycode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- mask = KeyEvent.META_ALT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_ALT_RIGHT:
- mask = KeyEvent.META_ALT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- mask = KeyEvent.META_SHIFT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- mask = KeyEvent.META_SHIFT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SYM:
- mask = KeyEvent.META_SYM_ON;
- break;
- default:
- return old;
- }
- int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
- & (down ? (old | mask) : (old & ~mask));
- if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
- result |= KeyEvent.META_ALT_ON;
- }
- if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
- result |= KeyEvent.META_SHIFT_ON;
- }
- return result;
- }
-
- private void computeGlobalMetaStateLocked() {
- int i = mDevices.size();
- mGlobalMetaState = 0;
- while ((--i) >= 0) {
- mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
- }
- mHaveGlobalMetaState = true;
- }
-
- /*
- * Return true if you want the event to get passed on to the
- * rest of the system, and false if you've handled it and want
- * it dropped.
- */
- abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
-
- InputDevice getInputDevice(int deviceId) {
- synchronized (mFirst) {
- return getInputDeviceLocked(deviceId);
- }
- }
-
- private InputDevice getInputDeviceLocked(int deviceId) {
- return mDevices.get(deviceId);
- }
-
- public void setOrientation(int orientation) {
- synchronized(mFirst) {
- mOrientation = orientation;
- switch (orientation) {
- case Surface.ROTATION_90:
- mKeyRotationMap = KEY_90_MAP;
- break;
- case Surface.ROTATION_180:
- mKeyRotationMap = KEY_180_MAP;
- break;
- case Surface.ROTATION_270:
- mKeyRotationMap = KEY_270_MAP;
- break;
- default:
- mKeyRotationMap = null;
- break;
- }
- }
- }
-
- public int rotateKeyCode(int keyCode) {
- synchronized(mFirst) {
- return rotateKeyCodeLocked(keyCode);
- }
- }
-
- private int rotateKeyCodeLocked(int keyCode) {
- int[] map = mKeyRotationMap;
- if (map != null) {
- final int N = map.length;
- for (int i=0; i<N; i+=2) {
- if (map[i] == keyCode) {
- return map[i+1];
- }
- }
- }
- return keyCode;
- }
-
- boolean hasEvents() {
- synchronized (mFirst) {
- return mFirst.next != mLast;
- }
- }
-
- /*
- * returns true if we returned an event, and false if we timed out
- */
- QueuedEvent getEvent(long timeoutMS) {
- long begin = SystemClock.uptimeMillis();
- final long end = begin+timeoutMS;
- long now = begin;
- synchronized (mFirst) {
- while (mFirst.next == mLast && end > now) {
- try {
- mWakeLock.release();
- mFirst.wait(end-now);
- }
- catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- if (begin > now) {
- begin = now;
- }
- }
- if (mFirst.next == mLast) {
- return null;
- }
- QueuedEvent p = mFirst.next;
- mFirst.next = p.next;
- mFirst.next.prev = mFirst;
- p.inQueue = false;
- return p;
- }
- }
-
- /**
- * Return true if the queue has an up event pending that corresponds
- * to the same key as the given key event.
- */
- boolean hasKeyUpEvent(KeyEvent origEvent) {
- synchronized (mFirst) {
- final int keyCode = origEvent.getKeyCode();
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
- KeyEvent ke = (KeyEvent)cur.event;
- if (ke.getAction() == KeyEvent.ACTION_UP
- && ke.getKeyCode() == keyCode) {
- return true;
- }
- }
- cur = cur.prev;
- }
- }
-
- return false;
- }
-
- void recycleEvent(QueuedEvent ev) {
- synchronized (mFirst) {
- //Slog.i(TAG, "Recycle event: " + ev);
- if (ev.event == ev.inputDevice.mAbs.currentMove) {
- ev.inputDevice.mAbs.currentMove = null;
- }
- if (ev.event == ev.inputDevice.mRel.currentMove) {
- if (false) Slog.i(TAG, "Detach rel " + ev.event);
- ev.inputDevice.mRel.currentMove = null;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
- }
- recycleLocked(ev);
- }
- }
-
- void filterQueue(FilterCallback cb) {
- synchronized (mFirst) {
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- switch (cb.filterEvent(cur)) {
- case FILTER_REMOVE:
- cur.prev.next = cur.next;
- cur.next.prev = cur.prev;
- break;
- case FILTER_ABORT:
- return;
- }
- cur = cur.prev;
- }
- }
- }
-
- private QueuedEvent obtainLocked(InputDevice device, long whenNano,
- int flags, int classType, Object event) {
- QueuedEvent ev;
- if (mCacheCount == 0) {
- ev = new QueuedEvent();
- } else {
- ev = mCache;
- ev.inQueue = false;
- mCache = ev.next;
- mCacheCount--;
- }
- ev.inputDevice = device;
- ev.whenNano = whenNano;
- ev.flags = flags;
- ev.classType = classType;
- ev.event = event;
- return ev;
- }
-
- private void recycleLocked(QueuedEvent ev) {
- if (ev.inQueue) {
- throw new RuntimeException("Event already in queue!");
- }
- if (mCacheCount < 10) {
- mCacheCount++;
- ev.next = mCache;
- mCache = ev;
- ev.inQueue = true;
- }
- }
-
- private void addLocked(InputDevice device, long whenNano, int flags,
- int classType, Object event) {
- boolean poke = mFirst.next == mLast;
-
- QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
- QueuedEvent p = mLast.prev;
- while (p != mFirst && ev.whenNano < p.whenNano) {
- p = p.prev;
- }
-
- ev.next = p.next;
- ev.prev = p;
- p.next = ev;
- ev.next.prev = ev;
- ev.inQueue = true;
-
- if (poke) {
- long time;
- if (MEASURE_LATENCY) {
- time = System.nanoTime();
- }
- mFirst.notify();
- mWakeLock.acquire();
- if (MEASURE_LATENCY) {
- lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
- }
- }
- }
-
- private InputDevice newInputDevice(int deviceId) {
- int classes = getDeviceClasses(deviceId);
- String name = getDeviceName(deviceId);
- InputDevice.AbsoluteInfo absX = null;
- InputDevice.AbsoluteInfo absY = null;
- InputDevice.AbsoluteInfo absPressure = null;
- InputDevice.AbsoluteInfo absSize = null;
- if (classes != 0) {
- Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
- + ", name=" + name
- + ", classes=" + Integer.toHexString(classes));
- if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
- } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_PRESSURE, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_TOOL_WIDTH, "Size");
- }
- }
-
- return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
- }
-
- private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
- String name) {
- InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
- if (getAbsoluteInfo(id, channel, info)
- && info.minValue != info.maxValue) {
- Slog.i(TAG, " " + name + ": min=" + info.minValue
- + " max=" + info.maxValue
- + " flat=" + info.flat
- + " fuzz=" + info.fuzz);
- info.range = info.maxValue-info.minValue;
- return info;
- }
- Slog.i(TAG, " " + name + ": unknown values");
- return null;
- }
- private static native boolean readEvent(RawInputEvent outEvent);
-
- void dump(PrintWriter pw, String prefix) {
- synchronized (mFirst) {
- for (int i=0; i<mDevices.size(); i++) {
- InputDevice dev = mDevices.valueAt(i);
- pw.print(prefix); pw.print("Device #");
- pw.print(mDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println("):");
- pw.print(prefix); pw.print(" mKeyDownTime=");
- pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
- pw.println(dev.mMetaKeysState);
- if (dev.absX != null) {
- pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
- pw.println("");
- }
- if (dev.absY != null) {
- pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
- pw.println("");
- }
- if (dev.absPressure != null) {
- pw.print(prefix); pw.print(" absPressure: ");
- dev.absPressure.dump(pw); pw.println("");
- }
- if (dev.absSize != null) {
- pw.print(prefix); pw.print(" absSize: ");
- dev.absSize.dump(pw); pw.println("");
- }
- if (dev.mAbs.everChanged) {
- pw.print(prefix); pw.println(" mAbs:");
- dev.mAbs.dump(pw, prefix + " ");
- }
- if (dev.mRel.everChanged) {
- pw.print(prefix); pw.println(" mRel:");
- dev.mRel.dump(pw, prefix + " ");
- }
- }
- pw.println(" ");
- for (int i=0; i<mIgnoredDevices.size(); i++) {
- InputDevice dev = mIgnoredDevices.valueAt(i);
- pw.print(prefix); pw.print("Ignored Device #");
- pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println(")");
- }
- pw.println(" ");
- for (int i=0; i<mVirtualKeys.size(); i++) {
- VirtualKey vk = mVirtualKeys.get(i);
- pw.print(prefix); pw.print("Virtual Key #");
- pw.print(i); pw.println(":");
- pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
- pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
- pw.print(" centery="); pw.print(vk.centery);
- pw.print(" width="); pw.print(vk.width);
- pw.print(" height="); pw.println(vk.height);
- pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
- pw.print(" hitTop="); pw.print(vk.hitTop);
- pw.print(" hitRight="); pw.print(vk.hitRight);
- pw.print(" hitBottom="); pw.println(vk.hitBottom);
- if (vk.lastDevice != null) {
- pw.print(prefix); pw.print(" lastDevice=#");
- pw.println(vk.lastDevice.id);
- }
- if (vk.lastKeycode != 0) {
- pw.print(prefix); pw.print(" lastKeycode=");
- pw.println(vk.lastKeycode);
- }
- }
- pw.println(" ");
- pw.print(prefix); pw.print(" Default keyboard: ");
- pw.println(SystemProperties.get("hw.keyboards.0.devname"));
- pw.print(prefix); pw.print(" mGlobalMetaState=");
- pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
- pw.println(mHaveGlobalMetaState);
- pw.print(prefix); pw.print(" mDisplayWidth=");
- pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
- pw.println(mDisplayHeight);
- pw.print(prefix); pw.print(" mOrientation=");
- pw.println(mOrientation);
- if (mPressedVirtualKey != null) {
- pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
- pw.println(mPressedVirtualKey.scancode);
- }
- }
- }
-}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1fd053f..af2145e 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4583,6 +4583,8 @@
}
};
+ private static final boolean DEBUG_OBB = false;
+
private static final void sendPackageBroadcast(String action, String pkg,
Bundle extras, IIntentReceiver finishedReceiver) {
IActivityManager am = ActivityManagerNative.getDefault();
@@ -4757,6 +4759,29 @@
mHandler.sendMessage(msg);
}
+ public void setPackageObbPath(String packageName, String path) {
+ if (DEBUG_OBB)
+ Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ throw new SecurityException("Permission denial: attempt to set .obb file from pid="
+ + Binder.getCallingPid() + ", uid=" + uid + ", package uid="
+ + pkgSetting.userId);
+ }
+ pkgSetting.obbPathString = path;
+ mSettings.writeLP();
+ }
+ }
+
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
@@ -7118,6 +7143,7 @@
pw.print(" pkg="); pw.println(ps.pkg);
pw.print(" codePath="); pw.println(ps.codePathString);
pw.print(" resourcePath="); pw.println(ps.resourcePathString);
+ pw.print(" obbPath="); pw.println(ps.obbPathString);
if (ps.pkg != null) {
pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
@@ -7684,6 +7710,7 @@
String codePathString;
File resourcePath;
String resourcePathString;
+ String obbPathString;
private long timeStamp;
private String timeStampString = "0";
int versionCode;
@@ -8684,6 +8711,9 @@
if (pkg.installerPackageName != null) {
serializer.attribute(null, "installer", pkg.installerPackageName);
}
+ if (pkg.obbPathString != null) {
+ serializer.attribute(null, "obbPath", pkg.obbPathString);
+ }
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
serializer.startTag(null, "perms");
@@ -9060,6 +9090,7 @@
String sharedIdStr = null;
String codePathStr = null;
String resourcePathStr = null;
+ String obbPathStr = null;
String systemStr = null;
String installerPackageName = null;
String uidError = null;
@@ -9077,6 +9108,7 @@
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ obbPathStr = parser.getAttributeValue(null, "obbPath");
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -9174,6 +9206,7 @@
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
+ packageSetting.obbPathString = obbPathStr;
final String enabledStr = parser.getAttributeValue(null, "enabled");
if (enabledStr != null) {
if (enabledStr.equalsIgnoreCase("true")) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 493a348..e9d5efc 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -247,6 +247,9 @@
private static final boolean mSpew = false;
private static final boolean mDebugProximitySensor = (true || mSpew);
private static final boolean mDebugLightSensor = (false || mSpew);
+
+ private native void nativeInit();
+ private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
/*
static PrintStream mLog;
@@ -481,6 +484,11 @@
}
}
}
+
+ nativeInit();
+ synchronized (mLocks) {
+ updateNativePowerStateLocked();
+ }
}
void initInThread() {
@@ -1557,8 +1565,16 @@
}
}
}
+
+ updateNativePowerStateLocked();
}
}
+
+ private void updateNativePowerStateLocked() {
+ nativeSetPowerState(
+ (mPowerState & SCREEN_ON_BIT) != 0,
+ (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+ }
private int screenOffFinishedAnimatingLocked(int reason) {
// I don't think we need to check the current state here because all of these
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index b1ca7852..73234df 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.NetworkProperties;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -34,6 +35,7 @@
import java.util.ArrayList;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.NetworkInterface;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -90,7 +92,7 @@
private ArrayList<String> mConnectedApns;
- private String mDataConnectionInterfaceName = "";
+ private NetworkProperties mDataConnectionProperties;
private Bundle mCellLocation = new Bundle();
@@ -353,7 +355,8 @@
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String apnType, String interfaceName, int networkType) {
+ String reason, String apn, String apnType, NetworkProperties networkProperties,
+ int networkType) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -373,14 +376,14 @@
mDataConnectionState = state;
modified = true;
} else {
- // we're still connected, so send that out if we send anything.
- state = mDataConnectionState;
+ // leave mDataConnectionState as is and
+ // send out the new status for the APN in question.
}
}
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
mDataConnectionApn = apn;
- mDataConnectionInterfaceName = interfaceName;
+ mDataConnectionProperties = networkProperties;
if (mDataConnectionNetworkType != networkType) {
mDataConnectionNetworkType = networkType;
modified = true;
@@ -400,7 +403,7 @@
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnType, interfaceName);
+ apnType, networkProperties);
}
public void notifyDataConnectionFailed(String reason, String apnType) {
@@ -441,7 +444,7 @@
/**
* Copy the service state object so they can't mess it up in the local calls
*/
- public void sendServiceState(Record r, ServiceState state) {
+ private void sendServiceState(Record r, ServiceState state) {
try {
r.callback.onServiceStateChanged(new ServiceState(state));
} catch (RemoteException ex) {
@@ -487,7 +490,7 @@
pw.println(" mDataConnectionPossible=" + mDataConnectionPossible);
pw.println(" mDataConnectionReason=" + mDataConnectionReason);
pw.println(" mDataConnectionApn=" + mDataConnectionApn);
- pw.println(" mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
+ pw.println(" mDataConnectionProperties=" + mDataConnectionProperties);
pw.println(" mCellLocation=" + mCellLocation);
pw.println("registrations: count=" + recordCount);
for (Record r : mRecords) {
@@ -561,7 +564,7 @@
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
- String reason, String apn, String apnType, String interfaceName) {
+ String reason, String apn, String apnType, NetworkProperties networkProperties) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -574,9 +577,15 @@
if (reason != null) {
intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
}
+ if (networkProperties != null) {
+ intent.putExtra(Phone.DATA_NETWORK_PROPERTIES_KEY, networkProperties);
+ NetworkInterface iface = networkProperties.getInterface();
+ if (iface != null) {
+ intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface.getName());
+ }
+ }
intent.putExtra(Phone.DATA_APN_KEY, apn);
intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
- intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
mContext.sendStickyBroadcast(intent);
}
diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/ViewServer.java
index ae00438..b369f71 100644
--- a/services/java/com/android/server/ViewServer.java
+++ b/services/java/com/android/server/ViewServer.java
@@ -21,6 +21,8 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -41,11 +43,13 @@
*/
public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
+ private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
+
// Debug facility
private static final String LOG_TAG = "ViewServer";
- private static final String VALUE_PROTOCOL_VERSION = "2";
- private static final String VALUE_SERVER_VERSION = "3";
+ private static final String VALUE_PROTOCOL_VERSION = "3";
+ private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
// Returns the protocol version
@@ -54,6 +58,8 @@
private static final String COMMAND_SERVER_VERSION = "SERVER";
// Lists all of the available windows in the system
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
+ // Keeps a connection open and notifies when the list of windows changes
+ private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
private ServerSocket mServer;
private Thread mThread;
@@ -61,6 +67,8 @@
private final WindowManagerService mWindowManager;
private final int mPort;
+ private ExecutorService mThreadPool;
+
/**
* Creates a new ViewServer associated with the specified window manager.
* The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
@@ -103,8 +111,9 @@
return false;
}
- mServer = new ServerSocket(mPort, 1, InetAddress.getLocalHost());
+ mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
+ mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start();
return true;
@@ -122,7 +131,16 @@
*/
boolean stop() {
if (mThread != null) {
+
mThread.interrupt();
+ if (mThreadPool != null) {
+ try {
+ mThreadPool.shutdownNow();
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Could not stop all view server threads");
+ }
+ }
+ mThreadPool = null;
mThread = null;
try {
mServer.close();
@@ -152,62 +170,21 @@
* Main server loop.
*/
public void run() {
- final ServerSocket server = mServer;
-
while (Thread.currentThread() == mThread) {
- Socket client = null;
// Any uncaught exception will crash the system process
try {
- client = server.accept();
-
- BufferedReader in = null;
- try {
- in = new BufferedReader(new InputStreamReader(client.getInputStream()), 1024);
-
- final String request = in.readLine();
-
- String command;
- String parameters;
-
- int index = request.indexOf(' ');
- if (index == -1) {
- command = request;
- parameters = "";
- } else {
- command = request.substring(0, index);
- parameters = request.substring(index + 1);
- }
-
- boolean result;
- if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_PROTOCOL_VERSION);
- } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_SERVER_VERSION);
- } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
- result = mWindowManager.viewServerListWindows(client);
- } else {
- result = mWindowManager.viewServerWindowCommand(client,
- command, parameters);
- }
-
- if (!result) {
- Slog.w(LOG_TAG, "An error occured with the command: " + command);
- }
- } finally {
- if (in != null) {
- in.close();
- }
- }
- } catch (Exception e) {
- Slog.w(LOG_TAG, "Connection error: ", e);
- } finally {
- if (client != null) {
+ Socket client = mServer.accept();
+ if(mThreadPool != null) {
+ mThreadPool.submit(new ViewServerWorker(client));
+ } else {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
}
}
}
@@ -235,4 +212,131 @@
}
return result;
}
+
+ class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
+ private Socket mClient;
+ private boolean mNeedWindowListUpdate;
+ private boolean mNeedFocusedWindowUpdate;
+ public ViewServerWorker(Socket client) {
+ mClient = client;
+ mNeedWindowListUpdate = false;
+ mNeedFocusedWindowUpdate = false;
+ }
+
+ public void run() {
+
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
+
+ final String request = in.readLine();
+
+ String command;
+ String parameters;
+
+ int index = request.indexOf(' ');
+ if (index == -1) {
+ command = request;
+ parameters = "";
+ } else {
+ command = request.substring(0, index);
+ parameters = request.substring(index + 1);
+ }
+
+ boolean result;
+ if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
+ } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_SERVER_VERSION);
+ } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerListWindows(mClient);
+ } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
+ result = windowManagerAutolistLoop();
+ } else {
+ result = mWindowManager.viewServerWindowCommand(mClient,
+ command, parameters);
+ }
+
+ if (!result) {
+ Slog.w(LOG_TAG, "An error occured with the command: " + command);
+ }
+ } catch(IOException e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mClient != null) {
+ try {
+ mClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void windowsChanged() {
+ synchronized(this) {
+ mNeedWindowListUpdate = true;
+ notifyAll();
+ }
+ }
+
+ public void focusChanged() {
+ synchronized(this) {
+ mNeedFocusedWindowUpdate = true;
+ notifyAll();
+ }
+ }
+
+ private boolean windowManagerAutolistLoop() {
+ mWindowManager.addWindowChangeListener(this);
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
+ while (!Thread.interrupted()) {
+ boolean needWindowListUpdate = false;
+ boolean needFocusedWindowUpdate = false;
+ synchronized (this) {
+ while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
+ wait();
+ }
+ if (mNeedWindowListUpdate) {
+ mNeedWindowListUpdate = false;
+ needWindowListUpdate = true;
+ }
+ if (mNeedFocusedWindowUpdate) {
+ mNeedFocusedWindowUpdate = false;
+ needFocusedWindowUpdate = true;
+ }
+ }
+ if(needWindowListUpdate) {
+ out.write("LIST UPDATE\n");
+ out.flush();
+ }
+ if(needFocusedWindowUpdate) {
+ out.write("FOCUS UPDATE\n");
+ out.flush();
+ }
+ }
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ mWindowManager.removeWindowChangeListener(this);
+ }
+ return true;
+ }
+ }
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 580eea4..3475b2fc 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -48,7 +48,6 @@
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
-import com.android.server.KeyInputQueue.QueuedEvent;
import com.android.server.am.BatteryStatsService;
import android.Manifest;
@@ -95,6 +94,7 @@
import android.util.SparseIntArray;
import android.view.Display;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -105,7 +105,6 @@
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.RawInputEvent;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
@@ -137,7 +136,7 @@
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
+ implements Watchdog.Monitor {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -158,17 +157,12 @@
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;
- static final boolean ENABLE_NATIVE_INPUT_DISPATCH =
- WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH;
static private LatencyTimer lt;
static final boolean PROFILE_ORIENTATION = false;
static final boolean BLUR = true;
static final boolean localLOGV = DEBUG;
- /** How long to wait for subsequent key repeats, in milliseconds */
- static final int KEY_REPEAT_DELAY = 50;
-
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET. */
@@ -209,34 +203,11 @@
// Default input dispatching timeout in nanoseconds.
private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
- static final int INJECT_FAILED = 0;
- static final int INJECT_SUCCEEDED = 1;
- static final int INJECT_NO_PERMISSION = -1;
-
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
- /** The minimum time between dispatching touch events. */
- int mMinWaitTimeBetweenTouchEvents = 1000 / 35;
-
- // Last touch event time
- long mLastTouchEventTime = 0;
-
- // Last touch event type
- int mLastTouchEventType = OTHER_EVENT;
-
- // Time to wait before calling useractivity again. This saves CPU usage
- // when we get a flood of touch events.
- static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000;
-
- // Last time we call user activity
- long mLastUserActivityCallTime = 0;
-
- // Last time we updated battery stats
- long mLastBatteryStatsCallTime = 0;
-
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
@@ -485,7 +456,6 @@
float mLastWallpaperY = -1;
float mLastWallpaperXStep = -1;
float mLastWallpaperYStep = -1;
- boolean mSendingPointersToWallpaper = false;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
WindowState mWaitingOnWallpaper;
@@ -503,10 +473,7 @@
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.0f;
- final KeyWaiter mKeyWaiter = new KeyWaiter();
- final KeyQ mQueue;
final InputManager mInputManager;
- final InputDispatcherThread mInputThread;
// Who is holding the screen on.
Session mHoldingScreenOn;
@@ -521,8 +488,14 @@
boolean mInTouchMode = false;
private ViewServer mViewServer;
+ private ArrayList<WindowChangeListener> mWindowChangeListeners =
+ new ArrayList<WindowChangeListener>();
+ private boolean mWindowsChanged = false;
- final Rect mTempRect = new Rect();
+ public interface WindowChangeListener {
+ public void windowsChanged();
+ public void focusChanged();
+ }
final Configuration mTempConfiguration = new Configuration();
int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED;
@@ -651,28 +624,11 @@
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
- }
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
-
mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
mHoldingScreenWakeLock.setReferenceCounted(false);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputManager = new InputManager(context, this, mPolicy, pmc, mPowerManager);
- } else {
- mInputManager = null;
- }
- mQueue = new KeyQ();
- mInputThread = new InputDispatcherThread();
+ mInputManager = new InputManager(context, this, pmc, mPowerManager);
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
@@ -686,11 +642,7 @@
}
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputManager.start();
- } else {
- mInputThread.start();
- }
+ mInputManager.start();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -717,6 +669,7 @@
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
mWindows.add(i+1, window);
+ mWindowsChanged = true;
}
private void placeWindowBefore(Object pos, WindowState window) {
@@ -725,6 +678,7 @@
TAG, "Adding window " + window + " at "
+ i + " of " + mWindows.size() + " (before " + pos + ")");
mWindows.add(i, window);
+ mWindowsChanged = true;
}
//This method finds out the index of a window that has the same app token as
@@ -782,6 +736,7 @@
TAG, "Adding window " + win + " at "
+ (newIdx+1) + " of " + N);
localmWindows.add(newIdx+1, win);
+ mWindowsChanged = true;
}
}
}
@@ -864,6 +819,7 @@
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
}
}
@@ -881,6 +837,7 @@
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
@@ -1089,6 +1046,7 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding input method window " + win + " at " + pos);
mWindows.add(pos, win);
+ mWindowsChanged = true;
moveInputMethodDialogsLocked(pos+1);
return;
}
@@ -1130,6 +1088,7 @@
if (wpos < interestingPos) interestingPos--;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
@@ -1156,6 +1115,7 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos
+ ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
reAddWindowLocked(wpos, win);
}
}
@@ -1616,6 +1576,7 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
+ oldIndex + ": " + wallpaper);
localmWindows.remove(oldIndex);
+ mWindowsChanged = true;
if (oldIndex < foundI) {
foundI--;
}
@@ -1627,6 +1588,7 @@
+ " from " + oldIndex + " to " + foundI);
localmWindows.add(foundI, wallpaper);
+ mWindowsChanged = true;
changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
}
}
@@ -1816,70 +1778,6 @@
}
}
}
-
- void sendPointerToWallpaperLocked(WindowState srcWin,
- MotionEvent pointer, long eventTime) {
- int curTokenIndex = mWallpaperTokens.size();
- while (curTokenIndex > 0) {
- curTokenIndex--;
- WindowToken token = mWallpaperTokens.get(curTokenIndex);
- int curWallpaperIndex = token.windows.size();
- while (curWallpaperIndex > 0) {
- curWallpaperIndex--;
- WindowState wallpaper = token.windows.get(curWallpaperIndex);
- if ((wallpaper.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- continue;
- }
- try {
- MotionEvent ev = MotionEvent.obtainNoHistory(pointer);
- if (srcWin != null) {
- ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
- srcWin.mFrame.top-wallpaper.mFrame.top);
- } else {
- ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top);
- }
- switch (pointer.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mSendingPointersToWallpaper = true;
- break;
- case MotionEvent.ACTION_UP:
- mSendingPointersToWallpaper = false;
- break;
- }
- wallpaper.mClient.dispatchPointer(ev, eventTime, false);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending pointer to wallpaper", e);
- }
- }
- }
- }
-
- void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin,
- MotionEvent pointer, long eventTime, boolean skipped) {
- if (relWin != null) {
- mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top);
- } else {
- mPolicy.dispatchedPointerEventLw(pointer, 0, 0);
- }
-
- // If we sent an initial down to the wallpaper, then continue
- // sending events until the final up.
- if (mSendingPointersToWallpaper) {
- if (skipped) {
- Slog.i(TAG, "Sending skipped pointer to wallpaper!");
- }
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
-
- // If we are on top of the wallpaper, then the wallpaper also
- // gets to see this movement.
- } else if (srcWin != null
- && pointer.getAction() == MotionEvent.ACTION_DOWN
- && mWallpaperTarget == srcWin
- && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
- }
- }
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
@@ -1902,12 +1800,7 @@
mDisplay = wm.getDefaultDisplay();
mInitialDisplayWidth = mDisplay.getWidth();
mInitialDisplayHeight = mDisplay.getHeight();
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputManager.setDisplaySize(0,
- mInitialDisplayWidth, mInitialDisplayHeight);
- } else {
- mQueue.setDisplay(mDisplay);
- }
+ mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
reportNewConfig = true;
}
@@ -2001,15 +1894,13 @@
return res;
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- if (outInputChannel != null) {
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.mInputChannel = inputChannels[0];
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
-
- mInputManager.registerInputChannel(win.mInputChannel);
- }
+ if (outInputChannel != null) {
+ String name = win.makeInputChannelName();
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ win.mInputChannel = inputChannels[0];
+ inputChannels[1].transferToBinderOutParameter(outInputChannel);
+
+ mInputManager.registerInputChannel(win.mInputChannel);
}
// From now on, no exceptions or errors allowed!
@@ -2185,14 +2076,7 @@
}
private void removeWindowInnerLocked(Session session, WindowState win) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBeingRemovedLw(win);
- } else {
- mKeyWaiter.finishedKey(session, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(win.mSession);
- mKeyWaiter.releasePendingTrackballLocked(win.mSession);
- }
+ mInputMonitor.windowIsBeingRemovedLw(win);
win.mRemoved = true;
@@ -2211,6 +2095,7 @@
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
if (mInputMethodWindow == win) {
@@ -2560,12 +2445,7 @@
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBecomingInvisibleLw(win);
- } else {
- mKeyWaiter.finishedKey(session, client, true,
- KeyWaiter.RETURN_NOTHING);
- }
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
@@ -3026,12 +2906,7 @@
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBeingRemovedLw(win);
- } else {
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- }
+ mInputMonitor.windowIsBeingRemovedLw(win);
changed = true;
}
}
@@ -3348,12 +3223,8 @@
if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
changed = mFocusedApp != null;
mFocusedApp = null;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- if (changed) {
- mInputMonitor.setFocusedAppLw(null);
- }
- } else {
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(null);
}
} else {
AppWindowToken newFocus = findAppWindowToken(token);
@@ -3364,12 +3235,8 @@
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- if (changed) {
- mInputMonitor.setFocusedAppLw(newFocus);
- }
- } else {
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(newFocus);
}
}
@@ -3511,6 +3378,7 @@
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Removing starting window: " + startingWindow);
mWindows.remove(startingWindow);
+ mWindowsChanged = true;
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
@@ -3681,12 +3549,7 @@
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBecomingInvisibleLw(win);
- } else {
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- }
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
changed = true;
}
}
@@ -3971,11 +3834,7 @@
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.setFocusedAppLw(null);
- } else {
- mKeyWaiter.tickle();
- }
+ mInputMonitor.setFocusedAppLw(null);
}
} else {
Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -4001,6 +3860,7 @@
WindowState win = token.windows.get(i);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win);
mWindows.remove(win);
+ mWindowsChanged = true;
int j = win.mChildWindows.size();
while (j > 0) {
j--;
@@ -4105,6 +3965,7 @@
mWindows.add(index, win);
index++;
}
+ mWindowsChanged = true;
return index;
}
@@ -4440,11 +4301,7 @@
"getSwitchState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getSwitchState(sw);
- } else {
- return KeyInputQueue.getSwitchState(sw);
- }
+ return mInputManager.getSwitchState(sw);
}
public int getSwitchStateForDevice(int devid, int sw) {
@@ -4452,11 +4309,7 @@
"getSwitchStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getSwitchState(devid, sw);
- } else {
- return KeyInputQueue.getSwitchState(devid, sw);
- }
+ return mInputManager.getSwitchState(devid, sw);
}
public int getScancodeState(int sw) {
@@ -4464,11 +4317,7 @@
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getScancodeState(sw);
- } else {
- return mQueue.getScancodeState(sw);
- }
+ return mInputManager.getScancodeState(sw);
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -4476,11 +4325,7 @@
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getScancodeState(devid, sw);
- } else {
- return mQueue.getScancodeState(devid, sw);
- }
+ return mInputManager.getScancodeState(devid, sw);
}
public int getTrackballScancodeState(int sw) {
@@ -4488,11 +4333,7 @@
"getTrackballScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getTrackballScancodeState(sw);
- } else {
- return mQueue.getTrackballScancodeState(sw);
- }
+ return mInputManager.getTrackballScancodeState(sw);
}
public int getDPadScancodeState(int sw) {
@@ -4500,11 +4341,7 @@
"getDPadScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getDPadScancodeState(sw);
- } else {
- return mQueue.getDPadScancodeState(sw);
- }
+ return mInputManager.getDPadScancodeState(sw);
}
public int getKeycodeState(int sw) {
@@ -4512,11 +4349,7 @@
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getKeycodeState(sw);
- } else {
- return mQueue.getKeycodeState(sw);
- }
+ return mInputManager.getKeycodeState(sw);
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4524,11 +4357,7 @@
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getKeycodeState(devid, sw);
- } else {
- return mQueue.getKeycodeState(devid, sw);
- }
+ return mInputManager.getKeycodeState(devid, sw);
}
public int getTrackballKeycodeState(int sw) {
@@ -4536,11 +4365,7 @@
"getTrackballKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getTrackballKeycodeState(sw);
- } else {
- return mQueue.getTrackballKeycodeState(sw);
- }
+ return mInputManager.getTrackballKeycodeState(sw);
}
public int getDPadKeycodeState(int sw) {
@@ -4548,19 +4373,11 @@
"getDPadKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.getDPadKeycodeState(sw);
- } else {
- return mQueue.getDPadKeycodeState(sw);
- }
+ return mInputManager.getDPadKeycodeState(sw);
}
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- return mInputManager.hasKeys(keycodes, keyExists);
- } else {
- return KeyInputQueue.hasKeys(keycodes, keyExists);
- }
+ return mInputManager.hasKeys(keycodes, keyExists);
}
public void enableScreenAfterBoot() {
@@ -4704,11 +4521,7 @@
mLayoutNeeded = true;
startFreezingDisplayLocked();
Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputManager.setDisplayOrientation(0, rotation);
- } else {
- mQueue.setOrientation(rotation);
- }
+ mInputManager.setDisplayOrientation(0, rotation);
if (mDisplayEnabled) {
Surface.setOrientation(0, rotation, animFlags);
}
@@ -4990,6 +4803,48 @@
return success;
}
+ public void addWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.add(listener);
+ }
+ }
+
+ public void removeWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.remove(listener);
+ }
+ }
+
+ private void notifyWindowsChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].windowsChanged();
+ }
+ }
+
+ private void notifyFocusChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].focusChanged();
+ }
+ }
+
private WindowState findWindow(int hashCode) {
if (hashCode == -1) {
return getFocusedWindow();
@@ -5039,11 +4894,8 @@
if (mDisplay == null) {
return false;
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputManager.getInputConfiguration(config);
- } else {
- mQueue.getInputConfiguration(config);
- }
+
+ mInputManager.getInputConfiguration(config);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -5324,6 +5176,13 @@
mTempInputWindows.clear();
}
+ /* Provides feedback for a virtual key down. */
+ public void virtualKeyDownFeedback() {
+ synchronized (mWindowMap) {
+ mPolicy.performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+ }
+ }
+
/* Notifies that an app switch key (BACK / HOME) has just been pressed.
* This essentially starts a .5 second timeout for the application to process
* subsequent input events while waiting for the app switch to occur. If it takes longer
@@ -5332,30 +5191,28 @@
public void notifyAppSwitchComing() {
// TODO Not implemented yet. Should go in the native side.
}
-
+
+ /* Notifies that the lid switch changed state. */
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+ mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
+
/* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device. */
- public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
- int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
- RawInputEvent event = new RawInputEvent();
- event.deviceId = deviceId;
- event.type = type;
- event.scancode = scanCode;
- event.keycode = keyCode;
- event.flags = policyFlags;
- event.value = value;
- event.when = whenNanos / 1000000;
-
- return mPolicy.interceptKeyTq(event, isScreenOn);
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mPolicy.interceptKeyBeforeQueueing(whenNanos,
+ keyCode, down, policyFlags, isScreenOn);
}
/* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch. */
- public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
- int metaState, boolean down, int repeatCount, int policyFlags) {
+ public boolean interceptKeyBeforeDispatching(InputChannel focus,
+ int action, int flags, int keyCode, int metaState, int repeatCount,
+ int policyFlags) {
WindowState windowState = getWindowStateForInputChannel(focus);
- return mPolicy.interceptKeyTi(windowState, keyCode, metaState, down, repeatCount,
- policyFlags);
+ return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
+ keyCode, metaState, repeatCount, policyFlags);
}
/* Called when the current input focus changes.
@@ -5494,455 +5351,6 @@
}
}
- private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
- long curTime = SystemClock.uptimeMillis();
-
- if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
- if (mLastTouchEventType == eventType &&
- (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
- return;
- }
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- }
-
- if (targetWin == null
- || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- mPowerManager.userActivity(curTime, false, eventType, false);
- }
- }
-
- // tells if it's a cheek event or not -- this function is stateful
- private static final int EVENT_NONE = 0;
- private static final int EVENT_UNKNOWN = 0;
- private static final int EVENT_CHEEK = 0;
- private static final int EVENT_IGNORE_DURATION = 300; // ms
- private static final float CHEEK_THRESHOLD = 0.6f;
- private int mEventState = EVENT_NONE;
- private float mEventSize;
-
- private int eventType(MotionEvent ev) {
- float size = ev.getSize();
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT;
- case MotionEvent.ACTION_UP:
- if (size > mEventSize) mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_UP_EVENT;
- case MotionEvent.ACTION_MOVE:
- final int N = ev.getHistorySize();
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- for (int i=0; i<N; i++) {
- size = ev.getHistoricalSize(i);
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- }
- if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) {
- return TOUCH_EVENT;
- } else {
- return LONG_TOUCH_EVENT;
- }
- default:
- // not good
- return OTHER_EVENT;
- }
- }
-
- private boolean mFatTouch; // remove me together with dispatchPointer
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG,
- "dispatchPointer " + ev);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
- }
-
- Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, true, false, pid, uid);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
- }
-
- int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_UP) {
- // let go of our target
- mKeyWaiter.mMotionTarget = null;
- mPowerManager.logPointerUpEvent();
- } else if (action == MotionEvent.ACTION_DOWN) {
- mPowerManager.logPointerDownEvent();
- }
-
- if (targetObj == null) {
- // In this case we are either dropping the event, or have received
- // a move or up without a down. It is common to receive move
- // events in such a way, since this means the user is moving the
- // pointer without actually pressing down. All other cases should
- // be atypical, so let's log them.
- if (action != MotionEvent.ACTION_MOVE) {
- Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction());
- }
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
- }
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_FAILED;
- }
- if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
- }
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
- }
-
- WindowState target = (WindowState)targetObj;
-
- final long eventTime = ev.getEventTime();
- final long eventTimeNano = ev.getEventTimeNano();
-
- //Slog.i(TAG, "Sending " + ev + " to " + target);
-
- if (uid != 0 && uid != target.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting pointer event from pid "
- + pid + " uid " + uid + " to window " + target
- + " owned by uid " + target.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_NO_PERMISSION;
- }
- }
-
- if (MEASURE_LATENCY) {
- lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano);
- }
-
- if ((target.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
- //target wants to ignore fat touch events
- boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
- //explicit flag to return without processing event further
- boolean returnFlag = false;
- if((action == MotionEvent.ACTION_DOWN)) {
- mFatTouch = false;
- if(cheekPress) {
- mFatTouch = true;
- returnFlag = true;
- }
- } else {
- if(action == MotionEvent.ACTION_UP) {
- if(mFatTouch) {
- //earlier even was invalid doesnt matter if current up is cheekpress or not
- mFatTouch = false;
- returnFlag = true;
- } else if(cheekPress) {
- //cancel the earlier event
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
- }
- } else if(action == MotionEvent.ACTION_MOVE) {
- if(mFatTouch) {
- //two cases here
- //an invalid down followed by 0 or moves(valid or invalid)
- //a valid down, invalid move, more moves. want to ignore till up
- returnFlag = true;
- } else if(cheekPress) {
- //valid down followed by invalid moves
- //an invalid move have to cancel earlier action
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
- if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
- //note that the subsequent invalid moves will not get here
- mFatTouch = true;
- }
- }
- } //else if action
- if(returnFlag) {
- //recycle que, ev
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_FAILED;
- }
- } //end if target
-
- // Enable this for testing the "right" value
- if (false && action == MotionEvent.ACTION_DOWN) {
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
- }
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
- }
-
- /*
- * Throttle events to minimize CPU usage when there's a flood of events
- * e.g. constant contact with the screen
- */
- if (action == MotionEvent.ACTION_MOVE) {
- long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents;
- long now = SystemClock.uptimeMillis();
- if (now < nextEventTime) {
- try {
- Thread.sleep(nextEventTime - now);
- } catch (InterruptedException e) {
- }
- mLastTouchEventTime = nextEventTime;
- } else {
- mLastTouchEventTime = now;
- }
- }
-
- if (MEASURE_LATENCY) {
- lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano);
- }
-
- synchronized(mWindowMap) {
- if (!target.isVisibleLw()) {
- // During this motion dispatch, the target window has become
- // invisible.
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
- }
-
- if (qev != null && action == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(target,
- KeyWaiter.RETURN_PENDING_POINTER, qev);
- ev = null;
- } else {
- if (action == MotionEvent.ACTION_DOWN) {
- WindowState out = mKeyWaiter.mOutsideTouchTargets;
- if (out != null) {
- MotionEvent oev = MotionEvent.obtain(ev);
- try {
- oev.setAction(MotionEvent.ACTION_OUTSIDE);
- do {
- final Rect frame = out.mFrame;
- oev.offsetLocation(-(float)frame.left, -(float)frame.top);
- try {
- out.mClient.dispatchPointer(oev, eventTime, false);
- } catch (android.os.RemoteException e) {
- Slog.i(TAG,
- "WINDOW DIED during outside motion dispatch: " + out);
- }
- oev.offsetLocation((float)frame.left, (float)frame.top);
- out = out.mNextOutsideTouch;
- } while (out != null);
- mKeyWaiter.mOutsideTouchTargets = null;
- } finally {
- oev.recycle();
- }
- }
- }
-
- dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false);
-
- final Rect frame = target.mFrame;
- ev.offsetLocation(-(float)frame.left, -(float)frame.top);
- mKeyWaiter.bindTargetWindowLocked(target);
- }
- }
-
- // finally offset the event to the target's coordinate system and
- // dispatch the event.
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
- Slog.v(TAG, "Delivering pointer " + qev + " to " + target);
- }
-
- if (MEASURE_LATENCY) {
- lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
- }
-
- target.mClient.dispatchPointer(ev, eventTime, true);
-
- if (MEASURE_LATENCY) {
- lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano);
- }
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target);
- mKeyWaiter.mMotionTarget = null;
- try {
- removeWindow(target.mSession, target.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
- }
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping trackball: " + ev);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_FAILED;
- }
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
- }
-
- WindowState focus = (WindowState)focusObj;
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_NO_PERMISSION;
- }
- }
-
- final long eventTime = ev.getEventTime();
-
- synchronized(mWindowMap) {
- if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(focus,
- KeyWaiter.RETURN_PENDING_TRACKBALL, qev);
- // We don't deliver movement events to the client, we hold
- // them and wait for them to call back.
- ev = null;
- } else {
- mKeyWaiter.bindTargetWindowLocked(focus);
- }
- }
-
- try {
- focus.mClient.dispatchTrackball(ev, eventTime, true);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
- }
-
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchKey(KeyEvent event, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
- null, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping: " + event);
- return INJECT_FAILED;
- }
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- return INJECT_SUCCEEDED;
- }
-
- // Okay we have finished waiting for the last event to be processed.
- // First off, if this is a repeat event, check to see if there is
- // a corresponding up event in the queue. If there is, we will
- // just drop the repeat, because it makes no sense to repeat after
- // the user has released a key. (This is especially important for
- // long presses.)
- if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) {
- return INJECT_SUCCEEDED;
- }
-
- WindowState focus = (WindowState)focusObj;
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Dispatching to " + focus + ": " + event);
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- return INJECT_NO_PERMISSION;
- }
- }
-
- synchronized(mWindowMap) {
- mKeyWaiter.bindTargetWindowLocked(focus);
- }
-
- // NOSHIP extra state logging
- mKeyWaiter.recordDispatchState(event, focus);
- // END NOSHIP
-
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS) {
- Slog.v(TAG, "Delivering key " + event.getKeyCode()
- + " to " + focus);
- }
- focus.mClient.dispatchKey(event);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
- }
-
- return INJECT_FAILED;
- }
-
public void pauseKeyDispatching(IBinder _token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"pauseKeyDispatching()")) {
@@ -5952,11 +5360,7 @@
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.pauseDispatchingLw(token);
- } else {
- mKeyWaiter.pauseDispatchingLocked(token);
- }
+ mInputMonitor.pauseDispatchingLw(token);
}
}
}
@@ -5970,11 +5374,7 @@
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.resumeDispatchingLw(token);
- } else {
- mKeyWaiter.resumeDispatchingLocked(token);
- }
+ mInputMonitor.resumeDispatchingLw(token);
}
}
}
@@ -5986,11 +5386,7 @@
}
synchronized (mWindowMap) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.setEventDispatchingLw(enabled);
- } else {
- mKeyWaiter.setEventDispatchingLocked(enabled);
- }
+ mInputMonitor.setEventDispatchingLw(enabled);
}
}
@@ -6023,16 +5419,8 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
- } else {
- result = dispatchKey(newEvent, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
- }
- }
+ final int result = mInputManager.injectKeyEvent(newEvent,
+ InputQueue.INPUT_EVENT_NATURE_KEY, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
@@ -6052,16 +5440,8 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
- } else {
- result = dispatchPointer(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
- }
- }
+ final int result = mInputManager.injectMotionEvent(ev,
+ InputQueue.INPUT_EVENT_NATURE_TOUCH, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
@@ -6081,48 +5461,29 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL,
- pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
- } else {
- result = dispatchTrackball(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
- }
- }
+ final int result = mInputManager.injectMotionEvent(ev,
+ InputQueue.INPUT_EVENT_NATURE_TRACKBALL, pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
Binder.restoreCallingIdentity(ident);
return reportInjectionResult(result);
}
private boolean reportInjectionResult(int result) {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- switch (result) {
- case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
- Slog.w(TAG, "Input event injection permission denied.");
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
- Slog.v(TAG, "Input event injection succeeded.");
- return true;
- case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
- Slog.w(TAG, "Input event injection timed out.");
- return false;
- case InputManager.INPUT_EVENT_INJECTION_FAILED:
- default:
- Slog.w(TAG, "Input event injection failed.");
- return false;
- }
- } else {
- switch (result) {
- case INJECT_NO_PERMISSION:
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
- return true;
- }
- return false;
+ switch (result) {
+ case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ Slog.w(TAG, "Input event injection permission denied.");
+ throw new SecurityException(
+ "Injecting to another application requires INJECT_EVENTS permission");
+ case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
+ Slog.v(TAG, "Input event injection succeeded.");
+ return true;
+ case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
+ Slog.w(TAG, "Input event injection timed out.");
+ return false;
+ case InputManager.INPUT_EVENT_INJECTION_FAILED:
+ default:
+ Slog.w(TAG, "Input event injection failed.");
+ return false;
}
}
@@ -6136,867 +5497,6 @@
return mCurrentFocus;
}
- /**
- * This class holds the state for dispatching key events. This state
- * is protected by the KeyWaiter instance, NOT by the window lock. You
- * can be holding the main window lock while acquire the KeyWaiter lock,
- * but not the other way around.
- */
- final class KeyWaiter {
- // NOSHIP debugging
- public class DispatchState {
- private KeyEvent event;
- private WindowState focus;
- private long time;
- private WindowState lastWin;
- private IBinder lastBinder;
- private boolean finished;
- private boolean gotFirstWindow;
- private boolean eventDispatching;
- private long timeToSwitch;
- private boolean wasFrozen;
- private boolean focusPaused;
- private WindowState curFocus;
-
- DispatchState(KeyEvent theEvent, WindowState theFocus) {
- focus = theFocus;
- event = theEvent;
- time = System.currentTimeMillis();
- // snapshot KeyWaiter state
- lastWin = mLastWin;
- lastBinder = mLastBinder;
- finished = mFinished;
- gotFirstWindow = mGotFirstWindow;
- eventDispatching = mEventDispatching;
- timeToSwitch = mTimeToSwitch;
- wasFrozen = mWasFrozen;
- curFocus = mCurrentFocus;
- // cache the paused state at ctor time as well
- if (theFocus == null || theFocus.mToken == null) {
- focusPaused = false;
- } else {
- focusPaused = theFocus.mToken.paused;
- }
- }
-
- public String toString() {
- return "{{" + event + " to " + focus + " @ " + time
- + " lw=" + lastWin + " lb=" + lastBinder
- + " fin=" + finished + " gfw=" + gotFirstWindow
- + " ed=" + eventDispatching + " tts=" + timeToSwitch
- + " wf=" + wasFrozen + " fp=" + focusPaused
- + " mcf=" + curFocus + "}}";
- }
- };
- private DispatchState mDispatchState = null;
- public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) {
- mDispatchState = new DispatchState(theEvent, theFocus);
- }
- // END NOSHIP
-
- public static final int RETURN_NOTHING = 0;
- public static final int RETURN_PENDING_POINTER = 1;
- public static final int RETURN_PENDING_TRACKBALL = 2;
-
- final Object SKIP_TARGET_TOKEN = new Object();
- final Object CONSUMED_EVENT_TOKEN = new Object();
-
- private WindowState mLastWin = null;
- private IBinder mLastBinder = null;
- private boolean mFinished = true;
- private boolean mGotFirstWindow = false;
- private boolean mEventDispatching = true;
- private long mTimeToSwitch = 0;
- /* package */ boolean mWasFrozen = false;
-
- // Target of Motion events
- WindowState mMotionTarget;
-
- // Windows above the target who would like to receive an "outside"
- // touch event for any down events outside of them.
- WindowState mOutsideTouchTargets;
-
- /**
- * Wait for the last event dispatch to complete, then find the next
- * target that should receive the given event and wait for that one
- * to be ready to receive it.
- */
- Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- boolean failIfTimeout, int callingPid, int callingUid) {
- long startTime = SystemClock.uptimeMillis();
- long keyDispatchingTimeout = 5 * 1000;
- long waitedFor = 0;
-
- while (true) {
- // Figure out which window we care about. It is either the
- // last window we are waiting to have process the event or,
- // if none, then the next window we think the event should go
- // to. Note: we retrieve mLastWin outside of the lock, so
- // it may change before we lock. Thus we must check it again.
- WindowState targetWin = mLastWin;
- boolean targetIsNew = targetWin == null;
- if (DEBUG_INPUT) Slog.v(
- TAG, "waitForLastKey: mFinished=" + mFinished +
- ", mLastWin=" + mLastWin);
- if (targetIsNew) {
- Object target = findTargetWindow(nextKey, qev, nextMotion,
- isPointerEvent, callingPid, callingUid);
- if (target == SKIP_TARGET_TOKEN) {
- // The user has pressed a special key, and we are
- // dropping all pending events before it.
- if (DEBUG_INPUT) Slog.v(TAG, "Skipping: " + nextKey
- + " " + nextMotion);
- return null;
- }
- if (target == CONSUMED_EVENT_TOKEN) {
- if (DEBUG_INPUT) Slog.v(TAG, "Consumed: " + nextKey
- + " " + nextMotion);
- return target;
- }
- targetWin = (WindowState)target;
- }
-
- AppWindowToken targetApp = null;
-
- // Now: is it okay to send the next event to this window?
- synchronized (this) {
- // First: did we come here based on the last window not
- // being null, but it changed by the time we got here?
- // If so, try again.
- if (!targetIsNew && mLastWin == null) {
- continue;
- }
-
- // We never dispatch events if not finished with the
- // last one, or the display is frozen.
- if (mFinished && !mDisplayFrozen) {
- // If event dispatching is disabled, then we
- // just consume the events.
- if (!mEventDispatching) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; dispatching disabled: "
- + nextKey + " " + nextMotion);
- return null;
- }
- if (targetWin != null) {
- // If this is a new target, and that target is not
- // paused or unresponsive, then all looks good to
- // handle the event.
- if (targetIsNew && !targetWin.mToken.paused) {
- return targetWin;
- }
-
- // If we didn't find a target window, and there is no
- // focused app window, then just eat the events.
- } else if (mFocusedApp == null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; no focused app: "
- + nextKey + " " + nextMotion);
- return null;
- }
- }
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for last key in " + mLastBinder
- + " target=" + targetWin
- + " mFinished=" + mFinished
- + " mDisplayFrozen=" + mDisplayFrozen
- + " targetIsNew=" + targetIsNew
- + " paused="
- + (targetWin != null ? targetWin.mToken.paused : false)
- + " mFocusedApp=" + mFocusedApp
- + " mCurrentFocus=" + mCurrentFocus);
-
- targetApp = targetWin != null
- ? targetWin.mAppToken : mFocusedApp;
-
- long curTimeout = keyDispatchingTimeout;
- if (mTimeToSwitch != 0) {
- long now = SystemClock.uptimeMillis();
- if (mTimeToSwitch <= now) {
- // If an app switch key has been pressed, and we have
- // waited too long for the current app to finish
- // processing keys, then wait no more!
- doFinishedKeyLocked(false);
- continue;
- }
- long switchTimeout = mTimeToSwitch - now;
- if (curTimeout > switchTimeout) {
- curTimeout = switchTimeout;
- }
- }
-
- try {
- // after that continue
- // processing keys, so we don't get stuck.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for key dispatch: " + curTimeout);
- wait(curTimeout);
- if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @"
- + SystemClock.uptimeMillis() + " startTime="
- + startTime + " switchTime=" + mTimeToSwitch
- + " target=" + targetWin + " mLW=" + mLastWin
- + " mLB=" + mLastBinder + " fin=" + mFinished
- + " mCurrentFocus=" + mCurrentFocus);
- } catch (InterruptedException e) {
- }
- }
-
- // If we were frozen during configuration change, restart the
- // timeout checks from now; otherwise look at whether we timed
- // out before awakening.
- if (mWasFrozen) {
- waitedFor = 0;
- mWasFrozen = false;
- } else {
- waitedFor = SystemClock.uptimeMillis() - startTime;
- }
-
- if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
- IApplicationToken at = null;
- synchronized (this) {
- Slog.w(TAG, "Key dispatching timed out sending to " +
- (targetWin != null ? targetWin.mAttrs.getTitle()
- : "<null>: no window ready for key dispatch"));
- // NOSHIP debugging
- Slog.w(TAG, "Previous dispatch state: " + mDispatchState);
- Slog.w(TAG, "Current dispatch state: " +
- new DispatchState(nextKey, targetWin));
- // END NOSHIP
- //dump();
- if (targetWin != null) {
- at = targetWin.getAppToken();
- } else if (targetApp != null) {
- at = targetApp.appToken;
- }
- }
-
- boolean abort = true;
- if (at != null) {
- try {
- long timeout = at.getKeyDispatchingTimeout();
- if (timeout > waitedFor) {
- // we did not wait the proper amount of time for this application.
- // set the timeout to be the real timeout and wait again.
- keyDispatchingTimeout = timeout - waitedFor;
- continue;
- } else {
- abort = at.keyDispatchingTimedOut();
- }
- } catch (RemoteException ex) {
- }
- }
-
- synchronized (this) {
- if (abort && (mLastWin == targetWin || targetWin == null)) {
- mFinished = true;
- if (mLastWin != null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Window " + mLastWin +
- " timed out on key input");
- if (mLastWin.mToken.paused) {
- Slog.w(TAG, "Un-pausing dispatching to this window");
- mLastWin.mToken.paused = false;
- }
- }
- if (mMotionTarget == targetWin) {
- mMotionTarget = null;
- }
- mLastWin = null;
- mLastBinder = null;
- if (failIfTimeout || targetWin == null) {
- return null;
- }
- } else {
- Slog.w(TAG, "Continuing to wait for key to be dispatched");
- startTime = SystemClock.uptimeMillis();
- }
- }
- }
- }
- }
-
- Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- int callingPid, int callingUid) {
- mOutsideTouchTargets = null;
-
- if (nextKey != null) {
- // Find the target window for a normal key event.
- final int keycode = nextKey.getKeyCode();
- final int repeatCount = nextKey.getRepeatCount();
- final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
-
- if (!dispatch) {
- if (callingUid == 0 ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags());
- }
- Slog.w(TAG, "Event timeout during app switch: dropping "
- + nextKey);
- return SKIP_TARGET_TOKEN;
- }
-
- // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- if (callingUid == 0 ||
- (focus != null && callingUid == focus.mSession.mUid) ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags())) {
- return CONSUMED_EVENT_TOKEN;
- }
- }
-
- return focus;
-
- } else if (!isPointerEvent) {
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping trackball "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
- return focus;
- }
-
- if (nextMotion == null) {
- return SKIP_TARGET_TOKEN;
- }
-
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
- KeyEvent.KEYCODE_UNKNOWN);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping pointer "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- // Find the target window for a pointer event.
- int action = nextMotion.getAction();
- final float xf = nextMotion.getX();
- final float yf = nextMotion.getY();
- final long eventTime = nextMotion.getEventTime();
-
- final boolean screenWasOff = qev != null
- && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-
- WindowState target = null;
-
- synchronized(mWindowMap) {
- synchronized (this) {
- if (action == MotionEvent.ACTION_DOWN) {
- if (mMotionTarget != null) {
- // this is weird, we got a pen down, but we thought it was
- // already down!
- // XXX: We should probably send an ACTION_UP to the current
- // target.
- Slog.w(TAG, "Pointer down received while already down in: "
- + mMotionTarget);
- mMotionTarget = null;
- }
-
- // ACTION_DOWN is special, because we need to lock next events to
- // the window we'll land onto.
- final int x = (int)xf;
- final int y = (int)yf;
-
- final ArrayList windows = mWindows;
- final int N = windows.size();
- WindowState topErrWindow = null;
- final Rect tmpRect = mTempRect;
- for (int i=N-1; i>=0; i--) {
- WindowState child = (WindowState)windows.get(i);
- //Slog.i(TAG, "Checking dispatch to: " + child);
- final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
- continue;
- }
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- //Slog.i(TAG, "Not touchable!");
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- }
- continue;
- }
- tmpRect.set(child.mFrame);
- if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
- // The touch is inside of the window if it is
- // inside the frame, AND the content part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenContentInsets.left;
- tmpRect.top += child.mGivenContentInsets.top;
- tmpRect.right -= child.mGivenContentInsets.right;
- tmpRect.bottom -= child.mGivenContentInsets.bottom;
- } else if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
- // The touch is inside of the window if it is
- // inside the frame, AND the visible part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenVisibleInsets.left;
- tmpRect.top += child.mGivenVisibleInsets.top;
- tmpRect.right -= child.mGivenVisibleInsets.right;
- tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
- }
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (tmpRect.contains(x, y) || touchFlags == 0) {
- //Slog.i(TAG, "Using this target!");
- if (!screenWasOff || (flags &
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
- mMotionTarget = child;
- } else {
- //Slog.i(TAG, "Waking, skip!");
- mMotionTarget = null;
- }
- break;
- }
-
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- //Slog.i(TAG, "Adding to outside target list: " + child);
- }
- }
-
- // if there's an error window but it's not accepting
- // focus (typically because it is not yet visible) just
- // wait for it -- any other focused window may in fact
- // be in ANR state.
- if (topErrWindow != null && mMotionTarget != topErrWindow) {
- mMotionTarget = null;
- }
- }
-
- target = mMotionTarget;
- }
- }
-
- wakeupIfNeeded(target, eventType(nextMotion));
-
- // Pointer events are a little different -- if there isn't a
- // target found for any event, then just drop it.
- return target != null ? target : SKIP_TARGET_TOKEN;
- }
-
- boolean checkShouldDispatchKey(int keycode) {
- synchronized (this) {
- if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
- mTimeToSwitch = 0;
- return true;
- }
- if (mTimeToSwitch != 0
- && mTimeToSwitch < SystemClock.uptimeMillis()) {
- return false;
- }
- return true;
- }
- }
-
- void bindTargetWindowLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
- }
- }
-
- void bindTargetWindowLocked(WindowState win) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
- }
- }
-
- void bindTargetWindowLockedLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- mLastWin = win;
- mLastBinder = win.mClient.asBinder();
- mFinished = false;
- if (pendingMotion != null) {
- final Session s = win.mSession;
- if (pendingWhat == RETURN_PENDING_POINTER) {
- releasePendingPointerLocked(s);
- s.mPendingPointerMove = pendingMotion;
- s.mPendingPointerWindow = win;
- if (DEBUG_INPUT) Slog.v(TAG,
- "bindTargetToWindow " + s.mPendingPointerMove);
- } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
- releasePendingTrackballLocked(s);
- s.mPendingTrackballMove = pendingMotion;
- s.mPendingTrackballWindow = win;
- }
- }
- }
-
- void releasePendingPointerLocked(Session s) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "releasePendingPointer " + s.mPendingPointerMove);
- if (s.mPendingPointerMove != null) {
- mQueue.recycleEvent(s.mPendingPointerMove);
- s.mPendingPointerMove = null;
- }
- }
-
- void releasePendingTrackballLocked(Session s) {
- if (s.mPendingTrackballMove != null) {
- mQueue.recycleEvent(s.mPendingTrackballMove);
- s.mPendingTrackballMove = null;
- }
- }
-
- MotionEvent finishedKey(Session session, IWindow client, boolean force,
- int returnWhat) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client + ", force=" + force);
-
- if (client == null) {
- return null;
- }
-
- MotionEvent res = null;
- QueuedEvent qev = null;
- WindowState win = null;
-
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client.asBinder()
- + ", force=" + force + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
-
- if (returnWhat == RETURN_PENDING_POINTER) {
- qev = session.mPendingPointerMove;
- win = session.mPendingPointerWindow;
- session.mPendingPointerMove = null;
- session.mPendingPointerWindow = null;
- } else if (returnWhat == RETURN_PENDING_TRACKBALL) {
- qev = session.mPendingTrackballMove;
- win = session.mPendingTrackballWindow;
- session.mPendingTrackballMove = null;
- session.mPendingTrackballWindow = null;
- }
-
- if (mLastBinder == client.asBinder()) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: last paused="
- + ((mLastWin != null) ? mLastWin.mToken.paused : "null"));
- if (mLastWin != null && (!mLastWin.mToken.paused || force
- || !mEventDispatching)) {
- doFinishedKeyLocked(true);
- } else {
- // Make sure to wake up anyone currently waiting to
- // dispatch a key, so they can re-evaluate their
- // current situation.
- mFinished = true;
- notifyAll();
- }
- }
-
- if (qev != null) {
- res = (MotionEvent)qev.event;
- if (DEBUG_INPUT) Slog.v(TAG,
- "Returning pending motion: " + res);
- mQueue.recycleEvent(qev);
- if (win != null && returnWhat == RETURN_PENDING_POINTER) {
- res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
- }
- }
- }
-
- if (res != null && returnWhat == RETURN_PENDING_POINTER) {
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false);
- }
- }
-
- return res;
- }
-
- void tickle() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- void handleNewWindowLocked(WindowState newWindow) {
- if (!newWindow.canReceiveKeys()) {
- return;
- }
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "New key dispatch window: win="
- + newWindow.mClient.asBinder()
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + newWindow.mToken.paused);
-
- // Displaying a window implicitly causes dispatching to
- // be unpaused. (This is to protect against bugs if someone
- // pauses dispatching but forgets to resume.)
- newWindow.mToken.paused = false;
-
- mGotFirstWindow = true;
-
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "New SYSTEM_ERROR window; resetting state");
- mLastWin = null;
- mLastBinder = null;
- mMotionTarget = null;
- mFinished = true;
- } else if (mLastWin != null) {
- // If the new window is above the window we are
- // waiting on, then stop waiting and let key dispatching
- // start on the new guy.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Last win layer=" + mLastWin.mLayer
- + ", new win layer=" + newWindow.mLayer);
- if (newWindow.mLayer >= mLastWin.mLayer) {
- // The new window is above the old; finish pending input to the last
- // window and start directing it to the new one.
- mLastWin.mToken.paused = false;
- doFinishedKeyLocked(false); // does a notifyAll()
- return;
- }
- }
-
- // Now that we've put a new window state in place, make the event waiter
- // take notice and retarget its attentions.
- notifyAll();
- }
- }
-
- void pauseDispatchingLocked(WindowToken token) {
- synchronized (this)
- {
- if (DEBUG_INPUT) Slog.v(TAG, "Pausing WindowToken " + token);
- token.paused = true;
-
- /*
- if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) {
- mPaused = true;
- } else {
- if (mLastWin == null) {
- Slog.i(TAG, "Key dispatching not paused: no last window.");
- } else if (mFinished) {
- Slog.i(TAG, "Key dispatching not paused: finished last key.");
- } else {
- Slog.i(TAG, "Key dispatching not paused: window in higher layer.");
- }
- }
- */
- }
- }
-
- void resumeDispatchingLocked(WindowToken token) {
- synchronized (this) {
- if (token.paused) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "Resuming WindowToken " + token
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + token.paused);
- token.paused = false;
- if (mLastWin != null && mLastWin.mToken == token && mFinished) {
- doFinishedKeyLocked(false);
- } else {
- notifyAll();
- }
- }
- }
- }
-
- void setEventDispatchingLocked(boolean enabled) {
- synchronized (this) {
- mEventDispatching = enabled;
- notifyAll();
- }
- }
-
- void appSwitchComing() {
- synchronized (this) {
- // Don't wait for more than .5 seconds for app to finish
- // processing the pending events.
- long now = SystemClock.uptimeMillis() + 500;
- if (DEBUG_INPUT) Slog.v(TAG, "appSwitchComing: " + now);
- if (mTimeToSwitch == 0 || now < mTimeToSwitch) {
- mTimeToSwitch = now;
- }
- notifyAll();
- }
- }
-
- private final void doFinishedKeyLocked(boolean force) {
- if (mLastWin != null) {
- releasePendingPointerLocked(mLastWin.mSession);
- releasePendingTrackballLocked(mLastWin.mSession);
- }
-
- if (force || mLastWin == null || !mLastWin.mToken.paused
- || !mLastWin.isVisibleLw()) {
- // If the current window has been paused, we aren't -really-
- // finished... so let the waiters still wait.
- mLastWin = null;
- mLastBinder = null;
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- private class KeyQ extends KeyInputQueue
- implements KeyInputQueue.FilterCallback {
- KeyQ() {
- super(mContext, WindowManagerService.this);
- }
-
- @Override
- boolean preprocessEvent(InputDevice device, RawInputEvent event) {
- if (mPolicy.preprocessInputEventTq(event)) {
- return true;
- }
-
- switch (event.type) {
- case RawInputEvent.EV_KEY: {
- // XXX begin hack
- if (DEBUG) {
- if (event.keycode == KeyEvent.KEYCODE_G) {
- if (event.value != 0) {
- // G down
- mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
- return false;
- }
- if (event.keycode == KeyEvent.KEYCODE_D) {
- if (event.value != 0) {
- //dump();
- }
- return false;
- }
- }
- // XXX end hack
-
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
-
- if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
- mPowerManager.goToSleep(event.when);
- }
-
- if (screenIsOff) {
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
- mPowerManager.userActivity(event.when, false,
- LocalPowerManager.BUTTON_EVENT, false);
- }
-
- if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
- if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
- filterQueue(this);
- mKeyWaiter.appSwitchComing();
- }
- return true;
- } else {
- return false;
- }
- }
-
- case RawInputEvent.EV_REL: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeRelMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- case RawInputEvent.EV_ABS: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- default:
- return true;
- }
- }
-
- public int filterEvent(QueuedEvent ev) {
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (mPolicy.isMovementKeyTi(ke.getKeyCode())) {
- Slog.w(TAG, "Dropping movement key during app switch: "
- + ke.getKeyCode() + ", action=" + ke.getAction());
- return FILTER_REMOVE;
- }
- return FILTER_ABORT;
- default:
- return FILTER_KEEP;
- }
- }
- }
-
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
return mSafeMode;
@@ -7006,219 +5506,6 @@
mPolicy.systemReady();
}
- private final class InputDispatcherThread extends Thread {
- // Time to wait when there is nothing to do: 9999 seconds.
- static final int LONG_WAIT=9999*1000;
-
- public InputDispatcherThread() {
- super("InputDispatcher");
- }
-
- @Override
- public void run() {
- while (true) {
- try {
- process();
- } catch (Exception e) {
- Slog.e(TAG, "Exception in input dispatcher", e);
- }
- }
- }
-
- private void process() {
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- // The last key event we saw
- KeyEvent lastKey = null;
-
- // Last keydown time for auto-repeating keys
- long lastKeyTime = SystemClock.uptimeMillis();
- long nextKeyTime = lastKeyTime+LONG_WAIT;
- long downTime = 0;
-
- // How many successive repeats we generated
- int keyRepeatCount = 0;
-
- // Need to report that configuration has changed?
- boolean configChanged = false;
-
- while (true) {
- long curTime = SystemClock.uptimeMillis();
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for next key: now=" + curTime
- + ", repeat @ " + nextKeyTime);
-
- // Retrieve next event, waiting only as long as the next
- // repeat timeout. If the configuration has changed, then
- // don't wait at all -- we'll report the change as soon as
- // we have processed all events.
- QueuedEvent ev = mQueue.getEvent(
- (int)((!configChanged && curTime < nextKeyTime)
- ? (nextKeyTime-curTime) : 0));
-
- if (DEBUG_INPUT && ev != null) Slog.v(
- TAG, "Event: type=" + ev.classType + " data=" + ev.event);
-
- if (MEASURE_LATENCY) {
- lt.sample("2 got event ", System.nanoTime() - ev.whenNano);
- }
-
- if (lastKey != null && !mPolicy.allowKeyRepeat()) {
- // cancel key repeat at the request of the policy.
- lastKey = null;
- downTime = 0;
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
- try {
- if (ev != null) {
- curTime = SystemClock.uptimeMillis();
- int eventType;
- if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
- eventType = eventType((MotionEvent)ev.event);
- } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
- ev.classType == RawInputEvent.CLASS_TRACKBALL) {
- eventType = LocalPowerManager.BUTTON_EVENT;
- } else {
- eventType = LocalPowerManager.OTHER_EVENT;
- }
- try {
- if ((curTime - mLastBatteryStatsCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = curTime;
- mBatteryStats.noteInputEvent();
- }
- } catch (RemoteException e) {
- // Ignore
- }
-
- if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) {
- // do not wake screen in this case
- } else if (eventType != TOUCH_EVENT
- && eventType != LONG_TOUCH_EVENT
- && eventType != CHEEK_EVENT) {
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- } else if (mLastTouchEventType != eventType
- || (curTime - mLastUserActivityCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- }
-
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (ke.isDown()) {
- lastKeyTime = curTime;
- if (lastKey != null &&
- ke.getKeyCode() == lastKey.getKeyCode()) {
- keyRepeatCount++;
- // Arbitrary long timeout to block
- // repeating here since we know that
- // the device driver takes care of it.
- nextKeyTime = lastKeyTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received repeated key down");
- } else {
- downTime = curTime;
- keyRepeatCount = 0;
- nextKeyTime = lastKeyTime
- + ViewConfiguration.getLongPressTimeout();
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key down: first repeat @ "
- + nextKeyTime);
- }
- lastKey = ke;
- } else {
- lastKey = null;
- downTime = 0;
- keyRepeatCount = 0;
- // Arbitrary long timeout.
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key up: ignore repeat @ "
- + nextKeyTime);
- }
- if (keyRepeatCount > 0) {
- dispatchKey(KeyEvent.changeTimeRepeat(ke,
- ke.getEventTime(), keyRepeatCount), 0, 0);
- } else {
- dispatchKey(ke, 0, 0);
- }
- mQueue.recycleEvent(ev);
- break;
- case RawInputEvent.CLASS_TOUCHSCREEN:
- //Slog.i(TAG, "Read next event " + ev);
- dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_TRACKBALL:
- dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
- configChanged = true;
- break;
- default:
- mQueue.recycleEvent(ev);
- break;
- }
-
- } else if (configChanged) {
- configChanged = false;
- sendNewConfiguration();
-
- } else if (lastKey != null) {
- curTime = SystemClock.uptimeMillis();
-
- // Timeout occurred while key was down. If it is at or
- // past the key repeat time, dispatch the repeat.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key timeout: repeat=" + nextKeyTime
- + ", now=" + curTime);
- if (curTime < nextKeyTime) {
- continue;
- }
-
- lastKeyTime = nextKeyTime;
- nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
- keyRepeatCount++;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key repeat: count=" + keyRepeatCount
- + ", next @ " + nextKeyTime);
- KeyEvent newEvent;
- if (downTime != 0 && (downTime
- + ViewConfiguration.getLongPressTimeout())
- <= curTime) {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount,
- lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);
- downTime = 0;
- } else {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount);
- }
- dispatchKey(newEvent, 0, 0);
-
- } else {
- curTime = SystemClock.uptimeMillis();
-
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
-
- } catch (Exception e) {
- Slog.e(TAG,
- "Input thread received uncaught exception: " + e, e);
- }
- }
- }
- }
-
// -------------------------------------------------------------
// Client Session State
// -------------------------------------------------------------
@@ -7234,20 +5521,6 @@
int mNumWindow = 0;
boolean mClientDead = false;
- /**
- * Current pointer move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingPointerMove;
- WindowState mPendingPointerWindow;
-
- /**
- * Current trackball move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingTrackballMove;
- WindowState mPendingTrackballWindow;
-
public Session(IInputMethodClient client, IInputContext inputContext) {
mClient = client;
mInputContext = inputContext;
@@ -7366,36 +5639,6 @@
finishDrawingWindow(this, window);
}
- public void finishKey(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow finishKey called for " + window);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be called anymore.");
- }
- mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_NOTHING);
- }
-
- public MotionEvent getPendingPointerMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be called anymore.");
- }
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_POINTER);
- }
-
- public MotionEvent getPendingTrackballMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- throw new IllegalStateException("Should not be called anymore.");
- }
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_TRACKBALL);
- }
-
public void setInTouchMode(boolean mode) {
synchronized(mWindowMap) {
mInTouchMode = mode;
@@ -7499,16 +5742,6 @@
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
- if (mPendingPointerWindow != null || mPendingPointerMove != null) {
- pw.print(prefix);
- pw.print("mPendingPointerWindow="); pw.print(mPendingPointerWindow);
- pw.print(" mPendingPointerMove="); pw.println(mPendingPointerMove);
- }
- if (mPendingTrackballWindow != null || mPendingTrackballMove != null) {
- pw.print(prefix);
- pw.print("mPendingTrackballWindow="); pw.print(mPendingTrackballWindow);
- pw.print(" mPendingTrackballMove="); pw.println(mPendingTrackballMove);
- }
}
@Override
@@ -7559,8 +5792,6 @@
boolean mObscured;
boolean mTurnOnScreen;
- WindowState mNextOutsideTouch;
-
int mLayoutSeq = -1;
Configuration mConfiguration = null;
@@ -8077,16 +6308,6 @@
}
void destroySurfaceLocked() {
- // Window is no longer on-screen, so can no longer receive
- // key events... if we were waiting for it to finish
- // handling a key event, the wait is over!
- if (! ENABLE_NATIVE_INPUT_DISPATCH) {
- mKeyWaiter.finishedKey(mSession, mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(mSession);
- mKeyWaiter.releasePendingTrackballLocked(mSession);
- }
-
if (mAppToken != null && this == mAppToken.startingWindow) {
mAppToken.startingDisplayed = false;
}
@@ -8102,9 +6323,7 @@
WindowState c = (WindowState)mChildWindows.get(i);
c.mAttachedHidden = true;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBecomingInvisibleLw(c);
- }
+ mInputMonitor.windowIsBecomingInvisibleLw(c);
}
if (mReportDestroySurface) {
@@ -8413,12 +6632,8 @@
}
mLastHidden = true;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- for (int i=0; i<N; i++) {
- mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
- }
- } else {
- mKeyWaiter.releasePendingPointerLocked(mSession);
+ for (int i=0; i<N; i++) {
+ mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
}
}
mExiting = false;
@@ -8753,13 +6968,11 @@
// we are doing this as part of processing a death note.)
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- if (mInputChannel != null) {
- mInputManager.unregisterInputChannel(mInputChannel);
-
- mInputChannel.dispose();
- mInputChannel = null;
- }
+ if (mInputChannel != null) {
+ mInputManager.unregisterInputChannel(mInputChannel);
+
+ mInputChannel.dispose();
+ mInputChannel = null;
}
}
@@ -9519,6 +7732,7 @@
public static final int ENABLE_SCREEN = 16;
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
+ public static final int REPORT_WINDOWS_CHANGE = 19;
private Session mLastReportedHold;
@@ -9570,6 +7784,7 @@
// Ignore if process has died.
}
}
+ notifyFocusChanged();
}
} break;
@@ -9850,6 +8065,16 @@
break;
}
+ case REPORT_WINDOWS_CHANGE: {
+ if (mWindowsChanged) {
+ synchronized (mWindowMap) {
+ mWindowsChanged = false;
+ }
+ notifyWindowsChanged();
+ }
+ break;
+ }
+
}
}
}
@@ -9934,6 +8159,7 @@
WindowState w = (WindowState)mWindows.get(i);
if (w.mAppToken != null) {
WindowState win = (WindowState)mWindows.remove(i);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Rebuild removing window: " + win);
NW--;
@@ -10069,6 +8295,10 @@
requestAnimationLocked(0);
}
}
+ if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
+ mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
+ mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
+ }
} catch (RuntimeException e) {
mInLayout = false;
Slog.e(TAG, "Unhandled exception while layout out windows", e);
@@ -10175,9 +8405,7 @@
}
// Window frames may have changed. Tell the input dispatcher about it.
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.updateInputWindowsLw();
- }
+ mInputMonitor.updateInputWindowsLw();
return mPolicy.finishLayoutLw();
}
@@ -10977,11 +9205,7 @@
Slog.w(TAG, "Exception hiding surface in " + w);
}
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.windowIsBecomingInvisibleLw(w);
- } else {
- mKeyWaiter.releasePendingPointerLocked(w.mSession);
- }
+ mInputMonitor.windowIsBecomingInvisibleLw(w);
}
// If we are waiting for this window to handle an
// orientation change, well, it is hidden, so
@@ -11578,13 +9802,7 @@
}
private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.setInputFocusLw(mCurrentFocus);
- } else {
- if (mCurrentFocus != null) {
- mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
- }
- }
+ mInputMonitor.setInputFocusLw(mCurrentFocus);
}
private WindowState computeFocusedWindowLocked() {
@@ -11658,17 +9876,6 @@
private void startFreezingDisplayLocked() {
if (mDisplayFrozen) {
- // Freezing the display also suspends key event delivery, to
- // keep events from going astray while the display is reconfigured.
- // If someone has changed orientation again while the screen is
- // still frozen, the events will continue to be blocked while the
- // successive orientation change is processed. To prevent spurious
- // ANRs, we reset the event dispatch timeout in this case.
- if (! ENABLE_NATIVE_INPUT_DISPATCH) {
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- }
- }
return;
}
@@ -11689,9 +9896,7 @@
mDisplayFrozen = true;
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.freezeInputDispatchingLw();
- }
+ mInputMonitor.freezeInputDispatchingLw();
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
@@ -11722,16 +9927,7 @@
}
Surface.unfreezeDisplay(0);
- // Reset the key delivery timeout on unfreeze, too. We force a wakeup here
- // too because regular key delivery processing should resume immediately.
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- mInputMonitor.thawInputDispatchingLw();
- } else {
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- mKeyWaiter.notifyAll();
- }
- }
+ mInputMonitor.thawInputDispatchingLw();
// While the display is frozen we don't re-compute the orientation
// to avoid inconsistent states. However, something interesting
@@ -11763,13 +9959,8 @@
return;
}
- if (ENABLE_NATIVE_INPUT_DISPATCH) {
- pw.println("Input Dispatcher State:");
- mInputManager.dump(pw);
- } else {
- pw.println("Input State:");
- mQueue.dump(pw, " ");
- }
+ pw.println("Input Dispatcher State:");
+ mInputManager.dump(pw);
pw.println(" ");
synchronized(mWindowMap) {
@@ -11986,16 +10177,6 @@
}
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
-
- if (! ENABLE_NATIVE_INPUT_DISPATCH) {
- pw.println(" KeyWaiter state:");
- pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
- pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
- pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
- pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
- pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
- pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
- }
}
}
@@ -12003,12 +10184,6 @@
public void monitor() {
synchronized (mWindowMap) { }
synchronized (mKeyguardTokenWatcher) { }
- synchronized (mKeyWaiter) { }
- synchronized (mInputMonitor) { }
- }
-
- public void virtualKeyFeedback(KeyEvent event) {
- mPolicy.keyFeedbackFromInput(event);
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 93122c4..58aab08 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11562,7 +11562,69 @@
}
}
}
-
+
+ public boolean dumpHeap(String process, boolean managed,
+ String path, ParcelFileDescriptor fd) throws RemoteException {
+
+ try {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (fd == null) {
+ throw new IllegalArgumentException("null fd");
+ }
+
+ ProcessRecord proc = null;
+ try {
+ int pid = Integer.parseInt(process);
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ } catch (NumberFormatException e) {
+ }
+
+ if (proc == null) {
+ HashMap<String, SparseArray<ProcessRecord>> all
+ = mProcessNames.getMap();
+ SparseArray<ProcessRecord> procs = all.get(process);
+ if (procs != null && procs.size() > 0) {
+ proc = procs.valueAt(0);
+ }
+ }
+
+ if (proc == null || proc.thread == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+
+ boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
+ if (isSecure) {
+ if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + proc);
+ }
+ }
+
+ proc.thread.dumpHeap(managed, path, fd);
+ fd = null;
+ return true;
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ } finally {
+ if (fd != null) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
/** In this method we try to acquire our lock to make sure that we have not deadlocked */
public void monitor() {
synchronized (this) { }
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 499ca86..0cf36b3 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -4,9 +4,9 @@
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
- com_android_server_KeyInputQueue.cpp \
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
+ com_android_server_PowerManagerService.cpp \
com_android_server_SensorService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_VibratorService.cpp \
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index d0f856b..26e105a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -40,6 +40,7 @@
#include "../../core/jni/android_view_KeyEvent.h"
#include "../../core/jni/android_view_MotionEvent.h"
#include "../../core/jni/android_view_InputChannel.h"
+#include "com_android_server_PowerManagerService.h"
namespace android {
@@ -107,16 +108,6 @@
LAST_SYSTEM_WINDOW = 2999,
};
-enum {
- POWER_MANAGER_OTHER_EVENT = 0,
- POWER_MANAGER_CHEEK_EVENT = 1,
- POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
- // up events or LONG_TOUCH events.
- POWER_MANAGER_LONG_TOUCH_EVENT = 3,
- POWER_MANAGER_TOUCH_UP_EVENT = 4,
- POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
-};
-
// Delay between reporting long touch events to the power manager.
const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
@@ -133,20 +124,16 @@
static struct {
jclass clazz;
- jmethodID isScreenOn;
- jmethodID isScreenBright;
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
jmethodID notifyInputChannelANR;
jmethodID notifyInputChannelRecoveredFromANR;
jmethodID notifyANR;
- jmethodID virtualKeyFeedback;
+ jmethodID virtualKeyDownFeedback;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptKeyBeforeDispatching;
jmethodID checkInjectEventsPermission;
- jmethodID goToSleep;
- jmethodID pokeUserActivity;
jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
@@ -228,9 +215,7 @@
virtual bool getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation);
- virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t flags, int32_t keyCode,
- int32_t scanCode, int32_t metaState, nsecs_t downTime);
+ virtual void virtualKeyDownFeedback();
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
@@ -321,7 +306,7 @@
int32_t mDisplayWidth, mDisplayHeight;
int32_t mDisplayOrientation;
- // Callbacks.
+ // Power manager interactions.
bool isScreenOn();
bool isScreenBright();
@@ -369,9 +354,9 @@
void releaseTouchedWindowLd();
- int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t waitForTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
- int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
bool interceptKeyBeforeDispatching(const InputTarget& target,
@@ -391,6 +376,7 @@
}
static bool isAppSwitchKey(int32_t keyCode);
+ static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
};
@@ -419,7 +405,37 @@
}
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
- return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
+ // Special keys that the WindowManagerPolicy might care about.
+ switch (keyCode) {
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_POWER:
+ case AKEYCODE_CALL:
+ case AKEYCODE_HOME:
+ case AKEYCODE_MENU:
+ case AKEYCODE_SEARCH:
+ // media keys
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ return true;
+ default:
+ // We need to pass all keys to the policy in the following cases:
+ // - screen is off
+ // - keyguard is visible
+ // - policy is performing key chording
+ //return ! isScreenOn || keyguardVisible || chording;
+ return true; // XXX stubbed out for now
+ }
}
bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -546,39 +562,22 @@
}
bool NativeInputManager::isScreenOn() {
- JNIEnv* env = jniEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "isScreenOn")) {
- return true;
- }
- return result;
+ return android_server_PowerManagerService_isScreenOn();
}
bool NativeInputManager::isScreenBright() {
- JNIEnv* env = jniEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
- if (checkAndClearExceptionFromCallback(env, "isScreenBright")) {
- return true;
- }
- return result;
+ return android_server_PowerManagerService_isScreenBright();
}
-void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t flags, int32_t keyCode,
- int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+void NativeInputManager::virtualKeyDownFeedback() {
#if DEBUG_INPUT_READER_POLICY
- LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, "
- "scanCode=%d, metaState=%d, downTime=%lld",
- when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
+ LOGD("virtualKeyDownFeedback");
#endif
JNIEnv* env = jniEnv();
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
- when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
- checkAndClearExceptionFromCallback(env, "virtualKeyFeedback");
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback);
+ checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback");
}
int32_t NativeInputManager::interceptKey(nsecs_t when,
@@ -593,16 +592,21 @@
const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
const int32_t WM_ACTION_GO_TO_SLEEP = 4;
- JNIEnv* env = jniEnv();
-
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
- jint wmActions = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeQueueing,
- deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
- wmActions = 0;
+ jint wmActions = 0;
+ if (isPolicyKey(keyCode, isScreenOn)) {
+ JNIEnv* env = jniEnv();
+
+ wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
+ when, keyCode, down, policyFlags, isScreenOn);
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+ wmActions = 0;
+ }
+ } else {
+ wmActions = WM_ACTION_PASS_TO_USER;
}
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
@@ -617,8 +621,7 @@
}
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
- checkAndClearExceptionFromCallback(env, "goToSleep");
+ android_server_PowerManagerService_goToSleep(when);
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
@@ -629,6 +632,8 @@
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
if (down && isAppSwitchKey(keyCode)) {
+ JNIEnv* env = jniEnv();
+
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
@@ -1531,11 +1536,13 @@
windowType = focusedWindow->layoutParamsType;
} // release lock
- const InputTarget& target = outTargets.top();
- bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
- if (consumed) {
- outTargets.clear();
- return INPUT_EVENT_INJECTION_SUCCEEDED;
+ if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) {
+ const InputTarget& target = outTargets.top();
+ bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
+ if (consumed) {
+ outTargets.clear();
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+ }
}
pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1552,11 +1559,11 @@
switch (motionEvent->getNature()) {
case INPUT_EVENT_NATURE_TRACKBALL:
- return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ return waitForTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
outTargets);
case INPUT_EVENT_NATURE_TOUCH:
- return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
outTargets);
default:
@@ -1565,11 +1572,11 @@
}
}
-int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent,
+int32_t NativeInputManager::waitForTrackballEventTargets(MotionEvent* motionEvent,
uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) {
#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("identifyTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ LOGD("waitForTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
policyFlags, injectorPid, injectorUid);
#endif
@@ -1591,11 +1598,11 @@
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
-int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent,
+int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent,
uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) {
#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("identifyTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
policyFlags, injectorPid, injectorUid);
#endif
@@ -1642,8 +1649,8 @@
if (inputChannelObj) {
jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.interceptKeyBeforeDispatching,
- inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(),
- keyEvent->getAction() == KEY_EVENT_ACTION_DOWN,
+ inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getMetaState(),
keyEvent->getRepeatCount(), policyFlags);
bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
@@ -1665,10 +1672,7 @@
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
- JNIEnv* env = jniEnv();
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity,
- eventTime, eventType);
- checkAndClearExceptionFromCallback(env, "pokeUserActivity");
+ android_server_PowerManagerService_userActivity(eventTime, eventType);
}
void NativeInputManager::dumpDispatchStateLd() {
@@ -2082,12 +2086,6 @@
FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
- GET_METHOD_ID(gCallbacksClassInfo.isScreenOn, gCallbacksClassInfo.clazz,
- "isScreenOn", "()Z");
-
- GET_METHOD_ID(gCallbacksClassInfo.isScreenBright, gCallbacksClassInfo.clazz,
- "isScreenBright", "()Z");
-
GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
"notifyConfigurationChanged", "(JIII)V");
@@ -2106,24 +2104,18 @@
GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
"notifyANR", "(Ljava/lang/Object;)J");
- GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
- "virtualKeyFeedback", "(JIIIIIIJ)V");
+ GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz,
+ "virtualKeyDownFeedback", "()V");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
- "interceptKeyBeforeQueueing", "(IIIIIIJZ)I");
+ "interceptKeyBeforeQueueing", "(JIZIZ)I");
GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
- "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z");
+ "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z");
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");
- GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz,
- "goToSleep", "(J)V");
-
- GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz,
- "pokeUserActivity", "(JI)V");
-
GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
"notifyAppSwitchComing", "()V");
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
deleted file mode 100644
index f9e3585..0000000
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Input"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include <utils/misc.h>
-#include <utils/Log.h>
-
-#include <ui/EventHub.h>
-#include <utils/threads.h>
-
-#include <stdio.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct input_offsets_t
-{
- jfieldID mMinValue;
- jfieldID mMaxValue;
- jfieldID mFlat;
- jfieldID mFuzz;
-
- jfieldID mDeviceId;
- jfieldID mType;
- jfieldID mScancode;
- jfieldID mKeycode;
- jfieldID mFlags;
- jfieldID mValue;
- jfieldID mWhen;
-} gInputOffsets;
-
-// ----------------------------------------------------------------------------
-
-static Mutex gLock;
-static sp<EventHub> gHub;
-
-static jboolean
-android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
- jobject event)
-{
- gLock.lock();
- sp<EventHub> hub = gHub;
- if (hub == NULL) {
- hub = new EventHub;
- gHub = hub;
- }
- gLock.unlock();
-
- int32_t deviceId;
- int32_t type;
- int32_t scancode, keycode;
- uint32_t flags;
- int32_t value;
- nsecs_t when;
- bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
- &flags, &value, &when);
-
- env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
- env->SetIntField(event, gInputOffsets.mType, (jint)type);
- env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
- env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
- env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
- env->SetIntField(event, gInputOffsets.mValue, value);
- env->SetLongField(event, gInputOffsets.mWhen,
- (jlong)(nanoseconds_to_milliseconds(when)));
-
- return res;
-}
-
-static jint
-android_server_KeyInputQueue_getDeviceClasses(JNIEnv* env, jobject clazz,
- jint deviceId)
-{
- jint classes = 0;
- gLock.lock();
- if (gHub != NULL) classes = gHub->getDeviceClasses(deviceId);
- gLock.unlock();
- return classes;
-}
-
-static jstring
-android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz,
- jint deviceId)
-{
- String8 name;
- gLock.lock();
- if (gHub != NULL) name = gHub->getDeviceName(deviceId);
- gLock.unlock();
-
- if (name.size() > 0) {
- return env->NewStringUTF(name.string());
- }
- return NULL;
-}
-
-static void
-android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz,
- jstring deviceName)
-{
- gLock.lock();
- sp<EventHub> hub = gHub;
- if (hub == NULL) {
- hub = new EventHub;
- gHub = hub;
- }
- gLock.unlock();
-
- const char* nameStr = env->GetStringUTFChars(deviceName, NULL);
- gHub->addExcludedDevice(nameStr);
- env->ReleaseStringUTFChars(deviceName, nameStr);
-}
-
-static jboolean
-android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz,
- jint deviceId, jint axis,
- jobject info)
-{
- int32_t minValue, maxValue, flat, fuzz;
- int res = -1;
- gLock.lock();
- if (gHub != NULL) {
- res = gHub->getAbsoluteInfo(deviceId, axis,
- &minValue, &maxValue, &flat, &fuzz);
- }
- gLock.unlock();
-
- if (res < 0) return JNI_FALSE;
-
- env->SetIntField(info, gInputOffsets.mMinValue, (jint)minValue);
- env->SetIntField(info, gInputOffsets.mMaxValue, (jint)maxValue);
- env->SetIntField(info, gInputOffsets.mFlat, (jint)flat);
- env->SetIntField(info, gInputOffsets.mFuzz, (jint)fuzz);
- return JNI_TRUE;
-}
-
-static jint
-android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getSwitchState(-1, -1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getSwitchState(deviceId, -1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getScanCodeState(0, -1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getScanCodeState(deviceId, -1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getKeyCodeState(0, -1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getKeyCodeState(deviceId,-1, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_scancodeToKeycode(JNIEnv* env, jobject clazz,
- jint deviceId, jint scancode)
-{
- jint res = 0;
- gLock.lock();
- if (gHub != NULL) {
- int32_t keycode;
- uint32_t flags;
- gHub->scancodeToKeycode(deviceId, scancode, &keycode, &flags);
- res = keycode;
- }
- gLock.unlock();
-
- return res;
-}
-
-static jboolean
-android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz,
- jintArray keyCodes, jbooleanArray outFlags)
-{
- jboolean ret = JNI_FALSE;
-
- int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
- uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
- jsize numCodes = env->GetArrayLength(keyCodes);
- if (numCodes == env->GetArrayLength(outFlags)) {
- gLock.lock();
- if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags);
- gLock.unlock();
- }
-
- env->ReleaseBooleanArrayElements(outFlags, flags, 0);
- env->ReleaseIntArrayElements(keyCodes, codes, 0);
- return ret;
-}
-
-// ----------------------------------------------------------------------------
-
-/*
- * JNI registration.
- */
-static JNINativeMethod gInputMethods[] = {
- /* name, signature, funcPtr */
- { "readEvent", "(Landroid/view/RawInputEvent;)Z",
- (void*) android_server_KeyInputQueue_readEvent },
- { "getDeviceClasses", "(I)I",
- (void*) android_server_KeyInputQueue_getDeviceClasses },
- { "getDeviceName", "(I)Ljava/lang/String;",
- (void*) android_server_KeyInputQueue_getDeviceName },
- { "addExcludedDevice", "(Ljava/lang/String;)V",
- (void*) android_server_KeyInputQueue_addExcludedDevice },
- { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z",
- (void*) android_server_KeyInputQueue_getAbsoluteInfo },
- { "getSwitchState", "(I)I",
- (void*) android_server_KeyInputQueue_getSwitchState },
- { "getSwitchState", "(II)I",
- (void*) android_server_KeyInputQueue_getSwitchStateDevice },
- { "nativeGetScancodeState", "(I)I",
- (void*) android_server_KeyInputQueue_getScancodeState },
- { "nativeGetScancodeState", "(II)I",
- (void*) android_server_KeyInputQueue_getScancodeStateDevice },
- { "nativeGetKeycodeState", "(I)I",
- (void*) android_server_KeyInputQueue_getKeycodeState },
- { "nativeGetKeycodeState", "(II)I",
- (void*) android_server_KeyInputQueue_getKeycodeStateDevice },
- { "hasKeys", "([I[Z)Z",
- (void*) android_server_KeyInputQueue_hasKeys },
- { "scancodeToKeycode", "(II)I",
- (void*) android_server_KeyInputQueue_scancodeToKeycode },
-};
-
-int register_android_server_KeyInputQueue(JNIEnv* env)
-{
- jclass input = env->FindClass("com/android/server/KeyInputQueue");
- LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/KeyInputQueue");
- int res = jniRegisterNativeMethods(env, "com/android/server/KeyInputQueue",
- gInputMethods, NELEM(gInputMethods));
-
- jclass absoluteInfo = env->FindClass("com/android/server/InputDevice$AbsoluteInfo");
- LOG_FATAL_IF(absoluteInfo == NULL, "Unable to find class com/android/server/InputDevice$AbsoluteInfo");
-
- gInputOffsets.mMinValue
- = env->GetFieldID(absoluteInfo, "minValue", "I");
- LOG_FATAL_IF(gInputOffsets.mMinValue == NULL, "Unable to find InputDevice.AbsoluteInfo.minValue");
-
- gInputOffsets.mMaxValue
- = env->GetFieldID(absoluteInfo, "maxValue", "I");
- LOG_FATAL_IF(gInputOffsets.mMaxValue == NULL, "Unable to find InputDevice.AbsoluteInfo.maxValue");
-
- gInputOffsets.mFlat
- = env->GetFieldID(absoluteInfo, "flat", "I");
- LOG_FATAL_IF(gInputOffsets.mFlat == NULL, "Unable to find InputDevice.AbsoluteInfo.flat");
-
- gInputOffsets.mFuzz
- = env->GetFieldID(absoluteInfo, "fuzz", "I");
- LOG_FATAL_IF(gInputOffsets.mFuzz == NULL, "Unable to find InputDevice.AbsoluteInfo.fuzz");
-
- jclass inputEvent = env->FindClass("android/view/RawInputEvent");
- LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent");
-
- gInputOffsets.mDeviceId
- = env->GetFieldID(inputEvent, "deviceId", "I");
- LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId");
-
- gInputOffsets.mType
- = env->GetFieldID(inputEvent, "type", "I");
- LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type");
-
- gInputOffsets.mScancode
- = env->GetFieldID(inputEvent, "scancode", "I");
- LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode");
-
- gInputOffsets.mKeycode
- = env->GetFieldID(inputEvent, "keycode", "I");
- LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode");
-
- gInputOffsets.mFlags
- = env->GetFieldID(inputEvent, "flags", "I");
- LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags");
-
- gInputOffsets.mValue
- = env->GetFieldID(inputEvent, "value", "I");
- LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value");
-
- gInputOffsets.mWhen
- = env->GetFieldID(inputEvent, "when", "J");
- LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when");
-
- return res;
-}
-
-}; // namespace android
-
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
new file mode 100644
index 0000000..b80dbc5
--- /dev/null
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "PowerManagerService-JNI"
+
+//#define LOG_NDEBUG 0
+
+#include "JNIHelp.h"
+#include "jni.h"
+#include <limits.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "com_android_server_PowerManagerService.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jmethodID goToSleep;
+ jmethodID userActivity;
+} gPowerManagerServiceClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static jobject gPowerManagerServiceObj;
+
+static Mutex gPowerManagerLock;
+static bool gScreenOn;
+static bool gScreenBright;
+
+// ----------------------------------------------------------------------------
+
+static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}
+
+bool android_server_PowerManagerService_isScreenOn() {
+ AutoMutex _l(gPowerManagerLock);
+ return gScreenOn;
+}
+
+bool android_server_PowerManagerService_isScreenBright() {
+ AutoMutex _l(gPowerManagerLock);
+ return gScreenBright;
+}
+
+void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
+ if (gPowerManagerServiceObj) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
+ nanoseconds_to_milliseconds(eventTime), false, eventType, false);
+ checkAndClearExceptionFromCallback(env, "userActivity");
+ }
+}
+
+void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) {
+ if (gPowerManagerServiceObj) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep,
+ nanoseconds_to_milliseconds(eventTime));
+ checkAndClearExceptionFromCallback(env, "goToSleep");
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void android_server_PowerManagerService_nativeInit(JNIEnv* env, jobject obj) {
+ gPowerManagerServiceObj = env->NewGlobalRef(obj);
+}
+
+static void android_server_PowerManagerService_nativeSetPowerState(JNIEnv* env,
+ jobject serviceObj, jboolean screenOn, jboolean screenBright) {
+ AutoMutex _l(gPowerManagerLock);
+ gScreenOn = screenOn;
+ gScreenBright = screenBright;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gPowerManagerServiceMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit", "()V",
+ (void*) android_server_PowerManagerService_nativeInit },
+ { "nativeSetPowerState", "(ZZ)V",
+ (void*) android_server_PowerManagerService_nativeSetPowerState },
+};
+
+#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_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+#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_server_PowerManagerService(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/PowerManagerService",
+ gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ // Callbacks
+
+ FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService");
+
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz,
+ "goToSleep", "(J)V");
+
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
+ "userActivity", "(JZIZ)V");
+
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
new file mode 100644
index 0000000..9b05f38
--- /dev/null
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -0,0 +1,42 @@
+/*
+ * 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_SERVER_POWER_MANAGER_SERVICE_H
+#define _ANDROID_SERVER_POWER_MANAGER_SERVICE_H
+
+#include "JNIHelp.h"
+#include "jni.h"
+
+namespace android {
+
+enum {
+ POWER_MANAGER_OTHER_EVENT = 0,
+ POWER_MANAGER_CHEEK_EVENT = 1,
+ POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+ // up events or LONG_TOUCH events.
+ POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+ POWER_MANAGER_TOUCH_UP_EVENT = 4,
+ POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+};
+
+extern bool android_server_PowerManagerService_isScreenOn();
+extern bool android_server_PowerManagerService_isScreenBright();
+extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
+extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime);
+
+} // namespace android
+
+#endif // _ANDROID_SERVER_POWER_MANAGER_SERVICE_H
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index a1a6838..1a2d8b6 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -6,9 +6,9 @@
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
-int register_android_server_KeyInputQueue(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
+int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SensorService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
@@ -28,7 +28,7 @@
}
LOG_ASSERT(env, "Could not retrieve the env!");
- register_android_server_KeyInputQueue(env);
+ register_android_server_PowerManagerService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
diff --git a/libs/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
similarity index 100%
rename from libs/surfaceflinger/Android.mk
rename to services/surfaceflinger/Android.mk
diff --git a/libs/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
similarity index 100%
rename from libs/surfaceflinger/Barrier.h
rename to services/surfaceflinger/Barrier.h
diff --git a/libs/surfaceflinger/BlurFilter.cpp b/services/surfaceflinger/BlurFilter.cpp
similarity index 100%
rename from libs/surfaceflinger/BlurFilter.cpp
rename to services/surfaceflinger/BlurFilter.cpp
diff --git a/libs/surfaceflinger/BlurFilter.h b/services/surfaceflinger/BlurFilter.h
similarity index 100%
rename from libs/surfaceflinger/BlurFilter.h
rename to services/surfaceflinger/BlurFilter.h
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
similarity index 100%
rename from libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
rename to services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
similarity index 100%
rename from libs/surfaceflinger/DisplayHardware/DisplayHardware.h
rename to services/surfaceflinger/DisplayHardware/DisplayHardware.h
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
similarity index 100%
rename from libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
rename to services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
similarity index 100%
rename from libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
rename to services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
diff --git a/libs/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
similarity index 100%
rename from libs/surfaceflinger/GLExtensions.cpp
rename to services/surfaceflinger/GLExtensions.cpp
diff --git a/libs/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
similarity index 100%
rename from libs/surfaceflinger/GLExtensions.h
rename to services/surfaceflinger/GLExtensions.h
diff --git a/libs/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
similarity index 100%
rename from libs/surfaceflinger/Layer.cpp
rename to services/surfaceflinger/Layer.cpp
diff --git a/libs/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
similarity index 100%
rename from libs/surfaceflinger/Layer.h
rename to services/surfaceflinger/Layer.h
diff --git a/libs/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
similarity index 100%
rename from libs/surfaceflinger/LayerBase.cpp
rename to services/surfaceflinger/LayerBase.cpp
diff --git a/libs/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
similarity index 100%
rename from libs/surfaceflinger/LayerBase.h
rename to services/surfaceflinger/LayerBase.h
diff --git a/libs/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
similarity index 100%
rename from libs/surfaceflinger/LayerBlur.cpp
rename to services/surfaceflinger/LayerBlur.cpp
diff --git a/libs/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h
similarity index 100%
rename from libs/surfaceflinger/LayerBlur.h
rename to services/surfaceflinger/LayerBlur.h
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
similarity index 100%
rename from libs/surfaceflinger/LayerBuffer.cpp
rename to services/surfaceflinger/LayerBuffer.cpp
diff --git a/libs/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
similarity index 100%
rename from libs/surfaceflinger/LayerBuffer.h
rename to services/surfaceflinger/LayerBuffer.h
diff --git a/libs/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
similarity index 100%
rename from libs/surfaceflinger/LayerDim.cpp
rename to services/surfaceflinger/LayerDim.cpp
diff --git a/libs/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h
similarity index 100%
rename from libs/surfaceflinger/LayerDim.h
rename to services/surfaceflinger/LayerDim.h
diff --git a/libs/surfaceflinger/MODULE_LICENSE_APACHE2 b/services/surfaceflinger/MODULE_LICENSE_APACHE2
similarity index 100%
rename from libs/surfaceflinger/MODULE_LICENSE_APACHE2
rename to services/surfaceflinger/MODULE_LICENSE_APACHE2
diff --git a/libs/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
similarity index 100%
rename from libs/surfaceflinger/MessageQueue.cpp
rename to services/surfaceflinger/MessageQueue.cpp
diff --git a/libs/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
similarity index 100%
rename from libs/surfaceflinger/MessageQueue.h
rename to services/surfaceflinger/MessageQueue.h
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
similarity index 98%
rename from libs/surfaceflinger/SurfaceFlinger.cpp
rename to services/surfaceflinger/SurfaceFlinger.cpp
index 68e8f19..3167c4c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -63,20 +63,6 @@
#define DISPLAY_COUNT 1
namespace android {
-
-// ---------------------------------------------------------------------------
-
-void SurfaceFlinger::instantiate() {
- defaultServiceManager()->addService(
- String16("SurfaceFlinger"), new SurfaceFlinger());
-}
-
-void SurfaceFlinger::shutdown() {
- // we should unregister here, but not really because
- // when (if) the service manager goes away, all the services
- // it has a reference to will leave too.
-}
-
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
similarity index 98%
rename from libs/surfaceflinger/SurfaceFlinger.h
rename to services/surfaceflinger/SurfaceFlinger.h
index 0bfc170..8821e5c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,6 +29,7 @@
#include <binder/IMemory.h>
#include <binder/Permission.h>
+#include <binder/BinderService.h>
#include <ui/PixelFormat.h>
#include <surfaceflinger/ISurfaceComposer.h>
@@ -167,11 +168,13 @@
eTraversalNeeded = 0x02
};
-class SurfaceFlinger : public BnSurfaceComposer, protected Thread
+class SurfaceFlinger :
+ public BinderService<SurfaceFlinger>,
+ public BnSurfaceComposer,
+ protected Thread
{
public:
- static void instantiate();
- static void shutdown();
+ static char const* getServiceName() { return "SurfaceFlinger"; }
SurfaceFlinger();
virtual ~SurfaceFlinger();
diff --git a/libs/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
similarity index 99%
rename from libs/surfaceflinger/TextureManager.cpp
rename to services/surfaceflinger/TextureManager.cpp
index 6526032..3b326df 100644
--- a/libs/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -107,7 +107,6 @@
{
switch (format) {
case HAL_PIXEL_FORMAT_YV12:
- case HAL_PIXEL_FORMAT_YV16:
return true;
}
return false;
@@ -118,7 +117,6 @@
switch (format) {
// supported YUV formats
case HAL_PIXEL_FORMAT_YV12:
- case HAL_PIXEL_FORMAT_YV16:
// Legacy/deprecated YUV formats
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
diff --git a/libs/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
similarity index 100%
rename from libs/surfaceflinger/TextureManager.h
rename to services/surfaceflinger/TextureManager.h
diff --git a/libs/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
similarity index 100%
rename from libs/surfaceflinger/Transform.cpp
rename to services/surfaceflinger/Transform.cpp
diff --git a/libs/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
similarity index 100%
rename from libs/surfaceflinger/Transform.h
rename to services/surfaceflinger/Transform.h
diff --git a/libs/surfaceflinger/clz.cpp b/services/surfaceflinger/clz.cpp
similarity index 100%
rename from libs/surfaceflinger/clz.cpp
rename to services/surfaceflinger/clz.cpp
diff --git a/libs/surfaceflinger/clz.h b/services/surfaceflinger/clz.h
similarity index 100%
rename from libs/surfaceflinger/clz.h
rename to services/surfaceflinger/clz.h
diff --git a/libs/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
similarity index 100%
rename from libs/surfaceflinger/tests/Android.mk
rename to services/surfaceflinger/tests/Android.mk
diff --git a/libs/surfaceflinger/tests/overlays/Android.mk b/services/surfaceflinger/tests/overlays/Android.mk
similarity index 100%
rename from libs/surfaceflinger/tests/overlays/Android.mk
rename to services/surfaceflinger/tests/overlays/Android.mk
diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/services/surfaceflinger/tests/overlays/overlays.cpp
similarity index 100%
rename from libs/surfaceflinger/tests/overlays/overlays.cpp
rename to services/surfaceflinger/tests/overlays/overlays.cpp
diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk
similarity index 100%
rename from libs/surfaceflinger/tests/resize/Android.mk
rename to services/surfaceflinger/tests/resize/Android.mk
diff --git a/libs/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
similarity index 100%
rename from libs/surfaceflinger/tests/resize/resize.cpp
rename to services/surfaceflinger/tests/resize/resize.cpp
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index e71fe2e..06807c6 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.app.PendingIntent;
+import android.net.NetworkProperties;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -26,6 +27,10 @@
import android.text.TextUtils;
import android.util.Log;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.ArrayList;
/**
@@ -191,6 +196,9 @@
/** indication of our availability (preconditions to trysetupData are met) **/
protected boolean mAvailability = false;
+ /** all our network properties (dns, gateway, ip, etc) */
+ protected NetworkProperties mNetworkProperties;
+
/**
* Default constructor
*/
@@ -424,6 +432,15 @@
protected abstract void setState(State s);
+ protected NetworkProperties getNetworkProperties(String apnType) {
+ int id = apnTypeToId(apnType);
+ if (isApnIdEnabled(id)) {
+ return mNetworkProperties;
+ } else {
+ return null;
+ }
+ }
+
// tell all active apns of the current condition
protected void notifyDataConnection(String reason) {
for (int id = 0; id < APN_NUM_TYPES; id++) {
@@ -668,5 +685,43 @@
}
}
+ protected NetworkProperties makeNetworkProperties(DataConnection connection) {
+ NetworkProperties properties = new NetworkProperties();
+ try {
+ properties.setInterface(NetworkInterface.getByName(connection.getInterface()));
+ } catch (SocketException e) {
+ Log.e(LOG_TAG, "SocketException creating NetworkInterface: " + e);
+ } catch (NullPointerException e) {
+ Log.e(LOG_TAG, "NPE trying to makeNetworkProperties: " + e);
+ }
+ try {
+ properties.addAddress(InetAddress.getByName(connection.getIpAddress()));
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "UnknownHostException setting IpAddress: " + e);
+ } catch (SecurityException e) {
+ Log.e(LOG_TAG, "SecurityException setting IpAddress: " + e);
+ }
+
+ try {
+ properties.setGateway(InetAddress.getByName(connection.getGatewayAddress()));
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "UnknownHostException setting GatewayAddress: " + e);
+ } catch (SecurityException e) {
+ Log.e(LOG_TAG, "SecurityException setting GatewayAddress: " + e);
+ }
+
+ try {
+ String[] dnsStrings = connection.getDnsServers();
+ for (int i = 0; i<dnsStrings.length; i++) {
+ properties.addDns(InetAddress.getByName(dnsStrings[i]));
+ }
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "UnknownHostException setting DnsAddress: " + e);
+ } catch (SecurityException e) {
+ Log.e(LOG_TAG, "SecurityException setting DnsAddress: " + e);
+ }
+ // TODO - set Proxy info
+ return properties;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 0646101..382c19f 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.net.NetworkProperties;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -107,13 +108,17 @@
// use apnType as the key to which connection we're talking about.
// pass apnType back up to fetch particular for this one.
TelephonyManager telephony = TelephonyManager.getDefault();
+ NetworkProperties networkProperties = null;
+ if (state == Phone.DataState.CONNECTED) {
+ networkProperties = sender.getNetworkProperties(apnType);
+ }
try {
mRegistry.notifyDataConnection(
convertDataState(state),
sender.isDataConnectivityPossible(), reason,
sender.getActiveApn(),
apnType,
- sender.getInterfaceName(null),
+ networkProperties,
((telephony!=null) ? telephony.getNetworkType() :
TelephonyManager.NETWORK_TYPE_UNKNOWN));
} catch (RemoteException ex) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 79c2b40..f7b70ee 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.content.Intent;
+import android.net.NetworkProperties;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -32,7 +33,8 @@
void notifyCallForwardingChanged(boolean cfi);
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String apnType, String interfaceName, int networkType);
+ String reason, String apn, String apnType, in NetworkProperties networkProperties,
+ int networkType);
void notifyDataConnectionFailed(String reason, String apnType);
void notifyCellLocation(in Bundle cellLocation);
}
diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 9f8e57f..48257cc 100644
--- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -62,8 +62,8 @@
logd("GET_RECORD_SIZE Size " + recordSize[0] +
" total " + recordSize[1] +
" #record " + recordSize[2]);
- mLock.notifyAll();
}
+ mLock.notifyAll();
}
break;
case EVENT_UPDATE_DONE:
diff --git a/telephony/java/com/android/internal/telephony/MccTable.java b/telephony/java/com/android/internal/telephony/MccTable.java
index b73c2f7..5dd29af 100644
--- a/telephony/java/com/android/internal/telephony/MccTable.java
+++ b/telephony/java/com/android/internal/telephony/MccTable.java
@@ -23,346 +23,13 @@
import android.net.wifi.WifiManager;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
-import java.util.Arrays;
-
-/**
- * The table below is built from two resources:
- *
- * 1) ITU "Mobile Network Code (MNC) for the international
- * identification plan for mobile terminals and mobile users"
- * which is available as an annex to the ITU operational bulletin
- * available here: http://www.itu.int/itu-t/bulletin/annex.html
- *
- * 2) The ISO 3166 country codes list, available here:
- * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
- *
- * This table was verified (28 Aug 2009) against
- * http://en.wikipedia.org/wiki/List_of_mobile_country_codes with the
- * only unresolved discrepancy being that this list has an extra entry
- * (461) for China.
- *
- * TODO: Complete the mappings for timezones and language/locale codes.
- *
- * The actual table data used in the Java code is generated from the
- * below Python code for efficiency. The information is expected to
- * be static, but if changes are required, the table in the python
- * code can be modified and the trailing code run to re-generate the
- * tables that are to be used by Java.
-
-mcc_table = [
- (202, 'gr', 2, 'Greece'),
- (204, 'nl', 2, 'Europe/Amsterdam', 'nl', 13, 'Netherlands (Kingdom of the)'),
- (206, 'be', 2, 'Belgium'),
- (208, 'fr', 2, 'Europe/Paris', 'fr', 'France'),
- (212, 'mc', 2, 'Monaco (Principality of)'),
- (213, 'ad', 2, 'Andorra (Principality of)'),
- (214, 'es', 2, 'Europe/Madrid', 'es', 'Spain'),
- (216, 'hu', 2, 'Hungary (Republic of)'),
- (218, 'ba', 2, 'Bosnia and Herzegovina'),
- (219, 'hr', 2, 'Croatia (Republic of)'),
- (220, 'rs', 2, 'Serbia and Montenegro'),
- (222, 'it', 2, 'Europe/Rome', 'it', 'Italy'),
- (225, 'va', 2, 'Europe/Rome', 'it', 'Vatican City State'),
- (226, 'ro', 2, 'Romania'),
- (228, 'ch', 2, 'Europe/Zurich', 'de', 'Switzerland (Confederation of)'),
- (230, 'cz', 2, 'Europe/Prague', 'cs', 13, 'Czech Republic'),
- (231, 'sk', 2, 'Slovak Republic'),
- (232, 'at', 2, 'Europe/Vienna', 'de', 13, 'Austria'),
- (234, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
- (235, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
- (238, 'dk', 2, 'Denmark'),
- (240, 'se', 2, 'Sweden'),
- (242, 'no', 2, 'Norway'),
- (244, 'fi', 2, 'Finland'),
- (246, 'lt', 2, 'Lithuania (Republic of)'),
- (247, 'lv', 2, 'Latvia (Republic of)'),
- (248, 'ee', 2, 'Estonia (Republic of)'),
- (250, 'ru', 2, 'Russian Federation'),
- (255, 'ua', 2, 'Ukraine'),
- (257, 'by', 2, 'Belarus (Republic of)'),
- (259, 'md', 2, 'Moldova (Republic of)'),
- (260, 'pl', 2, 'Europe/Warsaw', 'Poland (Republic of)'),
- (262, 'de', 2, 'Europe/Berlin', 'de', 13, 'Germany (Federal Republic of)'),
- (266, 'gi', 2, 'Gibraltar'),
- (268, 'pt', 2, 'Portugal'),
- (270, 'lu', 2, 'Luxembourg'),
- (272, 'ie', 2, 'Europe/Dublin', 'en', 'Ireland'),
- (274, 'is', 2, 'Iceland'),
- (276, 'al', 2, 'Albania (Republic of)'),
- (278, 'mt', 2, 'Malta'),
- (280, 'cy', 2, 'Cyprus (Republic of)'),
- (282, 'ge', 2, 'Georgia'),
- (283, 'am', 2, 'Armenia (Republic of)'),
- (284, 'bg', 2, 'Bulgaria (Republic of)'),
- (286, 'tr', 2, 'Turkey'),
- (288, 'fo', 2, 'Faroe Islands'),
- (289, 'ge', 2, 'Abkhazia (Georgia)'),
- (290, 'gl', 2, 'Greenland (Denmark)'),
- (292, 'sm', 2, 'San Marino (Republic of)'),
- (293, 'sl', 2, 'Slovenia (Republic of)'),
- (294, 'mk', 2, 'The Former Yugoslav Republic of Macedonia'),
- (295, 'li', 2, 'Liechtenstein (Principality of)'),
- (297, 'me', 2, 'Montenegro (Republic of)'),
- (302, 'ca', 3, '', '', 11, 'Canada'),
- (308, 'pm', 2, 'Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)'),
- (310, 'us', 3, '', 'en', 11, 'United States of America'),
- (311, 'us', 3, '', 'en', 11, 'United States of America'),
- (312, 'us', 3, '', 'en', 11, 'United States of America'),
- (313, 'us', 3, '', 'en', 11, 'United States of America'),
- (314, 'us', 3, '', 'en', 11, 'United States of America'),
- (315, 'us', 3, '', 'en', 11, 'United States of America'),
- (316, 'us', 3, '', 'en', 11, 'United States of America'),
- (330, 'pr', 2, 'Puerto Rico'),
- (332, 'vi', 2, 'United States Virgin Islands'),
- (334, 'mx', 3, 'Mexico'),
- (338, 'jm', 3, 'Jamaica'),
- (340, 'gp', 2, 'Guadeloupe (French Department of)'),
- (342, 'bb', 3, 'Barbados'),
- (344, 'ag', 3, 'Antigua and Barbuda'),
- (346, 'ky', 3, 'Cayman Islands'),
- (348, 'vg', 3, 'British Virgin Islands'),
- (350, 'bm', 2, 'Bermuda'),
- (352, 'gd', 2, 'Grenada'),
- (354, 'ms', 2, 'Montserrat'),
- (356, 'kn', 2, 'Saint Kitts and Nevis'),
- (358, 'lc', 2, 'Saint Lucia'),
- (360, 'vc', 2, 'Saint Vincent and the Grenadines'),
- (362, 'nl', 2, 'Netherlands Antilles'),
- (363, 'aw', 2, 'Aruba'),
- (364, 'bs', 2, 'Bahamas (Commonwealth of the)'),
- (365, 'ai', 3, 'Anguilla'),
- (366, 'dm', 2, 'Dominica (Commonwealth of)'),
- (368, 'cu', 2, 'Cuba'),
- (370, 'do', 2, 'Dominican Republic'),
- (372, 'ht', 2, 'Haiti (Republic of)'),
- (374, 'tt', 2, 'Trinidad and Tobago'),
- (376, 'tc', 2, 'Turks and Caicos Islands'),
- (400, 'az', 2, 'Azerbaijani Republic'),
- (401, 'kz', 2, 'Kazakhstan (Republic of)'),
- (402, 'bt', 2, 'Bhutan (Kingdom of)'),
- (404, 'in', 2, 'India (Republic of)'),
- (405, 'in', 2, 'India (Republic of)'),
- (410, 'pk', 2, 'Pakistan (Islamic Republic of)'),
- (412, 'af', 2, 'Afghanistan'),
- (413, 'lk', 2, 'Sri Lanka (Democratic Socialist Republic of)'),
- (414, 'mm', 2, 'Myanmar (Union of)'),
- (415, 'lb', 2, 'Lebanon'),
- (416, 'jo', 2, 'Jordan (Hashemite Kingdom of)'),
- (417, 'sy', 2, 'Syrian Arab Republic'),
- (418, 'iq', 2, 'Iraq (Republic of)'),
- (419, 'kw', 2, 'Kuwait (State of)'),
- (420, 'sa', 2, 'Saudi Arabia (Kingdom of)'),
- (421, 'ye', 2, 'Yemen (Republic of)'),
- (422, 'om', 2, 'Oman (Sultanate of)'),
- (423, 'ps', 2, 'Palestine'),
- (424, 'ae', 2, 'United Arab Emirates'),
- (425, 'il', 2, 'Israel (State of)'),
- (426, 'bh', 2, 'Bahrain (Kingdom of)'),
- (427, 'qa', 2, 'Qatar (State of)'),
- (428, 'mn', 2, 'Mongolia'),
- (429, 'np', 2, 'Nepal'),
- (430, 'ae', 2, 'United Arab Emirates'),
- (431, 'ae', 2, 'United Arab Emirates'),
- (432, 'ir', 2, 'Iran (Islamic Republic of)'),
- (434, 'uz', 2, 'Uzbekistan (Republic of)'),
- (436, 'tj', 2, 'Tajikistan (Republic of)'),
- (437, 'kg', 2, 'Kyrgyz Republic'),
- (438, 'tm', 2, 'Turkmenistan'),
- (440, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
- (441, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
- (450, 'kr', 2, 'Korea (Republic of)'),
- (452, 'vn', 2, 'Viet Nam (Socialist Republic of)'),
- (454, 'hk', 2, '"Hong Kong, China"'),
- (455, 'mo', 2, '"Macao, China"'),
- (456, 'kh', 2, 'Cambodia (Kingdom of)'),
- (457, 'la', 2, "Lao People's Democratic Republic"),
- (460, 'cn', 2, "Asia/Beijing", 'zh', 13, "China (People's Republic of)"),
- (461, 'cn', 2, "Asia/Beijing", 'zh', 13, "China (People's Republic of)"),
- (466, 'tw', 2, "Taiwan (Republic of China)"),
- (467, 'kp', 2, "Democratic People's Republic of Korea"),
- (470, 'bd', 2, "Bangladesh (People's Republic of)"),
- (472, 'mv', 2, 'Maldives (Republic of)'),
- (502, 'my', 2, 'Malaysia'),
- (505, 'au', 2, 'Australia/Sydney', 'en', 11, 'Australia'),
- (510, 'id', 2, 'Indonesia (Republic of)'),
- (514, 'tl', 2, 'Democratic Republic of Timor-Leste'),
- (515, 'ph', 2, 'Philippines (Republic of the)'),
- (520, 'th', 2, 'Thailand'),
- (525, 'sg', 2, 'Asia/Singapore', 'en', 11, 'Singapore (Republic of)'),
- (528, 'bn', 2, 'Brunei Darussalam'),
- (530, 'nz', 2, 'Pacific/Auckland', 'en', 'New Zealand'),
- (534, 'mp', 2, 'Northern Mariana Islands (Commonwealth of the)'),
- (535, 'gu', 2, 'Guam'),
- (536, 'nr', 2, 'Nauru (Republic of)'),
- (537, 'pg', 2, 'Papua New Guinea'),
- (539, 'to', 2, 'Tonga (Kingdom of)'),
- (540, 'sb', 2, 'Solomon Islands'),
- (541, 'vu', 2, 'Vanuatu (Republic of)'),
- (542, 'fj', 2, 'Fiji (Republic of)'),
- (543, 'wf', 2, "Wallis and Futuna (Territoire franais d'outre-mer)"),
- (544, 'as', 2, 'American Samoa'),
- (545, 'ki', 2, 'Kiribati (Republic of)'),
- (546, 'nc', 2, "New Caledonia (Territoire franais d'outre-mer)"),
- (547, 'pf', 2, "French Polynesia (Territoire franais d'outre-mer)"),
- (548, 'ck', 2, 'Cook Islands'),
- (549, 'ws', 2, 'Samoa (Independent State of)'),
- (550, 'fm', 2, 'Micronesia (Federated States of)'),
- (551, 'mh', 2, 'Marshall Islands (Republic of the)'),
- (552, 'pw', 2, 'Palau (Republic of)'),
- (602, 'eg', 2, 'Egypt (Arab Republic of)'),
- (603, 'dz', 2, "Algeria (People's Democratic Republic of)"),
- (604, 'ma', 2, 'Morocco (Kingdom of)'),
- (605, 'tn', 2, 'Tunisia'),
- (606, 'ly', 2, "Libya (Socialist People's Libyan Arab Jamahiriya)"),
- (607, 'gm', 2, 'Gambia (Republic of the)'),
- (608, 'sn', 2, 'Senegal (Republic of)'),
- (609, 'mr', 2, 'Mauritania (Islamic Republic of)'),
- (610, 'ml', 2, 'Mali (Republic of)'),
- (611, 'gn', 2, 'Guinea (Republic of)'),
- (612, 'ci', 2, "Cte d'Ivoire (Republic of)"),
- (613, 'bf', 2, 'Burkina Faso'),
- (614, 'ne', 2, 'Niger (Republic of the)'),
- (615, 'tg', 2, 'Togolese Republic'),
- (616, 'bj', 2, 'Benin (Republic of)'),
- (617, 'mu', 2, 'Mauritius (Republic of)'),
- (618, 'lr', 2, 'Liberia (Republic of)'),
- (619, 'sl', 2, 'Sierra Leone'),
- (620, 'gh', 2, 'Ghana'),
- (621, 'ng', 2, 'Nigeria (Federal Republic of)'),
- (622, 'td', 2, 'Chad (Republic of)'),
- (623, 'cf', 2, 'Central African Republic'),
- (624, 'cm', 2, 'Cameroon (Republic of)'),
- (625, 'cv', 2, 'Cape Verde (Republic of)'),
- (626, 'st', 2, 'Sao Tome and Principe (Democratic Republic of)'),
- (627, 'gq', 2, 'Equatorial Guinea (Republic of)'),
- (628, 'ga', 2, 'Gabonese Republic'),
- (629, 'cg', 2, 'Congo (Republic of the)'),
- (630, 'cg', 2, 'Democratic Republic of the Congo'),
- (631, 'ao', 2, 'Angola (Republic of)'),
- (632, 'gw', 2, 'Guinea-Bissau (Republic of)'),
- (633, 'sc', 2, 'Seychelles (Republic of)'),
- (634, 'sd', 2, 'Sudan (Republic of the)'),
- (635, 'rw', 2, 'Rwanda (Republic of)'),
- (636, 'et', 2, 'Ethiopia (Federal Democratic Republic of)'),
- (637, 'so', 2, 'Somali Democratic Republic'),
- (638, 'dj', 2, 'Djibouti (Republic of)'),
- (639, 'ke', 2, 'Kenya (Republic of)'),
- (640, 'tz', 2, 'Tanzania (United Republic of)'),
- (641, 'ug', 2, 'Uganda (Republic of)'),
- (642, 'bi', 2, 'Burundi (Republic of)'),
- (643, 'mz', 2, 'Mozambique (Republic of)'),
- (645, 'zm', 2, 'Zambia (Republic of)'),
- (646, 'mg', 2, 'Madagascar (Republic of)'),
- (647, 're', 2, 'Reunion (French Department of)'),
- (648, 'zw', 2, 'Zimbabwe (Republic of)'),
- (649, 'na', 2, 'Namibia (Republic of)'),
- (650, 'mw', 2, 'Malawi'),
- (651, 'ls', 2, 'Lesotho (Kingdom of)'),
- (652, 'bw', 2, 'Botswana (Republic of)'),
- (653, 'sz', 2, 'Swaziland (Kingdom of)'),
- (654, 'km', 2, 'Comoros (Union of the)'),
- (655, 'za', 2, 'Africa/Johannesburg', 'en', 'South Africa (Republic of)'),
- (657, 'er', 2, 'Eritrea'),
- (702, 'bz', 2, 'Belize'),
- (704, 'gt', 2, 'Guatemala (Republic of)'),
- (706, 'sv', 2, 'El Salvador (Republic of)'),
- (708, 'hn', 3, 'Honduras (Republic of)'),
- (710, 'ni', 2, 'Nicaragua'),
- (712, 'cr', 2, 'Costa Rica'),
- (714, 'pa', 2, 'Panama (Republic of)'),
- (716, 'pe', 2, 'Peru'),
- (722, 'ar', 3, 'Argentine Republic'),
- (724, 'br', 2, 'Brazil (Federative Republic of)'),
- (730, 'cl', 2, 'Chile'),
- (732, 'co', 3, 'Colombia (Republic of)'),
- (734, 've', 2, 'Venezuela (Bolivarian Republic of)'),
- (736, 'bo', 2, 'Bolivia (Republic of)'),
- (738, 'gy', 2, 'Guyana'),
- (740, 'ec', 2, 'Ecuador'),
- (742, 'gf', 2, 'French Guiana (French Department of)'),
- (744, 'py', 2, 'Paraguay (Republic of)'),
- (746, 'sr', 2, 'Suriname (Republic of)'),
- (748, 'uy', 2, 'Uruguay (Eastern Republic of)'),
- (750, 'fk', 2, 'Falkland Islands (Malvinas)')]
-
-get_mcc = lambda elt: elt[0]
-get_iso = lambda elt: elt[1]
-get_sd = lambda elt: elt[2]
-get_tz = lambda elt: len(elt) > 4 and elt[3] or ''
-get_lang = lambda elt: len(elt) > 5 and elt[4] or ''
-get_wifi = lambda elt: len(elt) > 6 and elt[5] or 0
-
-mcc_codes = ['0x%04x' % get_mcc(elt) for elt in mcc_table]
-tz_set = sorted(x for x in set(get_tz(elt) for elt in mcc_table))
-lang_set = sorted(x for x in set(get_lang(elt) for elt in mcc_table))
-
-def mk_ind_code(elt):
- iso = get_iso(elt)
- iso_code = ((ord(iso[0]) << 8) | ord(iso[1])) & 0xFFFF # 16 bits
- wifi = get_wifi(elt) & 0x000F # 4 bits
- sd = get_sd(elt) & 0x0003 # 2 bits
- tz_ind = tz_set.index(get_tz(elt)) & 0x001F # 5 bits
- lang_ind = lang_set.index(get_lang(elt)) & 0x000F # 4 bits
- return (iso_code << 16) | (wifi << 11) | (sd << 9) | (tz_ind << 4) | lang_ind
-
-ind_codes = ['0x%08x' % mk_ind_code(elt) for elt in mcc_table]
-
-def fmt_list(title, l, batch_sz):
- sl = []
- for i in range(len(l) / batch_sz + (len(l) % batch_sz and 1 or 0)):
- j = i * batch_sz
- sl.append((' ' * 8) + ', '.join(l[j:j + batch_sz]))
- return ' private static final %s = {\n' % title + ',\n'.join(sl) + '\n };\n'
-
-def do_autogen_comment(extra_desc=[]):
- print ' /' + '**\n * AUTO GENERATED (by the Python code above)'
- for line in extra_desc:
- print ' * %s' % line
- print ' *' + '/'
-
-do_autogen_comment()
-print fmt_list('String[] TZ_STRINGS', ['"%s"' % x for x in tz_set], 1)
-do_autogen_comment()
-print fmt_list('String[] LANG_STRINGS', ['"%s"' % x for x in lang_set], 10)
-do_autogen_comment(['This table is a list of MCC codes. The index in this table',
- 'of a given MCC code is the index of extra information about',
- 'that MCC in the IND_CODES table.'])
-print fmt_list('short[] MCC_CODES', mcc_codes, 10)
-do_autogen_comment(['The values in this table are broken down as follows (msb to lsb):',
- ' iso country code 16 bits',
- ' (unused) 1 bit',
- ' wifi channel 4 bits',
- ' smalled digit 2 bits',
- ' default timezone 5 bits',
- ' default language 4 bits'])
-print fmt_list('int[] IND_CODES', ind_codes, 6)
-
-def parse_ind_code(ind):
- mcc = eval(mcc_codes[ind])
- code = eval(ind_codes[ind])
- iso_lsb = int((code >> 16) & 0x00FF)
- iso_msb = int((code >> 24) & 0x00FF)
- iso = '%s%s' % (chr(iso_msb), chr(iso_lsb))
- wifi = int((code >> 11) & 0x000F)
- sd = int((code >> 9) & 0x0003)
- tz_ind = (code >> 4) & 0x001F
- lang_ind = (code >> 0) & 0x000F
- return (mcc, iso, sd, tz_set[tz_ind], lang_set[lang_ind], wifi)
-
-fmt_str = 'mcc = %s, iso = %s, sd = %s, tz = %s, lang = %s, wifi = %s'
-orig_table = [fmt_str % (get_mcc(elt), get_iso(elt), get_sd(elt),
- get_tz(elt), get_lang(elt), get_wifi(elt))
- for elt in mcc_table]
-derived_table = [fmt_str % parse_ind_code(i) for i in range(len(ind_codes))]
-for i in range(len(orig_table)):
- if orig_table[i] == derived_table[i]: continue
- print 'MISMATCH ERROR : ', orig_table[i], " != ", derived_table[i]
-
-*/
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Locale;
+import libcore.icu.TimeZones;
/**
* Mobile Country Code
@@ -371,200 +38,153 @@
*/
public final class MccTable
{
- /**
- * AUTO GENERATED (by the Python code above)
- */
- private static final String[] TZ_STRINGS = {
- "",
- "Africa/Johannesburg",
- "Asia/Beijing",
- "Asia/Singapore",
- "Asia/Tokyo",
- "Australia/Sydney",
- "Europe/Amsterdam",
- "Europe/Berlin",
- "Europe/Dublin",
- "Europe/London",
- "Europe/Madrid",
- "Europe/Paris",
- "Europe/Prague",
- "Europe/Rome",
- "Europe/Vienna",
- "Europe/Warsaw",
- "Europe/Zurich",
- "Pacific/Auckland"
- };
-
- /**
- * AUTO GENERATED (by the Python code above)
- */
- private static final String[] LANG_STRINGS = {
- "", "cs", "de", "en", "es", "fr", "it", "ja", "nl", "zh"
- };
-
- /**
- * AUTO GENERATED (by the Python code above)
- * This table is a list of MCC codes. The index in this table
- * of a given MCC code is the index of extra information about
- * that MCC in the IND_CODES table.
- */
- private static final short[] MCC_CODES = {
- 0x00ca, 0x00cc, 0x00ce, 0x00d0, 0x00d4, 0x00d5, 0x00d6, 0x00d8, 0x00da, 0x00db,
- 0x00dc, 0x00de, 0x00e1, 0x00e2, 0x00e4, 0x00e6, 0x00e7, 0x00e8, 0x00ea, 0x00eb,
- 0x00ee, 0x00f0, 0x00f2, 0x00f4, 0x00f6, 0x00f7, 0x00f8, 0x00fa, 0x00ff, 0x0101,
- 0x0103, 0x0104, 0x0106, 0x010a, 0x010c, 0x010e, 0x0110, 0x0112, 0x0114, 0x0116,
- 0x0118, 0x011a, 0x011b, 0x011c, 0x011e, 0x0120, 0x0121, 0x0122, 0x0124, 0x0125,
- 0x0126, 0x0127, 0x0129, 0x012e, 0x0134, 0x0136, 0x0137, 0x0138, 0x0139, 0x013a,
- 0x013b, 0x013c, 0x014a, 0x014c, 0x014e, 0x0152, 0x0154, 0x0156, 0x0158, 0x015a,
- 0x015c, 0x015e, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016a, 0x016b, 0x016c,
- 0x016d, 0x016e, 0x0170, 0x0172, 0x0174, 0x0176, 0x0178, 0x0190, 0x0191, 0x0192,
- 0x0194, 0x0195, 0x019a, 0x019c, 0x019d, 0x019e, 0x019f, 0x01a0, 0x01a1, 0x01a2,
- 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac,
- 0x01ad, 0x01ae, 0x01af, 0x01b0, 0x01b2, 0x01b4, 0x01b5, 0x01b6, 0x01b8, 0x01b9,
- 0x01c2, 0x01c4, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01cc, 0x01cd, 0x01d2, 0x01d3,
- 0x01d6, 0x01d8, 0x01f6, 0x01f9, 0x01fe, 0x0202, 0x0203, 0x0208, 0x020d, 0x0210,
- 0x0212, 0x0216, 0x0217, 0x0218, 0x0219, 0x021b, 0x021c, 0x021d, 0x021e, 0x021f,
- 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, 0x0228, 0x025a,
- 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264,
- 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e,
- 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278,
- 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282,
- 0x0283, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d,
- 0x028e, 0x028f, 0x0291, 0x02be, 0x02c0, 0x02c2, 0x02c4, 0x02c6, 0x02c8, 0x02ca,
- 0x02cc, 0x02d2, 0x02d4, 0x02da, 0x02dc, 0x02de, 0x02e0, 0x02e2, 0x02e4, 0x02e6,
- 0x02e8, 0x02ea, 0x02ec, 0x02ee
- };
-
- /**
- * AUTO GENERATED (by the Python code above)
- * The values in this table are broken down as follows (msb to lsb):
- * iso country code 16 bits
- * (unused) 1 bit
- * wifi channel 4 bits
- * smalled digit 2 bits
- * default timezone 5 bits
- * default language 4 bits
- */
- private static final int[] IND_CODES = {
- 0x67720400, 0x6e6c6c68, 0x62650400, 0x667204b5, 0x6d630400, 0x61640400,
- 0x657304a4, 0x68750400, 0x62610400, 0x68720400, 0x72730400, 0x697404d6,
- 0x766104d6, 0x726f0400, 0x63680502, 0x637a6cc1, 0x736b0400, 0x61746ce2,
- 0x67626c93, 0x67626c93, 0x646b0400, 0x73650400, 0x6e6f0400, 0x66690400,
- 0x6c740400, 0x6c760400, 0x65650400, 0x72750400, 0x75610400, 0x62790400,
- 0x6d640400, 0x706c04f0, 0x64656c72, 0x67690400, 0x70740400, 0x6c750400,
- 0x69650483, 0x69730400, 0x616c0400, 0x6d740400, 0x63790400, 0x67650400,
- 0x616d0400, 0x62670400, 0x74720400, 0x666f0400, 0x67650400, 0x676c0400,
- 0x736d0400, 0x736c0400, 0x6d6b0400, 0x6c690400, 0x6d650400, 0x63615e00,
- 0x706d0400, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03,
- 0x75735e03, 0x75735e03, 0x70720400, 0x76690400, 0x6d780600, 0x6a6d0600,
- 0x67700400, 0x62620600, 0x61670600, 0x6b790600, 0x76670600, 0x626d0400,
- 0x67640400, 0x6d730400, 0x6b6e0400, 0x6c630400, 0x76630400, 0x6e6c0400,
- 0x61770400, 0x62730400, 0x61690600, 0x646d0400, 0x63750400, 0x646f0400,
- 0x68740400, 0x74740400, 0x74630400, 0x617a0400, 0x6b7a0400, 0x62740400,
- 0x696e0400, 0x696e0400, 0x706b0400, 0x61660400, 0x6c6b0400, 0x6d6d0400,
- 0x6c620400, 0x6a6f0400, 0x73790400, 0x69710400, 0x6b770400, 0x73610400,
- 0x79650400, 0x6f6d0400, 0x70730400, 0x61650400, 0x696c0400, 0x62680400,
- 0x71610400, 0x6d6e0400, 0x6e700400, 0x61650400, 0x61650400, 0x69720400,
- 0x757a0400, 0x746a0400, 0x6b670400, 0x746d0400, 0x6a707447, 0x6a707447,
- 0x6b720400, 0x766e0400, 0x686b0400, 0x6d6f0400, 0x6b680400, 0x6c610400,
- 0x636e6c29, 0x636e6c29, 0x74770400, 0x6b700400, 0x62640400, 0x6d760400,
- 0x6d790400, 0x61755c53, 0x69640400, 0x746c0400, 0x70680400, 0x74680400,
- 0x73675c33, 0x626e0400, 0x6e7a0513, 0x6d700400, 0x67750400, 0x6e720400,
- 0x70670400, 0x746f0400, 0x73620400, 0x76750400, 0x666a0400, 0x77660400,
- 0x61730400, 0x6b690400, 0x6e630400, 0x70660400, 0x636b0400, 0x77730400,
- 0x666d0400, 0x6d680400, 0x70770400, 0x65670400, 0x647a0400, 0x6d610400,
- 0x746e0400, 0x6c790400, 0x676d0400, 0x736e0400, 0x6d720400, 0x6d6c0400,
- 0x676e0400, 0x63690400, 0x62660400, 0x6e650400, 0x74670400, 0x626a0400,
- 0x6d750400, 0x6c720400, 0x736c0400, 0x67680400, 0x6e670400, 0x74640400,
- 0x63660400, 0x636d0400, 0x63760400, 0x73740400, 0x67710400, 0x67610400,
- 0x63670400, 0x63670400, 0x616f0400, 0x67770400, 0x73630400, 0x73640400,
- 0x72770400, 0x65740400, 0x736f0400, 0x646a0400, 0x6b650400, 0x747a0400,
- 0x75670400, 0x62690400, 0x6d7a0400, 0x7a6d0400, 0x6d670400, 0x72650400,
- 0x7a770400, 0x6e610400, 0x6d770400, 0x6c730400, 0x62770400, 0x737a0400,
- 0x6b6d0400, 0x7a610413, 0x65720400, 0x627a0400, 0x67740400, 0x73760400,
- 0x686e0600, 0x6e690400, 0x63720400, 0x70610400, 0x70650400, 0x61720600,
- 0x62720400, 0x636c0400, 0x636f0600, 0x76650400, 0x626f0400, 0x67790400,
- 0x65630400, 0x67660400, 0x70790400, 0x73720400, 0x75790400, 0x666b0400
- };
-
static final String LOG_TAG = "MccTable";
+ static ArrayList<MccEntry> table;
+
+ static class MccEntry implements Comparable<MccEntry>
+ {
+ int mcc;
+ String iso;
+ int smallestDigitsMnc;
+ String language;
+ int wifiChannels;
+
+ MccEntry(int mnc, String iso, int smallestDigitsMCC) {
+ this(mnc, iso, smallestDigitsMCC, null);
+ }
+
+ MccEntry(int mnc, String iso, int smallestDigitsMCC, String language) {
+ this(mnc, iso, smallestDigitsMCC, language, 0);
+ }
+
+ MccEntry(int mnc, String iso, int smallestDigitsMCC, String language, int wifiChannels) {
+ this.mcc = mnc;
+ this.iso = iso;
+ this.smallestDigitsMnc = smallestDigitsMCC;
+ this.language = language;
+ this.wifiChannels = wifiChannels;
+ }
+
+
+ public int compareTo(MccEntry o)
+ {
+ return mcc - o.mcc;
+ }
+ }
+
+ private static MccEntry
+ entryForMcc(int mcc)
+ {
+ int index;
+
+ MccEntry m;
+
+ m = new MccEntry(mcc, null, 0);
+
+ index = Collections.binarySearch(table, m);
+
+ if (index < 0) {
+ return null;
+ } else {
+ return table.get(index);
+ }
+ }
+
/**
- * Given a Mobile Country Code, returns a default time zone ID
- * if available. Returns null if unavailable.
+ * Returns a default time zone ID for the given MCC.
+ * @param mcc Mobile Country Code
+ * @return default TimeZone ID, or null if not specified
*/
public static String defaultTimeZoneForMcc(int mcc) {
- int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
- if (index < 0) {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+ if (entry == null || entry.iso == null) {
return null;
+ } else {
+ Locale locale;
+ if (entry.language == null) {
+ locale = new Locale(entry.iso);
+ } else {
+ locale = new Locale(entry.language, entry.iso);
+ }
+ String[] tz = TimeZones.forLocale(locale);
+ if (tz.length == 0) return null;
+ return tz[0];
}
- int indCode = IND_CODES[index];
- int tzInd = (indCode >>> 4) & 0x001F;
- String tz = TZ_STRINGS[tzInd];
- if (tz == "") {
- return null;
- }
- return tz;
}
/**
- * Given a Mobile Country Code, returns an ISO two-character
- * country code if available. Returns "" if unavailable.
+ * Given a GSM Mobile Country Code, returns
+ * an ISO two-character country code if available.
+ * Returns "" if unavailable.
*/
- public static String countryCodeForMcc(int mcc) {
- int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
- if (index < 0) {
+ public static String
+ countryCodeForMcc(int mcc)
+ {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
return "";
+ } else {
+ return entry.iso;
}
- int indCode = IND_CODES[index];
- byte[] iso = {(byte)((indCode >>> 24) & 0x00FF), (byte)((indCode >>> 16) & 0x00FF)};
- return new String(iso);
}
/**
- * Given a GSM Mobile Country Code, returns an ISO 2-3 character
- * language code if available. Returns null if unavailable.
+ * Given a GSM Mobile Country Code, returns
+ * an ISO 2-3 character language code if available.
+ * Returns null if unavailable.
*/
public static String defaultLanguageForMcc(int mcc) {
- int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
- if (index < 0) {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
return null;
+ } else {
+ return entry.language;
}
- int indCode = IND_CODES[index];
- int langInd = indCode & 0x000F;
- String lang = LANG_STRINGS[langInd];
- if (lang == "") {
- return null;
- }
- return lang;
}
/**
- * Given a GSM Mobile Country Code, returns the corresponding
- * smallest number of digits field. Returns 2 if unavailable.
+ * Given a GSM Mobile Country Code, returns
+ * the smallest number of digits that M if available.
+ * Returns 2 if unavailable.
*/
- public static int smallestDigitsMccForMnc(int mcc) {
- int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
- if (index < 0) {
+ public static int
+ smallestDigitsMccForMnc(int mcc)
+ {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
return 2;
+ } else {
+ return entry.smallestDigitsMnc;
}
- int indCode = IND_CODES[index];
- int smDig = (indCode >>> 9) & 0x0003;
- return smDig;
}
/**
* Given a GSM Mobile Country Code, returns the number of wifi
* channels allowed in that country. Returns 0 if unavailable.
*/
- public static int wifiChannelsForMcc(int mcc) {
- int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
- if (index < 0) {
+ public static int
+ wifiChannelsForMcc(int mcc) {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
return 0;
+ } else {
+ return entry.wifiChannels;
}
- int indCode = IND_CODES[index];
- int wifi = (indCode >>> 11) & 0x000F;
- return wifi;
}
/**
@@ -656,4 +276,262 @@
wM.setNumAllowedChannels(wifiChannels, true);
}
}
+
+ static {
+ table = new ArrayList<MccEntry>(240);
+
+
+ /*
+ * The table below is built from two resources:
+ *
+ * 1) ITU "Mobile Network Code (MNC) for the international
+ * identification plan for mobile terminals and mobile users"
+ * which is available as an annex to the ITU operational bulletin
+ * available here: http://www.itu.int/itu-t/bulletin/annex.html
+ *
+ * 2) The ISO 3166 country codes list, available here:
+ * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
+ *
+ * This table has not been verified.
+ *
+ */
+
+ table.add(new MccEntry(202,"gr",2)); //Greece
+ table.add(new MccEntry(204,"nl",2,"nl",13)); //Netherlands (Kingdom of the)
+ table.add(new MccEntry(206,"be",2)); //Belgium
+ table.add(new MccEntry(208,"fr",2,"fr")); //France
+ table.add(new MccEntry(212,"mc",2)); //Monaco (Principality of)
+ table.add(new MccEntry(213,"ad",2)); //Andorra (Principality of)
+ table.add(new MccEntry(214,"es",2,"es")); //Spain
+ table.add(new MccEntry(216,"hu",2)); //Hungary (Republic of)
+ table.add(new MccEntry(218,"ba",2)); //Bosnia and Herzegovina
+ table.add(new MccEntry(219,"hr",2)); //Croatia (Republic of)
+ table.add(new MccEntry(220,"rs",2)); //Serbia and Montenegro
+ table.add(new MccEntry(222,"it",2,"it")); //Italy
+ table.add(new MccEntry(225,"va",2,"it")); //Vatican City State
+ table.add(new MccEntry(226,"ro",2)); //Romania
+ table.add(new MccEntry(228,"ch",2,"de")); //Switzerland (Confederation of)
+ table.add(new MccEntry(230,"cz",2,"cs",13)); //Czech Republic
+ table.add(new MccEntry(231,"sk",2)); //Slovak Republic
+ table.add(new MccEntry(232,"at",2,"de",13)); //Austria
+ table.add(new MccEntry(234,"gb",2,"en",13)); //United Kingdom of Great Britain and Northern Ireland
+ table.add(new MccEntry(235,"gb",2,"en",13)); //United Kingdom of Great Britain and Northern Ireland
+ table.add(new MccEntry(238,"dk",2)); //Denmark
+ table.add(new MccEntry(240,"se",2)); //Sweden
+ table.add(new MccEntry(242,"no",2)); //Norway
+ table.add(new MccEntry(244,"fi",2)); //Finland
+ table.add(new MccEntry(246,"lt",2)); //Lithuania (Republic of)
+ table.add(new MccEntry(247,"lv",2)); //Latvia (Republic of)
+ table.add(new MccEntry(248,"ee",2)); //Estonia (Republic of)
+ table.add(new MccEntry(250,"ru",2)); //Russian Federation
+ table.add(new MccEntry(255,"ua",2)); //Ukraine
+ table.add(new MccEntry(257,"by",2)); //Belarus (Republic of)
+ table.add(new MccEntry(259,"md",2)); //Moldova (Republic of)
+ table.add(new MccEntry(260,"pl",2)); //Poland (Republic of)
+ table.add(new MccEntry(262,"de",2,"de",13)); //Germany (Federal Republic of)
+ table.add(new MccEntry(266,"gi",2)); //Gibraltar
+ table.add(new MccEntry(268,"pt",2)); //Portugal
+ table.add(new MccEntry(270,"lu",2)); //Luxembourg
+ table.add(new MccEntry(272,"ie",2,"en")); //Ireland
+ table.add(new MccEntry(274,"is",2)); //Iceland
+ table.add(new MccEntry(276,"al",2)); //Albania (Republic of)
+ table.add(new MccEntry(278,"mt",2)); //Malta
+ table.add(new MccEntry(280,"cy",2)); //Cyprus (Republic of)
+ table.add(new MccEntry(282,"ge",2)); //Georgia
+ table.add(new MccEntry(283,"am",2)); //Armenia (Republic of)
+ table.add(new MccEntry(284,"bg",2)); //Bulgaria (Republic of)
+ table.add(new MccEntry(286,"tr",2)); //Turkey
+ table.add(new MccEntry(288,"fo",2)); //Faroe Islands
+ table.add(new MccEntry(289,"ge",2)); //Abkhazia (Georgia)
+ table.add(new MccEntry(290,"gl",2)); //Greenland (Denmark)
+ table.add(new MccEntry(292,"sm",2)); //San Marino (Republic of)
+ table.add(new MccEntry(293,"sl",2)); //Slovenia (Republic of)
+ table.add(new MccEntry(294,"mk",2)); //The Former Yugoslav Republic of Macedonia
+ table.add(new MccEntry(295,"li",2)); //Liechtenstein (Principality of)
+ table.add(new MccEntry(297,"me",2)); //Montenegro (Republic of)
+ table.add(new MccEntry(302,"ca",3,"",11)); //Canada
+ table.add(new MccEntry(308,"pm",2)); //Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)
+ table.add(new MccEntry(310,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(311,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(312,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(313,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(314,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(315,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(316,"us",3,"en",11)); //United States of America
+ table.add(new MccEntry(330,"pr",2)); //Puerto Rico
+ table.add(new MccEntry(332,"vi",2)); //United States Virgin Islands
+ table.add(new MccEntry(334,"mx",3)); //Mexico
+ table.add(new MccEntry(338,"jm",3)); //Jamaica
+ table.add(new MccEntry(340,"gp",2)); //Guadeloupe (French Department of)
+ table.add(new MccEntry(342,"bb",3)); //Barbados
+ table.add(new MccEntry(344,"ag",3)); //Antigua and Barbuda
+ table.add(new MccEntry(346,"ky",3)); //Cayman Islands
+ table.add(new MccEntry(348,"vg",3)); //British Virgin Islands
+ table.add(new MccEntry(350,"bm",2)); //Bermuda
+ table.add(new MccEntry(352,"gd",2)); //Grenada
+ table.add(new MccEntry(354,"ms",2)); //Montserrat
+ table.add(new MccEntry(356,"kn",2)); //Saint Kitts and Nevis
+ table.add(new MccEntry(358,"lc",2)); //Saint Lucia
+ table.add(new MccEntry(360,"vc",2)); //Saint Vincent and the Grenadines
+ table.add(new MccEntry(362,"nl",2)); //Netherlands Antilles
+ table.add(new MccEntry(363,"aw",2)); //Aruba
+ table.add(new MccEntry(364,"bs",2)); //Bahamas (Commonwealth of the)
+ table.add(new MccEntry(365,"ai",3)); //Anguilla
+ table.add(new MccEntry(366,"dm",2)); //Dominica (Commonwealth of)
+ table.add(new MccEntry(368,"cu",2)); //Cuba
+ table.add(new MccEntry(370,"do",2)); //Dominican Republic
+ table.add(new MccEntry(372,"ht",2)); //Haiti (Republic of)
+ table.add(new MccEntry(374,"tt",2)); //Trinidad and Tobago
+ table.add(new MccEntry(376,"tc",2)); //Turks and Caicos Islands
+ table.add(new MccEntry(400,"az",2)); //Azerbaijani Republic
+ table.add(new MccEntry(401,"kz",2)); //Kazakhstan (Republic of)
+ table.add(new MccEntry(402,"bt",2)); //Bhutan (Kingdom of)
+ table.add(new MccEntry(404,"in",2)); //India (Republic of)
+ table.add(new MccEntry(405,"in",2)); //India (Republic of)
+ table.add(new MccEntry(410,"pk",2)); //Pakistan (Islamic Republic of)
+ table.add(new MccEntry(412,"af",2)); //Afghanistan
+ table.add(new MccEntry(413,"lk",2)); //Sri Lanka (Democratic Socialist Republic of)
+ table.add(new MccEntry(414,"mm",2)); //Myanmar (Union of)
+ table.add(new MccEntry(415,"lb",2)); //Lebanon
+ table.add(new MccEntry(416,"jo",2)); //Jordan (Hashemite Kingdom of)
+ table.add(new MccEntry(417,"sy",2)); //Syrian Arab Republic
+ table.add(new MccEntry(418,"iq",2)); //Iraq (Republic of)
+ table.add(new MccEntry(419,"kw",2)); //Kuwait (State of)
+ table.add(new MccEntry(420,"sa",2)); //Saudi Arabia (Kingdom of)
+ table.add(new MccEntry(421,"ye",2)); //Yemen (Republic of)
+ table.add(new MccEntry(422,"om",2)); //Oman (Sultanate of)
+ table.add(new MccEntry(423,"ps",2)); //Palestine
+ table.add(new MccEntry(424,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(425,"il",2)); //Israel (State of)
+ table.add(new MccEntry(426,"bh",2)); //Bahrain (Kingdom of)
+ table.add(new MccEntry(427,"qa",2)); //Qatar (State of)
+ table.add(new MccEntry(428,"mn",2)); //Mongolia
+ table.add(new MccEntry(429,"np",2)); //Nepal
+ table.add(new MccEntry(430,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(431,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(432,"ir",2)); //Iran (Islamic Republic of)
+ table.add(new MccEntry(434,"uz",2)); //Uzbekistan (Republic of)
+ table.add(new MccEntry(436,"tj",2)); //Tajikistan (Republic of)
+ table.add(new MccEntry(437,"kg",2)); //Kyrgyz Republic
+ table.add(new MccEntry(438,"tm",2)); //Turkmenistan
+ table.add(new MccEntry(440,"jp",2,"ja",14)); //Japan
+ table.add(new MccEntry(441,"jp",2,"ja",14)); //Japan
+ table.add(new MccEntry(450,"kr",2)); //Korea (Republic of)
+ table.add(new MccEntry(452,"vn",2)); //Viet Nam (Socialist Republic of)
+ table.add(new MccEntry(454,"hk",2)); //"Hong Kong, China"
+ table.add(new MccEntry(455,"mo",2)); //"Macao, China"
+ table.add(new MccEntry(456,"kh",2)); //Cambodia (Kingdom of)
+ table.add(new MccEntry(457,"la",2)); //Lao People's Democratic Republic
+ table.add(new MccEntry(460,"cn",2,"zh",13)); //China (People's Republic of)
+ table.add(new MccEntry(461,"cn",2,"zh",13)); //China (People's Republic of)
+ table.add(new MccEntry(466,"tw",2)); //"Taiwan, China"
+ table.add(new MccEntry(467,"kp",2)); //Democratic People's Republic of Korea
+ table.add(new MccEntry(470,"bd",2)); //Bangladesh (People's Republic of)
+ table.add(new MccEntry(472,"mv",2)); //Maldives (Republic of)
+ table.add(new MccEntry(502,"my",2)); //Malaysia
+ table.add(new MccEntry(505,"au",2,"en",11)); //Australia
+ table.add(new MccEntry(510,"id",2)); //Indonesia (Republic of)
+ table.add(new MccEntry(514,"tl",2)); //Democratic Republic of Timor-Leste
+ table.add(new MccEntry(515,"ph",2)); //Philippines (Republic of the)
+ table.add(new MccEntry(520,"th",2)); //Thailand
+ table.add(new MccEntry(525,"sg",2,"en",11)); //Singapore (Republic of)
+ table.add(new MccEntry(528,"bn",2)); //Brunei Darussalam
+ table.add(new MccEntry(530,"nz",2, "en")); //New Zealand
+ table.add(new MccEntry(534,"mp",2)); //Northern Mariana Islands (Commonwealth of the)
+ table.add(new MccEntry(535,"gu",2)); //Guam
+ table.add(new MccEntry(536,"nr",2)); //Nauru (Republic of)
+ table.add(new MccEntry(537,"pg",2)); //Papua New Guinea
+ table.add(new MccEntry(539,"to",2)); //Tonga (Kingdom of)
+ table.add(new MccEntry(540,"sb",2)); //Solomon Islands
+ table.add(new MccEntry(541,"vu",2)); //Vanuatu (Republic of)
+ table.add(new MccEntry(542,"fj",2)); //Fiji (Republic of)
+ table.add(new MccEntry(543,"wf",2)); //Wallis and Futuna (Territoire franais d'outre-mer)
+ table.add(new MccEntry(544,"as",2)); //American Samoa
+ table.add(new MccEntry(545,"ki",2)); //Kiribati (Republic of)
+ table.add(new MccEntry(546,"nc",2)); //New Caledonia (Territoire franais d'outre-mer)
+ table.add(new MccEntry(547,"pf",2)); //French Polynesia (Territoire franais d'outre-mer)
+ table.add(new MccEntry(548,"ck",2)); //Cook Islands
+ table.add(new MccEntry(549,"ws",2)); //Samoa (Independent State of)
+ table.add(new MccEntry(550,"fm",2)); //Micronesia (Federated States of)
+ table.add(new MccEntry(551,"mh",2)); //Marshall Islands (Republic of the)
+ table.add(new MccEntry(552,"pw",2)); //Palau (Republic of)
+ table.add(new MccEntry(602,"eg",2)); //Egypt (Arab Republic of)
+ table.add(new MccEntry(603,"dz",2)); //Algeria (People's Democratic Republic of)
+ table.add(new MccEntry(604,"ma",2)); //Morocco (Kingdom of)
+ table.add(new MccEntry(605,"tn",2)); //Tunisia
+ table.add(new MccEntry(606,"ly",2)); //Libya (Socialist People's Libyan Arab Jamahiriya)
+ table.add(new MccEntry(607,"gm",2)); //Gambia (Republic of the)
+ table.add(new MccEntry(608,"sn",2)); //Senegal (Republic of)
+ table.add(new MccEntry(609,"mr",2)); //Mauritania (Islamic Republic of)
+ table.add(new MccEntry(610,"ml",2)); //Mali (Republic of)
+ table.add(new MccEntry(611,"gn",2)); //Guinea (Republic of)
+ table.add(new MccEntry(612,"ci",2)); //Cte d'Ivoire (Republic of)
+ table.add(new MccEntry(613,"bf",2)); //Burkina Faso
+ table.add(new MccEntry(614,"ne",2)); //Niger (Republic of the)
+ table.add(new MccEntry(615,"tg",2)); //Togolese Republic
+ table.add(new MccEntry(616,"bj",2)); //Benin (Republic of)
+ table.add(new MccEntry(617,"mu",2)); //Mauritius (Republic of)
+ table.add(new MccEntry(618,"lr",2)); //Liberia (Republic of)
+ table.add(new MccEntry(619,"sl",2)); //Sierra Leone
+ table.add(new MccEntry(620,"gh",2)); //Ghana
+ table.add(new MccEntry(621,"ng",2)); //Nigeria (Federal Republic of)
+ table.add(new MccEntry(622,"td",2)); //Chad (Republic of)
+ table.add(new MccEntry(623,"cf",2)); //Central African Republic
+ table.add(new MccEntry(624,"cm",2)); //Cameroon (Republic of)
+ table.add(new MccEntry(625,"cv",2)); //Cape Verde (Republic of)
+ table.add(new MccEntry(626,"st",2)); //Sao Tome and Principe (Democratic Republic of)
+ table.add(new MccEntry(627,"gq",2)); //Equatorial Guinea (Republic of)
+ table.add(new MccEntry(628,"ga",2)); //Gabonese Republic
+ table.add(new MccEntry(629,"cg",2)); //Congo (Republic of the)
+ table.add(new MccEntry(630,"cg",2)); //Democratic Republic of the Congo
+ table.add(new MccEntry(631,"ao",2)); //Angola (Republic of)
+ table.add(new MccEntry(632,"gw",2)); //Guinea-Bissau (Republic of)
+ table.add(new MccEntry(633,"sc",2)); //Seychelles (Republic of)
+ table.add(new MccEntry(634,"sd",2)); //Sudan (Republic of the)
+ table.add(new MccEntry(635,"rw",2)); //Rwanda (Republic of)
+ table.add(new MccEntry(636,"et",2)); //Ethiopia (Federal Democratic Republic of)
+ table.add(new MccEntry(637,"so",2)); //Somali Democratic Republic
+ table.add(new MccEntry(638,"dj",2)); //Djibouti (Republic of)
+ table.add(new MccEntry(639,"ke",2)); //Kenya (Republic of)
+ table.add(new MccEntry(640,"tz",2)); //Tanzania (United Republic of)
+ table.add(new MccEntry(641,"ug",2)); //Uganda (Republic of)
+ table.add(new MccEntry(642,"bi",2)); //Burundi (Republic of)
+ table.add(new MccEntry(643,"mz",2)); //Mozambique (Republic of)
+ table.add(new MccEntry(645,"zm",2)); //Zambia (Republic of)
+ table.add(new MccEntry(646,"mg",2)); //Madagascar (Republic of)
+ table.add(new MccEntry(647,"re",2)); //Reunion (French Department of)
+ table.add(new MccEntry(648,"zw",2)); //Zimbabwe (Republic of)
+ table.add(new MccEntry(649,"na",2)); //Namibia (Republic of)
+ table.add(new MccEntry(650,"mw",2)); //Malawi
+ table.add(new MccEntry(651,"ls",2)); //Lesotho (Kingdom of)
+ table.add(new MccEntry(652,"bw",2)); //Botswana (Republic of)
+ table.add(new MccEntry(653,"sz",2)); //Swaziland (Kingdom of)
+ table.add(new MccEntry(654,"km",2)); //Comoros (Union of the)
+ table.add(new MccEntry(655,"za",2,"en")); //South Africa (Republic of)
+ table.add(new MccEntry(657,"er",2)); //Eritrea
+ table.add(new MccEntry(702,"bz",2)); //Belize
+ table.add(new MccEntry(704,"gt",2)); //Guatemala (Republic of)
+ table.add(new MccEntry(706,"sv",2)); //El Salvador (Republic of)
+ table.add(new MccEntry(708,"hn",3)); //Honduras (Republic of)
+ table.add(new MccEntry(710,"ni",2)); //Nicaragua
+ table.add(new MccEntry(712,"cr",2)); //Costa Rica
+ table.add(new MccEntry(714,"pa",2)); //Panama (Republic of)
+ table.add(new MccEntry(716,"pe",2)); //Peru
+ table.add(new MccEntry(722,"ar",3)); //Argentine Republic
+ table.add(new MccEntry(724,"br",2)); //Brazil (Federative Republic of)
+ table.add(new MccEntry(730,"cl",2)); //Chile
+ table.add(new MccEntry(732,"co",3)); //Colombia (Republic of)
+ table.add(new MccEntry(734,"ve",2)); //Venezuela (Bolivarian Republic of)
+ table.add(new MccEntry(736,"bo",2)); //Bolivia (Republic of)
+ table.add(new MccEntry(738,"gy",2)); //Guyana
+ table.add(new MccEntry(740,"ec",2)); //Ecuador
+ table.add(new MccEntry(742,"gf",2)); //French Guiana (French Department of)
+ table.add(new MccEntry(744,"py",2)); //Paraguay (Republic of)
+ table.add(new MccEntry(746,"sr",2)); //Suriname (Republic of)
+ table.add(new MccEntry(748,"uy",2)); //Uruguay (Eastern Republic of)
+ table.add(new MccEntry(750,"fk",2)); //Falkland Islands (Malvinas)
+ //table.add(new MccEntry(901,"",2)); //"International Mobile, shared code"
+
+ Collections.sort(table);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 7029031..769b2fc8 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.SharedPreferences;
+import android.net.NetworkProperties;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
@@ -101,6 +102,7 @@
static final String STATE_CHANGE_REASON_KEY = "reason";
static final String DATA_APN_TYPE_KEY = "apnType";
static final String DATA_APN_KEY = "apn";
+ static final String DATA_NETWORK_PROPERTIES_KEY = "dataProperties";
static final String DATA_IFACE_NAME_KEY = "iface";
static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
@@ -319,6 +321,11 @@
String getActiveApn();
/**
+ * Return the NetworkProperties for the named apn or null if not available
+ */
+ NetworkProperties getNetworkProperties(String apnType);
+
+ /**
* Get current signal strength. No change notification available on this
* interface. Use <code>PhoneStateNotifier</code> or an equivalent.
* An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index cf80691..e5968a7 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.SharedPreferences;
+import android.net.NetworkProperties;
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Handler;
@@ -955,6 +956,10 @@
return mDataConnection.getActiveApnTypes();
}
+ public NetworkProperties getNetworkProperties(String apnType) {
+ return mDataConnection.getNetworkProperties(apnType);
+ }
+
public String getActiveApn() {
return mDataConnection.getActiveApnString();
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index fb2a938..d84859c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.NetworkProperties;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
@@ -211,6 +212,10 @@
return mActivePhone.getActiveApnTypes();
}
+ public NetworkProperties getNetworkProperties(String apnType) {
+ return mActivePhone.getNetworkProperties(apnType);
+ }
+
public String getActiveApn() {
return mActivePhone.getActiveApn();
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index bd103d4..8a3af3b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -53,6 +53,7 @@
import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.ServiceStateTracker;
+import java.net.NetworkInterface;
import java.util.ArrayList;
/**
@@ -736,6 +737,8 @@
}
if (ar.exception == null) {
+ mNetworkProperties = makeNetworkProperties(mActiveDataConnection);
+
// everything is setup
notifyDefaultData(reason);
} else {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 6826fa8..c76da80 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -30,6 +30,8 @@
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.NetworkInfo;
+import android.net.NetworkProperties;
+import android.net.ProxyProperties;
import android.net.TrafficStats;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -58,6 +60,9 @@
import com.android.internal.telephony.DataConnection.FailCause;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
import java.util.ArrayList;
/**
@@ -1133,6 +1138,25 @@
}
if (ar.exception == null) {
+ mNetworkProperties = makeNetworkProperties(mActivePdp);
+
+ ApnSetting apn = mActivePdp.getApn();
+ if (apn.proxy != null && apn.proxy.length() != 0) {
+ try {
+ ProxyProperties proxy = new ProxyProperties();
+ proxy.setAddress(InetAddress.getByName(apn.proxy));
+ proxy.setPort(Integer.parseInt(apn.port));
+ mNetworkProperties.setHttpProxy(proxy);
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "UnknownHostException making ProxyProperties: " + e);
+ } catch (SecurityException e) {
+ Log.e(LOG_TAG, "SecurityException making ProxyProperties: " + e);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "NumberFormatException making ProxyProperties (" + apn.port +
+ "): " + e);
+ }
+ }
+
// everything is setup
if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
SystemProperties.set("gsm.defaultpdpcontext.active", "true");
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 487372e..278e1ba 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -932,6 +932,8 @@
// TP-Message-Type-Indicator
// 9.2.3
case 0:
+ case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
+ //This should be processed in the same way as MTI == 0 (Deliver)
parseSmsDeliver(p, firstByte);
break;
case 2:
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index 2d6977c..7eb3df8 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -28,7 +28,7 @@
@SmallTest
public void testTimeZone() throws Exception {
- assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris");
+ assertEquals(MccTable.defaultTimeZoneForMcc(208), "ECT");
assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna");
assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg");
assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo");
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 a74c5c2..de59b81 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -496,9 +496,11 @@
assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
- TelephonyManager mTelephonyManager =
+ // This test fails on a device without a sim card
+ /*TelephonyManager mTelephonyManager =
(TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
String mVoiceMailNumber = mTelephonyManager.getDefault().getVoiceMailNumber();
assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
+ */
}
}
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 63d50c7..70d1643 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -496,9 +496,18 @@
return null;
}
+ /**
+ * Initialize the current thread as a looper.
+ * <p/>
+ * Exposed for unit testing.
+ */
+ void prepareLooper() {
+ Looper.prepare();
+ }
+
@Override
public void onStart() {
- Looper.prepare();
+ prepareLooper();
if (mJustCount) {
mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
@@ -521,6 +530,11 @@
long runTime = System.currentTimeMillis() - startTime;
resultPrinter.print(mTestRunner.getTestResult(), runTime);
+ } catch (Throwable t) {
+ // catch all exceptions so a more verbose error message can be outputted
+ writer.println(String.format("Test run aborted due to unexpected exception: %s",
+ t.getMessage()));
+ t.printStackTrace(writer);
} finally {
mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
String.format("\nTest results for %s=%s",
@@ -762,9 +776,11 @@
TimedTest.class).includeDetailedStats();
}
} catch (SecurityException e) {
- throw new IllegalStateException(e);
+ // ignore - the test with given name cannot be accessed. Will be handled during
+ // test execution
} catch (NoSuchMethodException e) {
- throw new IllegalStateException(e);
+ // ignore- the test with given name does not exist. Will be handled during test
+ // execution
}
if (mIsTimedTest && mIncludeDetailedStats) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 3e77b9b..e96173b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -483,4 +483,9 @@
public boolean isSafeMode() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void setPackageObbPath(String packageName, String path) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
index 6db72ad..d98b217 100644
--- a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
+++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
@@ -16,6 +16,7 @@
package android.test;
+import android.app.Instrumentation;
import android.content.Context;
import android.os.Bundle;
import android.test.mock.MockContext;
@@ -89,6 +90,42 @@
}
+ /**
+ * Test that runtime exceptions during runTest are handled gracefully
+ */
+ public void testUnhandledException() throws Exception {
+ StubAndroidTestRunner stubAndroidTestRunner = new StubAndroidTestRunner() {
+ @Override
+ public void runTest() {
+ throw new RuntimeException();
+ }
+ };
+ StubInstrumentationTestRunner instrumentationTestRunner = new StubInstrumentationTestRunner(
+ new StubContext("com.google.foo.tests"),
+ new StubContext(mTargetContextPackageName), stubAndroidTestRunner);
+ instrumentationTestRunner.onCreate(new Bundle());
+ instrumentationTestRunner.onStart();
+ assertTrue("Instrumentation did not finish", instrumentationTestRunner.isFinished());
+ // ensure a meaningful error message placed in results
+ String resultsData = instrumentationTestRunner.mResults.getString(
+ Instrumentation.REPORT_KEY_STREAMRESULT);
+ assertTrue("Instrumentation results is missing RuntimeException",
+ resultsData.contains("RuntimeException"));
+ }
+
+ /**
+ * Test that specifying a method which does not exist is handled gracefully
+ */
+ public void testBadMethodArgument() throws Exception {
+ String testClassName = PlaceHolderTest.class.getName();
+ String invalidMethodName = "testNoExist";
+ String classAndMethod = testClassName + "#" + invalidMethodName;
+ mInstrumentationTestRunner.onCreate(createBundle(
+ InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod));
+ assertTestRunnerCalledWithExpectedParameters(testClassName,
+ invalidMethodName);
+ }
+
public void testDelayParameter() throws Exception {
int delayMsec = 1000;
Bundle args = new Bundle();
@@ -170,6 +207,7 @@
private TestSuite mTestSuite;
private TestSuite mDefaultTestSuite;
private String mPackageNameForDefaultTests;
+ private Bundle mResults;
public StubInstrumentationTestRunner(Context context, Context targetContext,
AndroidTestRunner androidTestRunner) {
@@ -200,6 +238,7 @@
public void finish(int resultCode, Bundle results) {
mFinished = true;
+ mResults = results;
}
public boolean isStarted() {
@@ -221,6 +260,11 @@
public String getPackageNameForDefaultTests() {
return mPackageNameForDefaultTests;
}
+
+ @Override
+ void prepareLooper() {
+ // ignore
+ }
}
private static class StubContext extends MockContext {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
index 0883387..4475e92 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -77,7 +77,9 @@
continue;
}
- if ((s.toLowerCase().endsWith(".html") || s.toLowerCase().endsWith(".xml"))
+ if ((s.toLowerCase().endsWith(".html")
+ || s.toLowerCase().endsWith(".xml")
+ || s.toLowerCase().endsWith(".xhtml"))
&& !s.endsWith("TEMPLATE.html")) {
Log.v(LOGTAG, "Recording " + s);
bos.write(s.getBytes());
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index fabbf89..3618c7b 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -222,14 +222,22 @@
// The generic result is at <path>/<name>-expected.txt
// First try the Android-specific result at
// platform/android-<js-engine>/<path>/<name>-expected.txt
+ // then
+ // platform/android/<path>/<name>-expected.txt
int pos = test.lastIndexOf('.');
if (pos == -1)
return null;
String genericExpectedResult = test.substring(0, pos) + "-expected.txt";
String androidExpectedResultsDir = "platform/android-" + mJsEngine + "/";
- String androidExpectedResult =
- genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+ String androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+ LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
File f = new File(androidExpectedResult);
+ if (f.exists())
+ return androidExpectedResult;
+ androidExpectedResultsDir = "platform/android/";
+ androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+ LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+ f = new File(androidExpectedResult);
return f.exists() ? androidExpectedResult : genericExpectedResult;
}
@@ -328,6 +336,7 @@
intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, mTestCount);
intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, testNumber);
+ intent.putExtra(TestShellActivity.STOP_ON_REF_ERROR, true);
activity.startActivity(intent);
// Wait until done.
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 0a04712..bf66fae 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -179,6 +179,7 @@
mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
mGetDrawtime = intent.getBooleanExtra(GET_DRAW_TIME, false);
mSaveImagePath = intent.getStringExtra(SAVE_IMAGE);
+ mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false);
setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount);
float ratio = (float)mCurrentTestNumber / mTotalTestCount;
int progress = (int)(ratio * Window.PROGRESS_END);
@@ -699,8 +700,8 @@
// waiting for "notifyDone" signal to finish, then there's no point in waiting
// anymore because the JS execution is already terminated at this point and a
// "notifyDone" will never come out so it's just wasting time till timeout kicks in
- if (msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:")
- && mWaitUntilDone) {
+ if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:"))
+ && mWaitUntilDone && mStopOnRefError) {
Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
mHandler.postDelayed(new Runnable() {
public void run() {
@@ -857,6 +858,7 @@
private boolean mGetDrawtime;
private int mTotalTestCount;
private int mCurrentTestNumber;
+ private boolean mStopOnRefError;
// States
private boolean mTimedOut;
@@ -897,6 +899,7 @@
static final String SAVE_IMAGE = "SaveImage";
static final String TOTAL_TEST_COUNT = "TestCount";
static final String CURRENT_TEST_NUMBER = "TestNumber";
+ static final String STOP_ON_REF_ERROR = "StopOnReferenceError";
static final int DRAW_RUNS = 5;
static final String DRAW_TIME_LOG = "/sdcard/android/page_draw_time.txt";
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
index f3d2e9c..2aa6799 100644
--- a/tests/DumpRenderTree2/Android.mk
+++ b/tests/DumpRenderTree2/Android.mk
@@ -5,6 +5,6 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := DumpRenderTree2
+LOCAL_PACKAGE_NAME := DumpRenderTree2
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
new file mode 100644
index 0000000..baa365c
--- /dev/null
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2">
+ <application>
+ <activity android:name=".ui.DirListActivity"
+ android:label="Dump Render Tree 2"
+ android:configChanges="orientation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/drawable/folder.png b/tests/DumpRenderTree2/res/drawable/folder.png
new file mode 100644
index 0000000..5b3fcec
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/folder.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/drawable/runtest.png b/tests/DumpRenderTree2/res/drawable/runtest.png
new file mode 100644
index 0000000..910c654
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/runtest.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/layout/dirlist_row.xml b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
new file mode 100644
index 0000000..e5578a6
--- /dev/null
+++ b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="80px"
+ android:adjustViewBounds="true"
+ android:paddingLeft="15px"
+ android:paddingRight="15px"
+ android:paddingTop="15px"
+ android:paddingBottom="15px"
+ android:layout_height="wrap_content"
+ />
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="60px"
+ android:gravity="center_vertical"
+ android:textSize="14sp"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/values/strings.xml b/tests/DumpRenderTree2/res/values/strings.xml
new file mode 100644
index 0000000..2dcd3ca
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<resources>
+ <string name="dialog_run_abort_dir_title_prefix">Directory:</string>
+ <string name="dialog_run_abort_dir_msg">This will run all the tests in this directory and all
+ the subdirectories. It may take a few hours!</string>
+ <string name="dialog_run_abort_dir_ok_button">Run tests!</string>
+ <string name="dialog_run_abort_dir_abort_button">Abort</string>
+
+ <string name="dialog_progress_title">Loading items.</string>
+ <string name="dialog_progress_msg">Please wait...</string>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
index 98801d3..64ef8a4 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -227,13 +227,14 @@
}
/**
- * Checks if the file is a test or something else.
+ * Checks if the file is a test.
+ * Currently we run .html and .xhtml tests.
*
* @param testName
* @return
* if the file is a test
*/
public static boolean isTestFile(String testName) {
- return testName.endsWith(".html");
+ return testName.endsWith(".html") || testName.endsWith(".xhtml");
}
-}
\ 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
new file mode 100644
index 0000000..d8509c1
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -0,0 +1,389 @@
+/*
+ * 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.ui;
+
+import com.android.dumprendertree2.FileFilter;
+import com.android.dumprendertree2.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An Activity that allows navigating through tests folders and choosing folders or tests to run.
+ */
+public class DirListActivity extends ListActivity {
+
+ private static final String LOG_TAG = "DirListActivity";
+ private static final String ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests";
+
+ /** TODO: This is just a guess - think of a better way to achieve it */
+ private static final int MEAN_TITLE_CHAR_SIZE = 13;
+
+ private static final int PROGRESS_DIALOG_DELAY_MS = 200;
+
+ /** Code for the dialog, used in showDialog and onCreateDialog */
+ private static final int DIALOG_RUN_ABORT_DIR = 0;
+
+ /** Messages codes */
+ private static final int MSG_LOADED_ITEMS = 0;
+ private static final int MSG_SHOW_PROGRESS_DIALOG = 1;
+
+ /** Initialized lazily before first sProgressDialog.show() */
+ private static ProgressDialog sProgressDialog;
+
+ private ListView mListView;
+
+ /** This is a relative path! */
+ private String mCurrentDirPath;
+
+ /**
+ * TODO: This should not be a constant, but rather be configurable from somewhere.
+ */
+ private String mRootDirPath = ROOT_DIR_PATH;
+
+ /**
+ * A thread responsible for loading the contents of the directory from sd card
+ * and sending them via Message to main thread that then loads them into
+ * ListView
+ */
+ private class LoadListItemsThread extends Thread {
+ private Handler mHandler;
+ private String mRelativePath;
+
+ public LoadListItemsThread(String relativePath, Handler handler) {
+ mRelativePath = relativePath;
+ mHandler = handler;
+ }
+
+ @Override
+ public void run() {
+ Message msg = mHandler.obtainMessage(MSG_LOADED_ITEMS);
+ msg.obj = getDirList(mRelativePath);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Very simple object to use inside ListView as an item.
+ */
+ private static class ListItem implements Comparable<ListItem> {
+ private String mRelativePath;
+ private String mName;
+ private boolean mIsDirectory;
+
+ public ListItem(String relativePath, boolean isDirectory) {
+ mRelativePath = relativePath;
+ mName = new File(relativePath).getName();
+ mIsDirectory = isDirectory;
+ }
+
+ public boolean isDirectory() {
+ return mIsDirectory;
+ }
+
+ public String getRelativePath() {
+ return mRelativePath;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public int compareTo(ListItem another) {
+ return mRelativePath.compareTo(another.getRelativePath());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ListItem)) {
+ return false;
+ }
+
+ return mRelativePath.equals(((ListItem) o).getRelativePath());
+ }
+
+ @Override
+ public int hashCode() {
+ return mRelativePath.hashCode();
+ }
+
+ }
+
+ /**
+ * A custom adapter that sets the proper icon and label in the list view.
+ */
+ private static class DirListAdapter extends ArrayAdapter<ListItem> {
+ private Activity mContext;
+ private ListItem[] mItems;
+
+ public DirListAdapter(Activity context, ListItem[] items) {
+ super(context, R.layout.dirlist_row, items);
+
+ mContext = context;
+ mItems = items;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = mContext.getLayoutInflater();
+ View row = inflater.inflate(R.layout.dirlist_row, null);
+
+ TextView label = (TextView) row.findViewById(R.id.label);
+ label.setText(mItems[position].getName());
+
+ ImageView icon = (ImageView) row.findViewById(R.id.icon);
+ if (mItems[position].isDirectory()) {
+ icon.setImageResource(R.drawable.folder);
+ } else {
+ icon.setImageResource(R.drawable.runtest);
+ }
+
+ return row;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mListView = getListView();
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListItem item = (ListItem) parent.getItemAtPosition(position);
+
+ if (item.isDirectory()) {
+ showDir(item.getRelativePath());
+ } else {
+ /** TODO: run the test */
+ }
+ }
+ });
+
+ mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ ListItem item = (ListItem) parent.getItemAtPosition(position);
+
+ if (item.isDirectory()) {
+ Bundle arguments = new Bundle(1);
+ arguments.putString("name", item.getName());
+ arguments.putString("relativePath", item.getRelativePath());
+ showDialog(DIALOG_RUN_ABORT_DIR, arguments);
+ } else {
+ /** TODO: Maybe show some info about a test? */
+ }
+
+ return true;
+ }
+ });
+
+ /** All the paths are relative to test root dir where possible */
+ showDir("");
+ }
+
+ @Override
+ /**
+ * Moves to the parent directory if one exists. Does not allow to move above
+ * the test 'root' directory.
+ */
+ public void onBackPressed() {
+ File currentDirParent = new File(mCurrentDirPath).getParentFile();
+ if (currentDirParent != null) {
+ showDir(currentDirParent.getPath());
+ } else {
+ showDir("");
+ }
+ }
+
+ /**
+ * Prevents the activity from recreating on change of orientation. The title needs to
+ * be recalculated.
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ setTitle(shortenTitle(mCurrentDirPath));
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle args) {
+ Dialog dialog = null;
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ switch (id) {
+ case DIALOG_RUN_ABORT_DIR:
+ builder.setTitle(getText(R.string.dialog_run_abort_dir_title_prefix) + " " +
+ args.getString("name"));
+ builder.setMessage(R.string.dialog_run_abort_dir_msg);
+ builder.setCancelable(true);
+
+ builder.setPositiveButton(R.string.dialog_run_abort_dir_ok_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ /** TODO: Run tests from the dir */
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ }
+ });
+
+ builder.setNegativeButton(R.string.dialog_run_abort_dir_abort_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ }
+ });
+
+ dialog = builder.create();
+ dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ }
+ });
+ break;
+ }
+
+ return dialog;
+ }
+
+ /**
+ * Loads the contents of dir into the list view.
+ *
+ * @param dirPath
+ * directory to load into list view
+ */
+ private void showDir(String dirPath) {
+ mCurrentDirPath = dirPath;
+
+ /** Show progress dialog with a delay */
+ final Handler delayedDialogHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SHOW_PROGRESS_DIALOG) {
+ if (sProgressDialog == null) {
+ sProgressDialog = new ProgressDialog(DirListActivity.this);
+ sProgressDialog.setCancelable(false);
+ sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ sProgressDialog.setTitle(R.string.dialog_progress_title);
+ sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
+ }
+ sProgressDialog.show();
+ }
+ }
+ };
+ Message msgShowDialog = delayedDialogHandler.obtainMessage(MSG_SHOW_PROGRESS_DIALOG);
+ delayedDialogHandler.sendMessageDelayed(msgShowDialog, PROGRESS_DIALOG_DELAY_MS);
+
+ /** Delegate loading contents from SD card to a new thread */
+ new LoadListItemsThread(mCurrentDirPath, new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_LOADED_ITEMS) {
+ setListAdapter(new DirListAdapter(DirListActivity.this,
+ (ListItem[])msg.obj));
+ delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
+ setTitle(shortenTitle(mCurrentDirPath));
+ if (sProgressDialog != null) {
+ sProgressDialog.dismiss();
+ }
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * TODO: find a neat way to determine number of characters that fit in the title
+ * bar.
+ * */
+ private String shortenTitle(String title) {
+ if (title.equals("")) {
+ return "Tests' root dir:";
+ }
+ int charCount = mListView.getWidth() / MEAN_TITLE_CHAR_SIZE;
+
+ if (title.length() > charCount) {
+ return "..." + title.substring(title.length() - charCount);
+ } else {
+ return title;
+ }
+ }
+
+ /**
+ * Return the array with contents of the given directory.
+ * First it contains the subfolders, then the files. Both sorted
+ * alphabetically.
+ *
+ * The dirPath is relative.
+ */
+ private ListItem[] getDirList(String dirPath) {
+ File dir = new File(mRootDirPath, dirPath);
+
+ List<ListItem> subDirs = new ArrayList<ListItem>();
+ List<ListItem> subFiles = new ArrayList<ListItem>();
+
+ for (File item : dir.listFiles()) {
+ if (item.isDirectory() && FileFilter.isTestDir(item.getName())) {
+ subDirs.add(new ListItem(getRelativePath(item), true));
+ } else if (FileFilter.isTestFile(item.getName())) {
+ subFiles.add(new ListItem(getRelativePath(item), false));
+ }
+ }
+
+ Collections.sort(subDirs);
+ Collections.sort(subFiles);
+
+ /** Concatenate the two lists */
+ subDirs.addAll(subFiles);
+
+ return subDirs.toArray(new ListItem[subDirs.size()]);
+ }
+
+ private String getRelativePath(File file) {
+ File rootDir = new File(mRootDirPath);
+ return file.getAbsolutePath().replaceFirst(rootDir.getPath() + File.separator, "");
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 79eed4e..098359c 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -69,6 +69,42 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="NinePatchesActivity"
+ android:label="_9patch">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="QuickRejectActivity"
+ android:label="_QuickReject">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="RotationActivity"
+ android:label="_Rotation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="ShadersActivity"
+ android:label="_Shaders">
+ <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/src/com/google/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
index dfc8a71..cfa8d3c 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
@@ -25,9 +25,11 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Bundle;
+import android.view.Gravity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
@SuppressWarnings({"UnusedDeclaration"})
public class BitmapsActivity extends Activity {
@@ -35,7 +37,9 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final BitmapsView view = new BitmapsView(this);
- setContentView(view);
+ final FrameLayout layout = new FrameLayout(this);
+ layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+ setContentView(layout);
ScaleAnimation a = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
@@ -50,6 +54,7 @@
private Paint mBitmapPaint;
private final Bitmap mBitmap1;
private final Bitmap mBitmap2;
+ private final PorterDuffXfermode mDstIn;
BitmapsView(Context c) {
super(c);
@@ -58,6 +63,7 @@
mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
mBitmapPaint = new Paint();
+ mDstIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
@Override
@@ -81,10 +87,10 @@
canvas.translate(0.0f, 25.0f);
mBitmapPaint.setColor(0xffff0000);
canvas.drawRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), mBitmapPaint);
- mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+ mBitmapPaint.setXfermode(mDstIn);
canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint);
- mBitmapPaint = new Paint();
+ mBitmapPaint.reset();
}
}
}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java
new file mode 100644
index 0000000..3268fbf
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class NinePatchesActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ Button b = new Button(this);
+ b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+ b.setText("9 patches");
+ layout.addView(b);
+ layout.setBackgroundColor(0xffffffff);
+
+ setContentView(layout);
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
new file mode 100644
index 0000000..fd7a1e6
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class QuickRejectActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final QuickRejectView view = new QuickRejectView(this);
+ setContentView(view);
+ }
+
+ static class QuickRejectView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+
+ QuickRejectView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+ mBitmapPaint = new Paint();
+ mBitmapPaint.setFilterBitmap(true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.save();
+ canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+ canvas.drawBitmap(mBitmap1, -mBitmap1.getWidth(), 0.0f, mBitmapPaint);
+ canvas.drawBitmap(mBitmap1, 50.0f, 0.0f, mBitmapPaint);
+ canvas.restore();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java
new file mode 100644
index 0000000..e629cb8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java
@@ -0,0 +1,63 @@
+/*
+ * 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.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RotationActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ DrawingView container = new DrawingView(this);
+
+ setContentView(container);
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static int dipToPx(Context c, int dip) {
+ return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
+ }
+
+ static class DrawingView extends View {
+ private final Paint mPaint;
+
+ DrawingView(Context c) {
+ super(c);
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.translate(dipToPx(getContext(), 400), dipToPx(getContext(), 200));
+ canvas.rotate(45.0f);
+ canvas.drawRGB(255, 255, 255);
+ mPaint.setColor(0xffff0000);
+ canvas.drawRect(-80.0f, -80.0f, 80.0f, 80.0f, mPaint);
+ canvas.drawRect(0.0f, 0.0f, 220.0f, 220.0f, mPaint);
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
new file mode 100644
index 0000000..851a06c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
@@ -0,0 +1,131 @@
+/*
+ * 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.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ShadersActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mRepeatShader;
+ private BitmapShader mTranslatedShader;
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private LinearGradient mDiagGradient;
+ private LinearGradient mVertGradient;
+
+ 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;
+
+ mRepeatShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+
+ mTranslatedShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ Matrix m1 = new Matrix();
+ m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+ mTranslatedShader.setLocalMatrix(m1);
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.RED, Color.GREEN, Shader.TileMode.REPEAT);
+
+ mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
+ Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
+
+ mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+ Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ // Bitmap shaders
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mRepeatShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mTranslatedShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mScaledShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ // Gradients
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mHorGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mDiagGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mVertGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 6d606a9..135a633 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -52,13 +52,15 @@
$(LOCAL_BUILT_MODULE): $(built_core_dep) \
$(built_framework_dep) \
$(built_layoutlib_create_jar)
- @echo "host layoutlib_create: $@"
- @mkdir -p $(dir $@)
- @rm -f $@
+ $(hide) echo "host layoutlib_create: $@"
+ $(hide) mkdir -p $(dir $@)
+ $(hide) rm -f $@
+ $(hide) ls -l $(built_framework_classes)
$(hide) java -jar $(built_layoutlib_create_jar) \
$@ \
$(built_core_classes) \
$(built_framework_classes)
+ $(hide) ls -l $(built_framework_classes)
#
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 5b4faf97..5780a04 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -30,6 +30,7 @@
import android.net.ConnectivityManager;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
+import android.net.NetworkProperties;
import android.os.Message;
import android.os.Parcelable;
import android.os.Handler;
@@ -54,6 +55,9 @@
import android.database.ContentObserver;
import com.android.internal.app.IBatteryStats;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
@@ -211,6 +215,7 @@
private boolean mDisconnectExpected;
private DhcpHandler mDhcpTarget;
private DhcpInfo mDhcpInfo;
+ private NetworkProperties mNetworkProperties;
private int mLastSignalLevel = -1;
private String mLastBssid;
private String mLastSsid;
@@ -315,7 +320,6 @@
private String mInterfaceName;
private static String LS = System.getProperty("line.separator");
- private static String[] sDnsPropNames;
private Handler mTarget;
private Context mContext;
private boolean mPrivateDnsRouteSet = false;
@@ -379,10 +383,7 @@
mSettingsObserver = new SettingsObserver(new Handler());
mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- sDnsPropNames = new String[] {
- "dhcp." + mInterfaceName + ".dns1",
- "dhcp." + mInterfaceName + ".dns2"
- };
+ mNetworkProperties = new NetworkProperties();
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
}
@@ -477,15 +478,6 @@
}
/**
- * Return the IP addresses of the DNS servers available for the WLAN
- * network interface.
- * @return a list of DNS addresses, with no holes.
- */
- public String[] getDnsPropNames() {
- return sDnsPropNames;
- }
-
- /**
* Return the name of our WLAN network interface.
* @return the name of our interface.
*/
@@ -901,6 +893,7 @@
}
setDetailedState(DetailedState.DISCONNECTED);
setSupplicantState(SupplicantState.UNINITIALIZED);
+ mNetworkProperties.clear();
mHaveIpAddress = false;
mObtainingIpAddress = false;
if (died) {
@@ -1008,6 +1001,7 @@
reconnectCommand();
}
} else if (newState == SupplicantState.DISCONNECTED) {
+ mNetworkProperties.clear();
mHaveIpAddress = false;
if (isDriverStopped() || mDisconnectExpected) {
handleDisconnectedState(DetailedState.DISCONNECTED, true);
@@ -1192,6 +1186,7 @@
}
mReconnectCount = 0;
mHaveIpAddress = true;
+ configureNetworkProperties();
mObtainingIpAddress = false;
mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
mLastSignalLevel = -1; // force update of signal strength
@@ -1217,6 +1212,7 @@
// [31- 1] Reserved for future use
// [ 0- 0] Interface configuration succeeded (1) or failed (0)
EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
+ mNetworkProperties.clear();
mHaveIpAddress = false;
mWifiInfo.setIpAddress(0);
mObtainingIpAddress = false;
@@ -1289,6 +1285,49 @@
return disabledNetwork;
}
+
+ 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 configureInterface() {
checkPollTimer();
mLastSignalLevel = -1;
@@ -1300,11 +1339,9 @@
} else {
int event;
if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- mHaveIpAddress = true;
event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
} else {
- mHaveIpAddress = false;
event = EVENT_INTERFACE_CONFIGURATION_FAILED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
}
@@ -1339,6 +1376,7 @@
*/
public void resetConnections(boolean disableInterface) {
if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
+ mNetworkProperties.clear();
mHaveIpAddress = false;
mObtainingIpAddress = false;
mWifiInfo.setIpAddress(0);
@@ -2282,11 +2320,12 @@
mNotificationRepeatTime = 0;
mNumScansSinceNetworkStateChange = 0;
}
-
+
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("interface ").append(mInterfaceName);
+
+ sb.append(mNetworkProperties.toString());
sb.append(" runState=");
if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
sb.append(mRunStateNames[mRunState-1]);
@@ -2366,7 +2405,7 @@
setBluetoothCoexistenceMode(
WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
-
+
powerMode = getPowerMode();
if (powerMode < 0) {
// Handle the case where supplicant driver does not support
@@ -2588,4 +2627,8 @@
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
}
}
+
+ public NetworkProperties getNetworkProperties() {
+ return mNetworkProperties;
+ }
}