Merge "Handle RS reflection name changes."
diff --git a/Android.mk b/Android.mk
index 901f212..9492167 100644
--- a/Android.mk
+++ b/Android.mk
@@ -356,6 +356,7 @@
# The since flag (-since N.xml API_LEVEL) is used to add API Level information
# to the reference documentation. Must be in order of oldest to newest.
framework_docs_LOCAL_DROIDDOC_OPTIONS := \
+ -knowntags ./frameworks/base/docs/knowntags.txt \
-since ./frameworks/base/api/1.xml 1 \
-since ./frameworks/base/api/2.xml 2 \
-since ./frameworks/base/api/3.xml 3 \
@@ -365,11 +366,13 @@
-since ./frameworks/base/api/7.xml 7 \
-since ./frameworks/base/api/8.xml 8 \
-since ./frameworks/base/api/current.xml HC \
- -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 -warning 113 \
- -error 114 \
+ -werror -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+
+framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
+ frameworks/base/docs/knowntags.txt
sample_dir := development/samples
@@ -431,25 +434,12 @@
framework_docs_SDK_VERSION:=2.2
# release version (ie "Release x") (full releases only)
framework_docs_SDK_REL_ID:=1
- # name of current SDK directory (full releases only)
-framework_docs_SDK_CURRENT_DIR:=$(framework_docs_SDK_VERSION)_r$(framework_docs_SDK_REL_ID)
# flag to build offline docs for a preview release
framework_docs_SDK_PREVIEW:=0
-## Latest ADT version identifiers, for reference from published docs
-framework_docs_ADT_VERSION:=0.9.8
-framework_docs_ADT_DOWNLOAD:=ADT-0.9.8.zip
-framework_docs_ADT_BYTES:=8301417
-framework_docs_ADT_CHECKSUM:=27e0de800512f13feae46fb554e6ee2f
-
framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-hdf sdk.version $(framework_docs_SDK_VERSION) \
- -hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
- -hdf sdk.current $(framework_docs_SDK_CURRENT_DIR) \
- -hdf adt.zip.version $(framework_docs_ADT_VERSION) \
- -hdf adt.zip.download $(framework_docs_ADT_DOWNLOAD) \
- -hdf adt.zip.bytes $(framework_docs_ADT_BYTES) \
- -hdf adt.zip.checksum $(framework_docs_ADT_CHECKSUM)
+ -hdf sdk.rel.id $(framework_docs_SDK_REL_ID)
# ==== the api stubs and current.xml ===========================
include $(CLEAR_VARS)
@@ -461,6 +451,7 @@
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := api-stubs
@@ -491,6 +482,7 @@
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := doc-comment-check
@@ -519,6 +511,7 @@
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := offline-sdk
@@ -533,7 +526,7 @@
-hdf android.whichdoc offline
ifeq ($(framework_docs_SDK_PREVIEW),true)
- LOCAL_DROIDDOC_OPTIONS += -hdf sdk.current preview
+ LOCAL_DROIDDOC_OPTIONS += -hdf sdk.preview true
endif
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
@@ -560,6 +553,7 @@
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := online-sdk
@@ -587,6 +581,7 @@
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
LOCAL_MODULE := hidden
LOCAL_DROIDDOC_OPTIONS:=\
@@ -620,6 +615,7 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := core
LOCAL_JAVA_RESOURCE_DIRS := $(ext_res_dirs)
+LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := ext
LOCAL_NO_EMMA_INSTRUMENT := true
diff --git a/api/current.xml b/api/current.xml
index 857fc55..3049bcb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2792,6 +2792,28 @@
visibility="public"
>
</field>
+<field name="breadCrumbShortTitle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843589"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="breadCrumbTitle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843588"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="bufferType"
type="int"
transient="false"
@@ -17384,6 +17406,17 @@
visibility="public"
>
</field>
+<field name="Widget_FragmentBreadCrumbs"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973986"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget_Gallery"
type="int"
transient="false"
@@ -29343,6 +29376,113 @@
</parameter>
</constructor>
</class>
+<class name="FragmentBreadCrumbs"
+ extends="android.view.ViewGroup"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.app.FragmentManager.OnBackStackChangedListener">
+</implements>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
+<method name="onBackStackChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onLayout"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="changed" type="boolean">
+</parameter>
+<parameter name="l" type="int">
+</parameter>
+<parameter name="t" type="int">
+</parameter>
+<parameter name="r" type="int">
+</parameter>
+<parameter name="b" type="int">
+</parameter>
+</method>
+<method name="setActivity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="a" type="android.app.Activity">
+</parameter>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+<parameter name="shortTitle" type="java.lang.CharSequence">
+</parameter>
+</method>
+</class>
<interface name="FragmentManager"
abstract="true"
static="false"
@@ -29350,6 +29490,30 @@
deprecated="not deprecated"
visibility="public"
>
+<method name="addOnBackStackChangedListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.FragmentManager.OnBackStackChangedListener">
+</parameter>
+</method>
+<method name="countBackStackEntries"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="findFragmentById"
return="android.app.Fragment"
abstract="true"
@@ -29376,6 +29540,19 @@
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
+<method name="getBackStackEntry"
+ return="android.app.FragmentManager.BackStackEntry"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
<method name="getFragment"
return="android.app.Fragment"
abstract="true"
@@ -29460,6 +29637,19 @@
<parameter name="fragment" type="android.app.Fragment">
</parameter>
</method>
+<method name="removeOnBackStackChangedListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.FragmentManager.OnBackStackChangedListener">
+</parameter>
+</method>
<field name="POP_BACK_STACK_INCLUSIVE"
type="int"
transient="false"
@@ -29472,6 +29662,66 @@
>
</field>
</interface>
+<interface name="FragmentManager.BackStackEntry"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getBreadCrumbShortTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBreadCrumbTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
+<interface name="FragmentManager.OnBackStackChangedListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onBackStackChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<interface name="FragmentTransaction"
abstract="true"
static="false"
@@ -29619,6 +29869,58 @@
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
+<method name="setBreadCrumbShortTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="int">
+</parameter>
+</method>
+<method name="setBreadCrumbShortTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setBreadCrumbTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="int">
+</parameter>
+</method>
+<method name="setBreadCrumbTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
<method name="setCustomAnimations"
return="android.app.FragmentTransaction"
abstract="true"
@@ -33240,6 +33542,17 @@
visibility="public"
>
</field>
+<field name="FLAG_QUERY_REFINEMENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="INTENT_ACTION_GLOBAL_SEARCH"
type="java.lang.String"
transient="false"
@@ -33339,6 +33652,17 @@
visibility="public"
>
</field>
+<field name="SUGGEST_COLUMN_FLAGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""suggest_flags""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_COLUMN_FORMAT"
type="java.lang.String"
transient="false"
@@ -39375,7 +39699,7 @@
value="2743"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -64214,10 +64538,32 @@
visibility="public"
>
</field>
+<field name="STATEMENT_DDL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="STATEMENT_OTHER"
type="int"
transient="false"
volatile="false"
+ value="99"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_PRAGMA"
+ type="int"
+ transient="false"
+ volatile="false"
value="7"
static="true"
final="true"
@@ -64236,6 +64582,17 @@
visibility="public"
>
</field>
+<field name="STATEMENT_UNPREPARED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="STATEMENT_UPDATE"
type="int"
transient="false"
@@ -85535,7 +85892,7 @@
visibility="public"
>
</field>
-<field name="mFacing"
+<field name="facing"
type="int"
transient="false"
volatile="false"
@@ -85545,7 +85902,7 @@
visibility="public"
>
</field>
-<field name="mOrientation"
+<field name="orientation"
type="int"
transient="false"
volatile="false"
@@ -142347,6 +142704,21 @@
<parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
</parameter>
</method>
+<method name="showBreadCrumbs"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+<parameter name="shortTitle" type="java.lang.CharSequence">
+</parameter>
+</method>
<method name="startPreferenceFragment"
return="void"
abstract="false"
@@ -142362,6 +142734,21 @@
<parameter name="push" type="boolean">
</parameter>
</method>
+<method name="startPreferenceFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
<method name="startWithFragment"
return="void"
abstract="false"
@@ -142405,6 +142792,21 @@
<parameter name="header" type="android.preference.PreferenceActivity.Header">
</parameter>
</method>
+<method name="switchToHeaderInner"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragmentName" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
<field name="EXTRA_NO_HEADERS"
type="java.lang.String"
transient="false"
@@ -142517,6 +142919,26 @@
visibility="public"
>
</field>
+<field name="breadCrumbShortTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="breadCrumbTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="extras"
type="android.os.Bundle"
transient="false"
@@ -147962,7 +148384,7 @@
visibility="public"
>
</field>
-<field name="TYPE_MAINDEN_NAME"
+<field name="TYPE_MAIDEN_NAME"
type="int"
transient="false"
volatile="false"
@@ -147973,6 +148395,17 @@
visibility="public"
>
</field>
+<field name="TYPE_MAINDEN_NAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
<field name="TYPE_OTHER_NAME"
type="int"
transient="false"
@@ -148728,8 +149161,40 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.provider.ContactsContract.CommonDataKinds.CommonColumns">
+</implements>
<implements name="android.provider.ContactsContract.DataColumnsWithJoins">
</implements>
+<method name="getTypeLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="android.content.res.Resources">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="getTypeLabelResource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+</method>
<field name="CONTENT_ITEM_TYPE"
type="java.lang.String"
transient="false"
@@ -148752,6 +149217,39 @@
visibility="public"
>
</field>
+<field name="TYPE_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_OTHER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_WORK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ContactsContract.CommonDataKinds.StructuredName"
extends="java.lang.Object"
@@ -190008,17 +190506,6 @@
visibility="public"
>
</field>
-<field name="SOURCE_CLASS_JOYSTICK"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="SOURCE_CLASS_MASK"
type="int"
transient="false"
@@ -190074,39 +190561,6 @@
visibility="public"
>
</field>
-<field name="SOURCE_GAMEPAD"
- type="int"
- transient="false"
- volatile="false"
- value="1025"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_JOYSTICK_LEFT"
- type="int"
- transient="false"
- volatile="false"
- value="16777232"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_JOYSTICK_RIGHT"
- type="int"
- transient="false"
- volatile="false"
- value="33554448"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="SOURCE_KEYBOARD"
type="int"
transient="false"
@@ -233104,6 +233558,21 @@
<parameter name="value" type="long">
</parameter>
</method>
+<method name="setOnClickExtras"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
<method name="setOnClickPendingIntent"
return="void"
abstract="false"
@@ -233119,6 +233588,21 @@
<parameter name="pendingIntent" type="android.app.PendingIntent">
</parameter>
</method>
+<method name="setPendingIntentTemplate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="pendingIntentTemplate" type="android.app.PendingIntent">
+</parameter>
+</method>
<method name="setProgressBar"
return="void"
abstract="false"
@@ -234270,6 +234754,17 @@
visibility="public"
>
</method>
+<method name="isQueryRefinementEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isSubmitButtonEnabled"
return="boolean"
abstract="false"
@@ -234361,6 +234856,19 @@
<parameter name="hint" type="java.lang.CharSequence">
</parameter>
</method>
+<method name="setQueryRefinementEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enable" type="boolean">
+</parameter>
+</method>
<method name="setSearchableInfo"
return="void"
abstract="false"
@@ -234401,18 +234909,6 @@
</parameter>
</method>
</class>
-<interface name="SearchView.FilterableListAdapter"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.widget.Filterable">
-</implements>
-<implements name="android.widget.ListAdapter">
-</implements>
-</interface>
<interface name="SearchView.OnCloseListener"
abstract="true"
static="true"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 4a1d27b..8ab94ad 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -60,7 +60,7 @@
return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
}
-static void playSource(OMXClient *client, const sp<MediaSource> &source) {
+static void playSource(OMXClient *client, sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
const char *mime;
@@ -81,6 +81,8 @@
}
}
+ source.clear();
+
status_t err = rawSource->start();
if (err != OK) {
diff --git a/core/java/android/animation/AnimatorListenerAdapter.java b/core/java/android/animation/AnimatorListenerAdapter.java
index 6182389..e5d70a4 100644
--- a/core/java/android/animation/AnimatorListenerAdapter.java
+++ b/core/java/android/animation/AnimatorListenerAdapter.java
@@ -24,28 +24,28 @@
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
/**
- * {@inheritdoc}
+ * {@inheritDoc}
*/
@Override
public void onAnimationCancel(Animator animation) {
}
/**
- * {@inheritdoc}
+ * {@inheritDoc}
*/
@Override
public void onAnimationEnd(Animator animation) {
}
/**
- * {@inheritdoc}
+ * {@inheritDoc}
*/
@Override
public void onAnimationRepeat(Animator animation) {
}
/**
- * {@inheritdoc}
+ * {@inheritDoc}
*/
@Override
public void onAnimationStart(Animator animation) {
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackRecord.java
similarity index 83%
rename from core/java/android/app/BackStackEntry.java
rename to core/java/android/app/BackStackRecord.java
index 296b495..e6cc0f9 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@@ -28,16 +29,20 @@
final int mTransitionStyle;
final String mName;
final int mIndex;
-
- public BackStackState(FragmentManagerImpl fm, BackStackEntry bse) {
+ final int mBreadCrumbTitleRes;
+ final CharSequence mBreadCrumbTitleText;
+ final int mBreadCrumbShortTitleRes;
+ final CharSequence mBreadCrumbShortTitleText;
+
+ public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
int numRemoved = 0;
- BackStackEntry.Op op = bse.mHead;
+ BackStackRecord.Op op = bse.mHead;
while (op != null) {
if (op.removed != null) numRemoved += op.removed.size();
op = op.next;
}
mOps = new int[bse.mNumOp*5 + numRemoved];
-
+
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
}
@@ -64,21 +69,29 @@
mTransitionStyle = bse.mTransitionStyle;
mName = bse.mName;
mIndex = bse.mIndex;
+ mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
+ mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
+ mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
+ mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
}
-
+
public BackStackState(Parcel in) {
mOps = in.createIntArray();
mTransition = in.readInt();
mTransitionStyle = in.readInt();
mName = in.readString();
mIndex = in.readInt();
+ mBreadCrumbTitleRes = in.readInt();
+ mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mBreadCrumbShortTitleRes = in.readInt();
+ mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
-
- public BackStackEntry instantiate(FragmentManagerImpl fm) {
- BackStackEntry bse = new BackStackEntry(fm);
+
+ public BackStackRecord instantiate(FragmentManagerImpl fm) {
+ BackStackRecord bse = new BackStackRecord(fm);
int pos = 0;
while (pos < mOps.length) {
- BackStackEntry.Op op = new BackStackEntry.Op();
+ BackStackRecord.Op op = new BackStackRecord.Op();
op.cmd = mOps[pos++];
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"BSE " + bse + " set base fragment #" + mOps[pos]);
@@ -103,10 +116,14 @@
bse.mName = mName;
bse.mIndex = mIndex;
bse.mAddToBackStack = true;
+ bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
+ bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
+ bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
+ bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
bse.bumpBackStackNesting(1);
return bse;
}
-
+
public int describeContents() {
return 0;
}
@@ -117,14 +134,18 @@
dest.writeInt(mTransitionStyle);
dest.writeString(mName);
dest.writeInt(mIndex);
+ dest.writeInt(mBreadCrumbTitleRes);
+ TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
+ dest.writeInt(mBreadCrumbShortTitleRes);
+ TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
}
-
+
public static final Parcelable.Creator<BackStackState> CREATOR
= new Parcelable.Creator<BackStackState>() {
public BackStackState createFromParcel(Parcel in) {
return new BackStackState(in);
}
-
+
public BackStackState[] newArray(int size) {
return new BackStackState[size];
}
@@ -134,18 +155,19 @@
/**
* @hide Entry of an operation on the fragment back stack.
*/
-final class BackStackEntry implements FragmentTransaction, Runnable {
+final class BackStackRecord implements FragmentTransaction,
+ FragmentManager.BackStackEntry, Runnable {
static final String TAG = "BackStackEntry";
-
+
final FragmentManagerImpl mManager;
-
+
static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
-
+
static final class Op {
Op next;
Op prev;
@@ -155,7 +177,7 @@
int exitAnim;
ArrayList<Fragment> removed;
}
-
+
Op mHead;
Op mTail;
int mNumOp;
@@ -167,11 +189,34 @@
String mName;
boolean mCommitted;
int mIndex;
-
- public BackStackEntry(FragmentManagerImpl manager) {
+
+ int mBreadCrumbTitleRes;
+ CharSequence mBreadCrumbTitleText;
+ int mBreadCrumbShortTitleRes;
+ CharSequence mBreadCrumbShortTitleText;
+
+ public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
-
+
+ public int getId() {
+ return mIndex;
+ }
+
+ public CharSequence getBreadCrumbTitle() {
+ if (mBreadCrumbTitleRes != 0) {
+ return mManager.mActivity.getText(mBreadCrumbTitleRes);
+ }
+ return mBreadCrumbTitleText;
+ }
+
+ public CharSequence getBreadCrumbShortTitle() {
+ if (mBreadCrumbShortTitleRes != 0) {
+ return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
+ }
+ return mBreadCrumbShortTitleText;
+ }
+
void addOp(Op op) {
if (mHead == null) {
mHead = mTail = op;
@@ -184,7 +229,7 @@
op.exitAnim = mExitAnim;
mNumOp++;
}
-
+
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
@@ -206,7 +251,7 @@
}
fragment.mImmediateActivity = mManager.mActivity;
fragment.mFragmentManager = mManager;
-
+
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
@@ -215,7 +260,7 @@
}
fragment.mTag = tag;
}
-
+
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
@@ -224,7 +269,7 @@
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
-
+
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
@@ -234,27 +279,27 @@
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
-
+
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
-
+
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
-
+
public FragmentTransaction remove(Fragment fragment) {
if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
fragment.mImmediateActivity = null;
-
+
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);
-
+
return this;
}
@@ -262,50 +307,74 @@
if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
-
+
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);
-
+
return this;
}
-
+
public FragmentTransaction show(Fragment fragment) {
if (fragment.mImmediateActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
-
+
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);
-
+
return this;
}
-
+
public FragmentTransaction setCustomAnimations(int enter, int exit) {
mEnterAnim = enter;
mExitAnim = exit;
return this;
}
-
+
public FragmentTransaction setTransition(int transition) {
mTransition = transition;
return this;
}
-
+
public FragmentTransaction setTransitionStyle(int styleRes) {
mTransitionStyle = styleRes;
return this;
}
-
+
public FragmentTransaction addToBackStack(String name) {
mAddToBackStack = true;
mName = name;
return this;
}
+ public FragmentTransaction setBreadCrumbTitle(int res) {
+ mBreadCrumbTitleRes = res;
+ mBreadCrumbTitleText = null;
+ return this;
+ }
+
+ public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
+ mBreadCrumbTitleRes = 0;
+ mBreadCrumbTitleText = text;
+ return this;
+ }
+
+ public FragmentTransaction setBreadCrumbShortTitle(int res) {
+ mBreadCrumbShortTitleRes = res;
+ mBreadCrumbShortTitleText = null;
+ return this;
+ }
+
+ public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
+ mBreadCrumbShortTitleRes = 0;
+ mBreadCrumbShortTitleText = text;
+ return this;
+ }
+
void bumpBackStackNesting(int amt) {
if (!mAddToBackStack) {
return;
@@ -341,10 +410,10 @@
mManager.enqueueAction(this);
return mIndex;
}
-
+
public void run() {
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
-
+
if (mAddToBackStack) {
if (mIndex < 0) {
throw new IllegalStateException("addToBackStack() called after commit()");
@@ -405,23 +474,23 @@
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
-
+
op = op.next;
}
-
+
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
-
+
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
-
+
public void popFromBackStack(boolean doStateMove) {
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
bumpBackStackNesting(-1);
-
+
Op op = mTail;
while (op != null) {
switch (op.cmd) {
@@ -465,10 +534,10 @@
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
-
+
op = op.prev;
}
-
+
if (doStateMove) {
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
@@ -479,15 +548,15 @@
mIndex = -1;
}
}
-
+
public String getName() {
return mName;
}
-
+
public int getTransition() {
return mTransition;
}
-
+
public int getTransitionStyle() {
return mTransitionStyle;
}
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
new file mode 100644
index 0000000..0d39d0b
--- /dev/null
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -0,0 +1,175 @@
+/*
+ * 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.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Helper class for showing "bread crumbs" representing the fragment
+ * stack in an activity. This is intended to be used with
+ * {@link ActionBar#setCustomNavigationMode(View)
+ * ActionBar.setCustomNavigationMode(View)} to place the bread crumbs in
+ * the navigation area of the action bar.
+ *
+ * <p>The default style for this view is
+ * {@link android.R.style#Widget_FragmentBreadCrumbs}.
+ */
+public class FragmentBreadCrumbs extends ViewGroup
+ implements FragmentManager.OnBackStackChangedListener {
+ Activity mActivity;
+ LayoutInflater mInflater;
+ LinearLayout mContainer;
+
+ // Hahah
+ BackStackRecord mTopEntry;
+
+ public FragmentBreadCrumbs(Context context) {
+ this(context, null);
+ }
+
+ public FragmentBreadCrumbs(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.style.Widget_FragmentBreadCrumbs);
+ }
+
+ public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Attach the bread crumbs to their activity. This must be called once
+ * when creating the bread crumbs.
+ */
+ public void setActivity(Activity a) {
+ mActivity = a;
+ mInflater = (LayoutInflater)a.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mContainer = (LinearLayout)mInflater.inflate(
+ com.android.internal.R.layout.fragment_bread_crumbs,
+ this, false);
+ addView(mContainer);
+ a.getFragmentManager().addOnBackStackChangedListener(this);
+ updateCrumbs();
+ }
+
+ /**
+ * Set a custom title for the bread crumbs. This will be the first entry
+ * shown at the left, representing the root of the bread crumbs. If the
+ * title is null, it will not be shown.
+ */
+ public void setTitle(CharSequence title, CharSequence shortTitle) {
+ if (title == null) {
+ mTopEntry = null;
+ } else {
+ mTopEntry = new BackStackRecord((FragmentManagerImpl)
+ mActivity.getFragmentManager());
+ mTopEntry.setBreadCrumbTitle(title);
+ mTopEntry.setBreadCrumbShortTitle(shortTitle);
+ }
+ updateCrumbs();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Eventually we should implement our own layout of the views,
+ // rather than relying on a linear layout.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ int childRight = mPaddingLeft + child.getMeasuredWidth() - mPaddingRight;
+ int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
+ child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int count = getChildCount();
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+
+ // Find rightmost and bottom-most child
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ }
+ }
+
+ // Account for padding too
+ maxWidth += mPaddingLeft + mPaddingRight;
+ maxHeight += mPaddingTop + mPaddingBottom;
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
+ resolveSize(maxHeight, heightMeasureSpec));
+ }
+
+ @Override
+ public void onBackStackChanged() {
+ updateCrumbs();
+ }
+
+ void updateCrumbs() {
+ FragmentManager fm = mActivity.getFragmentManager();
+ int numEntries = fm.countBackStackEntries();
+ int numViews = mContainer.getChildCount();
+ for (int i=mTopEntry != null ? -1 : 0; i<numEntries; i++) {
+ FragmentManager.BackStackEntry bse = i == -1 ? mTopEntry : fm.getBackStackEntry(i);
+ int viewI = mTopEntry != null ? i+1 : i;
+ if (viewI < numViews) {
+ View v = mContainer.getChildAt(viewI);
+ Object tag = v.getTag();
+ if (tag != bse) {
+ for (int j=viewI; j<numViews; j++) {
+ mContainer.removeViewAt(viewI);
+ }
+ numViews = viewI;
+ }
+ }
+ if (viewI >= numViews) {
+ View item = mInflater.inflate(
+ com.android.internal.R.layout.fragment_bread_crumb_item,
+ this, false);
+ TextView text = (TextView)item.findViewById(com.android.internal.R.id.title);
+ text.setText(bse.getBreadCrumbTitle());
+ item.setTag(bse);
+ if (viewI == 0) {
+ text.setCompoundDrawables(null, null, null, null);
+ }
+ mContainer.addView(item);
+ }
+ }
+ int viewI = mTopEntry != null ? numEntries+1 : numEntries;
+ numViews = mContainer.getChildCount();
+ while (numViews > viewI) {
+ mContainer.removeViewAt(numViews-1);
+ numViews--;
+ }
+ }
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index a704ec8..9f958246 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -40,6 +40,48 @@
*/
public interface FragmentManager {
/**
+ * Representation of an entry on the fragment back stack, as created
+ * with {@link FragmentTransaction#addToBackStack(String)
+ * FragmentTransaction.addToBackStack()}. Entries can later be
+ * retrieved with {@link FragmentManager#getBackStackEntry(int)
+ * FragmentManager.getBackStackEntry()}.
+ *
+ * <p>Note that you should never hold on to a BackStackEntry object;
+ * the identifier as returned by {@link #getId} is the only thing that
+ * will be persisted across activity instances.
+ */
+ public interface BackStackEntry {
+ /**
+ * Return the unique identifier for the entry. This is the only
+ * representation of the entry that will persist across activity
+ * instances.
+ */
+ public int getId();
+
+ /**
+ * Return the full bread crumb title for the entry, or null if it
+ * does not have one.
+ */
+ public CharSequence getBreadCrumbTitle();
+
+ /**
+ * Return the short bread crumb title for the entry, or null if it
+ * does not have one.
+ */
+ public CharSequence getBreadCrumbShortTitle();
+ }
+
+ /**
+ * Interface to watch for changes to the back stack.
+ */
+ public interface OnBackStackChangedListener {
+ /**
+ * Called whenever the contents of the back stack change.
+ */
+ public void onBackStackChanged();
+ }
+
+ /**
* Start a series of edit operations on the Fragments associated with
* this FragmentManager.
*/
@@ -105,6 +147,28 @@
public boolean popBackStack(int id, int flags);
/**
+ * Return the number of entries currently in the back stack.
+ */
+ public int countBackStackEntries();
+
+ /**
+ * Return the BackStackEntry at index <var>index</var> in the back stack;
+ * entries start index 0 being the bottom of the stack.
+ */
+ public BackStackEntry getBackStackEntry(int index);
+
+ /**
+ * Add a new listener for changes to the fragment back stack.
+ */
+ public void addOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+ /**
+ * Remove a listener that was previously added with
+ * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
+ */
+ public void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+ /**
* Put a reference to a fragment in a Bundle. This Bundle can be
* persisted as saved state, and when later restoring
* {@link #getFragment(Bundle, String)} will return the current
@@ -182,12 +246,14 @@
ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;
ArrayList<Integer> mAvailIndices;
- ArrayList<BackStackEntry> mBackStack;
+ ArrayList<BackStackRecord> mBackStack;
// Must be accessed while locked.
- ArrayList<BackStackEntry> mBackStackIndices;
+ ArrayList<BackStackRecord> mBackStackIndices;
ArrayList<Integer> mAvailBackStackIndices;
+ ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
+
int mCurState = Fragment.INITIALIZING;
Activity mActivity;
@@ -205,7 +271,7 @@
}
};
public FragmentTransaction openTransaction() {
- return new BackStackEntry(this);
+ return new BackStackRecord(this);
}
public boolean popBackStack() {
@@ -223,6 +289,27 @@
return popBackStackState(mActivity.mHandler, null, id, flags);
}
+ public int countBackStackEntries() {
+ return mBackStack != null ? mBackStack.size() : 0;
+ }
+
+ public BackStackEntry getBackStackEntry(int index) {
+ return mBackStack.get(index);
+ }
+
+ public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
+ if (mBackStackChangeListeners == null) {
+ mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
+ }
+ mBackStackChangeListeners.add(listener);
+ }
+
+ public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
+ if (mBackStackChangeListeners != null) {
+ mBackStackChangeListeners.remove(listener);
+ }
+ }
+
public void putFragment(Bundle bundle, String key, Fragment fragment) {
if (fragment.mIndex < 0) {
throw new IllegalStateException("Fragment " + fragment
@@ -696,11 +783,11 @@
}
}
- public int allocBackStackIndex(BackStackEntry bse) {
+ public int allocBackStackIndex(BackStackRecord bse) {
synchronized (this) {
if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
if (mBackStackIndices == null) {
- mBackStackIndices = new ArrayList<BackStackEntry>();
+ mBackStackIndices = new ArrayList<BackStackRecord>();
}
int index = mBackStackIndices.size();
if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
@@ -716,10 +803,10 @@
}
}
- public void setBackStackIndex(int index, BackStackEntry bse) {
+ public void setBackStackIndex(int index, BackStackRecord bse) {
synchronized (this) {
if (mBackStackIndices == null) {
- mBackStackIndices = new ArrayList<BackStackEntry>();
+ mBackStackIndices = new ArrayList<BackStackRecord>();
}
int N = mBackStackIndices.size();
if (index < N) {
@@ -785,11 +872,20 @@
}
}
- public void addBackStackState(BackStackEntry state) {
+ void reportBackStackChanged() {
+ if (mBackStackChangeListeners != null) {
+ for (int i=0; i<mBackStackChangeListeners.size(); i++) {
+ mBackStackChangeListeners.get(i).onBackStackChanged();
+ }
+ }
+ }
+
+ void addBackStackState(BackStackRecord state) {
if (mBackStack == null) {
- mBackStack = new ArrayList<BackStackEntry>();
+ mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
+ reportBackStackChanged();
}
boolean popBackStackState(Handler handler, String name, int id, int flags) {
@@ -801,11 +897,12 @@
if (last < 0) {
return false;
}
- final BackStackEntry bss = mBackStack.remove(last);
+ final BackStackRecord bss = mBackStack.remove(last);
enqueueAction(new Runnable() {
public void run() {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
bss.popFromBackStack(true);
+ reportBackStackChanged();
}
});
} else {
@@ -815,7 +912,7 @@
// the stack.
index = mBackStack.size()-1;
while (index >= 0) {
- BackStackEntry bss = mBackStack.get(index);
+ BackStackRecord bss = mBackStack.get(index);
if (name != null && name.equals(bss.getName())) {
break;
}
@@ -831,7 +928,7 @@
index--;
// Consume all following entries that match.
while (index >= 0) {
- BackStackEntry bss = mBackStack.get(index);
+ BackStackRecord bss = mBackStack.get(index);
if ((name != null && name.equals(bss.getName()))
|| (id >= 0 && id == bss.mIndex)) {
index--;
@@ -844,8 +941,8 @@
if (index == mBackStack.size()-1) {
return false;
}
- final ArrayList<BackStackEntry> states
- = new ArrayList<BackStackEntry>();
+ final ArrayList<BackStackRecord> states
+ = new ArrayList<BackStackRecord>();
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
@@ -856,6 +953,7 @@
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
states.get(i).popFromBackStack(i == LAST);
}
+ reportBackStackChanged();
}
});
}
@@ -1073,9 +1171,9 @@
// Build the back stack.
if (fms.mBackStack != null) {
- mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length);
+ mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
for (int i=0; i<fms.mBackStack.length; i++) {
- BackStackEntry bse = fms.mBackStack[i].instantiate(this);
+ BackStackRecord bse = fms.mBackStack[i].instantiate(this);
if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i
+ " (index " + bse.mIndex + "): " + bse);
mBackStack.add(bse);
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 9d44106..09d8d26 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -88,7 +88,7 @@
/**
* @return <code>true</code> if this transaction contains no operations,
- * <code>false</code> otherwise.
+ * <code>false</code> otherwise.
*/
public boolean isEmpty();
@@ -111,14 +111,65 @@
/** Fragment is being removed */
public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
+ /**
+ * Set specific animation resources to run for the fragments that are
+ * entering and exiting in this transaction.
+ */
public FragmentTransaction setCustomAnimations(int enter, int exit);
+ /**
+ * Select a standard transition animation for this transaction. May be
+ * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
+ * or {@link #TRANSIT_FRAGMENT_CLOSE}
+ */
public FragmentTransaction setTransition(int transit);
+
+ /**
+ * Set a custom style resource that will be used for resolving transit
+ * animations.
+ */
public FragmentTransaction setTransitionStyle(int styleRes);
+ /**
+ * Add this transaction to the back stack. This means that the transaction
+ * will be remembered after it is committed, and will reverse its operation
+ * when later popped off the stack.
+ *
+ * @param name An optional name for this back stack state, or null.
+ */
public FragmentTransaction addToBackStack(String name);
/**
+ * Set the full title to show as a bread crumb when this transaction
+ * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+ *
+ * @param res A string resource containing the title.
+ */
+ public FragmentTransaction setBreadCrumbTitle(int res);
+
+ /**
+ * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
+ * method is <em>not</em> recommended, as the string can not be changed
+ * later if the locale changes.
+ */
+ public FragmentTransaction setBreadCrumbTitle(CharSequence text);
+
+ /**
+ * Set the short title to show as a bread crumb when this transaction
+ * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+ *
+ * @param res A string resource containing the title.
+ */
+ public FragmentTransaction setBreadCrumbShortTitle(int res);
+
+ /**
+ * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
+ * method is <em>not</em> recommended, as the string can not be changed
+ * later if the locale changes.
+ */
+ public FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
+
+ /**
* Schedules a commit of this transaction. Note that the commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
index 5d417a0..af71170 100644
--- a/core/java/android/app/LoaderManagingFragment.java
+++ b/core/java/android/app/LoaderManagingFragment.java
@@ -148,7 +148,7 @@
mStarted = false;
}
- /** TO DO: This needs to be turned into a retained fragment.
+ /* TO DO: This needs to be turned into a retained fragment.
@Override
public Object onRetainNonConfigurationInstance() {
// Pass the loader along to the next guy
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 1f115e8..18602df 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1083,7 +1083,7 @@
protected void launchQuerySearch(int actionKey, String actionMsg) {
String query = mSearchAutoComplete.getText().toString();
String action = Intent.ACTION_SEARCH;
- Intent intent = createIntent(action, null, null, query, null,
+ Intent intent = createIntent(action, null, null, query,
actionKey, actionMsg);
launchIntent(intent);
}
@@ -1185,11 +1185,6 @@
// use specific action if supplied, or default action if supplied, or fixed default
String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
- // some items are display only, or have effect via the cursor respond click reporting.
- if (SearchManager.INTENT_ACTION_NONE.equals(action)) {
- return null;
- }
-
if (action == null) {
action = mSearchable.getSuggestIntentAction();
}
@@ -1211,14 +1206,10 @@
}
Uri dataUri = (data == null) ? null : Uri.parse(data);
- String componentName = getColumnString(
- c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
-
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
- return createIntent(action, dataUri, extraData, query, componentName, actionKey,
- actionMsg);
+ return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
@@ -1239,7 +1230,6 @@
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
- * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
@@ -1249,7 +1239,7 @@
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
- String componentName, int actionKey, String actionMsg) {
+ int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 2e9cd96..6715012 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -16,16 +16,12 @@
package android.app;
-import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -40,7 +36,7 @@
/**
* This class provides access to the system search services.
- *
+ *
* <p>In practice, you won't interact with this class directly, as search
* services are provided through methods in {@link android.app.Activity Activity}
* and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
@@ -56,7 +52,7 @@
* href="{@docRoot}guide/topics/search/index.html">Search</a></strong>.</p>
* </div>
*/
-public class SearchManager
+public class SearchManager
implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
{
@@ -65,20 +61,20 @@
/**
* This is a shortcut definition for the default menu key to use for invoking search.
- *
+ *
* See Menu.Item.setAlphabeticShortcut() for more information.
*/
public final static char MENU_KEY = 's';
/**
* This is a shortcut definition for the default menu key to use for invoking search.
- *
+ *
* See Menu.Item.setAlphabeticShortcut() for more information.
*/
public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S;
/**
- * Intent extra data key: Use this key with
+ * Intent extra data key: Use this key with
* {@link android.content.Intent#getStringExtra
* content.Intent.getStringExtra()}
* to obtain the query string from Intent.ACTION_SEARCH.
@@ -103,7 +99,7 @@
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getBundleExtra
* content.Intent.getBundleExtra()}
- * to obtain any additional app-specific data that was inserted by the
+ * to obtain any additional app-specific data that was inserted by the
* activity that launched the search.
*/
public final static String APP_DATA = "app_data";
@@ -127,7 +123,7 @@
* file.
*/
public final static String ACTION_KEY = "action_key";
-
+
/**
* Intent extra data key: This key will be used for the extra populated by the
* {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
@@ -153,11 +149,19 @@
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
* to obtain the action message that was defined for a particular search action key and/or
- * suggestion. It will be null if the search was launched by typing "enter", touched the the
- * "GO" button, or other means not involving any action key.
+ * suggestion. It will be null if the search was launched by typing "enter", touched the the
+ * "GO" button, or other means not involving any action key.
*/
public final static String ACTION_MSG = "action_msg";
-
+
+ /**
+ * Flag to specify that the entry can be used for query refinement, i.e., the query text
+ * in the search field can be replaced with the text in this entry, when a query refinement
+ * icon is clicked. The suggestion list should show such a clickable icon beside the entry.
+ * <p>Use this flag as a bit-field for {@link #SUGGEST_COLUMN_FLAGS}.
+ */
+ public final static int FLAG_QUERY_REFINEMENT = 1 << 0;
+
/**
* Uri path for queried suggestions data. This is the path that the search manager
* will use when querying your content provider for suggestions data based on user input
@@ -182,12 +186,12 @@
* @see #SUGGEST_COLUMN_SHORTCUT_ID
*/
public final static String SUGGEST_URI_PATH_SHORTCUT = "search_suggest_shortcut";
-
+
/**
* MIME type for shortcut validation. You'll use this in your suggestions content provider
* in the getType() function.
*/
- public final static String SHORTCUT_MIME_TYPE =
+ public final static String SHORTCUT_MIME_TYPE =
"vnd.android.cursor.item/vnd.android.search.suggest";
/**
@@ -195,7 +199,7 @@
*/
public final static String SUGGEST_COLUMN_FORMAT = "suggest_format";
/**
- * Column name for suggestions cursor. <i>Required.</i> This is the primary line of text that
+ * Column name for suggestions cursor. <i>Required.</i> This is the primary line of text that
* will be presented to the user as the suggestion.
*/
public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1";
@@ -227,8 +231,8 @@
* <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
* </ul>
*
- * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
- * for more information on these schemes.
+ * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
+ * for more information on these schemes.
*/
public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
/**
@@ -243,8 +247,8 @@
* <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
* </ul>
*
- * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
- * for more information on these schemes.
+ * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
+ * for more information on these schemes.
*/
public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
/**
@@ -276,12 +280,6 @@
*/
public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
/**
- * TODO: Remove
- *
- * @hide
- */
- public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component";
- /**
* Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
* this element exists at the given row, then "/" and this value will be appended to the data
* field in the Intent. This should only be used if the data field has already been set to an
@@ -289,8 +287,8 @@
*/
public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
/**
- * Column name for suggestions cursor. <i>Required if action is
- * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this
+ * Column name for suggestions cursor. <i>Required if action is
+ * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this
* column exists <i>and</i> this element exists at the given row, this is the data that will be
* used when forming the suggestion's query.
*/
@@ -307,15 +305,6 @@
public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
/**
- * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify the
- * cursor item's background color if it needs a non-default background color. A non-zero value
- * indicates a valid background color to override the default.
- *
- * @hide For internal use, not part of the public API.
- */
- public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color";
-
- /**
* Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
* that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion
* is being refreshed.
@@ -324,6 +313,15 @@
"suggest_spinner_while_refreshing";
/**
+ * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
+ * additional flags per item. Multiple flags can be specified.
+ * <p>
+ * Must be one of {@link #FLAG_QUERY_REFINEMENT} or 0 to indicate no flags.
+ * </p>
+ */
+ public final static String SUGGEST_COLUMN_FLAGS = "suggest_flags";
+
+ /**
* Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
* should not be stored as a shortcut in global search.
*/
@@ -343,16 +341,16 @@
* {@link #EXTRA_SELECT_QUERY},
* {@link #APP_DATA}.
*/
- public final static String INTENT_ACTION_GLOBAL_SEARCH
+ public final static String INTENT_ACTION_GLOBAL_SEARCH
= "android.search.action.GLOBAL_SEARCH";
-
+
/**
* Intent action for starting the global search settings activity.
* The global search provider should handle this intent.
*/
- public final static String INTENT_ACTION_SEARCH_SETTINGS
+ public final static String INTENT_ACTION_SEARCH_SETTINGS
= "android.search.action.SEARCH_SETTINGS";
-
+
/**
* Intent action for starting a web search provider's settings activity.
* Web search providers should handle this intent if they have provider-specific
@@ -368,7 +366,7 @@
*/
public final static String INTENT_ACTION_SEARCHABLES_CHANGED
= "android.search.action.SEARCHABLES_CHANGED";
-
+
/**
* Intent action broadcasted to inform that the search settings have changed in some way.
* Either searchables have been enabled or disabled, or a different web search provider
@@ -378,14 +376,6 @@
= "android.search.action.SETTINGS_CHANGED";
/**
- * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
- * the search dialog will take no action.
- *
- * @hide
- */
- public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
-
- /**
* This means that context is voice, and therefore the SearchDialog should
* continue showing the microphone until the user indicates that he/she does
* not want to re-speak (e.g. by typing).
@@ -413,7 +403,7 @@
* The package associated with this seach manager.
*/
private String mAssociatedPackage;
-
+
// package private since they are used by the inner class SearchManagerCallback
/* package */ final Handler mHandler;
/* package */ OnDismissListener mDismissListener = null;
@@ -427,15 +417,15 @@
mService = ISearchManager.Stub.asInterface(
ServiceManager.getService(Context.SEARCH_SERVICE));
}
-
+
/**
* Launch search UI.
*
* <p>The search manager will open a search widget in an overlapping
- * window, and the underlying activity may be obscured. The search
+ * window, and the underlying activity may be obscured. The search
* entry state will remain in effect until one of the following events:
* <ul>
- * <li>The user completes the search. In most cases this will launch
+ * <li>The user completes the search. In most cases this will launch
* a search intent.</li>
* <li>The user uses the back, home, or other keys to exit the search.</li>
* <li>The application calls the {@link #stopSearch}
@@ -443,8 +433,8 @@
* activity from which it was launched.</li>
*
* <p>Most applications will <i>not</i> use this interface to invoke search.
- * The primary method for invoking search is to call
- * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or
+ * The primary method for invoking search is to call
+ * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or
* {@link android.app.Activity#startSearch Activity.startSearch()}.
*
* @param initialQuery A search string can be pre-entered here, but this
@@ -456,19 +446,19 @@
* and the user would expect to be able to keep typing. <i>This parameter is only meaningful
* if initialQuery is a non-empty string.</i>
* @param launchActivity The ComponentName of the activity that has launched this search.
- * @param appSearchData An application can insert application-specific
- * context here, in order to improve quality or specificity of its own
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
* searches. This data will be returned with SEARCH intent(s). Null if
* no extra data is required.
* @param globalSearch If false, this will only launch the search that has been specifically
- * defined by the application (which is usually defined as a local search). If no default
+ * defined by the application (which is usually defined as a local search). If no default
* search is defined in the current application or activity, global search will be launched.
* If true, this will always launch a platform-global (e.g. web-based) search instead.
- *
+ *
* @see android.app.Activity#onSearchRequested
* @see #stopSearch
*/
- public void startSearch(String initialQuery,
+ public void startSearch(String initialQuery,
boolean selectInitialQuery,
ComponentName launchActivity,
Bundle appSearchData,
@@ -595,7 +585,7 @@
* <p>Typically the user will terminate the search UI by launching a
* search or by canceling. This function allows the underlying application
* or activity to cancel the search prematurely (for any reason).
- *
+ *
* <p>This function can be safely called at any time (even if no search is active.)
*
* @see #startSearch
@@ -607,12 +597,12 @@
}
/**
- * Determine if the Search UI is currently displayed.
- *
+ * Determine if the Search UI is currently displayed.
+ *
* This is provided primarily for application test purposes.
*
* @return Returns true if the search UI is currently displayed.
- *
+ *
* @hide
*/
public boolean isVisible() {
@@ -631,7 +621,7 @@
*/
public void onDismiss();
}
-
+
/**
* See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor
* search UI state.
@@ -647,7 +637,7 @@
/**
* Set or clear the callback that will be invoked whenever the search UI is dismissed.
- *
+ *
* @param listener The {@link OnDismissListener} to use, or null.
*/
public void setOnDismissListener(final OnDismissListener listener) {
@@ -656,7 +646,7 @@
/**
* Set or clear the callback that will be invoked whenever the search UI is canceled.
- *
+ *
* @param listener The {@link OnCancelListener} to use, or null.
*/
public void setOnCancelListener(OnCancelListener listener) {
@@ -767,10 +757,10 @@
// finally, make the query
return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
}
-
+
/**
* Returns a list of the searchable activities that can be included in global search.
- *
+ *
* @return a list containing searchable information for all searchable activities
* that have the <code>android:includeInGlobalSearch</code> attribute set
* in their searchable meta-data.
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 8d8864f..5705bff 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -68,7 +68,6 @@
private SearchableInfo mSearchable;
private Context mProviderContext;
private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
- private SparseArray<Drawable.ConstantState> mBackgroundsCache;
private boolean mClosed = false;
// URL color
@@ -80,7 +79,6 @@
private int mText2UrlCol;
private int mIconName1Col;
private int mIconName2Col;
- private int mBackgroundColorCol;
static final int NONE = -1;
@@ -109,7 +107,6 @@
mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
mOutsideDrawablesCache = outsideDrawablesCache;
- mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
mStartSpinnerRunnable = new Runnable() {
public void run() {
@@ -243,8 +240,6 @@
mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
- mBackgroundColorCol =
- c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
}
} catch (Exception e) {
Log.e(LOG_TAG, "error changing cursor and caching columns", e);
@@ -283,13 +278,6 @@
public void bindView(View view, Context context, Cursor cursor) {
ChildViewCache views = (ChildViewCache) view.getTag();
- int backgroundColor = 0;
- if (mBackgroundColorCol != -1) {
- backgroundColor = cursor.getInt(mBackgroundColorCol);
- }
- Drawable background = getItemBackground(backgroundColor);
- view.setBackgroundDrawable(background);
-
if (views.mText1 != null) {
String text1 = getStringOrNull(cursor, mText1Col);
setViewText(views.mText1, text1);
@@ -342,33 +330,6 @@
return text;
}
- /**
- * Gets a drawable with no color when selected or pressed, and the given color when
- * neither selected nor pressed.
- *
- * @return A drawable, or {@code null} if the given color is transparent.
- */
- private Drawable getItemBackground(int backgroundColor) {
- if (backgroundColor == 0) {
- return null;
- } else {
- Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor);
- if (cachedBg != null) {
- if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
- return cachedBg.newDrawable(mProviderContext.getResources());
- }
- if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
- ColorDrawable transparent = new ColorDrawable(0);
- ColorDrawable background = new ColorDrawable(backgroundColor);
- StateListDrawable newBg = new StateListDrawable();
- newBg.addState(new int[]{android.R.attr.state_selected}, transparent);
- newBg.addState(new int[]{android.R.attr.state_pressed}, transparent);
- newBg.addState(new int[]{}, background);
- mBackgroundsCache.put(backgroundColor, newBg.getConstantState());
- return newBg;
- }
- }
-
private void setViewText(TextView v, CharSequence text) {
// Set the text even if it's null, since we need to clear any previous text.
v.setText(text);
@@ -601,21 +562,7 @@
* @return A non-null drawable.
*/
private Drawable getDefaultIcon1(Cursor cursor) {
- // First check the component that the suggestion is originally from
- String c = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
- if (c != null) {
- ComponentName component = ComponentName.unflattenFromString(c);
- if (component != null) {
- Drawable drawable = getActivityIconWithCache(component);
- if (drawable != null) {
- return drawable;
- }
- } else {
- Log.w(LOG_TAG, "Bad component name: " + c);
- }
- }
-
- // Then check the component that gave us the suggestion
+ // Check the component that gave us the suggestion
Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
if (drawable != null) {
return drawable;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 16a8c57..33fd395 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -465,11 +465,11 @@
}
/**
- * Set the friendly Bluetooth name of the local Bluetoth adapter.
+ * Set the friendly Bluetooth name of the local Bluetooth adapter.
* <p>This name is visible to remote Bluetooth devices.
- * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
- * many remote devices can only display the first 40 characters, and some
- * may be limited to just 20.
+ * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8
+ * encoding, although many remote devices can only display the first
+ * 40 characters, and some may be limited to just 20.
* <p>If Bluetooth state is not {@link #STATE_ON}, this API
* will return false. After turning on Bluetooth,
* wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
@@ -488,7 +488,7 @@
}
/**
- * Get the current Bluetooth scan mode of the local Bluetooth adaper.
+ * Get the current Bluetooth scan mode of the local Bluetooth adapter.
* <p>The Bluetooth scan mode determines if the local adapter is
* connectable and/or discoverable from remote Bluetooth devices.
* <p>Possible values are:
@@ -611,7 +611,7 @@
/**
* Cancel the current device discovery process.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
- * <p>Because discovery is a heavyweight precedure for the Bluetooth
+ * <p>Because discovery is a heavyweight procedure for the Bluetooth
* adapter, this method should always be called before attempting to connect
* to a remote device with {@link
* android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 9dd7b9f..c0a268f 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -37,8 +37,9 @@
public abstract class AbstractThreadedSyncAdapter {
/**
* Kernel event log tag. Also listed in data/etc/event-log-tags.
- * @Deprecated
+ * @deprecated Private constant. May go away in the next release.
*/
+ @Deprecated
public static final int LOG_SYNC_DETAILS = 2743;
private final Context mContext;
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 7945f3f..12e9bab 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -272,13 +272,14 @@
* The ContentValues back references are represented as a ContentValues object where the
* key refers to a column and the value is an index of the back reference whose
* valued should be associated with the column.
+ * <p>
+ * This is intended to be a private method but it is exposed for
+ * unit testing purposes
* @param backRefs an array of previous results
* @param numBackRefs the number of valid previous results in backRefs
* @return the ContentValues that should be used in this operation application after
* expansion of back references. This can be called if either mValues or mValuesBackReferences
* is null
- * @VisibleForTesting this is intended to be a private method but it is exposed for
- * unit testing purposes
*/
public ContentValues resolveValueBackReferences(
ContentProviderResult[] backRefs, int numBackRefs) {
@@ -308,13 +309,14 @@
* the key is an index into the selection argument array (see {@link Builder#withSelection})
* and the value is the index of the previous result that should be used for that selection
* argument array slot.
+ * <p>
+ * This is intended to be a private method but it is exposed for
+ * unit testing purposes
* @param backRefs an array of previous results
* @param numBackRefs the number of valid previous results in backRefs
* @return the ContentValues that should be used in this operation application after
* expansion of back references. This can be called if either mValues or mValuesBackReferences
* is null
- * @VisibleForTesting this is intended to be a private method but it is exposed for
- * unit testing purposes
*/
public String[] resolveSelectionArgsBackReferences(
ContentProviderResult[] backRefs, int numBackRefs) {
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index ce047f6..0d25f80 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -414,6 +414,8 @@
} catch (ClassCastException e) {
if (value instanceof CharSequence) {
return Boolean.valueOf(value.toString());
+ } else if (value instanceof Number) {
+ return ((Number) value).intValue() != 0;
} else {
Log.e(TAG, "Cannot cast value for " + key + " to a Boolean: " + value, e);
return null;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 90bb0e2..70a7fb6 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -66,7 +66,13 @@
/** One of the values returned by {@link #getSqlStatementType(String)}. */
public static final int STATEMENT_ABORT = 6;
/** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_OTHER = 7;
+ public static final int STATEMENT_PRAGMA = 7;
+ /** One of the values returned by {@link #getSqlStatementType(String)}. */
+ public static final int STATEMENT_DDL = 8;
+ /** One of the values returned by {@link #getSqlStatementType(String)}. */
+ public static final int STATEMENT_UNPREPARED = 9;
+ /** One of the values returned by {@link #getSqlStatementType(String)}. */
+ public static final int STATEMENT_OTHER = 99;
/**
* Special function for writing an exception result at the header of
@@ -1255,6 +1261,13 @@
return STATEMENT_ABORT;
} else if (prefixSql.equals("BEG")) {
return STATEMENT_BEGIN;
+ } else if (prefixSql.equals("PRA")) {
+ return STATEMENT_PRAGMA;
+ } else if (prefixSql.equals("CRE") || prefixSql.equals("DRO") ||
+ prefixSql.equals("ALT")) {
+ return STATEMENT_DDL;
+ } else if (prefixSql.equals("ANA") || prefixSql.equals("DET")) {
+ return STATEMENT_UNPREPARED;
}
return STATEMENT_OTHER;
}
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 58dab2b..a5e612b 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -76,12 +76,11 @@
return buff.toString();
}
+ // STOPSHIP remove this method before shipping
private void checkRefCount() {
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- if (mReferenceCount > 1000) {
- throw new IllegalStateException("refcount: " + mReferenceCount + ", " +
- getObjInfo());
- }
+ if (mReferenceCount > 1000) {
+ throw new IllegalStateException("bad refcount: " + mReferenceCount +
+ ". file bug against frameworks->database" + getObjInfo());
}
}
}
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index c7c0c79..4747a9e 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -86,6 +86,10 @@
*/
/* package */ HashMap<Integer, Object> mBindArgs = null;
/* package */ final int mStatementType;
+ /* package */ static final int STATEMENT_CACHEABLE = 16;
+ /* package */ static final int STATEMENT_DONT_PREPARE = 32;
+ /* package */ static final int STATEMENT_USE_POOLED_CONN = 64;
+ /* package */ static final int STATEMENT_TYPE_MASK = 0x0f;
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
this(db, sql, null, true);
@@ -94,7 +98,25 @@
/* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
boolean compileFlag) {
mSql = sql.trim();
- mStatementType = DatabaseUtils.getSqlStatementType(mSql);
+ int n = DatabaseUtils.getSqlStatementType(mSql);
+ switch (n) {
+ case DatabaseUtils.STATEMENT_UPDATE:
+ mStatementType = n | STATEMENT_CACHEABLE;
+ break;
+ case DatabaseUtils.STATEMENT_SELECT:
+ mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN;
+ break;
+ case DatabaseUtils.STATEMENT_ATTACH:
+ case DatabaseUtils.STATEMENT_BEGIN:
+ case DatabaseUtils.STATEMENT_COMMIT:
+ case DatabaseUtils.STATEMENT_ABORT:
+ case DatabaseUtils.STATEMENT_DDL:
+ case DatabaseUtils.STATEMENT_UNPREPARED:
+ mStatementType = n | STATEMENT_DONT_PREPARE;
+ break;
+ default:
+ mStatementType = n;
+ }
db.acquireReference();
db.addSQLiteClosable(this);
mDatabase = db;
@@ -112,8 +134,7 @@
private void compileSql() {
// only cache CRUD statements
- if (mStatementType != DatabaseUtils.STATEMENT_SELECT &&
- mStatementType != DatabaseUtils.STATEMENT_UPDATE) {
+ if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
nStatement = mCompiledSql.nStatement;
// since it is not in the cache, no need to acquire() it.
@@ -163,14 +184,19 @@
if (mCompiledSql == null) {
return;
}
- synchronized(mDatabase.mCompiledQueries) {
- if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
- // it is NOT in compiled-sql cache. i.e., responsibility of
- // releasing this statement is on me.
- mCompiledSql.releaseSqlStatement();
- } else {
- // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
- mCompiledSql.release();
+ if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
+ // this SQL statement was never in cache
+ mCompiledSql.releaseSqlStatement();
+ } else {
+ synchronized(mDatabase.mCompiledQueries) {
+ if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+ // it is NOT in compiled-sql cache. i.e., responsibility of
+ // releasing this statement is on me.
+ mCompiledSql.releaseSqlStatement();
+ } else {
+ // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
+ mCompiledSql.release();
+ }
}
}
mCompiledSql = null;
@@ -347,6 +373,16 @@
}
/* package */ synchronized void compileAndbindAllArgs() {
+ if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+ // no need to prepare this SQL statement
+ if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+ if (mBindArgs != null) {
+ throw new IllegalArgumentException("no need to pass bindargs for this sql :" +
+ mSql);
+ }
+ }
+ return;
+ }
if (nStatement == 0) {
// SQL statement is not compiled yet. compile it now.
compileSql();
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 658fc58..bd05e24 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -71,7 +71,7 @@
}
/**
- * Execute this SQL statement, if the the number of rows affected by exection of this SQL
+ * Execute this SQL statement, if the the number of rows affected by execution of this SQL
* statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
*
* @return the number of rows affected by this SQL statement execution.
@@ -82,7 +82,15 @@
synchronized(this) {
try {
long timeStart = acquireAndLock(WRITE);
- int numChanges = native_execute();
+ int numChanges = 0;
+ if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+ // since the statement doesn't have to be prepared,
+ // call the following native method which will not prepare
+ // the query plan
+ native_executeSql(mSql);
+ } else {
+ numChanges = native_execute();
+ }
mDatabase.logTimeStat(mSql, timeStart);
return numChanges;
} finally {
@@ -199,8 +207,8 @@
mState = 0;
// use pooled database connection handles for SELECT SQL statements
mDatabase.verifyDbIsOpen();
- SQLiteDatabase db = (mStatementType != DatabaseUtils.STATEMENT_SELECT) ? mDatabase
- : mDatabase.getDbConnection(mSql);
+ SQLiteDatabase db = ((mStatementType & SQLiteProgram.STATEMENT_USE_POOLED_CONN) > 0)
+ ? mDatabase.getDbConnection(mSql) : mDatabase;
// use the database connection obtained above
mOrigDb = mDatabase;
mDatabase = db;
@@ -217,13 +225,14 @@
* beginTransaction() methods in SQLiteDatabase call lockForced() before
* calling execSQL("BEGIN transaction").
*/
- if (mStatementType == DatabaseUtils.STATEMENT_BEGIN) {
+ if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) == DatabaseUtils.STATEMENT_BEGIN) {
if (!mDatabase.isDbLockedByCurrentThread()) {
// transaction is NOT started by calling beginTransaction() methods in
// SQLiteDatabase
mDatabase.setTransactionUsingExecSqlFlag();
}
- } else if (mStatementType == DatabaseUtils.STATEMENT_UPDATE) {
+ } else if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+ DatabaseUtils.STATEMENT_UPDATE) {
// got update SQL statement. if there is NO pending transaction, start one
if (!mDatabase.inTransaction()) {
mDatabase.beginTransactionNonExclusive();
@@ -257,8 +266,10 @@
} else if (mState == LOCK_ACQUIRED) {
mDatabase.unlock();
}
- if (mStatementType == DatabaseUtils.STATEMENT_COMMIT ||
- mStatementType == DatabaseUtils.STATEMENT_ABORT) {
+ if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+ DatabaseUtils.STATEMENT_COMMIT ||
+ (mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+ DatabaseUtils.STATEMENT_ABORT) {
mDatabase.resetTransactionUsingExecSqlFlag();
}
clearBindings();
@@ -275,4 +286,5 @@
private final native long native_1x1_long();
private final native String native_1x1_string();
private final native ParcelFileDescriptor native_1x1_blob_ashmem() throws IOException;
+ private final native void native_executeSql(String sql);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 3cc89e5..26600f3 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -154,14 +154,21 @@
* Information about a camera
*/
public static class CameraInfo {
+ /**
+ * The facing of the camera is opposite to that of the screen.
+ */
public static final int CAMERA_FACING_BACK = 0;
+
+ /**
+ * The facing of the camera is the same as that of the screen.
+ */
public static final int CAMERA_FACING_FRONT = 1;
/**
* The direction that the camera faces to. It should be
* CAMERA_FACING_BACK or CAMERA_FACING_FRONT.
*/
- public int mFacing;
+ public int facing;
/**
* The orientation of the camera image. The value is the angle that the
@@ -175,7 +182,7 @@
*
* @see #setDisplayOrientation(int)
*/
- public int mOrientation;
+ public int orientation;
};
/**
@@ -210,12 +217,14 @@
/**
* The id for the default camera.
+ * @see #open(int)
*/
public static int CAMERA_ID_DEFAULT = 0;
/**
* Equivalent to Camera.open(Camera.CAMERA_ID_DEFAULT).
* Creates a new Camera object to access the default camera.
+ * @see #open(int)
*/
public static Camera open() {
return new Camera(CAMERA_ID_DEFAULT);
@@ -787,7 +796,7 @@
* case Surface.ROTATION_270: degrees = 270; break;
* }
*
- * int result = (info.mOrientation - degrees + 360) % 360;
+ * int result = (info.orientation - degrees + 360) % 360;
* camera.setDisplayOrientation(result);
* }
* </pre>
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0068724..a271075 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -24,6 +24,7 @@
import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.Surface;
@@ -446,12 +447,12 @@
int accuracy = status[0];
synchronized (sListeners) {
if (sensor == -1 || sListeners.isEmpty()) {
- if (sensor == -1) {
- // we lost the connection to the event stream. this happens
- // when the last listener is removed.
- Log.d(TAG, "_sensors_data_poll() failed, we bail out.");
+ // we lost the connection to the event stream. this happens
+ // when the last listener is removed or if there is an error
+ if (sensor == -1 && !sListeners.isEmpty()) {
+ // log a warning in case of abnormal termination
+ Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
}
-
// we have no more listeners or polling failed, terminate the thread
sensors_destroy_queue(sQueue);
sQueue = 0;
@@ -487,7 +488,7 @@
private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
private final Handler mHandler;
private SensorEvent mValuesPool;
- public int mSensors;
+ public SparseBooleanArray mSensors = new SparseBooleanArray();
ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
mSensorEventListener = listener;
@@ -541,18 +542,17 @@
return mSensorEventListener;
}
- int addSensor(Sensor sensor) {
- mSensors |= 1<<sensor.getHandle();
+ void addSensor(Sensor sensor) {
+ mSensors.put(sensor.getHandle(), true);
mSensorList.add(sensor);
- return mSensors;
}
int removeSensor(Sensor sensor) {
- mSensors &= ~(1<<sensor.getHandle());
+ mSensors.delete(sensor.getHandle());
mSensorList.remove(sensor);
- return mSensors;
+ return mSensors.size();
}
boolean hasSensor(Sensor sensor) {
- return ((mSensors & (1<<sensor.getHandle())) != 0);
+ return mSensors.get(sensor.getHandle());
}
List<Sensor> getSensors() {
return mSensorList;
@@ -971,6 +971,31 @@
return registerListener(listener, sensor, rate, null);
}
+ private boolean enableSensorLocked(Sensor sensor, int delay) {
+ boolean result = false;
+ for (ListenerDelegate i : sListeners) {
+ if (i.hasSensor(sensor)) {
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ result = sensors_enable_sensor(sQueue, name, handle, delay);
+ break;
+ }
+ }
+ return result;
+ }
+
+ private boolean disableSensorLocked(Sensor sensor) {
+ for (ListenerDelegate i : sListeners) {
+ if (i.hasSensor(sensor)) {
+ // not an error, it's just that this sensor is still in use
+ return true;
+ }
+ }
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
+ }
+
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
@@ -1008,7 +1033,7 @@
if (listener == null || sensor == null) {
return false;
}
- boolean result;
+ boolean result = true;
int delay = -1;
switch (rate) {
case SENSOR_DELAY_FASTEST:
@@ -1029,6 +1054,7 @@
}
synchronized (sListeners) {
+ // look for this listener in our list
ListenerDelegate l = null;
for (ListenerDelegate i : sListeners) {
if (i.getListener() == listener) {
@@ -1037,29 +1063,37 @@
}
}
- String name = sensor.getName();
- int handle = sensor.getHandle();
+ // if we don't find it, add it to the list
if (l == null) {
- result = false;
l = new ListenerDelegate(listener, sensor, handler);
sListeners.add(l);
+ // if the list is not empty, start our main thread
if (!sListeners.isEmpty()) {
- result = sSensorThread.startLocked();
- if (result) {
- result = sensors_enable_sensor(sQueue, name, handle, delay);
- if (!result) {
- // there was an error, remove the listeners
+ if (sSensorThread.startLocked()) {
+ if (!enableSensorLocked(sensor, delay)) {
+ // oops. there was an error
sListeners.remove(l);
+ result = false;
}
+ } else {
+ // there was an error, remove the listener
+ sListeners.remove(l);
+ result = false;
}
+ } else {
+ // weird, we couldn't add the listener
+ result = false;
}
} else {
- result = sensors_enable_sensor(sQueue, name, handle, delay);
- if (result) {
- l.addSensor(sensor);
+ l.addSensor(sensor);
+ if (!enableSensorLocked(sensor, delay)) {
+ // oops. there was an error
+ l.removeSensor(sensor);
+ result = false;
}
}
}
+
return result;
}
@@ -1067,23 +1101,21 @@
if (listener == null || sensor == null) {
return;
}
+
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
- // disable these sensors
- String name = sensor.getName();
- int handle = sensor.getHandle();
- sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
- // if we have no more sensors enabled on this listener,
- // take it off the list.
if (l.removeSensor(sensor) == 0) {
+ // if we have no more sensors enabled on this listener,
+ // take it off the list.
sListeners.remove(i);
}
break;
}
}
+ disableSensorLocked(sensor);
}
}
@@ -1091,18 +1123,17 @@
if (listener == null) {
return;
}
+
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
+ sListeners.remove(i);
// disable all sensors for this listener
for (Sensor sensor : l.getSensors()) {
- String name = sensor.getName();
- int handle = sensor.getHandle();
- sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
+ disableSensorLocked(sensor);
}
- sListeners.remove(i);
break;
}
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
old mode 100755
new mode 100644
index 885a6b8..75c945b
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -307,6 +307,10 @@
/** Create an empty key with no attributes. */
public Key(Row parent) {
keyboard = parent.parent;
+ height = parent.defaultHeight;
+ width = parent.defaultWidth;
+ gap = parent.defaultHorizontalGap;
+ edgeFlags = parent.rowEdgeFlags;
}
/** Create a key with the given top-left coordinate and extract its attributes from
@@ -587,9 +591,6 @@
final Key key = new Key(row);
key.x = x;
key.y = y;
- key.width = mDefaultWidth;
- key.height = mDefaultHeight;
- key.gap = mDefaultHorizontalGap;
key.label = String.valueOf(c);
key.codes = new int[] { c };
column++;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 61fc9e6..b483f6c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -126,6 +126,17 @@
public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+
+ /**
+ * Broadcast Action: The network connection may not be good
+ * uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and
+ * {@code ConnectivityManager.EXTRA_NETWORK_INFO} to specify
+ * the network and it's condition.
+ * @hide
+ */
+ public static final String INET_CONDITION_ACTION =
+ "android.net.conn.INET_CONDITION_ACTION";
+
/**
* Broadcast Action: A tetherable connection has come or gone
* TODO - finish the doc
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index bcee995..00c1aac 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -17,6 +17,7 @@
package android.net;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.CursorWrapper;
@@ -536,12 +537,12 @@
* @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;
+ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
+ Uri uri = baseUri;
List<String> selectionParts = new ArrayList<String>();
if (mId != null) {
- uri = Uri.withAppendedPath(uri, mId.toString());
+ uri = ContentUris.withAppendedId(uri, mId);
}
if (mStatusFlags != null) {
@@ -597,6 +598,7 @@
private ContentResolver mResolver;
private String mPackageName;
+ private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
/**
* @hide
@@ -607,6 +609,19 @@
}
/**
+ * Makes this object access the download provider through /all_downloads URIs rather than
+ * /my_downloads URIs, for clients that have permission to do so.
+ * @hide
+ */
+ public void setAccessAllDownloads(boolean accessAllDownloads) {
+ if (accessAllDownloads) {
+ mBaseUri = Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
+ } else {
+ mBaseUri = Downloads.Impl.CONTENT_URI;
+ }
+ }
+
+ /**
* Enqueue a new download. The download will start automatically once the download manager is
* ready to execute it and connectivity is available.
*
@@ -642,11 +657,11 @@
* COLUMN_* constants.
*/
public Cursor query(Query query) {
- Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS);
+ Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS, mBaseUri);
if (underlyingCursor == null) {
return null;
}
- return new CursorTranslator(underlyingCursor);
+ return new CursorTranslator(underlyingCursor, mBaseUri);
}
/**
@@ -690,9 +705,8 @@
/**
* 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;
+ Uri getDownloadUri(long id) {
+ return ContentUris.withAppendedId(mBaseUri, id);
}
/**
@@ -702,8 +716,11 @@
* underlying data.
*/
private static class CursorTranslator extends CursorWrapper {
- public CursorTranslator(Cursor cursor) {
+ private Uri mBaseUri;
+
+ public CursorTranslator(Cursor cursor, Uri baseUri) {
super(cursor);
+ mBaseUri = baseUri;
}
@Override
@@ -799,11 +816,24 @@
}
assert column.equals(COLUMN_LOCAL_URI);
- String localUri = getUnderlyingString(Downloads._DATA);
+ return getLocalUri();
+ }
+
+ private String getLocalUri() {
+ String localUri = getUnderlyingString(Downloads.Impl._DATA);
if (localUri == null) {
return null;
}
- return Uri.fromFile(new File(localUri)).toString();
+
+ long destinationType = getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION);
+ if (destinationType == Downloads.Impl.DESTINATION_FILE_URI) {
+ // return file URI for external download
+ return Uri.fromFile(new File(localUri)).toString();
+ }
+
+ // return content URI for cache download
+ long downloadId = getUnderlyingLong(Downloads.Impl._ID);
+ return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
}
private long translateLong(String column) {
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/LinkCapabilities.aidl
new file mode 100644
index 0000000..5f47baf
--- /dev/null
+++ b/core/java/android/net/LinkCapabilities.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 LinkCapabilities;
+
diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java
new file mode 100644
index 0000000..d10a759
--- /dev/null
+++ b/core/java/android/net/LinkCapabilities.java
@@ -0,0 +1,362 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A class representing the capabilities of a link
+ *
+ * @hide
+ */
+public class LinkCapabilities implements Parcelable {
+ private static final String TAG = "LinkCapabilities";
+ private static final boolean DBG = false;
+
+ /** The Map of Keys to Values */
+ private HashMap<Integer, String> mCapabilities;
+
+
+ /**
+ * The set of keys defined for a links capabilities.
+ *
+ * Keys starting with RW are read + write, i.e. the application
+ * can request for a certain requirement corresponding to that key.
+ * Keys starting with RO are read only, i.e. the the application
+ * can read the value of that key from the socket but cannot request
+ * a corresponding requirement.
+ *
+ * TODO: Provide a documentation technique for concisely and precisely
+ * define the syntax for each value string associated with a key.
+ */
+ public static final class Key {
+ /** No constructor */
+ private Key() {}
+
+ /**
+ * An integer representing the network type.
+ * @see ConnectivityManager
+ */
+ public final static int RO_NETWORK_TYPE = 1;
+
+ /**
+ * Desired minimum forward link (download) bandwidth for the
+ * in kilobits per second (kbps). Values should be strings such
+ * "50", "100", "1500", etc.
+ */
+ public final static int RW_DESIRED_FWD_BW = 2;
+
+ /**
+ * Required minimum forward link (download) bandwidth, in
+ * per second (kbps), below which the socket cannot function.
+ * Values should be strings such as "50", "100", "1500", etc.
+ */
+ public final static int RW_REQUIRED_FWD_BW = 3;
+
+ /**
+ * Available forward link (download) bandwidth for the socket.
+ * This value is in kilobits per second (kbps).
+ * Values will be strings such as "50", "100", "1500", etc.
+ */
+ public final static int RO_AVAILABLE_FWD_BW = 4;
+
+ /**
+ * Desired minimum reverse link (upload) bandwidth for the socket
+ * in kilobits per second (kbps).
+ * Values should be strings such as "50", "100", "1500", etc.
+ * <p>
+ * This key is set via the needs map.
+ */
+ public final static int RW_DESIRED_REV_BW = 5;
+
+ /**
+ * Required minimum reverse link (upload) bandwidth, in kilobits
+ * per second (kbps), below which the socket cannot function.
+ * If a rate is not specified, the default rate of kbps will be
+ * Values should be strings such as "50", "100", "1500", etc.
+ */
+ public final static int RW_REQUIRED_REV_BW = 6;
+
+ /**
+ * Available reverse link (upload) bandwidth for the socket.
+ * This value is in kilobits per second (kbps).
+ * Values will be strings such as "50", "100", "1500", etc.
+ */
+ public final static int RO_AVAILABLE_REV_BW = 7;
+
+ /**
+ * Maximum latency for the socket, in milliseconds, above which
+ * socket cannot function.
+ * Values should be strings such as "50", "300", "500", etc.
+ */
+ public final static int RW_MAX_ALLOWED_LATENCY = 8;
+
+ /**
+ * Interface that the socket is bound to. This can be a virtual
+ * interface (e.g. VPN or Mobile IP) or a physical interface
+ * (e.g. wlan0 or rmnet0).
+ * Values will be strings such as "wlan0", "rmnet0"
+ */
+ public final static int RO_BOUND_INTERFACE = 9;
+
+ /**
+ * Physical interface that the socket is routed on.
+ * This can be different from BOUND_INTERFACE in cases such as
+ * VPN or Mobile IP. The physical interface may change over time
+ * if seamless mobility is supported.
+ * Values will be strings such as "wlan0", "rmnet0"
+ */
+ public final static int RO_PHYSICAL_INTERFACE = 10;
+ }
+
+ /**
+ * Role informs the LinkSocket about the data usage patterns of your
+ * application.
+ * <P>
+ * {@code Role.DEFAULT} is the default role, and is used whenever
+ * a role isn't set.
+ */
+ public static final class Role {
+ /** No constructor */
+ private Role() {}
+
+ // examples only, discuss which roles should be defined, and then
+ // code these to match
+
+ /** Default Role */
+ public static final String DEFAULT = "0";
+ /** Bulk down load */
+ public static final String BULK_DOWNLOAD = "bulk.download";
+ /** Bulk upload */
+ public static final String BULK_UPLOAD = "bulk.upload";
+
+ /** VoIP Application at 24kbps */
+ public static final String VOIP_24KBPS = "voip.24k";
+ /** VoIP Application at 32kbps */
+ public static final String VOIP_32KBPS = "voip.32k";
+
+ /** Video Streaming at 480p */
+ public static final String VIDEO_STREAMING_480P = "video.streaming.480p";
+ /** Video Streaming at 720p */
+ public static final String VIDEO_STREAMING_720I = "video.streaming.720i";
+
+ /** Video Chat Application at 360p */
+ public static final String VIDEO_CHAT_360P = "video.chat.360p";
+ /** Video Chat Application at 480p */
+ public static final String VIDEO_CHAT_480P = "video.chat.480i";
+ }
+
+ /**
+ * Constructor
+ */
+ public LinkCapabilities() {
+ mCapabilities = new HashMap<Integer, String>();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param source
+ */
+ public LinkCapabilities(LinkCapabilities source) {
+ if (source != null) {
+ mCapabilities = new HashMap<Integer, String>(source.mCapabilities);
+ } else {
+ mCapabilities = new HashMap<Integer, String>();
+ }
+ }
+
+ /**
+ * Create the {@code LinkCapabilities} with values depending on role type.
+ * @param applicationRole a {@code LinkSocket.Role}
+ * @return the {@code LinkCapabilities} associated with the applicationRole, empty if none
+ */
+ public static LinkCapabilities createNeedsMap(String applicationRole) {
+ if (DBG) log("createNeededCapabilities(applicationRole) EX");
+ return new LinkCapabilities();
+ }
+
+ /**
+ * Remove all capabilities
+ */
+ public void clear() {
+ mCapabilities.clear();
+ }
+
+ /**
+ * Returns whether this map is empty.
+ */
+ public boolean isEmpty() {
+ return mCapabilities.isEmpty();
+ }
+
+ /**
+ * Returns the number of elements in this map.
+ *
+ * @return the number of elements in this map.
+ */
+ public int size() {
+ return mCapabilities.size();
+ }
+
+ /**
+ * Given the key return the capability string
+ *
+ * @param key
+ * @return the capability string
+ */
+ public String get(int key) {
+ return mCapabilities.get(key);
+ }
+
+ /**
+ * Store the key/value capability pair
+ *
+ * @param key
+ * @param value
+ */
+ public void put(int key, String value) {
+ mCapabilities.put(key, value);
+ }
+
+ /**
+ * Returns whether this map contains the specified key.
+ *
+ * @param key to search for.
+ * @return {@code true} if this map contains the specified key,
+ * {@code false} otherwise.
+ */
+ public boolean containsKey(int key) {
+ return mCapabilities.containsKey(key);
+ }
+
+ /**
+ * Returns whether this map contains the specified value.
+ *
+ * @param value to search for.
+ * @return {@code true} if this map contains the specified value,
+ * {@code false} otherwise.
+ */
+ public boolean containsValue(String value) {
+ return mCapabilities.containsValue(value);
+ }
+
+ /**
+ * Returns a set containing all of the mappings in this map. Each mapping is
+ * an instance of {@link Map.Entry}. As the set is backed by this map,
+ * changes in one will be reflected in the other.
+ *
+ * @return a set of the mappings.
+ */
+ public Set<Entry<Integer, String>> entrySet() {
+ return mCapabilities.entrySet();
+ }
+
+ /**
+ * @return the set of the keys.
+ */
+ public Set<Integer> keySet() {
+ return mCapabilities.keySet();
+ }
+
+ /**
+ * @return the set of values
+ */
+ public Collection<String> values() {
+ return mCapabilities.values();
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Convert to string for debugging
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ boolean firstTime = true;
+ for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
+ if (firstTime) {
+ firstTime = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(entry.getKey());
+ sb.append(":\"");
+ sb.append(entry.getValue());
+ sb.append("\"");
+ return mCapabilities.toString();
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCapabilities.size());
+ for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
+ dest.writeInt(entry.getKey().intValue());
+ dest.writeString(entry.getValue());
+ }
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public static final Creator<LinkCapabilities> CREATOR =
+ new Creator<LinkCapabilities>() {
+ public LinkCapabilities createFromParcel(Parcel in) {
+ LinkCapabilities capabilities = new LinkCapabilities();
+ int size = in.readInt();
+ while (size-- != 0) {
+ int key = in.readInt();
+ String value = in.readString();
+ capabilities.mCapabilities.put(key, value);
+ }
+ return capabilities;
+ }
+
+ public LinkCapabilities[] newArray(int size) {
+ return new LinkCapabilities[size];
+ }
+ };
+
+ /**
+ * Debug logging
+ */
+ protected static void log(String s) {
+ Log.d(TAG, s);
+ }
+}
diff --git a/core/java/android/net/LinkSocket.java b/core/java/android/net/LinkSocket.java
new file mode 100644
index 0000000..d416ed0
--- /dev/null
+++ b/core/java/android/net/LinkSocket.java
@@ -0,0 +1,265 @@
+/*
+ * 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.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.LinkSocketNotifier;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+
+/** @hide */
+public class LinkSocket extends Socket {
+ private final static String TAG = "LinkSocket";
+ private final static boolean DBG = true;
+
+ /**
+ * Default constructor
+ */
+ public LinkSocket() {
+ if (DBG) log("LinkSocket() EX");
+ }
+
+ /**
+ * Creates a new unconnected socket.
+ * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
+ */
+ public LinkSocket(LinkSocketNotifier notifier) {
+ if (DBG) log("LinkSocket(notifier) EX");
+ }
+
+ /**
+ * Creates a new unconnected socket usign the given proxy type.
+ * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
+ * @param proxy the specified proxy for this socket
+ * @throws IllegalArgumentException if the argument proxy is null or of an invalid type.
+ * @throws SecurityException if a security manager exists and it denies the permission
+ * to connect to the given proxy.
+ */
+ public LinkSocket(LinkSocketNotifier notifier, Proxy proxy) {
+ if (DBG) log("LinkSocket(notifier, proxy) EX");
+ }
+
+ /**
+ * @return the {@code LinkProperties} for the socket
+ */
+ public LinkProperties getLinkProperties() {
+ if (DBG) log("LinkProperties() EX");
+ return new LinkProperties();
+ }
+
+ /**
+ * Set the {@code LinkCapabilies} needed for this socket. If the socket is already connected
+ * or is a duplicate socket the request is ignored and {@code false} will
+ * be returned. A needs map can be created via the {@code createNeedsMap} static
+ * method.
+ * @param needs the needs of the socket
+ * @return {@code true} if needs are successfully set, {@code false} otherwise
+ */
+ public boolean setNeededCapabilities(LinkCapabilities needs) {
+ if (DBG) log("setNeeds() EX");
+ return false;
+ }
+
+ /**
+ * @return the LinkCapabilites set by setNeededCapabilities, empty if none has been set
+ */
+ public LinkCapabilities getNeededCapabilities() {
+ if (DBG) log("getNeeds() EX");
+ return null;
+ }
+
+ /**
+ * @return all of the {@code LinkCapabilities} of the link used by this socket
+ */
+ public LinkCapabilities getCapabilities() {
+ if (DBG) log("getCapabilities() EX");
+ return null;
+ }
+
+ /**
+ * Returns this LinkSockets set of capabilities, filtered according to
+ * the given {@code Set}. Capabilities in the Set but not available from
+ * the link will not be reported in the results. Capabilities of the link
+ * but not listed in the Set will also not be reported in the results.
+ * @param capabilities {@code Set} of capabilities requested
+ * @return the filtered {@code LinkCapabilities} of this LinkSocket, may be empty
+ */
+ public LinkCapabilities getCapabilities(Set<Integer> capabilities) {
+ if (DBG) log("getCapabilities(capabilities) EX");
+ return new LinkCapabilities();
+ }
+
+ /**
+ * Provide the set of capabilities the application is interested in tracking
+ * for this LinkSocket.
+ * @param capabilities a {@code Set} of capabilities to track
+ */
+ public void setTrackedCapabilities(Set<Integer> capabilities) {
+ if (DBG) log("setTrackedCapabilities(capabilities) EX");
+ }
+
+ /**
+ * @return the {@code LinkCapabilities} that are tracked, empty if none has been set.
+ */
+ public Set<Integer> getTrackedCapabilities() {
+ if (DBG) log("getTrackedCapabilities(capabilities) EX");
+ return new HashSet<Integer>();
+ }
+
+ /**
+ * Connects this socket to the given remote host address and port specified
+ * by dstName and dstPort.
+ * @param dstName the address of the remote host to connect to
+ * @param dstPort the port to connect to on the remote host
+ * @param timeout the timeout value in milliseconds or 0 for infinite timeout
+ * @throws UnknownHostException if the given dstName is invalid
+ * @throws IOException if the socket is already connected or an error occurs
+ * while connecting
+ * @throws SocketTimeoutException if the timeout fires
+ */
+ public void connect(String dstName, int dstPort, int timeout)
+ throws UnknownHostException, IOException, SocketTimeoutException {
+ if (DBG) log("connect(dstName, dstPort, timeout) EX");
+ }
+
+ /**
+ * Connects this socket to the given remote host address and port specified
+ * by dstName and dstPort.
+ * @param dstName the address of the remote host to connect to
+ * @param dstPort the port to connect to on the remote host
+ * @throws UnknownHostException if the given dstName is invalid
+ * @throws IOException if the socket is already connected or an error occurs
+ * while connecting
+ */
+ public void connect(String dstName, int dstPort)
+ throws UnknownHostException, IOException {
+ if (DBG) log("connect(dstName, dstPort, timeout) EX");
+ }
+
+ /**
+ * Connects this socket to the given remote host address and port specified
+ * by the SocketAddress with the specified timeout.
+ * @deprecated Use {@code connect(String dstName, int dstPort, int timeout)}
+ * instead. Using this method may result in reduced functionality.
+ * @param remoteAddr the address and port of the remote host to connect to
+ * @throws IllegalArgumentException if the given SocketAddress is invalid
+ * @throws IOException if the socket is already connected or an error occurs
+ * while connecting
+ * @throws SocketTimeoutException if the timeout expires
+ */
+ @Override
+ @Deprecated
+ public void connect(SocketAddress remoteAddr, int timeout)
+ throws IOException, SocketTimeoutException {
+ if (DBG) log("connect(remoteAddr, timeout) EX DEPRECATED");
+ }
+
+ /**
+ * Connects this socket to the given remote host address and port specified
+ * by the SocketAddress.
+ * TODO add comment on all these that the network selection happens during connect
+ * and may take 30 seconds
+ * @deprecated Use {@code connect(String dstName, int dstPort)}
+ * Using this method may result in reduced functionality.
+ * @param remoteAddr the address and port of the remote host to connect to.
+ * @throws IllegalArgumentException if the SocketAddress is invalid or not supported.
+ * @throws IOException if the socket is already connected or an error occurs
+ * while connecting
+ */
+ @Override
+ @Deprecated
+ public void connect(SocketAddress remoteAddr) throws IOException {
+ if (DBG) log("connect(remoteAddr) EX DEPRECATED");
+ }
+
+ /**
+ * Connect a duplicate socket socket to the same remote host address and port
+ * as the original.
+ * @throws IOException if the socket is already connected or an error occurs
+ * while connecting
+ */
+ public void connect() throws IOException {
+ if (DBG) log("connect() EX");
+ }
+
+ /**
+ * Closes the socket. It is not possible to reconnect or rebind to this
+ * socket thereafter which means a new socket instance has to be created.
+ * @throws IOException if an error occurs while closing the socket
+ */
+ @Override
+ public synchronized void close() throws IOException {
+ if (DBG) log("close() EX");
+ }
+
+ /**
+ * Request that a new LinkSocket be created using a different radio
+ * (such as WiFi or 3G) than the current LinkSocket. If a different
+ * radio is available a call back will be made via {@code onBetterLinkAvail}.
+ * If unable to find a better radio, application will be notified via
+ * {@code onNewLinkUnavailable}
+ * @see LinkSocketNotifier#onBetterLinkAvailable(LinkSocket, LinkSocket)
+ * @param linkRequestReason reason for requesting a new link.
+ */
+ public void requestNewLink(LinkRequestReason linkRequestReason) {
+ if (DBG) log("requestNewLink(linkRequestReason) EX");
+ }
+
+ /**
+ * @deprecated LinkSocket will automatically pick the optimum interface
+ * to bind to
+ * @param localAddr the specific address and port on the local machine
+ * to bind to
+ * @throws IOException always as this method is deprecated for LinkSocket
+ */
+ @Override
+ @Deprecated
+ public void bind(SocketAddress localAddr) throws IOException {
+ if (DBG) log("bind(localAddr) EX throws IOException");
+ throw new IOException("bind is deprecated for LinkSocket");
+ }
+
+ /**
+ * Reason codes an application can specify when requesting for a new link.
+ * TODO: need better documentation
+ */
+ public static final class LinkRequestReason {
+ /** No constructor */
+ private LinkRequestReason() {}
+
+ /** This link is working properly */
+ public static final int LINK_PROBLEM_NONE = 0;
+ /** This link has an unknown issue */
+ public static final int LINK_PROBLEM_UNKNOWN = 1;
+ }
+
+ /**
+ * Debug logging
+ */
+ protected static void log(String s) {
+ Log.d(TAG, s);
+ }
+}
diff --git a/core/java/android/net/LinkSocketNotifier.java b/core/java/android/net/LinkSocketNotifier.java
new file mode 100644
index 0000000..183c767
--- /dev/null
+++ b/core/java/android/net/LinkSocketNotifier.java
@@ -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.
+ */
+
+package android.net;
+
+import java.util.Map;
+
+/**
+ * Interface used to get feedback about a {@link android.net.LinkSocket}. Instance is optionally
+ * passed when a LinkSocket is constructed. Multiple LinkSockets may use the same notifier.
+ * @hide
+ */
+public interface LinkSocketNotifier {
+ /**
+ * This callback function will be called if a better link
+ * becomes available.
+ * TODO - this shouldn't be checked for all cases - what's the conditional
+ * flag?
+ * If the duplicate socket is accepted, the original will be marked invalid
+ * and additional use will throw exceptions.
+ * @param original the original LinkSocket
+ * @param duplicate the new LinkSocket that better meets the application
+ * requirements
+ * @return {@code true} if the application intends to use this link
+ *
+ * REM
+ * TODO - how agressive should we be?
+ * At a minimum CS tracks which LS have this turned on and tracks the requirements
+ * When a new link becomes available, automatically check if any of the LinkSockets
+ * will care.
+ * If found, grab a refcount on the link so it doesn't go away and send notification
+ * Optionally, periodically setup connection on available networks to check for better links
+ * Maybe pass this info into the LinkFactories so condition changes can be acted on more quickly
+ */
+ public boolean onBetterLinkAvailable(LinkSocket original, LinkSocket duplicate);
+
+ /**
+ * This callback function will be called when a LinkSocket no longer has
+ * an active link.
+ * @param socket the LinkSocket that lost its link
+ *
+ * REM
+ * NetworkStateTracker tells us it is disconnected
+ * CS checks the table for LS on that link
+ * CS calls each callback (need to work out p2p cross process callback)
+ */
+ public void onLinkLost(LinkSocket socket);
+
+ /**
+ * This callback function will be called when an application calls
+ * requestNewLink on a LinkSocket but the LinkSocket is unable to find
+ * a suitable new link.
+ * @param socket the LinkSocket for which a new link was not found
+ * TODO - why the diff between initial request (sync) and requestNewLink?
+ *
+ * REM
+ * CS process of trying to find a new link must track the LS that started it
+ * on failure, call callback
+ */
+ public void onNewLinkUnavailable(LinkSocket socket);
+
+ /**
+ * This callback function will be called when any of the notification-marked
+ * capabilities of the LinkSocket (e.g. upstream bandwidth) have changed.
+ * @param socket the linkSocet for which capabilities have changed
+ * @param changedCapabilities the set of capabilities that the application
+ * is interested in and have changed (with new values)
+ *
+ * REM
+ * Maybe pass the interesting capabilities into the Links.
+ * Get notified of every capability change
+ * check for LinkSockets on that Link that are interested in that Capability - call them
+ */
+ public void onCapabilityChanged(LinkSocket socket, LinkCapabilities changedCapabilities);
+}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 63bdf8d..3df8ec0 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -57,6 +57,7 @@
private Handler mTarget;
private Context mContext;
private LinkProperties mLinkProperties;
+ private LinkCapabilities mLinkCapabilities;
private boolean mPrivateDnsRouteSet = false;
private int mDefaultGatewayAddr = 0;
private boolean mDefaultRouteSet = false;
@@ -231,8 +232,14 @@
mLinkProperties = intent.getParcelableExtra(
Phone.DATA_LINK_PROPERTIES_KEY);
if (mLinkProperties == null) {
- Log.d(TAG,
- "CONNECTED event did not supply link properties.");
+ Log.d(TAG, "CONNECTED event did not supply link properties.");
+ mLinkProperties = new LinkProperties();
+ }
+ mLinkCapabilities = intent.getParcelableExtra(
+ Phone.DATA_LINK_CAPABILITIES_KEY);
+ if (mLinkCapabilities == null) {
+ Log.d(TAG, "CONNECTED event did not supply link capabilities.");
+ mLinkCapabilities = new LinkCapabilities();
}
setDetailedState(DetailedState.CONNECTED, reason, apnName);
break;
@@ -517,7 +524,17 @@
}
}
+ /**
+ * @see android.net.NetworkStateTracker#getLinkProperties()
+ */
public LinkProperties getLinkProperties() {
return new LinkProperties(mLinkProperties);
}
+
+ /**
+ * @see android.net.NetworkStateTracker#getLinkCapabilities()
+ */
+ public LinkCapabilities getLinkCapabilities() {
+ return new LinkCapabilities(mLinkCapabilities);
+ }
}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 5420d8f..97c31fa 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -103,11 +103,21 @@
public NetworkInfo getNetworkInfo();
/**
- * Fetch LinkProperties for the network
+ * Return the LinkProperties for the connection.
+ *
+ * @return a copy of the LinkProperties, is never null.
*/
public LinkProperties getLinkProperties();
/**
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
+ *
+ * @return a copy of this connections capabilities, may be empty but never null.
+ */
+ public LinkCapabilities getLinkCapabilities();
+
+ /**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
*/
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 4247ae5..e78d2af 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -22,6 +22,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.app.Fragment;
+import android.app.FragmentBreadCrumbs;
import android.app.FragmentTransaction;
import android.app.ListActivity;
import android.content.Context;
@@ -117,7 +118,6 @@
// Constants for state save/restore
private static final String HEADERS_TAG = ":android:headers";
private static final String CUR_HEADER_TAG = ":android:cur_header";
- private static final String SINGLE_PANE_TAG = ":android:single_pane";
private static final String PREFERENCES_TAG = ":android:preferences";
/**
@@ -169,6 +169,8 @@
private View mPrefsContainer;
+ private FragmentBreadCrumbs mFragmentBreadCrumbs;
+
private boolean mSinglePane;
private Header mCurHeader;
@@ -300,6 +302,18 @@
public CharSequence summary;
/**
+ * Optional text to show as the title in the bread crumb.
+ * @attr ref android.R.styleable#PreferenceHeader_breadCrumbTitle
+ */
+ public CharSequence breadCrumbTitle;
+
+ /**
+ * Optional text to show as the short title in the bread crumb.
+ * @attr ref android.R.styleable#PreferenceHeader_breadCrumbShortTitle
+ */
+ public CharSequence breadCrumbShortTitle;
+
+ /**
* Optional icon resource to show for this header.
* @attr ref android.R.styleable#PreferenceHeader_icon
*/
@@ -341,6 +355,8 @@
dest.writeLong(id);
TextUtils.writeToParcel(title, dest, flags);
TextUtils.writeToParcel(summary, dest, flags);
+ TextUtils.writeToParcel(breadCrumbTitle, dest, flags);
+ TextUtils.writeToParcel(breadCrumbShortTitle, dest, flags);
dest.writeInt(iconRes);
dest.writeString(fragment);
dest.writeBundle(fragmentArguments);
@@ -357,6 +373,8 @@
id = in.readLong();
title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ breadCrumbTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ breadCrumbShortTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
iconRes = in.readInt();
fragment = in.readString();
fragmentArguments = in.readBundle();
@@ -405,7 +423,6 @@
setSelectedHeader(mHeaders.get(curHeader));
}
}
- mSinglePane = savedInstanceState.getBoolean(SINGLE_PANE_TAG);
} else {
if (initialFragment != null && mSinglePane) {
@@ -537,7 +554,11 @@
public boolean onIsMultiPane() {
Configuration config = getResources().getConfiguration();
if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
- == Configuration.SCREENLAYOUT_SIZE_XLARGE
+ == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
+ return true;
+ }
+ if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
+ == Configuration.SCREENLAYOUT_SIZE_LARGE
&& config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return true;
}
@@ -649,6 +670,10 @@
com.android.internal.R.styleable.PreferenceHeader_title);
header.summary = sa.getText(
com.android.internal.R.styleable.PreferenceHeader_summary);
+ header.breadCrumbTitle = sa.getText(
+ com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
+ header.breadCrumbShortTitle = sa.getText(
+ com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
header.iconRes = sa.getResourceId(
com.android.internal.R.styleable.PreferenceHeader_icon, 0);
header.fragment = sa.getString(
@@ -741,7 +766,6 @@
}
}
}
- outState.putBoolean(SINGLE_PANE_TAG, mSinglePane);
if (mPreferenceManager != null) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
@@ -837,6 +861,20 @@
startActivity(intent);
}
+ /**
+ * Change the base title of the bread crumbs for the current preferences.
+ * This will normally be called for you. See
+ * {@link android.app.FragmentBreadCrumbs} for more information.
+ */
+ public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) {
+ if (mFragmentBreadCrumbs == null) {
+ mFragmentBreadCrumbs = new FragmentBreadCrumbs(this);
+ mFragmentBreadCrumbs.setActivity(this);
+ getActionBar().setCustomNavigationMode(mFragmentBreadCrumbs);
+ }
+ mFragmentBreadCrumbs.setTitle(title, shortTitle);
+ }
+
void setSelectedHeader(Header header) {
mCurHeader = header;
int index = mHeaders.indexOf(header);
@@ -845,6 +883,21 @@
} else {
getListView().clearChoices();
}
+ if (header != null) {
+ CharSequence title = header.breadCrumbTitle;
+ if (title == null) title = header.title;
+ if (title == null) title = getTitle();
+ showBreadCrumbs(title, header.breadCrumbShortTitle);
+ } else {
+ showBreadCrumbs(getTitle(), null);
+ }
+ }
+
+ public void switchToHeaderInner(String fragmentName, Bundle args) {
+ getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+ Fragment f = Fragment.instantiate(this, fragmentName, args);
+ getFragmentManager().openTransaction().replace(
+ com.android.internal.R.id.prefs, f).commit();
}
/**
@@ -856,12 +909,7 @@
*/
public void switchToHeader(String fragmentName, Bundle args) {
setSelectedHeader(null);
-
- getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
-
- Fragment f = Fragment.instantiate(this, fragmentName, args);
- getFragmentManager().openTransaction().replace(
- com.android.internal.R.id.prefs, f).commit();
+ switchToHeaderInner(fragmentName, args);
}
/**
@@ -871,8 +919,7 @@
* @param header The new header to display.
*/
public void switchToHeader(Header header) {
- switchToHeader(header.fragment, header.fragmentArguments);
- mCurHeader = header;
+ switchToHeaderInner(header.fragment, header.fragmentArguments);
setSelectedHeader(header);
}
@@ -930,17 +977,32 @@
*/
public void startPreferenceFragment(Fragment fragment, boolean push) {
FragmentTransaction transaction = getFragmentManager().openTransaction();
- transaction.replace(com.android.internal.R.id.prefs, fragment);
+ startPreferenceFragment(fragment, transaction);
if (push) {
transaction.addToBackStack(BACK_STACK_PREFS);
}
transaction.commit();
}
+ /**
+ * Start a new fragment.
+ *
+ * @param fragment The fragment to start
+ * @param ft The FragmentTransaction in which to perform this operation.
+ * Will not be added to the back stack or committed for you; you use do that.
+ */
+ public void startPreferenceFragment(Fragment fragment, FragmentTransaction ft) {
+ ft.replace(com.android.internal.R.id.prefs, fragment);
+ }
+
@Override
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
- startPreferenceFragment(f, true);
+ FragmentTransaction transaction = getFragmentManager().openTransaction();
+ startPreferenceFragment(f, transaction);
+ transaction.setBreadCrumbTitle(pref.getTitle());
+ transaction.addToBackStack(BACK_STACK_PREFS);
+ transaction.commit();
return true;
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index cb6e18f..14d6485 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -469,7 +469,7 @@
iconDb.releaseIconForPageUrl(cursor.getString(0));
} while (cursor.moveToNext());
- cr.delete(BOOKMARKS_URI, whereClause, null);
+ cr.delete(History.CONTENT_URI, whereClause, null);
}
} catch (IllegalStateException e) {
Log.e(LOGTAG, "deleteHistoryWhere", e);
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
index f8a2a13..276bddc 100644
--- a/core/java/android/provider/BrowserContract.java
+++ b/core/java/android/provider/BrowserContract.java
@@ -47,6 +47,12 @@
public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
/**
+ * A parameter for use when querying any table that allows specifying a limit on the number
+ * of rows returned.
+ */
+ public static final String PARAM_LIMIT = "limit";
+
+ /**
* Generic columns for use by sync adapters. The specific functions of
* these columns are private to the sync adapter. Other clients of the API
* should not attempt to either read or write these columns.
@@ -122,21 +128,13 @@
public static final String DIRTY = "dirty";
/**
- * The time that this row was created on its originating client (msecs
- * since the epoch).
- * <P>Type: INTEGER</P>
- */
- public static final String DATE_CREATED = "created";
-
- /**
* The time that this row was last modified by a client (msecs since the epoch).
* <P>Type: INTEGER</P>
*/
public static final String DATE_MODIFIED = "modified";
-
}
- interface BookmarkColumns {
+ interface CommonColumns {
/**
* The unique ID for a row.
* <P>Type: INTEGER (long)</P>
@@ -156,6 +154,15 @@
public static final String TITLE = "title";
/**
+ * The time that this row was created on its originating client (msecs
+ * since the epoch).
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATE_CREATED = "created";
+ }
+
+ interface ImageColumns {
+ /**
* The favicon of the bookmark, may be NULL.
* Must decode via {@link BitmapFactory#decodeByteArray}.
* <p>Type: BLOB (image)</p>
@@ -178,10 +185,26 @@
public static final String TOUCH_ICON = "touch_icon";
}
+ interface HistoryColumns {
+ /**
+ * The date the item was last visited, in milliseconds since the epoch.
+ * <p>Type: INTEGER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE_LAST_VISITED = "date";
+
+ /**
+ * The number of times the item has been visited.
+ * <p>Type: INTEGER</p>
+ */
+ public static final String VISITS = "visits";
+
+ public static final String USER_ENTERED = "user_entered";
+ }
+
/**
* The bookmarks table, which holds the user's browser bookmarks.
*/
- public static final class Bookmarks implements BookmarkColumns, SyncColumns {
+ public static final class Bookmarks implements CommonColumns, ImageColumns, SyncColumns {
/**
* This utility class cannot be instantiated.
*/
@@ -199,6 +222,16 @@
Uri.withAppendedPath(CONTENT_URI, "folder");
/**
+ * Query parameter used to specify an account name
+ */
+ public static final String PARAM_ACCOUNT_NAME = "acct_name";
+
+ /**
+ * Query parameter used to specify an account type
+ */
+ public static final String PARAM_ACCOUNT_TYPE = "acct_type";
+
+ /**
* Builds a URI that points to a specific folder.
* @param folderId the ID of the folder to point to
*/
@@ -237,6 +270,12 @@
public static final String PARENT = "parent";
/**
+ * The source ID for an item's parent. Read-only.
+ * @see #PARENT
+ */
+ public static final String PARENT_SOURCE_ID = "parent_source";
+
+ /**
* The position of the bookmark in relation to it's siblings that share the same
* {@link #PARENT}. May be negative.
* <P>Type: INTEGER</P>
@@ -251,6 +290,14 @@
public static final String INSERT_AFTER = "insert_after";
/**
+ * The source ID for the item that the bookmark should be inserted after. Read-only.
+ * May be negative.
+ * <P>Type: INTEGER</P>
+ * @see #INSERT_AFTER
+ */
+ public static final String INSERT_AFTER_SOURCE_ID = "insert_after_source";
+
+ /**
* A flag to indicate if an item has been deleted. Queries will not return deleted
* entries unless you add the {@link #QUERY_PARAMETER_SHOW_DELETED} query paramter
* to the URI when performing your query.
@@ -261,9 +308,34 @@
}
/**
+ * Read-only table that lists all the accounts that are used to provide bookmarks.
+ */
+ public static final class Accounts {
+ /**
+ * Directory under {@link Bookmarks#CONTENT_URI}
+ */
+ public static final Uri CONTENT_URI =
+ AUTHORITY_URI.buildUpon().appendPath("accounts").build();
+
+ /**
+ * The name of the account instance to which this row belongs, which when paired with
+ * {@link #ACCOUNT_TYPE} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+ }
+
+ /**
* The history table, which holds the browsing history.
*/
- public static final class History implements BookmarkColumns {
+ public static final class History implements CommonColumns, HistoryColumns, ImageColumns {
/**
* This utility class cannot be instantiated.
*/
@@ -283,26 +355,6 @@
* The MIME type of a {@link #CONTENT_URI} of a single browser history item.
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/browser-history";
-
- /**
- * The date the item was last visited, in milliseconds since the epoch.
- * <p>Type: INTEGER (date in milliseconds since January 1, 1970)</p>
- */
- public static final String DATE_LAST_VISITED = "date";
-
- /**
- * The date the item created, in milliseconds since the epoch.
- * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
- */
- public static final String DATE_CREATED = "created";
-
- /**
- * The number of times the item has been visited.
- * <p>Type: INTEGER</p>
- */
- public static final String VISITS = "visits";
-
- public static final String USER_ENTERED = "user_entered";
}
/**
@@ -396,4 +448,49 @@
return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
}
}
+
+ /**
+ * Stores images for URLs. Only support query() and update().
+ * @hide
+ */
+ public static final class Images implements ImageColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Images() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "images");
+
+ /**
+ * The URL the images came from.
+ * <P>Type: TEXT (URL)</P>
+ */
+ public static final String URL = "url_key";
+ }
+
+ /**
+ * A combined view of bookmarks and history. All bookmarks in all folders are included and
+ * no folders are included.
+ */
+ public static final class Combined implements CommonColumns, HistoryColumns, ImageColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Combined() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "combined");
+
+ /**
+ * Flag indicating that an item is a bookmark. A value of 1 indicates a bookmark, a value
+ * of 0 indicates a history item.
+ * <p>Type: INTEGER (boolean)</p>
+ */
+ public static final String IS_BOOKMARK = "bookmark";
+ }
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index ecd9fe9..b2c1c2d 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4020,7 +4020,7 @@
* <li>{@link #TYPE_CUSTOM}. Put the actual type in {@link #LABEL}.</li>
* <li>{@link #TYPE_DEFAULT}</li>
* <li>{@link #TYPE_OTHER_NAME}</li>
- * <li>{@link #TYPE_MAINDEN_NAME}</li>
+ * <li>{@link #TYPE_MAIDEN_NAME}</li>
* <li>{@link #TYPE_SHORT_NAME}</li>
* <li>{@link #TYPE_INITIALS}</li>
* </ul>
@@ -4046,6 +4046,9 @@
public static final int TYPE_DEFAULT = 1;
public static final int TYPE_OTHER_NAME = 2;
+ public static final int TYPE_MAIDEN_NAME = 3;
+ /** @deprecated Use TYPE_MAIDEN_NAME instead. */
+ @Deprecated
public static final int TYPE_MAINDEN_NAME = 3;
public static final int TYPE_SHORT_NAME = 4;
public static final int TYPE_INITIALS = 5;
@@ -5357,17 +5360,30 @@
* <td>{@link #DATA1}</td>
* <td></td>
* </tr>
+ * <tr>
+ * <td>int</td>
+ * <td>{@link #TYPE}</td>
+ * <td>{@link #DATA2}</td>
+ * <td>Allowed values are:
+ * <p>
+ * <ul>
+ * <li>{@link #TYPE_CUSTOM}. Put the actual type in {@link #LABEL}.</li>
+ * <li>{@link #TYPE_HOME}</li>
+ * <li>{@link #TYPE_WORK}</li>
+ * <li>{@link #TYPE_OTHER}</li>
+ * </ul>
+ * </p>
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>String</td>
+ * <td>{@link #LABEL}</td>
+ * <td>{@link #DATA3}</td>
+ * <td></td>
+ * </tr>
* </table>
*/
- public static final class SipAddress implements DataColumnsWithJoins {
- // TODO: Ultimately this class will probably implement
- // CommonColumns too (in addition to DataColumnsWithJoins)
- // since it may make sense to have multiple SIP addresses with
- // different types+labels, just like with phone numbers.
- //
- // But that can be extended in the future without breaking any
- // public API, so let's keep this class ultra-simple for now.
-
+ public static final class SipAddress implements DataColumnsWithJoins, CommonColumns {
/**
* This utility class cannot be instantiated
*/
@@ -5376,11 +5392,44 @@
/** MIME type used when storing this in data table. */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
+ public static final int TYPE_HOME = 1;
+ public static final int TYPE_WORK = 2;
+ public static final int TYPE_OTHER = 3;
+
/**
* The SIP address.
* <P>Type: TEXT</P>
*/
public static final String SIP_ADDRESS = DATA1;
+ // ...and TYPE and LABEL come from the CommonColumns interface.
+
+ /**
+ * Return the string resource that best describes the given
+ * {@link #TYPE}. Will always return a valid resource.
+ */
+ public static final int getTypeLabelResource(int type) {
+ switch (type) {
+ case TYPE_HOME: return com.android.internal.R.string.sipAddressTypeHome;
+ case TYPE_WORK: return com.android.internal.R.string.sipAddressTypeWork;
+ case TYPE_OTHER: return com.android.internal.R.string.sipAddressTypeOther;
+ default: return com.android.internal.R.string.sipAddressTypeCustom;
+ }
+ }
+
+ /**
+ * Return a {@link CharSequence} that best describes the given type,
+ * possibly substituting the given {@link #LABEL} value
+ * for {@link #TYPE_CUSTOM}.
+ */
+ public static final CharSequence getTypeLabel(Resources res, int type,
+ CharSequence label) {
+ if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
+ return label;
+ } else {
+ final int labelRes = getTypeLabelResource(type);
+ return res.getText(labelRes);
+ }
+ }
}
}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 603e598..74c7372 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -60,7 +60,7 @@
* @hide
*/
public static final Uri CONTENT_URI =
- Uri.parse("content://downloads/download");
+ Uri.parse("content://downloads/my_downloads");
/**
* Broadcast Action: this is sent by the download manager to the app
@@ -637,10 +637,17 @@
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION";
/**
- * The content:// URI for the data table in the provider
+ * The content:// URI to access downloads owned by the caller's UID.
*/
public static final Uri CONTENT_URI =
- Uri.parse("content://downloads/download");
+ Uri.parse("content://downloads/my_downloads");
+
+ /**
+ * The content URI for accessing all downloads across all UIDs (requires the
+ * ACCESS_ALL_DOWNLOADS permission).
+ */
+ public static final Uri ALL_DOWNLOADS_CONTENT_URI =
+ Uri.parse("content://downloads/all_downloads");
/**
* Broadcast Action: this is sent by the download manager to the app
diff --git a/core/java/android/util/CalendarUtils.java b/core/java/android/util/CalendarUtils.java
index 9a4a67d..3d340d9 100644
--- a/core/java/android/util/CalendarUtils.java
+++ b/core/java/android/util/CalendarUtils.java
@@ -110,6 +110,7 @@
}
}
}
+ cursor.close();
if (writePrefs) {
SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName);
// Write the prefs
@@ -205,11 +206,11 @@
// Update the db
ContentValues values = new ContentValues();
- if (mHandler == null) {
- mHandler = new AsyncTZHandler(context.getContentResolver());
+ if (mHandler != null) {
+ mHandler.cancelOperation(mToken);
}
- mHandler.cancelOperation(mToken);
+ mHandler = new AsyncTZHandler(context.getContentResolver());
// skip 0 so query can use it
if (++mToken == 0) {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 11a5d69..f917001 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -148,9 +148,15 @@
void onPreDraw() {
nPrepare(mRenderer);
}
-
+
private native void nPrepare(int renderer);
+ void onPostDraw() {
+ nFinish(mRenderer);
+ }
+
+ private native void nFinish(int renderer);
+
@Override
public boolean acquireContext() {
if (!mContextLocked) {
@@ -531,17 +537,24 @@
mLine[1] = startY;
mLine[2] = stopX;
mLine[3] = stopY;
- drawLines(mLine, 0, 1, paint);
+ drawLines(mLine, 0, 4, paint);
}
@Override
public void drawLines(float[] pts, int offset, int count, Paint paint) {
- // TODO: Implement
+ if ((offset | count) < 0 || offset + count > pts.length) {
+ throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
+ }
+ boolean hasModifier = setupModifiers(paint);
+ nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
+ if (hasModifier) nResetModifiers(mRenderer);
}
+ private native void nDrawLines(int renderer, float[] points, int offset, int count, int paint);
+
@Override
public void drawLines(float[] pts, Paint paint) {
- drawLines(pts, 0, pts.length / 4, paint);
+ drawLines(pts, 0, pts.length, paint);
}
@Override
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5d85076a..3796994 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -210,7 +210,7 @@
* is invoked and the requested flag is turned off. The error code is
* also logged as a warning.
*/
- void checkErrors() {
+ void checkEglErrors() {
if (isEnabled()) {
int error = sEgl.eglGetError();
if (error != EGL10.EGL_SUCCESS) {
@@ -221,7 +221,7 @@
// we'll try again if it was context lost
setRequested(false);
}
- Log.w(LOG_TAG, "OpenGL error: " + error);
+ Log.w(LOG_TAG, "EGL error: " + Integer.toHexString(error));
}
}
}
@@ -348,7 +348,7 @@
void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
SurfaceHolder holder) {
if (isRequested()) {
- checkErrors();
+ checkEglErrors();
super.initializeIfNeeded(width, height, attachInfo, holder);
}
}
@@ -386,6 +386,9 @@
void onPreDraw() {
}
+ void onPostDraw() {
+ }
+
/**
* Defines the EGL configuration for this renderer. The default configuration
* is RGBX, no depth, no stencil.
@@ -418,10 +421,12 @@
canvas.restoreToCount(saveCount);
}
+ onPostDraw();
+
attachInfo.mIgnoreDirtyState = false;
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
- checkErrors();
+ checkEglErrors();
}
}
@@ -570,6 +575,11 @@
mGlCanvas.onPreDraw();
}
+ @Override
+ void onPostDraw() {
+ mGlCanvas.onPostDraw();
+ }
+
static HardwareRenderer create(boolean translucent) {
if (GLES20Canvas.isAvailable()) {
return new Gl20Renderer(translucent);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 7468579..dd04975 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -28,8 +28,7 @@
* keyboard may compose the capabilities of a standard keyboard together with a track pad mouse
* or other pointing device.
* </p><p>
- * Some input devices present multiple distinguishable sources of input. For example, a
- * game pad may have two analog joysticks, a directional pad and a full complement of buttons.
+ * Some input devices present multiple distinguishable sources of input.
* Applications can query the framework about the characteristics of each distinct source.
* </p><p>
* As a further wrinkle, different kinds of input sources uses different coordinate systems
@@ -55,7 +54,7 @@
/**
* The input source has buttons or keys.
- * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_GAMEPAD}, {@link #SOURCE_DPAD}.
+ * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
*
* A {@link KeyEvent} should be interpreted as a button or key press.
*
@@ -101,18 +100,6 @@
public static final int SOURCE_CLASS_POSITION = 0x00000008;
/**
- * The input source is a joystick.
- *
- * A {@link KeyEvent} should be interpreted as a joystick button press.
- *
- * A {@link MotionEvent} should be interpreted in absolute coordinates as a joystick
- * position in normalized device-specific units nominally between -1.0 and 1.0.
- *
- * Use {@link #getMotionRange} to query the range and precision of motion.
- */
- public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
-
- /**
* The input source is unknown.
*/
public static final int SOURCE_UNKNOWN = 0x00000000;
@@ -132,13 +119,6 @@
public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
/**
- * The input source is a gamepad.
- *
- * @see #SOURCE_CLASS_BUTTON
- */
- public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
-
- /**
* The input source is a touch screen pointing device.
*
* @see #SOURCE_CLASS_POINTER
@@ -168,20 +148,6 @@
* @see #SOURCE_CLASS_POSITION
*/
public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
-
- /**
- * The input source is a joystick mounted on the left or is a standalone joystick.
- *
- * @see #SOURCE_CLASS_JOYSTICK
- */
- public static final int SOURCE_JOYSTICK_LEFT = 0x01000000 | SOURCE_CLASS_JOYSTICK;
-
- /**
- * The input source is a joystick mounted on the right.
- *
- * @see #SOURCE_CLASS_JOYSTICK
- */
- public static final int SOURCE_JOYSTICK_RIGHT = 0x02000000 | SOURCE_CLASS_JOYSTICK;
/**
* A special input source constant that is used when filtering input devices
@@ -411,7 +377,7 @@
/**
* Gets the extent of the center flat position with respect to this coordinate.
* For example, a flat value of 8 means that the center position is between -8 and +8.
- * This value is mainly useful for calibrating joysticks.
+ * This value is mainly useful for calibrating self-centering devices.
* @return The extent of the center flat position.
*/
public float getFlat() {
@@ -506,13 +472,10 @@
description.append(" Sources:");
appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
- appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad");
appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen");
appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse");
appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball");
appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad");
- appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK_LEFT, "joystick_left");
- appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK_RIGHT, "joystick_right");
description.append("\n");
appendRangeDescriptionIfApplicable(description, MOTION_RANGE_X, "x");
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ed10e41..9e7eedf 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -24,120 +24,305 @@
import android.view.KeyCharacterMap.KeyData;
/**
- * Contains constants for key events.
+ * Object used to report key and button events.
+ * <p>
+ * Each key press is described by a sequence of key events. A key press
+ * starts with a key event with {@link #ACTION_DOWN}. If the key is held
+ * sufficiently long that it repeats, then the initial down is followed
+ * additional key events with {@link #ACTION_DOWN} and a non-zero value for
+ * {@link #getRepeatCount()}. The last key event is a {@link #ACTION_UP}
+ * for the key up. If the key press is canceled, the key up event will have the
+ * {@link #FLAG_CANCELED} flag set.
+ * </p><p>
+ * Key events are generally accompanied by a key code ({@link #getKeyCode()}),
+ * scan code ({@link #getScanCode()}) and meta state ({@link #getMetaState()}).
+ * Key code constants are defined in this class. Scan code constants are raw
+ * device-specific codes obtained from the OS and so are not generally meaningful
+ * to applications unless interpreted using the {@link KeyCharacterMap}.
+ * Meta states describe the pressed state of key modifiers
+ * such as {@link #META_SHIFT_ON} or {@link #META_ALT_ON}.
+ * </p><p>
+ * When interacting with an IME, the framework may deliver key events
+ * with the special action {@link #ACTION_MULTIPLE} that either specifies
+ * that single repeated key code or a sequence of characters to insert.
+ * </p><p>
+ * In general, the framework makes no guarantees that the key events delivered
+ * to a view constitute a complete key press. In particular, there is no
+ * guarantee that a view will always receive a key event with {@link #ACTION_UP}
+ * for each {@link #ACTION_DOWN} that was delivered.
+ * </p><p>
+ * Refer to {@link InputDevice} for more information about how different kinds of
+ * input devices and sources represent keys and buttons.
+ * </p>
*/
public class KeyEvent extends InputEvent implements Parcelable {
- // key codes
+ /** Key code constant: Unknown key code. */
public static final int KEYCODE_UNKNOWN = 0;
+ /** Key code constant: Soft Left key.
+ * Usually situated below the display on phones and used as a multi-function
+ * feature key for selecting a software defined function shown on the bottom left
+ * of the display. */
public static final int KEYCODE_SOFT_LEFT = 1;
+ /** Key code constant: Soft Right key.
+ * Usually situated below the display on phones and used as a multi-function
+ * feature key for selecting a software defined function shown on the bottom right
+ * of the display. */
public static final int KEYCODE_SOFT_RIGHT = 2;
+ /** Key code constant: Home key.
+ * This key is handled by the framework and is never delivered to applications. */
public static final int KEYCODE_HOME = 3;
+ /** Key code constant: Back key. */
public static final int KEYCODE_BACK = 4;
+ /** Key code constant: Call key. */
public static final int KEYCODE_CALL = 5;
+ /** Key code constant: End Call key. */
public static final int KEYCODE_ENDCALL = 6;
+ /** Key code constant: '0' key. */
public static final int KEYCODE_0 = 7;
+ /** Key code constant: '1' key. */
public static final int KEYCODE_1 = 8;
+ /** Key code constant: '2' key. */
public static final int KEYCODE_2 = 9;
+ /** Key code constant: '3' key. */
public static final int KEYCODE_3 = 10;
+ /** Key code constant: '4' key. */
public static final int KEYCODE_4 = 11;
+ /** Key code constant: '5' key. */
public static final int KEYCODE_5 = 12;
+ /** Key code constant: '6' key. */
public static final int KEYCODE_6 = 13;
+ /** Key code constant: '7' key. */
public static final int KEYCODE_7 = 14;
+ /** Key code constant: '8' key. */
public static final int KEYCODE_8 = 15;
+ /** Key code constant: '9' key. */
public static final int KEYCODE_9 = 16;
+ /** Key code constant: '*' key. */
public static final int KEYCODE_STAR = 17;
+ /** Key code constant: '#' key. */
public static final int KEYCODE_POUND = 18;
+ /** Key code constant: Directional Pad Up key.
+ * May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_UP = 19;
+ /** Key code constant: Directional Pad Down key.
+ * May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_DOWN = 20;
+ /** Key code constant: Directional Pad Left key.
+ * May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_LEFT = 21;
+ /** Key code constant: Directional Pad Right key.
+ * May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_RIGHT = 22;
+ /** Key code constant: Directional Pad Center key.
+ * May also be synthesized from trackball motions. */
public static final int KEYCODE_DPAD_CENTER = 23;
+ /** Key code constant: Volume Up key. */
public static final int KEYCODE_VOLUME_UP = 24;
+ /** Key code constant: Volume Down key. */
public static final int KEYCODE_VOLUME_DOWN = 25;
+ /** Key code constant: Power key. */
public static final int KEYCODE_POWER = 26;
+ /** Key code constant: Camera key.
+ * Used to launch a camera application or take pictures. */
public static final int KEYCODE_CAMERA = 27;
+ /** Key code constant: Clear key. */
public static final int KEYCODE_CLEAR = 28;
+ /** Key code constant: 'A' key. */
public static final int KEYCODE_A = 29;
+ /** Key code constant: 'B' key. */
public static final int KEYCODE_B = 30;
+ /** Key code constant: 'C' key. */
public static final int KEYCODE_C = 31;
+ /** Key code constant: 'D' key. */
public static final int KEYCODE_D = 32;
+ /** Key code constant: 'E' key. */
public static final int KEYCODE_E = 33;
+ /** Key code constant: 'F' key. */
public static final int KEYCODE_F = 34;
+ /** Key code constant: 'G' key. */
public static final int KEYCODE_G = 35;
+ /** Key code constant: 'H' key. */
public static final int KEYCODE_H = 36;
+ /** Key code constant: 'I' key. */
public static final int KEYCODE_I = 37;
+ /** Key code constant: 'J' key. */
public static final int KEYCODE_J = 38;
+ /** Key code constant: 'K' key. */
public static final int KEYCODE_K = 39;
+ /** Key code constant: 'L' key. */
public static final int KEYCODE_L = 40;
+ /** Key code constant: 'M' key. */
public static final int KEYCODE_M = 41;
+ /** Key code constant: 'N' key. */
public static final int KEYCODE_N = 42;
+ /** Key code constant: 'O' key. */
public static final int KEYCODE_O = 43;
+ /** Key code constant: 'P' key. */
public static final int KEYCODE_P = 44;
+ /** Key code constant: 'Q' key. */
public static final int KEYCODE_Q = 45;
+ /** Key code constant: 'R' key. */
public static final int KEYCODE_R = 46;
+ /** Key code constant: 'S' key. */
public static final int KEYCODE_S = 47;
+ /** Key code constant: 'T' key. */
public static final int KEYCODE_T = 48;
+ /** Key code constant: 'U' key. */
public static final int KEYCODE_U = 49;
+ /** Key code constant: 'V' key. */
public static final int KEYCODE_V = 50;
+ /** Key code constant: 'W' key. */
public static final int KEYCODE_W = 51;
+ /** Key code constant: 'X' key. */
public static final int KEYCODE_X = 52;
+ /** Key code constant: 'Y' key. */
public static final int KEYCODE_Y = 53;
+ /** Key code constant: 'Z' key. */
public static final int KEYCODE_Z = 54;
+ /** Key code constant: ',' key. */
public static final int KEYCODE_COMMA = 55;
+ /** Key code constant: '.' key. */
public static final int KEYCODE_PERIOD = 56;
+ /** Key code constant: Left Alt modifier key. */
public static final int KEYCODE_ALT_LEFT = 57;
+ /** Key code constant: Right Alt modifier key. */
public static final int KEYCODE_ALT_RIGHT = 58;
+ /** Key code constant: Left Shift modifier key. */
public static final int KEYCODE_SHIFT_LEFT = 59;
+ /** Key code constant: Right Shift modifier key. */
public static final int KEYCODE_SHIFT_RIGHT = 60;
+ /** Key code constant: Tab key. */
public static final int KEYCODE_TAB = 61;
+ /** Key code constant: Space key. */
public static final int KEYCODE_SPACE = 62;
+ /** Key code constant: Symbol modifier key. */
public static final int KEYCODE_SYM = 63;
+ /** Key code constant: Explorer special function key.
+ * Used to launch a browser application. */
public static final int KEYCODE_EXPLORER = 64;
+ /** Key code constant: Envelope special function key.
+ * Used to launch a mail application. */
public static final int KEYCODE_ENVELOPE = 65;
+ /** Key code constant: Enter key. */
public static final int KEYCODE_ENTER = 66;
+ /** Key code constant: Delete key. */
public static final int KEYCODE_DEL = 67;
+ /** Key code constant: '`' (backtick) key. */
public static final int KEYCODE_GRAVE = 68;
+ /** Key code constant: '-'. */
public static final int KEYCODE_MINUS = 69;
+ /** Key code constant: '=' key. */
public static final int KEYCODE_EQUALS = 70;
+ /** Key code constant: '[' key. */
public static final int KEYCODE_LEFT_BRACKET = 71;
+ /** Key code constant: ']' key. */
public static final int KEYCODE_RIGHT_BRACKET = 72;
+ /** Key code constant: '\' key. */
public static final int KEYCODE_BACKSLASH = 73;
+ /** Key code constant: ';' key. */
public static final int KEYCODE_SEMICOLON = 74;
+ /** Key code constant: ''' (apostrophe) key. */
public static final int KEYCODE_APOSTROPHE = 75;
+ /** Key code constant: '/' key. */
public static final int KEYCODE_SLASH = 76;
+ /** Key code constant: '@' key. */
public static final int KEYCODE_AT = 77;
+ /** Key code constant: Number Lock modifier key. */
public static final int KEYCODE_NUM = 78;
+ /** Key code constant: Headset Hook key.
+ * Used to hang up calls and stop media. */
public static final int KEYCODE_HEADSETHOOK = 79;
+ /** Key code constant: Camera Focus key.
+ * Used to focus the camera. */
public static final int KEYCODE_FOCUS = 80; // *Camera* focus
+ /** Key code constant: '+' key. */
public static final int KEYCODE_PLUS = 81;
+ /** Key code constant: Menu key. */
public static final int KEYCODE_MENU = 82;
+ /** Key code constant: Notification key. */
public static final int KEYCODE_NOTIFICATION = 83;
+ /** Key code constant: Search key. */
public static final int KEYCODE_SEARCH = 84;
+ /** Key code constant: Play/Pause media key. */
public static final int KEYCODE_MEDIA_PLAY_PAUSE= 85;
+ /** Key code constant: Stop media key. */
public static final int KEYCODE_MEDIA_STOP = 86;
+ /** Key code constant: Play Next media key. */
public static final int KEYCODE_MEDIA_NEXT = 87;
+ /** Key code constant: Play Previous media key. */
public static final int KEYCODE_MEDIA_PREVIOUS = 88;
+ /** Key code constant: Rewind media key. */
public static final int KEYCODE_MEDIA_REWIND = 89;
+ /** Key code constant: Fast Forward media key. */
public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
+ /** Key code constant: Mute key. */
public static final int KEYCODE_MUTE = 91;
+ /** Key code constant: Page Up key. */
public static final int KEYCODE_PAGE_UP = 92;
+ /** Key code constant: Page Down key. */
public static final int KEYCODE_PAGE_DOWN = 93;
+ /** Key code constant: Picture Symbols modifier key.
+ * Used to switch symbol sets (Emoji, Kao-moji). */
public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
+ /** Key code constant: Switch Charset modifier key.
+ * Used to switch character sets (Kanji, Katakana). */
public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
+ /** Key code constant: A Button key.
+ * On a game controller, the A button should be either the button labeled A
+ * or the first button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_A = 96;
+ /** Key code constant: B Button key.
+ * On a game controller, the B button should be either the button labeled B
+ * or the second button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_B = 97;
+ /** Key code constant: C Button key.
+ * On a game controller, the C button should be either the button labeled C
+ * or the third button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_C = 98;
+ /** Key code constant: X Button key.
+ * On a game controller, the X button should be either the button labeled X
+ * or the first button on the lower row of controller buttons. */
public static final int KEYCODE_BUTTON_X = 99;
+ /** Key code constant: Y Button key.
+ * On a game controller, the Y button should be either the button labeled Y
+ * or the second button on the lower row of controller buttons. */
public static final int KEYCODE_BUTTON_Y = 100;
+ /** Key code constant: Z Button key.
+ * On a game controller, the Z button should be either the button labeled Z
+ * or the third button on the lower row of controller buttons. */
public static final int KEYCODE_BUTTON_Z = 101;
+ /** Key code constant: L1 Button key.
+ * On a game controller, the L1 button should be either the button labeled L1 (or L)
+ * or the top left trigger button. */
public static final int KEYCODE_BUTTON_L1 = 102;
+ /** Key code constant: R1 Button key.
+ * On a game controller, the R1 button should be either the button labeled R1 (or R)
+ * or the top right trigger button. */
public static final int KEYCODE_BUTTON_R1 = 103;
+ /** Key code constant: L2 Button key.
+ * On a game controller, the L2 button should be either the button labeled L2
+ * or the bottom left trigger button. */
public static final int KEYCODE_BUTTON_L2 = 104;
+ /** Key code constant: R2 Button key.
+ * On a game controller, the R2 button should be either the button labeled R2
+ * or the bottom right trigger button. */
public static final int KEYCODE_BUTTON_R2 = 105;
+ /** Key code constant: Left Thumb Button key.
+ * On a game controller, the left thumb button indicates that the left (or only)
+ * joystick is pressed. */
public static final int KEYCODE_BUTTON_THUMBL = 106;
+ /** Key code constant: Right Thumb Button key.
+ * On a game controller, the right thumb button indicates that the right
+ * joystick is pressed. */
public static final int KEYCODE_BUTTON_THUMBR = 107;
+ /** Key code constant: Start Button key.
+ * On a game controller, the button labeled Start. */
public static final int KEYCODE_BUTTON_START = 108;
+ /** Key code constant: Select Button key.
+ * On a game controller, the button labeled Select. */
public static final int KEYCODE_BUTTON_SELECT = 109;
+ /** Key code constant: Mode Button key.
+ * On a game controller, the button labeled Mode. */
public static final int KEYCODE_BUTTON_MODE = 110;
// NOTE: If you add a new keycode here you must also add it to:
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 78b9b5d..6705596 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -24,9 +24,71 @@
* Object used to report movement (mouse, pen, finger, trackball) events. This
* class may hold either absolute or relative movements, depending on what
* it is being used for.
- *
- * Refer to {@link InputDevice} for information about how different kinds of
+ * <p>
+ * On pointing devices such as touch screens, pointer coordinates specify absolute
+ * positions such as view X/Y coordinates. Each complete gesture is represented
+ * by a sequence of motion events with actions that describe pointer state transitions
+ * and movements. A gesture starts with a motion event with {@link #ACTION_DOWN}
+ * that provides the location of the first pointer down. As each additional
+ * pointer that goes down or up, the framework will generate a motion event with
+ * {@link #ACTION_POINTER_DOWN} or {@link #ACTION_POINTER_UP} accordingly.
+ * Pointer movements are described by motion events with {@link #ACTION_MOVE}.
+ * Finally, a gesture end either when the final pointer goes up as represented
+ * by a motion event with {@link #ACTION_UP} or when gesture is canceled
+ * with {@link #ACTION_CANCEL}.
+ * </p><p>
+ * On trackballs, the pointer coordinates specify relative movements as X/Y deltas.
+ * A trackball gesture consists of a sequence of movements described by motion
+ * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN}
+ * or {@link #ACTION_UP} motion events when the trackball button is pressed or released.
+ * </p><p>
+ * Motion events always report movements for all pointers at once. The number
+ * of pointers only ever changes by one as individual pointers go up and down,
+ * except when the gesture is canceled.
+ * </p><p>
+ * The order in which individual pointers appear within a motion event can change
+ * from one event to the next. Use the {@link #getPointerId(int)} method to obtain a
+ * pointer id to track pointers across motion events in a gesture. Then for
+ * successive motion events, use the {@link #findPointerIndex(int)} method to obtain
+ * the pointer index for a given pointer id in that motion event.
+ * </p><p>
+ * For efficiency, motion events with {@link #ACTION_MOVE} may batch together
+ * multiple movement samples within a single object. The most current
+ * pointer coordinates are available using {@link #getX(int)} and {@link #getY(int)}.
+ * Earlier coordinates within the batch are accessed using {@link #getHistoricalX(int, int)}
+ * and {@link #getHistoricalY(int, int)}. The coordinates are "historical" only
+ * insofar as they are older than the current coordinates in the batch; however,
+ * they are still distinct from any other coordinates reported in prior motion events.
+ * To process all coordinates in the batch in time order, first consume the historical
+ * coordinates then consume the current coordinates.
+ * </p><p>
+ * Example: Consuming all samples for all pointers in a motion event in time order.
+ * </p><p><pre><code>
+ * void printSamples(MotionEvent ev) {
+ * final int historySize = ev.getHistorySize();
+ * final int pointerCount = ev.getPointerCount();
+ * for (int h = 0; h < historySize; h++) {
+ * System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
+ * for (int p = 0; p < pointerCount; p++) {
+ * System.out.printf(" pointer %d: (%f,%f)",
+ * ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
+ * }
+ * }
+ * System.out.printf("At time %d:", ev.getEventTime());
+ * for (int p = 0; p < pointerCount; p++) {
+ * System.out.printf(" pointer %d: (%f,%f)",
+ * ev.getPointerId(p), ev.getX(p), ev.getY(p));
+ * }
+ * }
+ * </code></pre></p><p>
+ * In general, the framework makes no guarantees that the motion events delivered
+ * to a view constitute a complete gesture. In particular, there is no
+ * guarantee that a view will always receive a motion event with {@link #ACTION_UP}
+ * for each {@link #ACTION_DOWN} that was delivered.
+ * </p><p>
+ * Refer to {@link InputDevice} for more information about how different kinds of
* input devices and sources represent pointer coordinates.
+ * </p>
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long MS_PER_NS = 1000000;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d04007c..ac65e09 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5286,7 +5286,8 @@
* 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
+ * <p>By default, this is 1.0f.
+ *
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
@@ -5318,7 +5319,8 @@
* 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
+ * <p>By default, this is 1.0f.
+ *
* @see #getPivotX()
* @see #getPivotY()
* @return The scaling factor.
@@ -5427,7 +5429,7 @@
* 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
+ * <p>By default this is 1.0f.
* @return The opacity of the view.
*/
public float getAlpha() {
@@ -8743,7 +8745,7 @@
mPrivateFlags = (mPrivateFlags & ~ACTIVATED) | (activated ? ACTIVATED : 0);
invalidate();
refreshDrawableState();
- dispatchSetSelected(activated);
+ dispatchSetActivated(activated);
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d81d7f2..122c268 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -178,6 +178,7 @@
private boolean mUseDoubleTree = false;
private boolean mUseWideViewport = false;
private boolean mUseFixedViewport = false;
+ private int mMaxFixedViewportWidth = WebView.DEFAULT_VIEWPORT_WIDTH;
private boolean mSupportMultipleWindows = false;
private boolean mShrinksStandaloneImagesToFit = false;
private long mMaximumDecodedImageSize = 0; // 0 means default
@@ -324,8 +325,9 @@
// Detect tablet device for fixed viewport mode.
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- mUseFixedViewport = (metrics.density == 1.0f
- && (metrics.widthPixels >= 800 ||metrics.heightPixels >= 800));
+ final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels);
+ mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= 800);
+ mMaxFixedViewportWidth = (int) (landscapeWidth * 1.25);
if (sLockForLocaleSettings == null) {
sLockForLocaleSettings = new Object();
@@ -1516,6 +1518,13 @@
}
/**
+ * Returns maximum fixed viewport width.
+ */
+ /* package */ int getMaxFixedViewportWidth() {
+ return mMaxFixedViewportWidth;
+ }
+
+ /**
* Returns whether private browsing is enabled.
*/
/* package */ boolean isPrivateBrowsingEnabled() {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5d4ae6d..c62894c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -667,7 +667,7 @@
// If the site doesn't use the viewport meta tag to specify the viewport,
// use DEFAULT_VIEWPORT_WIDTH as the default viewport width
- static final int DEFAULT_VIEWPORT_WIDTH = 1040;
+ static final int DEFAULT_VIEWPORT_WIDTH = 980;
// normally we try to fit the content to the minimum preferred width
// calculated by the Webkit. To avoid the bad behavior when some site's
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 56d6296..acea163 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1705,7 +1705,12 @@
nativeGetContentMinPrefWidth())));
}
} else if (mViewportWidth > 0) {
- width = Math.max(w, mViewportWidth);
+ if (mSettings.getUseFixedViewport()) {
+ // Use website specified or desired fixed viewport width.
+ width = mViewportWidth;
+ } else {
+ width = Math.max(w, mViewportWidth);
+ }
} else {
width = textwrapWidth;
}
@@ -1780,6 +1785,7 @@
int mScrollX;
int mScrollY;
boolean mMobileSite;
+ int mViewportWidth;
}
static class DrawData {
@@ -1821,6 +1827,13 @@
}
if (mInitialViewState != null) {
draw.mViewState = mInitialViewState;
+ if (mViewportWidth == -1 && mSettings.getUseFixedViewport() &&
+ mSettings.getUseWideViewPort()) {
+ // Use website's initial preferred width as the fixed viewport width.
+ mViewportWidth = Math.min(mSettings.getMaxFixedViewportWidth(),
+ draw.mMinPrefWidth);
+ draw.mViewState.mViewportWidth = mViewportWidth;
+ }
mInitialViewState = null;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
@@ -2154,6 +2167,7 @@
mInitialViewState.mScrollX = mRestoredX;
mInitialViewState.mScrollY = mRestoredY;
mInitialViewState.mMobileSite = (0 == mViewportWidth);
+ mInitialViewState.mViewportWidth = mViewportWidth;
if (mRestoredScale > 0) {
mInitialViewState.mViewScale = mRestoredScale / 100.0f;
if (mRestoredTextWrapScale > 0) {
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index a760e91..0d704a0 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -770,6 +770,12 @@
} else {
mMaxZoomScale = viewState.mMaxScale;
}
+ if (viewState.mViewportWidth > 0 &&
+ mWebView.getSettings().getUseFixedViewport() &&
+ mWebView.getSettings().getUseWideViewPort()) {
+ // Use website specified viewport width.
+ setZoomOverviewWidth(viewState.mViewportWidth);
+ }
}
/**
@@ -835,7 +841,7 @@
if (settings.getUseWideViewPort()
&& (settings.getLoadWithOverviewMode() || settings.getUseFixedViewport())) {
mInitialZoomOverview = true;
- scale = (float) mWebView.getViewWidth() / WebView.DEFAULT_VIEWPORT_WIDTH;
+ scale = (float) mWebView.getViewWidth() / mZoomOverviewWidth;
} else {
scale = viewState.mTextWrapScale;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f3dfb77..bb13f1d 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4365,6 +4365,14 @@
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
*/
public void setRemoteViewsAdapter(Intent intent) {
+ // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
+ // service handling the specified intent.
+ Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+ if (mRemoteAdapter != null && fc.equals(mRemoteAdapter.getRemoteViewsServiceIntent())) {
+ return;
+ }
+
+ // Otherwise, create a new RemoteViewsAdapter for binding
mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
}
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 0310a31..c08adb2 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -779,6 +779,15 @@
*/
@android.view.RemotableViewMethod
public void setRemoteViewsAdapter(Intent intent) {
+ // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
+ // service handling the specified intent.
+ Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+ if (mRemoteViewsAdapter != null &&
+ fc.equals(mRemoteViewsAdapter.getRemoteViewsServiceIntent())) {
+ return;
+ }
+
+ // Otherwise, create a new RemoteViewsAdapter for binding
mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0ca71e1..5645101 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -16,6 +16,13 @@
package android.widget;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
import android.app.PendingIntent;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
@@ -39,13 +46,6 @@
import android.view.LayoutInflater.Filter;
import android.view.View.OnClickListener;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-
/**
* A class that describes a view hierarchy that can be displayed in
@@ -76,6 +76,15 @@
/**
+ * This flag indicates whether this RemoteViews object is being created from a
+ * RemoteViewsService for use as a child of a widget collection. This flag is used
+ * to determine whether or not certain features are available, in particular,
+ * setting on click extras and setting on click pending intents. The former is enabled,
+ * and the latter disabled when this flag is true.
+ */
+ private boolean mIsWidgetCollectionChild = false;
+
+ /**
* This annotation indicates that a subclass of View is alllowed to be used
* with the {@link RemoteViews} mechanism.
*/
@@ -147,6 +156,134 @@
}
}
+ private class SetOnClickExtras extends Action {
+ public SetOnClickExtras(int id, Bundle extras) {
+ this.viewId = id;
+ this.extras = extras;
+ }
+
+ public SetOnClickExtras(Parcel parcel) {
+ viewId = parcel.readInt();
+ extras = Bundle.CREATOR.createFromParcel(parcel);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ extras.writeToParcel(dest, 0 /* no flags */);
+ }
+
+ @Override
+ public void apply(View root) {
+ final View target = root.findViewById(viewId);
+
+ if (!mIsWidgetCollectionChild) {
+ Log.e("RemoteViews", "The method setOnClickExtras is available " +
+ "only from RemoteViewsFactory (ie. on collection items).");
+ return;
+ }
+
+ if (target != null && extras != null) {
+ OnClickListener listener = new OnClickListener() {
+ public void onClick(View v) {
+ // Insure that this view is a child of an AdapterView
+ View parent = (View) v.getParent();
+ while (!(parent instanceof AdapterView<?>)
+ && !(parent instanceof AppWidgetHostView)) {
+ parent = (View) parent.getParent();
+ }
+
+ if (parent instanceof AppWidgetHostView) {
+ // Somehow they've managed to get this far without having
+ // and AdapterView as a parent.
+ Log.e("RemoteViews", "Collection item doesn't have AdapterView parent");
+ return;
+ }
+
+ // Insure that a template pending intent has been set on an ancestor
+ if (!(parent.getTag() instanceof PendingIntent)) {
+ Log.e("RemoteViews", "Attempting setOnClickExtras without calling " +
+ "setPendingIntentTemplate on parent.");
+ return;
+ }
+
+ PendingIntent pendingIntent = (PendingIntent) parent.getTag();
+
+ final float appScale = v.getContext().getResources()
+ .getCompatibilityInfo().applicationScale;
+ final int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+
+ final Rect rect = new Rect();
+ rect.left = (int) (pos[0] * appScale + 0.5f);
+ rect.top = (int) (pos[1] * appScale + 0.5f);
+ rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+ rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+
+ final Intent intent = new Intent();
+ intent.setSourceBounds(rect);
+ intent.putExtras(extras);
+
+ try {
+ // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+ v.getContext().startIntentSender(
+ pendingIntent.getIntentSender(), intent,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ Intent.FLAG_ACTIVITY_NEW_TASK, 0);
+ } catch (IntentSender.SendIntentException e) {
+ android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
+ }
+ }
+
+ };
+ target.setOnClickListener(listener);
+ }
+ }
+
+ int viewId;
+ Bundle extras;
+
+ public final static int TAG = 7;
+ }
+
+ private class SetPendingIntentTemplate extends Action {
+ public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
+ this.viewId = id;
+ this.pendingIntentTemplate = pendingIntentTemplate;
+ }
+
+ public SetPendingIntentTemplate(Parcel parcel) {
+ viewId = parcel.readInt();
+ pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
+ }
+
+ @Override
+ public void apply(View root) {
+ final View target = root.findViewById(viewId);
+
+ // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
+ if (target instanceof AdapterView<?>) {
+ // The PendingIntent template is stored in the view's tag.
+ target.setTag(pendingIntentTemplate);
+ } else {
+ Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" +
+ "an AdapterView.");
+ return;
+ }
+ }
+
+ int viewId;
+ PendingIntent pendingIntentTemplate;
+
+ public final static int TAG = 8;
+ }
+
/**
* Equivalent to calling
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
@@ -157,21 +294,29 @@
this.viewId = id;
this.pendingIntent = pendingIntent;
}
-
+
public SetOnClickPendingIntent(Parcel parcel) {
viewId = parcel.readInt();
pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
}
-
+
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(TAG);
dest.writeInt(viewId);
pendingIntent.writeToParcel(dest, 0 /* no flags */);
}
-
+
@Override
public void apply(View root) {
final View target = root.findViewById(viewId);
+
+ // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
+ // sense, do they mean to set a PendingIntent template for the AdapterView's children?
+ if (mIsWidgetCollectionChild) {
+ Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item.");
+ // TODO: return; We'll let this slide until apps are up to date.
+ }
+
if (target != null && pendingIntent != null) {
OnClickListener listener = new OnClickListener() {
public void onClick(View v) {
@@ -647,6 +792,8 @@
public RemoteViews(Parcel parcel) {
mPackage = parcel.readString();
mLayoutId = parcel.readInt();
+ mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
+
int count = parcel.readInt();
if (count > 0) {
mActions = new ArrayList<Action>(count);
@@ -671,6 +818,12 @@
case SetEmptyView.TAG:
mActions.add(new SetEmptyView(parcel));
break;
+ case SetOnClickExtras.TAG:
+ mActions.add(new SetOnClickExtras(parcel));
+ break;
+ case SetPendingIntentTemplate.TAG:
+ mActions.add(new SetPendingIntentTemplate(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -696,6 +849,17 @@
}
/**
+ * This flag indicates whether this RemoteViews object is being created from a
+ * RemoteViewsService for use as a child of a widget collection. This flag is used
+ * to determine whether or not certain features are available, in particular,
+ * setting on click extras and setting on click pending intents. The former is enabled,
+ * and the latter disabled when this flag is true.
+ */
+ void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
+ mIsWidgetCollectionChild = isWidgetCollectionChild;
+ }
+
+ /**
* Add an action to be executed on the remote side when apply is called.
*
* @param a The action to add
@@ -758,7 +922,7 @@
public void setViewVisibility(int viewId, int visibility) {
setInt(viewId, "setVisibility", visibility);
}
-
+
/**
* Equivalent to calling TextView.setText
*
@@ -864,6 +1028,35 @@
}
/**
+ * When using collections (eg. ListView, StackView etc.) in widgets, it is very costly
+ * to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * a single PendingIntent template can be set on the collection, and individual items can
+ * differentiate their click behavior using {@link RemoteViews#setOnClickExtras(int, Bundle)}.
+ *
+ * @param viewId The id of the collection who's children will use this PendingIntent template
+ * when clicked
+ * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
+ * by a child of viewId and executed when that child is clicked
+ */
+ public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
+ addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
+ }
+
+ /**
+ * When using collections (eg. ListView, StackView etc.) in widgets, it is very costly
+ * to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * a single PendingIntent template can be set on the collection, see {@link
+ * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the items click
+ * behaviour can be distinguished by setting extras.
+ *
+ * @param viewId The id of the
+ * @param extras
+ */
+ public void setOnClickExtras(int viewId, Bundle extras) {
+ addAction(new SetOnClickExtras(viewId, extras));
+ }
+
+ /**
* @hide
* Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -1179,6 +1372,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackage);
dest.writeInt(mLayoutId);
+ dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
int count;
if (mActions != null) {
count = mActions.size();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 70b9d59..91b4afa 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -669,6 +669,9 @@
public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
mContext = context;
mIntent = intent;
+ if (mIntent == null) {
+ throw new IllegalArgumentException("Non-null Intent must be specified.");
+ }
// initialize the worker thread
mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
@@ -689,6 +692,10 @@
unbindService();
}
+ public Intent getRemoteViewsServiceIntent() {
+ return mIntent;
+ }
+
public int getCount() {
requestBindService();
return mViewCache.getCount();
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 584fa25..b9ded190 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -36,7 +36,7 @@
private static final String LOG_TAG = "RemoteViewsService";
// multimap implementation for reference counting
- private HashMap<Intent, Pair<RemoteViewsFactory, Integer>> mRemoteViewFactories;
+ private HashMap<Intent.FilterComparison, Pair<RemoteViewsFactory, Integer>> mRemoteViewFactories;
private final Object mLock = new Object();
/**
@@ -89,7 +89,9 @@
return mFactory.getCount();
}
public RemoteViews getViewAt(int position) {
- return mFactory.getViewAt(position);
+ RemoteViews rv = mFactory.getViewAt(position);
+ rv.setIsWidgetCollectionChild(true);
+ return rv;
}
public RemoteViews getLoadingView() {
return mFactory.getLoadingView();
@@ -108,26 +110,28 @@
}
public RemoteViewsService() {
- mRemoteViewFactories = new HashMap<Intent, Pair<RemoteViewsFactory, Integer>>();
+ mRemoteViewFactories =
+ new HashMap<Intent.FilterComparison, Pair<RemoteViewsFactory, Integer>>();
}
@Override
public IBinder onBind(Intent intent) {
synchronized (mLock) {
// increment the reference count to the particular factory associated with this intent
+ Intent.FilterComparison fc = new Intent.FilterComparison(intent);
Pair<RemoteViewsFactory, Integer> factoryRef = null;
RemoteViewsFactory factory = null;
- if (!mRemoteViewFactories.containsKey(intent)) {
+ if (!mRemoteViewFactories.containsKey(fc)) {
factory = onGetViewFactory(intent);
factoryRef = new Pair<RemoteViewsFactory, Integer>(factory, 1);
- mRemoteViewFactories.put(intent, factoryRef);
+ mRemoteViewFactories.put(fc, factoryRef);
factory.onCreate();
} else {
- Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(intent);
+ Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(fc);
factory = oldFactoryRef.first;
int newRefCount = oldFactoryRef.second.intValue() + 1;
factoryRef = new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
- mRemoteViewFactories.put(intent, factoryRef);
+ mRemoteViewFactories.put(fc, factoryRef);
}
return new RemoteViewsFactoryAdapter(factory);
}
@@ -136,16 +140,19 @@
@Override
public boolean onUnbind(Intent intent) {
synchronized (mLock) {
- if (mRemoteViewFactories.containsKey(intent)) {
+ Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+ if (mRemoteViewFactories.containsKey(fc)) {
// this alleviates the user's responsibility of having to clear all factories
- Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(intent);
+ Pair<RemoteViewsFactory, Integer> oldFactoryRef =
+ mRemoteViewFactories.get(fc);
int newRefCount = oldFactoryRef.second.intValue() - 1;
if (newRefCount <= 0) {
oldFactoryRef.first.onDestroy();
- mRemoteViewFactories.remove(intent);
+ mRemoteViewFactories.remove(fc);
} else {
- Pair<RemoteViewsFactory, Integer> factoryRef = new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
- mRemoteViewFactories.put(intent, factoryRef);
+ Pair<RemoteViewsFactory, Integer> factoryRef =
+ new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
+ mRemoteViewFactories.put(fc, factoryRef);
}
}
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 57dc725..09217af 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -66,6 +66,7 @@
private AutoCompleteTextView mQueryTextView;
private boolean mSubmitButtonEnabled;
private CharSequence mQueryHint;
+ private boolean mQueryRefinement;
private SearchableInfo mSearchable;
@@ -282,7 +283,34 @@
return mSubmitButtonEnabled;
}
- public interface FilterableListAdapter extends ListAdapter, Filterable {
+ /**
+ * Specifies if a query refinement button should be displayed alongside each suggestion
+ * or if it should depend on the flags set in the individual items retrieved from the
+ * suggestions provider. Clicking on the query refinement button will replace the text
+ * in the query text field with the text from the suggestion. This flag only takes effect
+ * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
+ * and not when using a custom adapter.
+ *
+ * @param enable true if all items should have a query refinement button, false if only
+ * those items that have a query refinement flag set should have the button.
+ *
+ * @see SearchManager#SUGGEST_COLUMN_FLAGS
+ * @see SearchManager#FLAG_QUERY_REFINEMENT
+ */
+ public void setQueryRefinementEnabled(boolean enable) {
+ mQueryRefinement = enable;
+ if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
+ ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+ enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
+ }
+ }
+
+ /**
+ * Returns whether query refinement is enabled for all items or only specific ones.
+ * @return true if enabled for all items, false otherwise.
+ */
+ public boolean isQueryRefinementEnabled() {
+ return mQueryRefinement;
}
/**
@@ -333,6 +361,14 @@
}
}
+ /**
+ * Called by the SuggestionsAdapter
+ * @hide
+ */
+ /* package */void onQueryRefine(CharSequence queryText) {
+ setQuery(queryText);
+ }
+
private final OnClickListener mOnClickListener = new OnClickListener() {
public void onClick(View v) {
@@ -403,6 +439,9 @@
mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
this, mSearchable, mOutsideDrawablesCache);
mQueryTextView.setAdapter(mSuggestionsAdapter);
+ ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+ mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
+ : SuggestionsAdapter.REFINE_BY_ENTRY);
}
}
@@ -555,12 +594,14 @@
* Sets the text in the query box, without updating the suggestions.
*/
private void setQuery(CharSequence query) {
- mQueryTextView.setText(query, false);
+ mQueryTextView.setText(query, true);
+ // Move the cursor to the end
+ mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
}
private void launchQuerySearch(int actionKey, String actionMsg, String query) {
String action = Intent.ACTION_SEARCH;
- Intent intent = createIntent(action, null, null, query, null, actionKey, actionMsg);
+ Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
getContext().startActivity(intent);
}
@@ -571,7 +612,6 @@
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
- * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
@@ -581,7 +621,7 @@
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
- String componentName, int actionKey, String actionMsg) {
+ int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -624,11 +664,6 @@
// use specific action if supplied, or default action if supplied, or fixed default
String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
- // some items are display only, or have effect via the cursor respond click reporting.
- if (SearchManager.INTENT_ACTION_NONE.equals(action)) {
- return null;
- }
-
if (action == null) {
action = mSearchable.getSuggestIntentAction();
}
@@ -650,14 +685,10 @@
}
Uri dataUri = (data == null) ? null : Uri.parse(data);
- String componentName = getColumnString(
- c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
-
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
- return createIntent(action, dataUri, extraData, query, componentName, actionKey,
- actionMsg);
+ return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 0f1acbe..56dc5cd 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -869,21 +869,19 @@
View p = v;
if (!(v.getParent() != null && v.getParent() instanceof View)) return;
- View gp = (View) v.getParent();
boolean firstPass = true;
parentRect.set(0, 0, 0, 0);
int depth = 0;
- while (gp.getParent() != null && gp.getParent() instanceof View
+ while (p.getParent() != null && p.getParent() instanceof View
&& !parentRect.contains(r)) {
if (!firstPass) {
- r.offset(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY());
+ r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
depth++;
}
firstPass = false;
p = (View) p.getParent();
- gp = (View) p.getParent();
- parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(),
- p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY());
+ parentRect.set(p.getScrollX(), p.getScrollY(),
+ p.getWidth() + p.getScrollX(), p.getHeight() + p.getScrollY());
// TODO: we need to stop early here if we've hit the edge of the screen
// so as to prevent us from walking too high in the hierarchy. A lot of this
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index 1b2449e..1ebe622 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -45,6 +45,7 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.View.OnClickListener;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -56,19 +57,23 @@
*
* @hide
*/
-class SuggestionsAdapter extends ResourceCursorAdapter {
+class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListener {
private static final boolean DBG = false;
private static final String LOG_TAG = "SuggestionsAdapter";
private static final int QUERY_LIMIT = 50;
+ static final int REFINE_NONE = 0;
+ static final int REFINE_BY_ENTRY = 1;
+ static final int REFINE_ALL = 2;
+
private SearchManager mSearchManager;
private SearchView mSearchView;
private SearchableInfo mSearchable;
private Context mProviderContext;
private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
- private SparseArray<Drawable.ConstantState> mBackgroundsCache;
private boolean mClosed = false;
+ private int mQueryRefinement = REFINE_BY_ENTRY;
// URL color
private ColorStateList mUrlColor;
@@ -79,7 +84,7 @@
private int mText2UrlCol;
private int mIconName1Col;
private int mIconName2Col;
- private int mBackgroundColorCol;
+ private int mFlagsCol;
static final int NONE = -1;
@@ -102,14 +107,12 @@
mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
mSearchView = searchView;
mSearchable = searchable;
-
// set up provider resources (gives us icons, etc.)
Context activityContext = mSearchable.getActivityContext(mContext);
mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
mOutsideDrawablesCache = outsideDrawablesCache;
- mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
-
+
mStartSpinnerRunnable = new Runnable() {
public void run() {
// mSearchView.setWorking(true); // TODO:
@@ -138,6 +141,27 @@
}
/**
+ * Enables query refinement for all suggestions. This means that an additional icon
+ * will be shown for each entry. When clicked, the suggested text on that line will be
+ * copied to the query text field.
+ * <p>
+ *
+ * @param refine which queries to refine. Possible values are {@link #REFINE_NONE},
+ * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}.
+ */
+ public void setQueryRefinement(int refineWhat) {
+ mQueryRefinement = refineWhat;
+ }
+
+ /**
+ * Returns the current query refinement preference.
+ * @return value of query refinement preference
+ */
+ public int getQueryRefinement() {
+ return mQueryRefinement;
+ }
+
+ /**
* Overridden to always return <code>false</code>, since we cannot be sure that
* suggestion sources return stable IDs.
*/
@@ -242,8 +266,7 @@
mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
- mBackgroundColorCol =
- c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
+ mFlagsCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FLAGS);
}
} catch (Exception e) {
Log.e(LOG_TAG, "error changing cursor and caching columns", e);
@@ -269,12 +292,14 @@
public final TextView mText2;
public final ImageView mIcon1;
public final ImageView mIcon2;
+ public final ImageView mIconRefine;
public ChildViewCache(View v) {
mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1);
mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2);
mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1);
mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2);
+ mIconRefine = (ImageView) v.findViewById(com.android.internal.R.id.edit_query);
}
}
@@ -282,13 +307,10 @@
public void bindView(View view, Context context, Cursor cursor) {
ChildViewCache views = (ChildViewCache) view.getTag();
- int backgroundColor = 0;
- if (mBackgroundColorCol != -1) {
- backgroundColor = cursor.getInt(mBackgroundColorCol);
+ int flags = 0;
+ if (mFlagsCol != -1) {
+ flags = cursor.getInt(mFlagsCol);
}
- Drawable background = getItemBackground(backgroundColor);
- view.setBackgroundDrawable(background);
-
if (views.mText1 != null) {
String text1 = getStringOrNull(cursor, mText1Col);
setViewText(views.mText1, text1);
@@ -301,7 +323,7 @@
} else {
text2 = getStringOrNull(cursor, mText2Col);
}
-
+
// If no second line of text is indicated, allow the first line of text
// to be up to two lines if it wants to be.
if (TextUtils.isEmpty(text2)) {
@@ -324,6 +346,22 @@
if (views.mIcon2 != null) {
setViewDrawable(views.mIcon2, getIcon2(cursor));
}
+ if (mQueryRefinement == REFINE_ALL
+ || (mQueryRefinement == REFINE_BY_ENTRY
+ && (flags & SearchManager.FLAG_QUERY_REFINEMENT) != 0)) {
+ views.mIconRefine.setVisibility(View.VISIBLE);
+ views.mIconRefine.setTag(views.mText1.getText());
+ views.mIconRefine.setOnClickListener(this);
+ } else {
+ views.mIconRefine.setVisibility(View.GONE);
+ }
+ }
+
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof CharSequence) {
+ mSearchView.onQueryRefine((CharSequence) tag);
+ }
}
private CharSequence formatUrl(CharSequence url) {
@@ -341,33 +379,6 @@
return text;
}
- /**
- * Gets a drawable with no color when selected or pressed, and the given color when
- * neither selected nor pressed.
- *
- * @return A drawable, or {@code null} if the given color is transparent.
- */
- private Drawable getItemBackground(int backgroundColor) {
- if (backgroundColor == 0) {
- return null;
- } else {
- Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor);
- if (cachedBg != null) {
- if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
- return cachedBg.newDrawable(mProviderContext.getResources());
- }
- if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
- ColorDrawable transparent = new ColorDrawable(0);
- ColorDrawable background = new ColorDrawable(backgroundColor);
- StateListDrawable newBg = new StateListDrawable();
- newBg.addState(new int[]{android.R.attr.state_selected}, transparent);
- newBg.addState(new int[]{android.R.attr.state_pressed}, transparent);
- newBg.addState(new int[]{}, background);
- mBackgroundsCache.put(backgroundColor, newBg.getConstantState());
- return newBg;
- }
- }
-
private void setViewText(TextView v, CharSequence text) {
// Set the text even if it's null, since we need to clear any previous text.
v.setText(text);
@@ -600,21 +611,7 @@
* @return A non-null drawable.
*/
private Drawable getDefaultIcon1(Cursor cursor) {
- // First check the component that the suggestion is originally from
- String c = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
- if (c != null) {
- ComponentName component = ComponentName.unflattenFromString(c);
- if (component != null) {
- Drawable drawable = getActivityIconWithCache(component);
- if (drawable != null) {
- return drawable;
- }
- } else {
- Log.w(LOG_TAG, "Bad component name: " + c);
- }
- }
-
- // Then check the component that gave us the suggestion
+ // Check the component that gave us the suggestion
Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
if (drawable != null) {
return drawable;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2fb2d6d..2d550e4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3873,10 +3873,10 @@
mCurrentAlpha = alpha;
final Drawables dr = mDrawables;
if (dr != null) {
- if (dr.mDrawableLeft != null) dr.mDrawableLeft.setAlpha(alpha);
- if (dr.mDrawableTop != null) dr.mDrawableTop.setAlpha(alpha);
- if (dr.mDrawableRight != null) dr.mDrawableRight.setAlpha(alpha);
- if (dr.mDrawableBottom != null) dr.mDrawableBottom.setAlpha(alpha);
+ if (dr.mDrawableLeft != null) dr.mDrawableLeft.mutate().setAlpha(alpha);
+ if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha);
+ if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha);
+ if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha);
}
return true;
}
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index d38c03b..3030316 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -137,7 +137,7 @@
}
class State1 extends HierarchicalState {
- \@Override public boolean processMessage(Message message) {
+ @Override public boolean processMessage(Message message) {
Log.d(TAG, "Hello World");
return HANDLED;
}
@@ -257,10 +257,10 @@
}
class P1 extends HierarchicalState {
- \@Override public void enter() {
+ @Override public void enter() {
Log.d(TAG, "mP1.enter");
}
- \@Override public boolean processMessage(Message message) {
+ @Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mP1.processMessage what=" + message.what);
switch(message.what) {
@@ -278,16 +278,16 @@
}
return retVal;
}
- \@Override public void exit() {
+ @Override public void exit() {
Log.d(TAG, "mP1.exit");
}
}
class S1 extends HierarchicalState {
- \@Override public void enter() {
+ @Override public void enter() {
Log.d(TAG, "mS1.enter");
}
- \@Override public boolean processMessage(Message message) {
+ @Override public boolean processMessage(Message message) {
Log.d(TAG, "S1.processMessage what=" + message.what);
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
@@ -298,16 +298,16 @@
return NOT_HANDLED;
}
}
- \@Override public void exit() {
+ @Override public void exit() {
Log.d(TAG, "mS1.exit");
}
}
class S2 extends HierarchicalState {
- \@Override public void enter() {
+ @Override public void enter() {
Log.d(TAG, "mS2.enter");
}
- \@Override public boolean processMessage(Message message) {
+ @Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mS2.processMessage what=" + message.what);
switch(message.what) {
@@ -326,17 +326,17 @@
}
return retVal;
}
- \@Override public void exit() {
+ @Override public void exit() {
Log.d(TAG, "mS2.exit");
}
}
class P2 extends HierarchicalState {
- \@Override public void enter() {
+ @Override public void enter() {
Log.d(TAG, "mP2.enter");
sendMessage(obtainMessage(CMD_5));
}
- \@Override public boolean processMessage(Message message) {
+ @Override public boolean processMessage(Message message) {
Log.d(TAG, "P2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_3):
@@ -349,12 +349,12 @@
}
return HANDLED;
}
- \@Override public void exit() {
+ @Override public void exit() {
Log.d(TAG, "mP2.exit");
}
}
- \@Override
+ @Override
void halting() {
Log.d(TAG, "halting");
synchronized (this) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c9d7e7f..4da73d7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -703,42 +703,6 @@
}
}
- /* enable poisoning of memory of freed objects */
- property_get("dalvik.vm.gc.overwritefree", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- opt.optionString = "-Xgc:overwritefree";
- mOptions.add(opt);
- } else if (strcmp(propBuf, "false") != 0) {
- LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'");
- }
-
- /* enable heap verification before each gc */
- property_get("dalvik.vm.gc.preverify", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- opt.optionString = "-Xgc:preverify";
- mOptions.add(opt);
- } else if (strcmp(propBuf, "false") != 0) {
- LOGW("dalvik.vm.gc.preverify should be 'true' or 'false'");
- }
-
- /* enable heap verification after each gc */
- property_get("dalvik.vm.gc.postverify", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- opt.optionString = "-Xgc:postverify";
- mOptions.add(opt);
- } else if (strcmp(propBuf, "false") != 0) {
- LOGW("dalvik.vm.gc.postverify should be 'true' or 'false'");
- }
-
- /* enable card table verification for partial gc */
- property_get("dalvik.vm.gc.verifycardtable", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- opt.optionString = "-Xgc:verifycardtable";
- mOptions.add(opt);
- } else if (strcmp(propBuf, "false") != 0) {
- LOGW("dalvik.vm.gc.verifycardtable should be 'true' or 'false'");
- }
-
/* enable debugging; set suspend=y to pause during VM init */
#ifdef HAVE_ANDROID_OS
/* use android ADB transport */
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
index 0f3114b..97e0483 100644
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ b/core/jni/android_database_SQLiteStatement.cpp
@@ -239,6 +239,17 @@
return value;
}
+static void native_executeSql(JNIEnv* env, jobject object, jstring sql)
+{
+ char const* sqlString = env->GetStringUTFChars(sql, NULL);
+ sqlite3 * handle = GET_HANDLE(env, object);
+ int err = sqlite3_exec(handle, sqlString, NULL, NULL, NULL);
+ if (err != SQLITE_OK) {
+ throw_sqlite3_exception(env, handle);
+ }
+ env->ReleaseStringUTFChars(sql, sqlString);
+}
+
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
@@ -247,6 +258,7 @@
{"native_1x1_long", "()J", (void *)native_1x1_long},
{"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
{"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem},
+ {"native_executeSql", "(Ljava/lang/String;)V", (void *)native_executeSql},
};
int register_android_database_SQLiteStatement(JNIEnv * env)
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index c784974..10fe583 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -688,8 +688,8 @@
field fields_to_find[] = {
{ "android/hardware/Camera", "mNativeContext", "I", &fields.context },
{ "android/view/Surface", ANDROID_VIEW_SURFACE_JNI_ID, "I", &fields.surface },
- { "android/hardware/Camera$CameraInfo", "mFacing", "I", &fields.facing },
- { "android/hardware/Camera$CameraInfo", "mOrientation", "I", &fields.orientation },
+ { "android/hardware/Camera$CameraInfo", "facing", "I", &fields.facing },
+ { "android/hardware/Camera$CameraInfo", "orientation", "I", &fields.orientation },
};
if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa71746..3a85bc1 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -84,6 +84,11 @@
renderer->prepare();
}
+static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer) {
+ renderer->finish();
+}
+
static void android_view_GLES20Canvas_acquireContext(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer) {
renderer->acquireContext();
@@ -260,6 +265,15 @@
renderer->drawPath(path, paint);
}
+static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
+ jfloat* storage = env->GetFloatArrayElements(points, NULL);
+
+ renderer->drawLines(storage + offset, count, paint);
+
+ env->ReleaseFloatArrayElements(points, storage, 0);
+}
+
// ----------------------------------------------------------------------------
// Shaders and color filters
// ----------------------------------------------------------------------------
@@ -384,6 +398,7 @@
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
{ "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
{ "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
+ { "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish },
{ "nAcquireContext", "(I)V", (void*) android_view_GLES20Canvas_acquireContext },
{ "nReleaseContext", "(I)V", (void*) android_view_GLES20Canvas_releaseContext },
@@ -415,6 +430,7 @@
{ "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
{ "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects },
{ "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
+ { "nDrawLines", "(I[FIII)V", (void*) android_view_GLES20Canvas_drawLines },
{ "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
{ "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d02b60..06c8423 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -478,7 +478,7 @@
Does not include placing calls. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
android:permissionGroup="android.permission-group.PHONE_CALLS"
- android:protectionLevel="dangerous"
+ android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_modifyPhoneState"
android:description="@string/permdesc_modifyPhoneState" />
@@ -599,8 +599,8 @@
<!-- Allows an application to retrieve state dump information from system
services. -->
<permission android:name="android.permission.DUMP"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
+ android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
@@ -849,11 +849,10 @@
android:description="@string/permdesc_clearAppCache" />
<!-- Allows an application to read the low-level system log files.
- These can contain slightly private information about what is
- happening on the device, but should never contain the user's
- private information. -->
+ Log entries can contain the user's private information,
+ which is why this permission is 'dangerous'. -->
<permission android:name="android.permission.READ_LOGS"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
android:protectionLevel="dangerous"
android:label="@string/permlab_readLogs"
android:description="@string/permdesc_readLogs" />
diff --git a/core/res/res/color/primary_text_dark.xml b/core/res/res/color/primary_text_dark.xml
index 39c9e22..3dd73e7 100644
--- a/core/res/res/color/primary_text_dark.xml
+++ b/core/res/res/color/primary_text_dark.xml
@@ -19,7 +19,6 @@
<item android:state_window_focused="false" android:color="@android:color/bright_foreground_dark"/>
<item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
-
</selector>
-
diff --git a/core/res/res/color/primary_text_dark_focused.xml b/core/res/res/color/primary_text_dark_focused.xml
index 7f3906a..c97c0bd 100644
--- a/core/res/res/color/primary_text_dark_focused.xml
+++ b/core/res/res/color/primary_text_dark_focused.xml
@@ -16,6 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse" />
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse" />
<item android:state_focused="true" android:color="@android:color/bright_foreground_dark_inverse" />
<item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse" />
<item android:color="@android:color/bright_foreground_dark" />
diff --git a/core/res/res/color/primary_text_dark_nodisable.xml b/core/res/res/color/primary_text_dark_nodisable.xml
index be1b9f9..443f7f4 100644
--- a/core/res/res/color/primary_text_dark_nodisable.xml
+++ b/core/res/res/color/primary_text_dark_nodisable.xml
@@ -16,6 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/color/primary_text_light.xml b/core/res/res/color/primary_text_light.xml
index e112034..a12c6b4 100644
--- a/core/res/res/color/primary_text_light.xml
+++ b/core/res/res/color/primary_text_light.xml
@@ -19,6 +19,7 @@
<item android:state_window_focused="false" android:color="@android:color/bright_foreground_light"/>
<item android:state_pressed="true" android:color="@android:color/bright_foreground_light"/>
<item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
<item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/color/primary_text_light_nodisable.xml b/core/res/res/color/primary_text_light_nodisable.xml
index 2d35470..051cccf 100644
--- a/core/res/res/color/primary_text_light_nodisable.xml
+++ b/core/res/res/color/primary_text_light_nodisable.xml
@@ -16,6 +16,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
<item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/color/secondary_text_dark.xml b/core/res/res/color/secondary_text_dark.xml
index c195ef0..1a38fa9 100644
--- a/core/res/res/color/secondary_text_dark.xml
+++ b/core/res/res/color/secondary_text_dark.xml
@@ -20,6 +20,7 @@
<item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
<item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
<item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:state_pressed="true" android:color="@android:color/dim_foreground_dark_inverse"/>
<item android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
<item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
diff --git a/core/res/res/color/secondary_text_dark_nodisable.xml b/core/res/res/color/secondary_text_dark_nodisable.xml
index 2c87a25..cbc7b02 100644
--- a/core/res/res/color/secondary_text_dark_nodisable.xml
+++ b/core/res/res/color/secondary_text_dark_nodisable.xml
@@ -16,5 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/color/secondary_text_light.xml b/core/res/res/color/secondary_text_light.xml
index 99249a5..293f4aa 100644
--- a/core/res/res/color/secondary_text_light.xml
+++ b/core/res/res/color/secondary_text_light.xml
@@ -22,6 +22,7 @@
<item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
<item android:state_pressed="true" android:color="@android:color/dim_foreground_light"/>
<item android:state_selected="true" android:color="@android:color/dim_foreground_light"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
<item android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
<item android:color="@android:color/dim_foreground_light"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/color/secondary_text_light_nodisable.xml b/core/res/res/color/secondary_text_light_nodisable.xml
index 2c87a25..cbc7b02 100644
--- a/core/res/res/color/secondary_text_light_nodisable.xml
+++ b/core/res/res/color/secondary_text_light_nodisable.xml
@@ -16,5 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+ <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
<item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/drawable-hdpi/edit_query.png b/core/res/res/drawable-hdpi/edit_query.png
new file mode 100644
index 0000000..d3e64b2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_normal.9.png b/core/res/res/drawable-hdpi/edit_query_background_normal.9.png
new file mode 100644
index 0000000..c083129
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png b/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png
new file mode 100644
index 0000000..41f3970
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_selected.9.png b/core/res/res/drawable-hdpi/edit_query_background_selected.9.png
new file mode 100644
index 0000000..04a8d12
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/nav_divider.png b/core/res/res/drawable-hdpi/nav_divider.png
new file mode 100644
index 0000000..7ca3e61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/nav_divider.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query.png b/core/res/res/drawable-mdpi/edit_query.png
new file mode 100644
index 0000000..22322cb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_normal.9.png b/core/res/res/drawable-mdpi/edit_query_background_normal.9.png
new file mode 100644
index 0000000..8f957b8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png b/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png
new file mode 100644
index 0000000..c88d4d5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_selected.9.png b/core/res/res/drawable-mdpi/edit_query_background_selected.9.png
new file mode 100644
index 0000000..ffe5791
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/nav_divider.png b/core/res/res/drawable-mdpi/nav_divider.png
new file mode 100644
index 0000000..c9413d7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/nav_divider.png
Binary files differ
diff --git a/core/res/res/drawable/edit_query_background.xml b/core/res/res/drawable/edit_query_background.xml
new file mode 100644
index 0000000..1446f8c
--- /dev/null
+++ b/core/res/res/drawable/edit_query_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_window_focused="false"
+ android:drawable="@drawable/edit_query_background_normal" />
+
+ <item android:state_focused="true" android:state_pressed="true"
+ android:drawable="@drawable/edit_query_background_pressed" />
+
+ <item android:state_focused="false" android:state_pressed="true"
+ android:drawable="@drawable/edit_query_background_pressed" />
+
+ <item android:state_focused="true"
+ android:drawable="@drawable/edit_query_background_selected" />
+
+ <item android:drawable="@drawable/edit_query_background_normal" />
+
+</selector>
diff --git a/core/res/res/layout-port/preference_header_item.xml b/core/res/res/layout-port/preference_header_item.xml
new file mode 100644
index 0000000..cc76c8e
--- /dev/null
+++ b/core/res/res/layout-port/preference_header_item.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout of a header item in PreferenceActivity. -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="96dp"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingRight="?android:attr/scrollbarSize">
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ android:paddingLeft="4dip"
+ android:paddingRight="4dip"
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="2dip" />
+
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="2dip"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:ellipsize="end"
+ android:maxLines="2" />
+
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/fragment_bread_crumb_item.xml b/core/res/res/layout/fragment_bread_crumb_item.xml
new file mode 100644
index 0000000..408f6e8
--- /dev/null
+++ b/core/res/res/layout/fragment_bread_crumb_item.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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse"
+ android:drawableLeft="@drawable/nav_divider"
+ android:paddingLeft="12dp"
+ android:drawablePadding="12dp"
+ />
diff --git a/core/res/res/layout/fragment_bread_crumbs.xml b/core/res/res/layout/fragment_bread_crumbs.xml
new file mode 100644
index 0000000..f289e14
--- /dev/null
+++ b/core/res/res/layout/fragment_bread_crumbs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+</LinearLayout>
diff --git a/core/res/res/layout/preference_header_item.xml b/core/res/res/layout/preference_header_item.xml
index 80a3ac2..aba7b2ad 100644
--- a/core/res/res/layout/preference_header_item.xml
+++ b/core/res/res/layout/preference_header_item.xml
@@ -54,6 +54,7 @@
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:ellipsize="end"
android:maxLines="2" />
</RelativeLayout>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index fe950b2..9661c64 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -33,7 +33,7 @@
android:orientation="vertical"
android:layout_width="0px"
android:layout_height="match_parent"
- android:layout_weight="1">
+ android:layout_weight="10">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
@@ -52,7 +52,7 @@
<FrameLayout android:id="@+id/prefs"
android:layout_width="0px"
android:layout_height="match_parent"
- android:layout_weight="3"
+ android:layout_weight="33"
android:visibility="gone" />
</LinearLayout>
diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml
index d71b4f7..fcdf91b 100644
--- a/core/res/res/layout/search_dropdown_item_icons_2line.xml
+++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml
@@ -1,31 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingLeft="4dip"
android:paddingRight="2dip"
android:layout_width="match_parent"
android:layout_height="?android:attr/searchResultListItemHeight" >
<!-- Icons come first in the layout, since their placement doesn't depend on
- the placement of the text views. -->
+ the placement of the text views. -->
<ImageView android:id="@android:id/icon1"
android:layout_width="48dip"
android:layout_height="48dip"
@@ -35,31 +35,44 @@
android:layout_alignParentBottom="true"
android:visibility="gone" />
- <ImageView android:id="@android:id/icon2"
+ <ImageView android:id="@+id/edit_query"
android:layout_width="48dip"
android:layout_height="48dip"
android:scaleType="centerInside"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
+ android:src="@android:drawable/edit_query"
+ android:background="@android:drawable/edit_query_background"
android:visibility="gone" />
+ <ImageView android:id="@android:id/icon2"
+ android:layout_width="48dip"
+ android:layout_height="48dip"
+ android:scaleType="centerInside"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_toLeftOf="@id/edit_query"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone" />
+
+
<!-- The subtitle comes before the title, since the height of the title depends on whether the
- subtitle is visible or gone. -->
+ subtitle is visible or gone. -->
<TextView android:id="@android:id/text2"
style="?android:attr/dropDownItemStyle"
android:textAppearance="?android:attr/textAppearanceSearchResultSubtitle"
android:singleLine="true"
android:layout_width="match_parent"
- android:layout_height="29dip"
+ android:layout_height="29dip"
android:paddingBottom="4dip"
android:gravity="top"
android:layout_toRightOf="@android:id/icon1"
android:layout_toLeftOf="@android:id/icon2"
- android:layout_alignWithParentIfMissing="true"
+ android:layout_alignWithParentIfMissing="true"
android:layout_alignParentBottom="true"
android:visibility="gone" />
-
+
<!-- The title is placed above the subtitle, if there is one. If there is no
subtitle, it fills the parent. -->
<TextView android:id="@android:id/text1"
@@ -72,5 +85,5 @@
android:layout_toRightOf="@android:id/icon1"
android:layout_toLeftOf="@android:id/icon2"
android:layout_above="@android:id/text2" />
-
+
</RelativeLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b3d39f3..2dec5c5 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3561,6 +3561,10 @@
<attr name="title" />
<!-- The summary for the item. -->
<attr name="summary" format="string" />
+ <!-- The title for the bread crumb of this item. -->
+ <attr name="breadCrumbTitle" format="string" />
+ <!-- The short title for the bread crumb of this item. -->
+ <attr name="breadCrumbShortTitle" format="string" />
<!-- An icon for the item. -->
<attr name="icon" />
<!-- The fragment that is displayed when the user selects this item. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3b69acb9..fbee438 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1352,6 +1352,8 @@
<public type="attr" name="popupMenuStyle" />
<public type="attr" name="textAppearanceLargePopupMenu" />
<public type="attr" name="textAppearanceSmallPopupMenu" />
+ <public type="attr" name="breadCrumbTitle" />
+ <public type="attr" name="breadCrumbShortTitle" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
@@ -1390,6 +1392,7 @@
<public type="style" name="Widget.ActionButton.CloseMode" />
<public type="style" name="TextAppearance.Widget.PopupMenu.Large" />
<public type="style" name="TextAppearance.Widget.PopupMenu.Small" />
+ <public type="style" name="Widget.FragmentBreadCrumbs" />
<public type="string" name="selectTextMode" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 433c005..95934d5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -710,12 +710,12 @@
<string name="permdesc_movePackage">Allows an application to move application resources from internal to external media and vice versa.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_readLogs">read system log files</string>
+ <string name="permlab_readLogs">read sensitive log data</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readLogs">Allows an application to read from the
system\'s various log files. This allows it to discover general
- information about what you are doing with the phone, but they should
- not contain any personal or private information.</string>
+ information about what you are doing with the phone, potentially
+ including personal or private information.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_diagnostic">read/write to resources owned by diag</string>
@@ -1390,7 +1390,7 @@
<!-- Other IM address type -->
<string name="imTypeOther">Other</string>
- <!-- Custom IM address type -->
+ <!-- Custom IM protocol type -->
<string name="imProtocolCustom">Custom</string>
<!-- AIM IM protocol type -->
<string name="imProtocolAim">AIM</string>
@@ -1418,6 +1418,15 @@
<!-- Custom organization type -->
<string name="orgTypeCustom">Custom</string>
+ <!-- Custom SIP address type -->
+ <string name="sipAddressTypeCustom">Custom</string>
+ <!-- Home SIP address type -->
+ <string name="sipAddressTypeHome">Home</string>
+ <!-- Work SIP address type -->
+ <string name="sipAddressTypeWork">Work</string>
+ <!-- Other SIP address type -->
+ <string name="sipAddressTypeOther">Other</string>
+
<!-- Instructions telling the user to enter their SIM PIN to unlock the keyguard.
Displayed in one line in a large font. -->
<string name="keyguard_password_enter_pin_code">Enter PIN code</string>
@@ -1602,7 +1611,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.7 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.7</string>
+ AppleWebKit/534.8 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.8</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9ba0131..1db6f87 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -563,6 +563,11 @@
<item name="android:popupBackground">@android:drawable/editbox_dropdown_background_dark</item>
</style>
+ <!-- Default style for {@link android.app.FragmentBreadCrumbs} view. -->
+ <style name="Widget.FragmentBreadCrumbs">
+ <item name="android:padding">4dp</item>
+ </style>
+
<style name="Widget.KeyboardView" parent="android:Widget">
<item name="android:background">@android:drawable/keyboard_background</item>
<item name="android:keyBackground">@android:drawable/btn_keyboard_key</item>
diff --git a/core/tests/coretests/src/android/net/LinkSocketTest.java b/core/tests/coretests/src/android/net/LinkSocketTest.java
new file mode 100644
index 0000000..af77d63
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LinkSocketTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.net.LinkSocket;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+/**
+ * Test LinkSocket
+ */
+public class LinkSocketTest extends TestCase {
+
+ @SmallTest
+ public void testBasic() throws Exception {
+ LinkSocket ls;
+
+ ls = new LinkSocket();
+ ls.close();
+ }
+
+ @SmallTest
+ public void testLinkCapabilities() throws Exception {
+ LinkCapabilities lc;
+
+ lc = new LinkCapabilities();
+ assertEquals(0, lc.size());
+ assertEquals(true, lc.isEmpty());
+ }
+}
diff --git a/docs/html/guide/developing/testing/testing_eclipse.jd b/docs/html/guide/developing/testing/testing_eclipse.jd
index 3258af9..ba7eaba 100644
--- a/docs/html/guide/developing/testing/testing_eclipse.jd
+++ b/docs/html/guide/developing/testing/testing_eclipse.jd
@@ -304,6 +304,7 @@
Android Emulator</a>.
</li>
</ul>
+ </li>
<li>
Click the Common tab. In the Save As pane, click Local to save this run configuration
locally, or click Shared to save it to another project.
@@ -333,13 +334,33 @@
</ol>
<p>
The progress of your test appears in the Console view as a series of messages. Each message is
- preceded by a timestamp and the Android package name to which it applies.
+ preceded by a timestamp and the <code>.apk</code> filename to which it applies. For example,
+ this message appears when you run a test to the emulator, and the emulator is not yet started:
+</p>
+<div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Message Examples</h2>
+ <p>
+ The examples shown in this section come from the
+ <a href="{@docRoot}resources/samples/SpinnerTest/index.html">SpinnerTest</a>
+ sample test package, which tests the
+ <a href="{@docRoot}resources/samples/Spinner/index.html">Spinner</a>
+ sample application. This test package is also featured in the
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
+ tutorial.
+ </p>
+ </div>
+</div>
+<pre>
+ [<em>yyyy-mm-dd hh:mm:ss</em> - <em>testfile</em>] Waiting for HOME ('android.process.acore') to be launched...
+</pre>
+<p>
In the following description of these messages, <code><em>devicename</em></code> is the name of
the device or emulator you are using to run the test, and <code><em>port</em></code> is the
port number for the device. The name and port number are in the format used by the
<code><a href="{@docRoot}guide/developing/tools/adb.html#devicestatus">adb devices</a></code>
- command. Also, <code><em>testpackage</em></code> is the name of the test package you are
- running, and <em>app_package</em> is the label for the application under test.
+ command. Also, <code><em>testfile</em></code> is the <code>.apk</code> filename of the test
+ package you are running, and <em>appfile</em> is the filename of the application under test.
</p>
<ul>
<li>
@@ -354,33 +375,57 @@
If you have not already installed your test package, then you see
the message:
<p>
- <code>Uploading <em>testpackage</em> onto device '<em>devicename</em>-<em>port</em>'
+ <code>Uploading <em>testfile</em> onto device '<em>devicename</em>-<em>port</em>'
</code>
</p>
<p>
- then the message <code>Installing <em>testpackage</em></code>.
+ then the message <code>Installing <em>testfile</em></code>.
</p>
<p>
and finally the message <code>Success!</code>
</p>
</li>
+</ul>
+<p>
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-07-01 12:44:40 - MyTest] HOME is up on device 'emulator-5554'<br>
+[2010-07-01 12:44:40 - MyTest] Uploading MyTest.apk onto device 'emulator-5554'<br>
+[2010-07-01 12:44:40 - MyTest] Installing MyTest.apk...<br>
+[2010-07-01 12:44:49 - MyTest] Success!<br>
+</code>
+<br>
+<ul>
<li>
Next, if you have not yet installed the application under test to the device or
emulator, you see the message
<p>
- <code>Project dependency found, installing: <em>app_package</em></code>
+ <code>Project dependency found, installing: <em>appfile</em></code>
</p>
<p>
- then the message <code>Uploading <em>app_name</em>.apk</code> onto device
+ then the message <code>Uploading <em>appfile</em></code> onto device
'<em>devicename</em>-<em>port</em>'
</p>
<p>
- then the message <code>Installing <em>app_name</em>.apk</code>
+ then the message <code>Installing <em>appfile</em></code>
</p>
<p>
and finally the message <code>Success!</code>
</p>
</li>
+</ul>
+<p>
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-07-01 12:44:49 - MyTest] Project dependency found, installing: MyApp<br>
+[2010-07-01 12:44:49 - MyApp] Uploading MyApp.apk onto device 'emulator-5554'<br>
+[2010-07-01 12:44:49 - MyApp] Installing MyApp.apk...<br>
+[2010-07-01 12:44:54 - MyApp] Success!<br>
+</code>
+<br>
+<ul>
<li>
Next, you see the message
<code>Launching instrumentation <em>instrumentation_class</em> on device
@@ -412,6 +457,17 @@
</li>
</ul>
<p>
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-01-01 12:45:02 - MyTest] Launching instrumentation android.test.InstrumentationTestRunner on device emulator-5554<br>
+[2010-01-01 12:45:02 - MyTest] Collecting test information<br>
+[2010-01-01 12:45:02 - MyTest] Sending test information to Eclipse<br>
+[2010-01-01 12:45:02 - MyTest] Running tests...<br>
+[2010-01-01 12:45:22 - MyTest] Test run complete<br>
+</code>
+<br>
+<p>
The test results appear in the JUnit view. This is divided into an upper summary pane,
and a lower stack trace pane.
</p>
@@ -449,8 +505,30 @@
pane and moves the focus to the first line of the test method.
</p>
<p>
+ The results of a successful test are shown in
+ <a href="#TestResults">Figure 1. Messages for a successful test</a>:
+</p>
+<a href="{@docRoot}images/testing/eclipse_test_results.png">
+ <img src="{@docRoot}images/testing/eclipse_test_results.png"
+ alt="Messages for a successful test" height="327px" id="TestResults"/>
+</a>
+<p class="img-caption">
+ <strong>Figure 1.</strong> Messages for a successful test
+</p>
+<p>
The lower pane is for stack traces. If you highlight a failed test in the upper pane, the
lower pane contains a stack trace for the test. If a line corresponds to a point in your
test code, you can double-click it to display the code in an editor view pane, with the
line highlighted. For a successful test, the lower pane is empty.
</p>
+<p>
+ The results of a failed test are shown in
+ <a href="#FailedTestResults">Figure 2. Messages for a test failure</a>
+</p>
+<a href="{@docRoot}images/testing/eclipse_test_run_failure.png">
+ <img src="{@docRoot}images/testing/eclipse_test_run_failure.png"
+ alt="Messages for a test failure" height="372px" id="TestRun"/>
+</a>
+<p class="img-caption">
+ <strong>Figure 2.</strong> Messages for a test failure
+</p>
diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd
index 3d630c9d..36e12f6 100755
--- a/docs/html/guide/topics/resources/localization.jd
+++ b/docs/html/guide/topics/resources/localization.jd
@@ -433,12 +433,12 @@
href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a>.</p>
<h4>Creating and using a custom locale</h4>
-<p>A "custom" locale is a language/region combination that the
-Android system image does not explicitly support. (For a list of supported
-locales, see the <a href="{@docRoot}sdk/android-{@sdkCurrentVersion}.html">Android
-Version Notes</a>.) You can test how your application will run in a custom
-locale by creating a custom locale in the emulator. There are two ways to do
-this:</p>
+<p>A "custom" locale is a language/region combination that the Android
+system image does not explicitly support. (For a list of supported locales in
+Android platforms see the Version Notes in the <a
+href="{@docRoot}sdk/index.html">SDK</a> tab). You can test
+how your application will run in a custom locale by creating a custom locale in
+the emulator. There are two ways to do this:</p>
<ul>
<li>Use the Custom Locale application, which is accessible from the
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index d047b2d..f47a709 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -478,7 +478,7 @@
}
}
- @Override
+ @Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
diff --git a/docs/html/images/testing/eclipse_test_results.png b/docs/html/images/testing/eclipse_test_results.png
new file mode 100644
index 0000000..105e149
--- /dev/null
+++ b/docs/html/images/testing/eclipse_test_results.png
Binary files differ
diff --git a/docs/html/images/testing/eclipse_test_run_failure.png b/docs/html/images/testing/eclipse_test_run_failure.png
new file mode 100644
index 0000000..8111127
--- /dev/null
+++ b/docs/html/images/testing/eclipse_test_run_failure.png
Binary files differ
diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd
index 126c052..5e642d7 100644
--- a/docs/html/sdk/adt_download.jd
+++ b/docs/html/sdk/adt_download.jd
@@ -24,8 +24,8 @@
<tr>
<td>0.9.8</td>
<td><a href="http://dl-ssl.google.com/android/ADT-0.9.8.zip">ADT-0.9.8.zip</a></td>
- <td><nobr>{@adtZipBytes} bytes</nobr></td>
- <td>{@adtZipChecksum}</td>
+ <td><nobr>8703591 bytes</nobr></td>
+ <td>22070f8e52924605a3b3abf87c1ba39f</td>
<td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td>
</tr>
<tr>
diff --git a/docs/html/sdk/android-1.5.jd b/docs/html/sdk/android-1.5.jd
index 0c16b60..9ed798c 100644
--- a/docs/html/sdk/android-1.5.jd
+++ b/docs/html/sdk/android-1.5.jd
@@ -2,8 +2,6 @@
sdk.platform.version=1.5
sdk.platform.apiLevel=3
sdk.platform.majorMinor=major
-sdk.platform.releaseDate=April 2009
-sdk.platform.deployableDate=May 2009
@jd:body
@@ -43,7 +41,7 @@
<em>API Level:</em> <strong>{@sdkPlatformApiLevel}</strong></p>
<p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release
-deployable to Android-powered handsets starting in {@sdkPlatformDeployableDate}.
+deployable to Android-powered handsets starting in May 2009.
The release includes new features for users and developers, as well as changes
in the Android framework API. </p>
diff --git a/docs/html/sdk/android-1.6.jd b/docs/html/sdk/android-1.6.jd
index c4e08ff..a01a5f6 100644
--- a/docs/html/sdk/android-1.6.jd
+++ b/docs/html/sdk/android-1.6.jd
@@ -2,8 +2,6 @@
sdk.platform.version=1.6
sdk.platform.apiLevel=4
sdk.platform.majorMinor=minor
-sdk.platform.releaseDate=December 2009
-sdk.platform.deployableDate=October 2009
@jd:body
@@ -43,7 +41,7 @@
<em>API Level:</em> <strong>{@sdkPlatformApiLevel}</strong></p>
<p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release
-deployable to Android-powered handsets since {@sdkPlatformDeployableDate}.
+deployable to Android-powered handsets since October 2009.
The platform includes new features for users and developers, as well as changes
in the Android framework API. </p>
diff --git a/docs/html/sdk/android-2.0.1.jd b/docs/html/sdk/android-2.0.1.jd
index cacb6bf..0c8afb6 100644
--- a/docs/html/sdk/android-2.0.1.jd
+++ b/docs/html/sdk/android-2.0.1.jd
@@ -2,8 +2,6 @@
sdk.platform.version=2.0.1
sdk.platform.apiLevel=6
sdk.platform.majorMinor=minor
-sdk.platform.releaseDate=December 2009
-sdk.platform.deployableDate=December 2009
@jd:body
@@ -44,7 +42,7 @@
<em>API Level:</em> <strong>{@sdkPlatformApiLevel}</strong></p>
<p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release
-deployable to Android-powered handsets starting in {@sdkPlatformDeployableDate}.
+deployable to Android-powered handsets starting in December 2009.
This release includes minor API
changes, bug fixes and framework behavioral changes. For information on changes
and fixes, see the <a href="#api">Framework API</a> section.</p>
diff --git a/docs/html/sdk/android-2.0.jd b/docs/html/sdk/android-2.0.jd
index a430f34..2c31923 100644
--- a/docs/html/sdk/android-2.0.jd
+++ b/docs/html/sdk/android-2.0.jd
@@ -2,8 +2,6 @@
sdk.platform.version=2.0
sdk.platform.apiLevel=5
sdk.platform.majorMinor=major
-sdk.platform.releaseDate=October 2009
-sdk.platform.deployableDate=November 2009
@jd:body
@@ -38,7 +36,7 @@
<em>API Level:</em> <strong>{@sdkPlatformApiLevel}</strong></p>
<p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release
-deployable to Android-powered handsets starting in {@sdkPlatformDeployableDate}.
+deployable to Android-powered handsets starting in November 2009.
The release includes new features for users and developers, as well as changes
in the Android framework API. </p>
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index cd48a72..6eba6f09 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -2,7 +2,6 @@
sdk.platform.version=2.1
sdk.platform.apiLevel=7
sdk.platform.majorMinor=minor
-sdk.platform.deployableDate=January 2010
@jd:body
@@ -42,7 +41,7 @@
<em>API Level:</em> <strong>{@sdkPlatformApiLevel}</strong></p>
<p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release
-deployable to Android-powered handsets starting in {@sdkPlatformDeployableDate}.
+deployable to Android-powered handsets starting in January 2010.
This release includes new API
changes and bug fixes. For information on changes, see the <a href="#api">Framework API</a>
section.</p>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 3d4b6a12..a984d56 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,5 +1,9 @@
page.title=ADT Plugin for Eclipse
sdk.preview=0
+adt.zip.version=0.9.8
+adt.zip.download=ADT-0.9.8.zip
+adt.zip.bytes=8703591
+adt.zip.checksum=22070f8e52924605a3b3abf87c1ba39f
@jd:body
diff --git a/docs/knowntags.txt b/docs/knowntags.txt
new file mode 100644
index 0000000..5bebabb
--- /dev/null
+++ b/docs/knowntags.txt
@@ -0,0 +1,38 @@
+# 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.
+
+#
+# The grandfathered list. We should get rid of these if possible.
+#
+@ToBeFixed
+@stable
+@com.intel.drl.spec_ref
+@ar.org.fitc.spec_ref
+
+# Something about CTS?
+@cts
+
+# Auto-generated info about the SDK
+@sdkCurrent
+@sdkCurrentVersion
+@sdkCurrentRelId
+@sdkPlatformVersion
+@sdkPlatformApiLevel
+@sdkPlatformMajorMinor
+@sdkPlatformReleaseDate
+@sdkPlatformDeployableDate
+@adtZipVersion
+@adtZipDownload
+@adtZipBytes
+@adtZipChecksum
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 9ee597c..7ec70da 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -213,12 +213,12 @@
* Save DRM rights to specified rights path
* and make association with content path.
*
+ * <p class="note">In case of OMA or WM-DRM, rightsPath and contentPath could be null.</p>
+ *
* @param drmRights DrmRights to be saved
* @param rightsPath File path where rights to be saved
* @param contentPath File path where content was saved
* @throws IOException if failed to save rights information in the given path
- *
- * @note In case of OMA or WM-DRM, rightsPath and contentPath could be null
*/
public void saveRights(
DrmRights drmRights, String rightsPath, String contentPath) throws IOException {
@@ -256,9 +256,9 @@
* @param path Path of the content to be handled
* @param mimeType Mimetype of the object to be handled
* @return
- * true - if the given mimeType or path can be handled
- * false - cannot be handled.
- * @note false will be return in case the state is uninitialized
+ * true - if the given mimeType or path can be handled.
+ * false - cannot be handled. false will be returned in case
+ * the state is uninitialized
*/
public boolean canHandle(String path, String mimeType) {
if ((null == path || path.equals("")) && (null == mimeType || mimeType.equals(""))) {
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index b336995..66ed104a 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -422,6 +422,10 @@
* the transformed vectors into the array of vectors specified by dst. The
* two arrays represent their "vectors" as pairs of floats [x, y].
*
+ * Note: this method does not apply the translation associated with the matrix. Use
+ * {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the translation
+ * to be applied.
+ *
* @param dst The array of dst vectors (x,y pairs)
* @param dstIndex The index of the first [x,y] pair of dst floats
* @param src The array of src vectors (x,y pairs)
@@ -455,6 +459,9 @@
* the transformed vectors into the array of vectors specified by dst. The
* two arrays represent their "vectors" as pairs of floats [x, y].
*
+ * Note: this method does not apply the translation associated with the matrix. Use
+ * {@link Matrix#mapPoints(float[], float[])} if you want the translation to be applied.
+ *
* @param dst The array of dst vectors (x,y pairs)
* @param src The array of src vectors (x,y pairs)
*/
@@ -478,6 +485,10 @@
/**
* Apply this matrix to the array of 2D vectors, and write the transformed
* vectors back into the array.
+ *
+ * Note: this method does not apply the translation associated with the matrix. Use
+ * {@link Matrix#mapPoints(float[])} if you want the translation to be applied.
+ *
* @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
*/
public void mapVectors(float[] vecs) {
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 1f3e159..1312036 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -608,7 +608,7 @@
while(mRun) {
rbuf[0] = 0;
int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
- if (msg == 0) {
+ if ((msg == 0) && mRun) {
// Can happen for two reasons
if (rbuf[0] > 0) {
// 1: Buffer needs to be enlarged.
@@ -661,6 +661,10 @@
validate();
nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
+ try {
+ mMessageThread.join();
+ } catch(InterruptedException e) {
+ }
nContextDestroy();
mContext = 0;
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 7c5371a..72d5756 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -354,7 +354,10 @@
// for barcode reading.
static const char SCENE_MODE_BARCODE[];
- // Formats for setPreviewFormat and setPictureFormat.
+ // Pixel color formats for KEY_PREVIEW_FORMAT, KEY_PICTURE_FORMAT,
+ // and KEY_VIDEO_FRAME_FORMAT
+ // Planar variant of the YUV420 color format
+ static const char PIXEL_FORMAT_YUV420P[];
static const char PIXEL_FORMAT_YUV422SP[];
static const char PIXEL_FORMAT_YUV420SP[]; // NV21
static const char PIXEL_FORMAT_YUV422I[]; // YUY2
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index 0ec7eec..aa97874 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -302,10 +302,25 @@
// If the xml configuration file does not exist, use hard-coded values
static MediaProfiles* createDefaultInstance();
- static CamcorderProfile *createDefaultCamcorderLowProfile();
- static CamcorderProfile *createDefaultCamcorderHighProfile();
- static CamcorderProfile *createDefaultCamcorderTimeLapseLowProfile();
- static CamcorderProfile *createDefaultCamcorderTimeLapseHighProfile();
+
+ static CamcorderProfile *createDefaultCamcorderQcifProfile(camcorder_quality quality);
+ static CamcorderProfile *createDefaultCamcorderCifProfile(camcorder_quality quality);
+ static void createDefaultCamcorderLowProfiles(
+ MediaProfiles::CamcorderProfile **lowProfile,
+ MediaProfiles::CamcorderProfile **lowSpecificProfile);
+ static void createDefaultCamcorderHighProfiles(
+ MediaProfiles::CamcorderProfile **highProfile,
+ MediaProfiles::CamcorderProfile **highSpecificProfile);
+
+ static CamcorderProfile *createDefaultCamcorderTimeLapseQcifProfile(camcorder_quality quality);
+ static CamcorderProfile *createDefaultCamcorderTimeLapse480pProfile(camcorder_quality quality);
+ static void createDefaultCamcorderTimeLapseLowProfiles(
+ MediaProfiles::CamcorderProfile **lowTimeLapseProfile,
+ MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile);
+ static void createDefaultCamcorderTimeLapseHighProfiles(
+ MediaProfiles::CamcorderProfile **highTimeLapseProfile,
+ MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile);
+
static void createDefaultCamcorderProfiles(MediaProfiles *profiles);
static void createDefaultVideoEncoders(MediaProfiles *profiles);
static void createDefaultAudioEncoders(MediaProfiles *profiles);
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index e1cd0c8..3b303f8 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -43,6 +43,16 @@
virtual ~CameraSourceTimeLapse();
+ // If the frame capture interval is large, read will block for a long time.
+ // Due to the way the mediaRecorder framework works, a stop() call from
+ // mediaRecorder waits until the read returns, causing a long wait for
+ // stop() to return. To avoid this, we can make read() return a copy of the
+ // last read frame with the same time stamp frequently. This keeps the
+ // read() call from blocking too long. Calling this function quickly
+ // captures another frame, keeps its copy, and enables this mode of read()
+ // returning quickly.
+ void startQuickReadReturns();
+
private:
// If true, will use still camera takePicture() for time lapse frames
// If false, will use the videocamera frames instead.
@@ -82,14 +92,59 @@
// to know if current frame needs to be skipped.
bool mSkipCurrentFrame;
+ // Lock for accessing mCameraIdle
+ Mutex mCameraIdleLock;
+
+ // Condition variable to wait on if camera is is not yet idle. Once the
+ // camera gets idle, this variable will be signalled.
+ Condition mCameraIdleCondition;
+
// True if camera is in preview mode and ready for takePicture().
- bool mCameraIdle;
+ // False after a call to takePicture() but before the final compressed
+ // data callback has been called and preview has been restarted.
+ volatile bool mCameraIdle;
+
+ // True if stop() is waiting for camera to get idle, i.e. for the last
+ // takePicture() to complete. This is needed so that dataCallbackTimestamp()
+ // can return immediately.
+ volatile bool mStopWaitingForIdleCamera;
+
+ // Lock for accessing quick stop variables.
+ Mutex mQuickStopLock;
+
+ // Condition variable to wake up still picture thread.
+ Condition mTakePictureCondition;
+
+ // mQuickStop is set to true if we use quick read() returns, otherwise it is set
+ // to false. Once in this mode read() return a copy of the last read frame
+ // with the same time stamp. See startQuickReadReturns().
+ volatile bool mQuickStop;
+
+ // Forces the next frame passed to dataCallbackTimestamp() to be read
+ // as a time lapse frame. Used by startQuickReadReturns() so that the next
+ // frame wakes up any blocking read.
+ volatile bool mForceRead;
+
+ // Stores a copy of the MediaBuffer read in the last read() call after
+ // mQuickStop was true.
+ MediaBuffer* mLastReadBufferCopy;
+
+ // Status code for last read.
+ status_t mLastReadStatus;
CameraSourceTimeLapse(const sp<Camera> &camera,
int64_t timeBetweenTimeLapseFrameCaptureUs,
int32_t width, int32_t height,
int32_t videoFrameRate);
+ // Wrapper over CameraSource::signalBufferReturned() to implement quick stop.
+ // It only handles the case when mLastReadBufferCopy is signalled. Otherwise
+ // it calls the base class' function.
+ virtual void signalBufferReturned(MediaBuffer* buffer);
+
+ // Wrapper over CameraSource::read() to implement quick stop.
+ virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+
// For still camera case starts a thread which calls camera's takePicture()
// in a loop. For video camera case, just starts the camera's video recording.
virtual void startCameraRecording();
@@ -122,6 +177,10 @@
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
const sp<IMemory> &data);
+ // Convenience function to fill mLastReadBufferCopy from the just read
+ // buffer.
+ void fillLastReadBufferCopy(MediaBuffer& sourceBuffer);
+
// If the passed in size (width x height) is a supported preview size,
// the function sets the camera's preview size to it and returns true.
// Otherwise returns false.
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 25d5afb..d6b09dc 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -111,10 +111,10 @@
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
- /* The input device is a directional pad. */
+ /* The input device is a directional pad (implies keyboard, has DPAD keys). */
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
- /* The input device is a gamepad (implies keyboard). */
+ /* The input device is a gamepad (implies keyboard, has BUTTON keys). */
INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
/* The input device has switches. */
diff --git a/include/ui/GraphicLog.h b/include/ui/GraphicLog.h
index f929e6a..ee1b09a 100644
--- a/include/ui/GraphicLog.h
+++ b/include/ui/GraphicLog.h
@@ -30,24 +30,24 @@
public:
enum {
- SF_APP_DEQUEUE_BEFORE = 60000,
- SF_APP_DEQUEUE_AFTER = 60001,
- SF_APP_LOCK_BEFORE = 60002,
- SF_APP_LOCK_AFTER = 60003,
- SF_APP_QUEUE = 60004,
+ SF_APP_DEQUEUE_BEFORE = 60100,
+ SF_APP_DEQUEUE_AFTER = 60101,
+ SF_APP_LOCK_BEFORE = 60102,
+ SF_APP_LOCK_AFTER = 60103,
+ SF_APP_QUEUE = 60104,
- SF_REPAINT = 60005,
- SF_COMPOSITION_COMPLETE = 60006,
- SF_UNLOCK_CLIENTS = 60007,
- SF_SWAP_BUFFERS = 60008,
- SF_REPAINT_DONE = 60009,
+ SF_REPAINT = 60105,
+ SF_COMPOSITION_COMPLETE = 60106,
+ SF_UNLOCK_CLIENTS = 60107,
+ SF_SWAP_BUFFERS = 60108,
+ SF_REPAINT_DONE = 60109,
- SF_FB_POST_BEFORE = 60010,
- SF_FB_POST_AFTER = 60011,
- SF_FB_DEQUEUE_BEFORE = 60012,
- SF_FB_DEQUEUE_AFTER = 60013,
- SF_FB_LOCK_BEFORE = 60014,
- SF_FB_LOCK_AFTER = 60015,
+ SF_FB_POST_BEFORE = 60110,
+ SF_FB_POST_AFTER = 60111,
+ SF_FB_DEQUEUE_BEFORE = 60112,
+ SF_FB_DEQUEUE_AFTER = 60113,
+ SF_FB_LOCK_BEFORE = 60114,
+ SF_FB_LOCK_AFTER = 60115,
};
inline void log(int32_t tag, int32_t buffer) {
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d7e6254..96b4faed 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -81,9 +81,8 @@
*/
struct InputTarget {
enum {
- /* This flag indicates that subsequent event delivery should be held until the
- * current event is delivered to this target or a timeout occurs. */
- FLAG_SYNC = 0x01,
+ /* This flag indicates that the event is being delivered to a foreground application. */
+ FLAG_FOREGROUND = 0x01,
/* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
* of the area of this target and so should instead be delivered as an
@@ -109,12 +108,6 @@
// Flags for the input target.
int32_t flags;
- // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
- nsecs_t timeout;
-
- // The time already spent waiting for this target in nanoseconds, or 0 if none.
- nsecs_t timeSpentWaitingForApplication;
-
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
float xOffset, yOffset;
@@ -190,6 +183,7 @@
};
sp<InputChannel> inputChannel;
+ String8 name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@
int32_t touchableAreaRight;
int32_t touchableAreaBottom;
bool visible;
+ bool canReceiveKeys;
bool hasFocus;
bool hasWallpaper;
bool paused;
+ int32_t layer;
int32_t ownerPid;
int32_t ownerUid;
@@ -257,18 +253,12 @@
/* Notifies the system that an application is not responding.
* Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
- /* Notifies the system that an input channel is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
-
- /* Notifies the system that an input channel recovered from ANR. */
- virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
-
/* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
virtual nsecs_t getKeyRepeatTimeout() = 0;
@@ -361,16 +351,6 @@
*/
virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
- /* Preempts input dispatch in progress by making pending synchronous
- * dispatches asynchronous instead. This method is generally called during a focus
- * transition from one application to the next so as to enable the new application
- * to start receiving input as soon as possible without having to wait for the
- * old application to finish up.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void preemptInputDispatch() = 0;
-
/* Registers or unregister input channels that may be used as targets for input events.
* If monitor is true, the channel will receive a copy of all input events.
*
@@ -424,7 +404,6 @@
virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
virtual void setFocusedApplication(const InputApplication* inputApplication);
virtual void setInputDispatchMode(bool enabled, bool frozen);
- virtual void preemptInputDispatch();
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
@@ -454,7 +433,7 @@
int32_t injectorUid; // -1 if not injected
bool dispatchInProgress; // initially false, set to true while dispatching
- int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
+ int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
inline bool isInjected() { return injectorPid >= 0; }
@@ -522,7 +501,6 @@
int32_t targetFlags;
float xOffset;
float yOffset;
- nsecs_t timeout;
// True if dispatch has started.
bool inProgress;
@@ -540,12 +518,8 @@
// will be set to NULL.
MotionSample* tailMotionSample;
- inline bool isSyncTarget() const {
- return targetFlags & InputTarget::FLAG_SYNC;
- }
-
- inline void preemptSyncTarget() {
- targetFlags &= ~ InputTarget::FLAG_SYNC;
+ inline bool hasForegroundTarget() const {
+ return targetFlags & InputTarget::FLAG_FOREGROUND;
}
};
@@ -628,6 +602,8 @@
dequeue(first);
return first;
}
+
+ uint32_t count() const;
};
/* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@
nsecs_t downTime, uint32_t pointerCount,
const int32_t* pointerIds, const PointerCoords* pointerCoords);
DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
+ int32_t targetFlags, float xOffset, float yOffset);
CommandEntry* obtainCommandEntry(Command command);
void releaseEventEntry(EventEntry* entry);
@@ -761,8 +737,6 @@
STATUS_NORMAL,
// An unrecoverable communication error has occurred.
STATUS_BROKEN,
- // The client is not responding.
- STATUS_NOT_RESPONDING,
// The input channel has been unregistered.
STATUS_ZOMBIE
};
@@ -772,11 +746,9 @@
InputPublisher inputPublisher;
InputState inputState;
Queue<DispatchEntry> outboundQueue;
- nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
nsecs_t lastEventTime; // the time when the event was originally captured
nsecs_t lastDispatchTime; // the time when the last event was dispatched
- nsecs_t lastANRTime; // the time when the last ANR was recorded
explicit Connection(const sp<InputChannel>& inputChannel);
@@ -788,18 +760,6 @@
// Returns NULL if not found.
DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
- // Determine whether this connection has a pending synchronous dispatch target.
- // Since there can only ever be at most one such target at a time, if there is one,
- // it must be at the tail because nothing else can be enqueued after it.
- inline bool hasPendingSyncTarget() const {
- return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
- }
-
- // Assuming there is a pending sync target, make it async.
- inline void preemptSyncTarget() {
- outboundQueue.tailSentinel.prev->preemptSyncTarget();
- }
-
// Gets the time since the current event was originally obtained from the input driver.
inline double getEventLatencyMillis(nsecs_t currentTime) const {
return (currentTime - lastEventTime) / 1000000.0;
@@ -810,15 +770,7 @@
return (currentTime - lastDispatchTime) / 1000000.0;
}
- // Gets the time since the current event ANR was declared, if applicable.
- inline double getANRLatencyMillis(nsecs_t currentTime) const {
- return (currentTime - lastANRTime) / 1000000.0;
- }
-
status_t initialize();
-
- void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
- void resetTimeout(nsecs_t currentTime);
};
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -851,7 +803,7 @@
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
- ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+ ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
// Active connections are connections that have a non-empty outbound queue.
// We don't use a ref-counted pointer here because we explicitly abort connections
@@ -859,12 +811,6 @@
// and the connection itself to be deactivated.
Vector<Connection*> mActiveConnections;
- // List of connections that have timed out. Only used by dispatchOnce()
- // We don't use a ref-counted pointer here because it is not possible for a connection
- // to be unregistered while processing timed out connections since we hold the lock for
- // the duration.
- Vector<Connection*> mTimedOutConnections;
-
// Input channels that will receive a copy of all input events.
Vector<sp<InputChannel> > mMonitoringChannels;
@@ -877,7 +823,7 @@
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
Condition mInjectionSyncFinishedCondition;
- void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+ void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
// Throttling state.
struct ThrottleState {
@@ -905,8 +851,8 @@
// Inbound event processing.
void drainInboundQueueLocked();
- void releasePendingEventLocked(bool wasDropped);
- void releaseInboundEventLocked(EventEntry* entry, bool wasDropped);
+ void releasePendingEventLocked();
+ void releaseInboundEventLocked(EventEntry* entry);
bool isEventFromReliableSourceLocked(EventEntry* entry);
// Dispatch state.
@@ -940,10 +886,10 @@
nsecs_t currentTime, ConfigurationChangedEntry* entry);
bool dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
- nsecs_t* nextWakeupTime);
+ bool dropEvent, nsecs_t* nextWakeupTime);
bool dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry,
- nsecs_t* nextWakeupTime);
+ bool dropEvent, nsecs_t* nextWakeupTime);
void dispatchEventToCurrentInputTargetsLocked(
nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
@@ -951,8 +897,6 @@
void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
// The input targets that were most recently identified for dispatch.
- // If there is a synchronous event dispatch in progress, the current input targets will
- // remain unchanged until the dispatch has completed or been aborted.
bool mCurrentInputTargetsValid; // false while targets are being recomputed
Vector<InputTarget> mCurrentInputTargets;
int32_t mCurrentInputWindowType;
@@ -970,13 +914,14 @@
bool mInputTargetWaitTimeoutExpired;
// Finding targets for input events.
- void startFindingTargetsLocked();
- void finishFindingTargetsLocked(const InputWindow* window);
+ void resetTargetsLocked();
+ void commitTargetsLocked(const InputWindow* window);
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
const InputApplication* application, const InputWindow* window,
nsecs_t* nextWakeupTime);
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
- nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(nsecs_t currentTime);
+ void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel);
+ nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
void resetANRTimeoutsLocked();
int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
@@ -984,14 +929,16 @@
int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
nsecs_t* nextWakeupTime, InputWindow** outWindow);
- void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication);
+ void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
void addMonitoringTargetsLocked();
void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
bool checkInjectionPermission(const InputWindow* window,
int32_t injectorPid, int32_t injectorUid);
bool isWindowObscuredLocked(const InputWindow* window);
+ bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
void releaseTouchedWindowLocked();
+ String8 getApplicationWindowLabelLocked(const InputApplication* application,
+ const InputWindow* window);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
@@ -1000,21 +947,14 @@
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- nsecs_t timeSpentWaitingForApplication);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t newTimeout);
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool broken);
- void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
+ void drainOutboundQueueLocked(Connection* connection);
static int handleReceiveCallback(int receiveFd, int events, void* data);
- // Preempting input dispatch.
- bool preemptInputDispatchInnerLocked();
-
// Dump state.
void dumpDispatchStateLocked(String8& dump);
void logDispatchStateLocked();
@@ -1027,20 +967,23 @@
void onDispatchCycleStartedLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
- void onDispatchCycleANRLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
+ void onANRLocked(
+ nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+ nsecs_t eventTime, nsecs_t waitStartTime);
// Outbound policy interactions.
void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
- void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
+
+ // Statistics gathering.
+ void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
};
/* Enqueues and dispatches input events, endlessly. */
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 362d9ee..887b12c 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -129,7 +129,7 @@
const char CameraParameters::SCENE_MODE_CANDLELIGHT[] = "candlelight";
const char CameraParameters::SCENE_MODE_BARCODE[] = "barcode";
-// Formats for setPreviewFormat and setPictureFormat.
+const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp";
const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp";
const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index c3a9f22..f935524 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -70,9 +70,13 @@
ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents)
{
ssize_t size = mSensorChannel->read(events, numEvents*sizeof(events[0]));
+ LOGE_IF(size<0 && size!=-EAGAIN,
+ "SensorChannel::read error (%s)", strerror(-size));
if (size >= 0) {
if (size % sizeof(events[0])) {
// partial read!!! should never happen.
+ LOGE("SensorEventQueue partial read (event-size=%u, read=%d)",
+ sizeof(events[0]), int(size));
return -EINVAL;
}
// returns number of events read
@@ -95,8 +99,18 @@
{
const int fd = getFd();
sp<Looper> looper(getLooper());
- int32_t result = looper->pollOnce(-1);
- return (result == fd) ? status_t(NO_ERROR) : status_t(-1);
+
+ int32_t result;
+ do {
+ result = looper->pollOnce(-1);
+ if (result == ALOOPER_EVENT_ERROR) {
+ LOGE("SensorChannel::waitForEvent error (errno=%d)", errno);
+ result = -EPIPE; // unknown error, so we make up one
+ break;
+ }
+ } while (result != fd);
+
+ return (result == fd) ? status_t(NO_ERROR) : result;
}
status_t SensorEventQueue::wake() const
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 1a18766..39c3111 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -110,11 +110,9 @@
layer = new Layer;
layer->blend = true;
- // 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);
diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h
new file mode 100644
index 0000000..48e18eb
--- /dev/null
+++ b/libs/hwui/Line.h
@@ -0,0 +1,126 @@
+/*
+ * 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_LINE_H
+#define ANDROID_UI_LINE_H
+
+#include <GLES2/gl2.h>
+
+#include <cmath>
+
+#include <sys/types.h>
+
+#include "Patch.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+
+// Alpha8 texture used to perform texture anti-aliasing
+static const uint8_t gLineTexture[] = {
+ 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 0,
+ 0, 255, 255, 255, 0,
+ 0, 255, 255, 255, 0,
+ 0, 0, 0, 0, 0
+};
+static const GLsizei gLineTextureWidth = 5;
+static const GLsizei gLineTextureHeight = 5;
+static const float gLineAABias = 1.0f;
+
+///////////////////////////////////////////////////////////////////////////////
+// Line
+///////////////////////////////////////////////////////////////////////////////
+
+class Line {
+public:
+ Line(): mXDivsCount(2), mYDivsCount(2) {
+ mPatch = new Patch(mXDivsCount, mYDivsCount);
+ mXDivs = new int32_t[mXDivsCount];
+ mYDivs = new int32_t[mYDivsCount];
+
+ mXDivs[0] = mYDivs[0] = 2;
+ mXDivs[1] = mYDivs[1] = 3;
+
+ glGenTextures(1, &mTexture);
+ glBindTexture(GL_TEXTURE_2D, mTexture);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gLineTextureWidth, gLineTextureHeight, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, gLineTexture);
+ }
+
+ ~Line() {
+ delete mPatch;
+ delete mXDivs;
+ delete mYDivs;
+
+ glDeleteTextures(1, &mTexture);
+ }
+
+ void update(float x1, float y1, float x2, float y2, float lineWidth, float& tx, float& ty) {
+ const float length = sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
+ const float half = lineWidth * 0.5f;
+
+ mPatch->updateVertices(gLineTextureWidth, gLineTextureHeight,
+ -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias,
+ mXDivs, mYDivs, mXDivsCount, mYDivsCount);
+
+ tx = -gLineAABias;
+ ty = -half - gLineAABias;
+ }
+
+ inline GLvoid* getVertices() const {
+ return &mPatch->vertices[0].position[0];
+ }
+
+ inline GLvoid* getTexCoords() const {
+ return &mPatch->vertices[0].texture[0];
+ }
+
+ inline GLsizei getElementsCount() const {
+ return mPatch->verticesCount;
+ }
+
+ inline GLuint getTexture() const {
+ return mTexture;
+ }
+
+private:
+ uint32_t mXDivsCount;
+ uint32_t mYDivsCount;
+
+ int32_t* mXDivs;
+ int32_t* mYDivs;
+
+ Patch* mPatch;
+
+ GLuint mTexture;
+}; // class Line
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_LINE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b1f5f6b..ecc02fa 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -39,6 +39,9 @@
// Generates simple and textured vertices
#define FV(x, y, u, v) { { x, y }, { u, v } }
+#define RAD_TO_DEG (180.0f / 3.14159265f)
+#define MIN_ANGLE 0.001f
+
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
@@ -124,6 +127,8 @@
if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
}
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -161,6 +166,15 @@
mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
}
+void OpenGLRenderer::finish() {
+#if DEBUG_OPENGL
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ LOGD("GL error from OpenGLRenderer: 0x%x", status);
+ }
+#endif
+}
+
void OpenGLRenderer::acquireContext() {
if (mCaches.currentProgram) {
if (mCaches.currentProgram->isInUse()) {
@@ -342,7 +356,12 @@
// Layers only make sense if they are in the framebuffer's bounds
bounds.intersect(*mSnapshot->clipRect);
- if (bounds.isEmpty()) return false;
+ bounds.snapToPixelBoundaries();
+
+ if (bounds.isEmpty() || bounds.getWidth() > mMaxTextureSize ||
+ bounds.getHeight() > mMaxTextureSize) {
+ return false;
+ }
LayerSize size(bounds.getWidth(), bounds.getHeight());
Layer* layer = mCaches.layerCache.get(size);
@@ -399,7 +418,7 @@
drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
- &mMeshVertices[0].texture[0], NULL, 0, true, true);
+ &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
@@ -572,15 +591,58 @@
getAlphaAndMode(paint, &alpha, &mode);
Patch* mesh = mCaches.patchCache.get(patch);
- mesh->updateVertices(bitmap, left, top, right, bottom,
+ mesh->updateVertices(bitmap->width(), bitmap->height(),left, top, right, bottom,
&patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
- mesh->dump();
// 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);
+ &mesh->vertices[0].texture[0], GL_TRIANGLES, mesh->verticesCount);
+}
+
+void OpenGLRenderer::drawLines(float* points, int count, const SkPaint* paint) {
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ uint32_t color = paint->getColor();
+ const GLfloat a = alpha / 255.0f;
+ const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
+
+ GLuint textureUnit = 0;
+ setupTextureAlpha8(mLine.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
+ mode, false, true, mLine.getVertices(), mLine.getTexCoords());
+
+ for (int i = 0; i < count; i += 4) {
+ float tx = 0.0f;
+ float ty = 0.0f;
+
+ mLine.update(points[i], points[i + 1], points[i + 2], points[i + 3],
+ paint->getStrokeWidth(), tx, ty);
+
+ const float dx = points[i + 2] - points[i];
+ const float dy = points[i + 3] - points[i + 1];
+ const float mag = sqrtf(dx * dx + dy * dy);
+ const float angle = acos(dx / mag);
+
+ mModelView.loadTranslate(points[i], points[i + 1], 0.0f);
+ if (angle > MIN_ANGLE || angle < -MIN_ANGLE) {
+ mModelView.rotate(angle * RAD_TO_DEG, 0.0f, 0.0f, 1.0f);
+ }
+ mModelView.translate(tx, ty, 0.0f);
+ mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+
+ if (mShader) {
+ mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, mLine.getElementsCount());
+ }
+
+ glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
}
void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -778,12 +840,22 @@
float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
bool transforms, bool applyFilters) {
setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit,
- x, y, r, g, b, a, mode, transforms, applyFilters);
+ x, y, r, g, b, a, mode, transforms, applyFilters,
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
}
void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
SkXfermode::Mode mode, bool transforms, bool applyFilters) {
+ setupTextureAlpha8(texture, width, height, textureUnit,
+ x, y, r, g, b, a, mode, transforms, applyFilters,
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters,
+ GLvoid* vertices, GLvoid* texCoords) {
// Describe the required shaders
ProgramDescription description;
description.hasTexture = true;
@@ -812,9 +884,9 @@
// Setup attributes
glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].position[0]);
+ gMeshStride, vertices);
glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].texture[0]);
+ gMeshStride, texCoords);
// Setup uniforms
if (transforms) {
@@ -956,19 +1028,21 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
- &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+ texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ GL_TRIANGLE_STRIP, gMeshCount);
}
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,
- &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
+ &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+ GL_TRIANGLE_STRIP, gMeshCount);
}
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,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool swapSrcDst, bool ignoreTransform) {
clearLayerRegions();
@@ -1010,11 +1084,7 @@
mColorFilter->setupProgram(mCaches.currentProgram);
}
- if (!indices) {
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
- } else {
- glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
- }
+ glDrawArrays(drawMode, 0, elementsCount);
glDisableVertexAttribArray(texCoordsSlot);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3126754..cd7963f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -40,11 +40,19 @@
#include "SkiaShader.h"
#include "SkiaColorFilter.h"
#include "Caches.h"
+#include "Line.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_OPENGL 1
+
+///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -59,6 +67,7 @@
void setViewport(int width, int height);
void prepare();
+ void finish();
void acquireContext();
void releaseContext();
@@ -91,6 +100,7 @@
void drawColor(int color, SkXfermode::Mode mode);
void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
void drawPath(SkPath* path, SkPaint* paint);
+ void drawLines(float* points, int count, const SkPaint* paint);
void resetShader();
void setupShader(SkiaShader* shader);
@@ -229,7 +239,7 @@
*/
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,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
bool swapSrcDst = false, bool ignoreTransform = false);
/**
@@ -289,6 +299,15 @@
SkXfermode::Mode mode, bool transforms, bool applyFilters);
/**
+ * Same as above setupTextureAlpha8() but specifies the mesh's vertices
+ * and texCoords pointers.
+ */
+ void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters,
+ GLvoid* vertices, GLvoid* texCoords);
+
+ /**
* Draws text underline and strike-through if needed.
*
* @param text The text to decor
@@ -395,6 +414,12 @@
// List of rectangles to clear due to calls to saveLayer()
Vector<Rect*> mLayers;
+ // Single object used to draw lines
+ Line mLine;
+
+ // Misc
+ GLint mMaxTextureSize;
+
}; // class OpenGLRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 4b6bb37..0a6eca3 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -16,9 +16,7 @@
#define LOG_TAG "OpenGLRenderer"
-#include <cstring>
-
-#include <utils/Log.h>
+#include <cmath>
#include "Patch.h"
@@ -29,38 +27,13 @@
// 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];
-
+Patch::Patch(const uint32_t xCount, const uint32_t yCount) {
// 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;
- }
+ verticesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
+ vertices = new TextureVertex[verticesCount];
}
Patch::~Patch() {
- delete indices;
delete vertices;
}
@@ -68,30 +41,26 @@
// 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) {
+void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
+ 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;
+ float stretchX = 0.0f;
+ float stretchY = 0.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 xStretchTex = stretchSize;
const float fixed = bitmapWidth - stretchSize;
- xStretch = (right - left - fixed) / xStretchCount;
+ const float xStretch = right - left - fixed;
+ stretchX = xStretch / xStretchTex;
}
if (yStretchCount > 0) {
@@ -99,89 +68,86 @@
for (uint32_t i = 1; i < height; i += 2) {
stretchSize += yDivs[i] - yDivs[i - 1];
}
- yStretchTex = (stretchSize / bitmapHeight) / yStretchCount;
+ const float yStretchTex = stretchSize;
const float fixed = bitmapHeight - stretchSize;
- yStretch = (bottom - top - fixed) / yStretchCount;
+ const float yStretch = bottom - top - fixed;
+ stretchY = yStretch / yStretchTex;
}
- 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;
+ float previousStepY = 0.0f;
- for (uint32_t y = 0; y < height; y++) {
- if (y & 1) {
- vy += yStretch;
- ty += yStretchTex;
+ float y1 = 0.0f;
+ float v1 = 0.0f;
+
+ for (uint32_t i = 0; i < height; i++) {
+ float stepY = yDivs[i];
+
+ float y2 = 0.0f;
+ if (i & 1) {
+ const float segment = stepY - previousStepY;
+ y2 = y1 + segment * stretchY;
} else {
- const float step = float(yDivs[y]);
- vy += step;
- ty += step / bitmapHeight;
+ y2 = y1 + stepY - previousStepY;
}
- generateVertices(vertex, vy, ty, xDivs, width, xStretch, xStretchTex,
- meshWidth, bitmapWidth);
- vertex += width + 2;
+ float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight;
+
+ generateRow(vertex, y1, y2, v1, v2, xDivs, width, stretchX,
+ right - left, bitmapWidth);
+
+ y1 = y2;
+ v1 = (stepY + 0.5f) / bitmapHeight;
+
+ previousStepY = stepY;
}
- generateVertices(vertex, bottom - top, 1.0f, xDivs, width, xStretch, xStretchTex,
- meshWidth, bitmapWidth);
+ generateRow(vertex, y1, bottom - top, v1, 1.0f, xDivs, width, stretchX,
+ right - left, 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;
+inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
+ const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth) {
+ float previousStepX = 0.0f;
- TextureVertex::set(vertex, vx, y, tx, v);
- vertex++;
+ float x1 = 0.0f;
+ float u1 = 0.0f;
- for (uint32_t x = 0; x < xCount; x++) {
- if (x & 1) {
- vx += xStretch;
- tx += xStretchTex;
+ // Generate the row quad by quad
+ for (uint32_t i = 0; i < xCount; i++) {
+ float stepX = xDivs[i];
+
+ float x2 = 0.0f;
+ if (i & 1) {
+ const float segment = stepX - previousStepX;
+ x2 = x1 + segment * stretchX;
} else {
- const float step = float(xDivs[x]);
- vx += step;
- tx += step / widthTex;
+ x2 = x1 + stepX - previousStepX;
}
+ float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth;
- TextureVertex::set(vertex, vx, y, tx, v);
- vertex++;
+ generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2);
+
+ x1 = x2;
+ u1 = (stepX + 0.5f) / bitmapWidth;
+
+ previousStepX = stepX;
}
- TextureVertex::set(vertex, width, y, 1.0f, v);
- vertex++;
+ generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2);
}
-///////////////////////////////////////////////////////////////////////////////
-// Debug tools
-///////////////////////////////////////////////////////////////////////////////
+inline void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
+ float u1, float v1, float u2, float v2) {
+ // Left triangle
+ TextureVertex::set(vertex++, x1, y1, u1, v1);
+ TextureVertex::set(vertex++, x2, y1, u2, v1);
+ TextureVertex::set(vertex++, x1, y2, u1, v2);
-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);
+ // Right triangle
+ TextureVertex::set(vertex++, x1, y2, u1, v2);
+ TextureVertex::set(vertex++, x2, y1, u2, v1);
+ TextureVertex::set(vertex++, x2, y2, u2, v2);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 5d3ad03..1d08c64 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -19,8 +19,6 @@
#include <sys/types.h>
-#include <SkBitmap.h>
-
#include "Vertex.h"
namespace android {
@@ -59,24 +57,21 @@
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,
+ void updateVertices(const float bitmapWidth, const float bitmapHeight,
+ 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);
+ static inline void generateRow(TextureVertex*& vertex, float y1, float y2,
+ float v1, float v2, const int32_t xDivs[], uint32_t xCount,
+ float stretchX, float width, float bitmapWidth);
+ static inline void generateQuad(TextureVertex*& vertex,
+ float x1, float y1, float x2, float y2,
+ float u1, float v1, float u2, float v2);
}; // struct Patch
}; // namespace uirenderer
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 6b22c2b..f03a602 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -148,6 +148,13 @@
return false;
}
+ void snapToPixelBoundaries() {
+ left = floor(left);
+ top = floor(top);
+ right = ceil(right);
+ bottom = ceil(bottom);
+ }
+
void dump() const {
LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
}
@@ -157,4 +164,4 @@
}; // namespace uirenderer
}; // namespace android
-#endif // ANDROID_RECT_H
+#endif // ANDROID_UI_RECT_H
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index a5e0f78..946cc4b 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -128,6 +128,22 @@
glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
}
+void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
+ const Snapshot& snapshot) {
+ mat4 textureTransform;
+ if (mMatrix) {
+ SkMatrix inverse;
+ mMatrix->invert(&inverse);
+ textureTransform.load(inverse);
+ textureTransform.multiply(modelView);
+ } else {
+ textureTransform.load(modelView);
+ }
+
+ glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+ GL_FALSE, &textureTransform.data[0]);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Linear gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -185,6 +201,13 @@
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
+void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
+ const Snapshot& snapshot) {
+ mat4 screenSpace(*snapshot.transform);
+ screenSpace.multiply(modelView);
+ glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Compose shader
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index d95e3b0..cc94ae6 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -73,6 +73,10 @@
mGradientCache = gradientCache;
}
+ virtual void updateTransforms(Program* program, const mat4& modelView,
+ const Snapshot& snapshot) {
+ }
+
void setMatrix(SkMatrix* matrix) {
mMatrix = matrix;
}
@@ -106,6 +110,7 @@
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
+ void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
/**
@@ -130,6 +135,7 @@
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
+ void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
float* mBounds;
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 60998c31..87c4f2b 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -724,10 +724,7 @@
const Element *src = static_cast<const Element *>(_src);
const Element *dst = static_cast<const Element *>(_dst);
- // Check for pow2 on pre es 2.0 versions.
- rsAssert(rsc->checkVersion2_0() || (!(w & (w-1)) && !(h & (h-1))));
-
- //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips);
+ //LOGE("%p rsi_AllocationCreateFromBitmap %i %i %i", rsc, w, h, genMips);
rsi_TypeBegin(rsc, _dst);
rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index b6b5d2f..ce5372f 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -90,6 +90,7 @@
void decRefs(const void *ptr, size_t ct) const;
void sendDirty() const;
+ bool getHasGraphicsMipmaps() const {return mTextureGenMipmap;}
protected:
ObjectBaseRef<const Type> mType;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 5327aac..3dbdbfb 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -232,23 +232,15 @@
bool Context::setupCheck()
{
- if (checkVersion2_0()) {
- if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
- LOGE("Context::setupCheck() 1 fail");
- return false;
- }
-
- mFragmentStore->setupGL2(this, &mStateFragmentStore);
- mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
- mRaster->setupGL2(this, &mStateRaster);
- mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
-
- } else {
- mFragmentStore->setupGL(this, &mStateFragmentStore);
- mFragment->setupGL(this, &mStateFragment);
- mRaster->setupGL(this, &mStateRaster);
- mVertex->setupGL(this, &mStateVertex);
+ if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
+ LOGE("Context::setupCheck() 1 fail");
+ return false;
}
+
+ mFragmentStore->setupGL2(this, &mStateFragmentStore);
+ mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
+ mRaster->setupGL2(this, &mStateRaster);
+ mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
return true;
}
@@ -940,6 +932,7 @@
LOGV("rsContextCreateGL %p, %i", vdev, useDepth);
Device * dev = static_cast<Device *>(vdev);
Context *rsc = new Context(dev, true, useDepth);
+ LOGV("rsContextCreateGL ret %p ", rsc);
return rsc;
}
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 6d1a41d..e38ba55 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -153,9 +153,6 @@
void timerPrint();
void timerFrame();
- bool checkVersion1_1() const {return (mGL.mMajorVersion > 1) || (mGL.mMinorVersion >= 1); }
- bool checkVersion2_0() const {return mGL.mMajorVersion >= 2; }
-
struct {
bool mLogTimes;
bool mLogScripts;
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 3174e82..9817fca 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -103,13 +103,8 @@
mDirty = true;
}
-void ProgramFragment::setupGL(const Context *rsc, ProgramFragmentState *state)
-{
-}
-
void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
{
-
//LOGE("sgl2 frag1 %x", glGetError());
if ((state->mLast.get() == this) && !mDirty) {
return;
@@ -131,12 +126,12 @@
glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
rsc->checkError("ProgramFragment::setupGL2 tex bind");
if (mSamplers[ct].get()) {
- mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
+ mSamplers[ct]->setupGL(rsc, mTextures[ct].get());
} else {
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_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
rsc->checkError("ProgramFragment::setupGL2 tex env");
}
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
index f08bb25..fb78b3f 100644
--- a/libs/rs/rsProgramFragment.h
+++ b/libs/rs/rsProgramFragment.h
@@ -34,7 +34,6 @@
uint32_t paramLength);
virtual ~ProgramFragment();
- virtual void setupGL(const Context *, ProgramFragmentState *);
virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc);
virtual void createShader();
diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp
index 5b69370..62d060d 100644
--- a/libs/rs/rsProgramRaster.cpp
+++ b/libs/rs/rsProgramRaster.cpp
@@ -61,52 +61,6 @@
mDirty = true;
}
-void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state)
-{
- if (state->mLast.get() == this && !mDirty) {
- return;
- }
- state->mLast.set(this);
- mDirty = false;
-
- if (mPointSmooth) {
- glEnable(GL_POINT_SMOOTH);
- } else {
- glDisable(GL_POINT_SMOOTH);
- }
-
- glLineWidth(mLineWidth);
- if (mLineSmooth) {
- glEnable(GL_LINE_SMOOTH);
- } else {
- glDisable(GL_LINE_SMOOTH);
- }
-
- if (rsc->checkVersion1_1()) {
-#ifndef ANDROID_RS_BUILD_FOR_HOST
- if (mPointSprite) {
- glEnable(GL_POINT_SPRITE_OES);
- } else {
- glDisable(GL_POINT_SPRITE_OES);
- }
-#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 && !mDirty) {
diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h
index 801ab2a..d5ed686 100644
--- a/libs/rs/rsProgramRaster.h
+++ b/libs/rs/rsProgramRaster.h
@@ -34,7 +34,6 @@
bool pointSprite);
virtual ~ProgramRaster();
- virtual void setupGL(const Context *, ProgramRasterState *);
virtual void setupGL2(const Context *, ProgramRasterState *);
virtual void serialize(OStream *stream) const;
virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_RASTER; }
diff --git a/libs/rs/rsProgramStore.cpp b/libs/rs/rsProgramStore.cpp
index e741c0a..3f90d7a 100644
--- a/libs/rs/rsProgramStore.cpp
+++ b/libs/rs/rsProgramStore.cpp
@@ -56,41 +56,6 @@
{
}
-void ProgramStore::setupGL(const Context *rsc, ProgramStoreState *state)
-{
- if (state->mLast.get() == this) {
- return;
- }
- state->mLast.set(this);
-
- glColorMask(mColorRWriteEnable,
- mColorGWriteEnable,
- mColorBWriteEnable,
- mColorAWriteEnable);
- if (mBlendEnable) {
- glEnable(GL_BLEND);
- glBlendFunc(mBlendSrc, mBlendDst);
- } else {
- glDisable(GL_BLEND);
- }
-
- //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
-
- glDepthMask(mDepthWriteEnable);
- if(mDepthTestEnable || mDepthWriteEnable) {
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(mDepthFunc);
- } else {
- glDisable(GL_DEPTH_TEST);
- }
-
- if (mDitherEnable) {
- glEnable(GL_DITHER);
- } else {
- glDisable(GL_DITHER);
- }
-}
-
void ProgramStore::setupGL2(const Context *rsc, ProgramStoreState *state)
{
if (state->mLast.get() == this) {
diff --git a/libs/rs/rsProgramStore.h b/libs/rs/rsProgramStore.h
index fe8d78e..95bcf3c 100644
--- a/libs/rs/rsProgramStore.h
+++ b/libs/rs/rsProgramStore.h
@@ -32,7 +32,6 @@
ProgramStore(Context *);
virtual ~ProgramStore();
- virtual void setupGL(const Context *, ProgramStoreState *);
virtual void setupGL2(const Context *, ProgramStoreState *);
void setDepthFunc(RsDepthFunc);
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 28084d7..6446b55 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -68,11 +68,6 @@
LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[3], f[7], f[11], f[15]);
}
-void ProgramVertex::setupGL(const Context *rsc, ProgramVertexState *state)
-{
- assert(0);
-}
-
void ProgramVertex::loadShader(Context *rsc) {
Program::loadShader(rsc, GL_VERTEX_SHADER);
}
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index d6b3f5a..65ce541 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -35,7 +35,6 @@
ProgramVertex(Context *, bool texMat);
virtual ~ProgramVertex();
- virtual void setupGL(const Context *rsc, ProgramVertexState *state);
virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc);
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index 47b8a61..c6a848c 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -59,7 +59,7 @@
{
}
-void Sampler::setupGL(const Context *rsc, bool npot)
+void Sampler::setupGL(const Context *rsc, const Allocation *tex)
{
GLenum trans[] = {
GL_NEAREST, //RS_SAMPLER_NEAREST,
@@ -77,13 +77,17 @@
GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
};
- if (!rsc->ext_OES_texture_npot() && npot) {
+ if (!rsc->ext_OES_texture_npot() && tex->getType()->getIsNp2()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, transNP[mMagFilter]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, transNP[mWrapS]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, transNP[mWrapT]);
} else {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+ if (tex->getHasGraphicsMipmaps()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
+ }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 3786439..32a8efd 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -41,7 +41,7 @@
virtual ~Sampler();
void bind(Allocation *);
- void setupGL(const Context *, bool npot);
+ void setupGL(const Context *, const Allocation *);
void bindToContext(SamplerState *, uint32_t slot);
void unbindFromContext(SamplerState *);
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 662791d..cbc5df9 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -56,9 +56,16 @@
= nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
for (uint32_t ct=0; ct < mEnviroment.mFieldCount; ct++) {
- if (!mSlots[ct].get())
+ if (mSlots[ct].get() && !mTypes[ct].get()) {
+ mTypes[ct].set(mSlots[ct]->getType());
+ }
+
+ if (!mTypes[ct].get())
continue;
- void *ptr = mSlots[ct]->getPtr();
+ void *ptr = NULL;
+ if (mSlots[ct].get()) {
+ ptr = mSlots[ct]->getPtr();
+ }
void **dest = ((void ***)mEnviroment.mFieldAddress)[ct];
//LOGE("setupScript %i %p = %p %p %i", ct, dest, ptr, mSlots[ct]->getType(), mSlots[ct]->getType()->getDimX());
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 41828dc..22fd421 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -263,13 +263,23 @@
}
static void SC_setObject(void **vdst, void * vsrc) {
- static_cast<ObjectBase *>(vsrc)->incSysRef();
- static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ //LOGE("SC_setObject %p,%p %p", vdst, *vdst, vsrc);
+ if (vsrc) {
+ static_cast<ObjectBase *>(vsrc)->incSysRef();
+ }
+ if (vdst[0]) {
+ static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ }
*vdst = vsrc;
+ //LOGE("SC_setObject *");
}
static void SC_clearObject(void **vdst) {
- static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ //LOGE("SC_clearObject %p,%p", vdst, *vdst);
+ if (vdst[0]) {
+ static_cast<ObjectBase *>(vdst[0])->decSysRef();
+ }
*vdst = NULL;
+ //LOGE("SC_clearObject *");
}
static bool SC_isObject(RsAllocation vsrc) {
return vsrc != NULL;
@@ -423,10 +433,51 @@
{ "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY },
{ "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ },
- { "_Z11rsSetObjectP13rs_allocation13rs_allocation", (void *)&SC_setObject },
+ { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject },
+ { "_Z10rsIsObject10rs_element", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject },
+ { "_Z10rsIsObject7rs_type", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject },
{ "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject },
{ "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject },
+ { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject },
+ { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject },
+ { "_Z10rsIsObject9rs_script", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject },
+ { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject },
+ { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject },
+ { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject },
+ { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject },
+ { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject },
+
+ { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject },
+ { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject },
+ { "_Z10rsIsObject7rs_font", (void *)&SC_isObject },
+
+
{ "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty },
diff --git a/libs/rs/rsVertexArray.h b/libs/rs/rsVertexArray.h
index 7c609b2..bd76d87 100644
--- a/libs/rs/rsVertexArray.h
+++ b/libs/rs/rsVertexArray.h
@@ -65,7 +65,6 @@
//void addLegacy(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset);
void add(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name);
- void setupGL(const Context *rsc, class VertexArrayState *) const;
void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const;
void logAttrib(uint32_t idx, uint32_t slot) const;
diff --git a/libs/ui/GraphicLog.cpp b/libs/ui/GraphicLog.cpp
index b55ce23..7ba2779 100644
--- a/libs/ui/GraphicLog.cpp
+++ b/libs/ui/GraphicLog.cpp
@@ -30,7 +30,11 @@
static inline
void writeInt32(uint8_t* base, size_t& pos, int32_t value) {
+#ifdef HAVE_LITTLE_ENDIAN
+ int32_t v = value;
+#else
int32_t v = htole32(value);
+#endif
base[pos] = EVENT_TYPE_INT;
memcpy(&base[pos+1], &v, sizeof(int32_t));
pos += 1+sizeof(int32_t);
@@ -38,7 +42,11 @@
static inline
void writeInt64(uint8_t* base, size_t& pos, int64_t value) {
+#ifdef HAVE_LITTLE_ENDIAN
+ int64_t v = value;
+#else
int64_t v = htole64(value);
+#endif
base[pos] = EVENT_TYPE_LONG;
memcpy(&base[pos+1], &v, sizeof(int64_t));
pos += 1+sizeof(int64_t);
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 48dea57..1cf7592 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -122,7 +122,7 @@
AutoMutex _l(mLock);
resetKeyRepeatLocked();
- releasePendingEventLocked(true);
+ releasePendingEventLocked();
drainInboundQueueLocked();
}
@@ -174,7 +174,7 @@
if (! mDispatchEnabled) {
if (mPendingEvent || ! mInboundQueue.isEmpty()) {
LOGI("Dropping pending events because input dispatch is disabled.");
- releasePendingEventLocked(true);
+ releasePendingEventLocked();
drainInboundQueueLocked();
}
return;
@@ -196,42 +196,6 @@
*nextWakeupTime = mAppSwitchDueTime;
}
- // Detect and process timeouts for all connections and determine if there are any
- // synchronous event dispatches pending. This step is entirely non-interruptible.
- bool havePendingSyncTarget = false;
- size_t activeConnectionCount = mActiveConnections.size();
- for (size_t i = 0; i < activeConnectionCount; i++) {
- Connection* connection = mActiveConnections.itemAt(i);
-
- if (connection->hasPendingSyncTarget()) {
- if (isAppSwitchDue) {
- connection->preemptSyncTarget();
- } else {
- havePendingSyncTarget = true;
- }
- }
-
- nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
- if (connectionTimeoutTime <= currentTime) {
- mTimedOutConnections.add(connection);
- } else if (connectionTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = connectionTimeoutTime;
- }
- }
-
- size_t timedOutConnectionCount = mTimedOutConnections.size();
- for (size_t i = 0; i < timedOutConnectionCount; i++) {
- Connection* connection = mTimedOutConnections.itemAt(i);
- timeoutDispatchCycleLocked(currentTime, connection);
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
- mTimedOutConnections.clear();
-
- // If we have a pending synchronous target, skip dispatch.
- if (havePendingSyncTarget) {
- return;
- }
-
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
@@ -317,51 +281,50 @@
// Now we have an event to dispatch.
assert(mPendingEvent != NULL);
- bool wasDispatched = false;
- bool wasDropped = false;
+ bool done = false;
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
- wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+ done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
break;
}
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
- if (isAppSwitchPendingLocked()) {
- if (isAppSwitchKey(typedEntry->keyCode)) {
+ bool appSwitchKey = isAppSwitchKey(typedEntry->keyCode);
+ bool dropEvent = isAppSwitchDue && ! appSwitchKey;
+ done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, dropEvent,
+ nextWakeupTime);
+ if (done) {
+ if (dropEvent) {
+ LOGI("Dropped key because of pending overdue app switch.");
+ } else if (appSwitchKey) {
resetPendingAppSwitchLocked(true);
- } else if (isAppSwitchDue) {
- LOGI("Dropping key because of pending overdue app switch.");
- wasDropped = true;
- break;
}
}
- wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
- nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
- if (isAppSwitchDue) {
- LOGI("Dropping motion because of pending overdue app switch.");
- wasDropped = true;
- break;
+ bool dropEvent = isAppSwitchDue;
+ done = dispatchMotionLocked(currentTime, typedEntry, dropEvent, nextWakeupTime);
+ if (done) {
+ if (dropEvent) {
+ LOGI("Dropped motion because of pending overdue app switch.");
+ }
}
- wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime);
break;
}
default:
assert(false);
- wasDropped = true;
break;
}
- if (wasDispatched || wasDropped) {
- releasePendingEventLocked(wasDropped);
+ if (done) {
+ releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
@@ -439,21 +402,21 @@
void InputDispatcher::drainInboundQueueLocked() {
while (! mInboundQueue.isEmpty()) {
EventEntry* entry = mInboundQueue.dequeueAtHead();
- releaseInboundEventLocked(entry, true /*wasDropped*/);
+ releaseInboundEventLocked(entry);
}
}
-void InputDispatcher::releasePendingEventLocked(bool wasDropped) {
+void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
- releaseInboundEventLocked(mPendingEvent, wasDropped);
+ releaseInboundEventLocked(mPendingEvent);
mPendingEvent = NULL;
}
}
-void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) {
- if (wasDropped) {
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
+ if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("Pending event was dropped.");
+ LOGD("Inbound event was dropped. Setting injection result to failed.");
#endif
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
}
@@ -528,7 +491,41 @@
bool InputDispatcher::dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
- nsecs_t* nextWakeupTime) {
+ bool dropEvent, nsecs_t* nextWakeupTime) {
+ // Give the policy a chance to intercept the key.
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ bool trusted;
+ if (! dropEvent && mFocusedWindow) {
+ trusted = checkInjectionPermission(mFocusedWindow,
+ entry->injectorPid, entry->injectorUid);
+ } else {
+ trusted = isEventFromReliableSourceLocked(entry);
+ }
+ if (trusted) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+ if (! dropEvent && mFocusedWindow) {
+ commandEntry->inputChannel = mFocusedWindow->inputChannel;
+ }
+ commandEntry->keyEntry = entry;
+ entry->refCount += 1;
+ return false; // wait for the command to run
+ } else {
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ }
+ } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ resetTargetsLocked();
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_SUCCEEDED);
+ return true;
+ }
+
+ // Clean up if dropping the event.
+ if (dropEvent) {
+ resetTargetsLocked();
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ return true;
+ }
+
// Preprocessing.
if (! entry->dispatchInProgress) {
logOutboundKeyDetailsLocked("dispatchKey - ", entry);
@@ -557,7 +554,7 @@
}
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ resetTargetsLocked();
}
// Identify targets.
@@ -575,20 +572,7 @@
}
addMonitoringTargetsLocked();
- finishFindingTargetsLocked(window);
- }
-
- // Give the policy a chance to intercept the key.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- commandEntry->inputChannel = mCurrentInputChannel;
- commandEntry->keyEntry = entry;
- entry->refCount += 1;
- return false; // wait for the command to run
- }
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
- return true;
+ commitTargetsLocked(window);
}
// Dispatch the key.
@@ -612,13 +596,20 @@
}
bool InputDispatcher::dispatchMotionLocked(
- nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) {
+ nsecs_t currentTime, MotionEntry* entry, bool dropEvent, nsecs_t* nextWakeupTime) {
+ // Clean up if dropping the event.
+ if (dropEvent) {
+ resetTargetsLocked();
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ return true;
+ }
+
// Preprocessing.
if (! entry->dispatchInProgress) {
logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ resetTargetsLocked();
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
@@ -646,7 +637,7 @@
}
addMonitoringTargetsLocked();
- finishFindingTargetsLocked(window);
+ commitTargetsLocked(window);
}
// Dispatch the motion.
@@ -728,7 +719,7 @@
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
- ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -741,14 +732,14 @@
}
}
-void InputDispatcher::startFindingTargetsLocked() {
+void InputDispatcher::resetTargetsLocked() {
mCurrentInputTargetsValid = false;
mCurrentInputTargets.clear();
mCurrentInputChannel.clear();
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
-void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) {
+void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
mCurrentInputWindowType = window->layoutParamsType;
mCurrentInputChannel = window->inputChannel;
mCurrentInputTargetsValid = true;
@@ -770,9 +761,8 @@
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
- LOGD("Waiting for application to become ready for input: name=%s, window=%s",
- application ? application->name.string() : "<unknown>",
- window ? window->inputChannel->getName().string() : "<unknown>");
+ LOGD("Waiting for application to become ready for input: %s",
+ getApplicationWindowLabelLocked(application, window).string());
#endif
nsecs_t timeout = window ? window->dispatchingTimeout :
application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
@@ -789,21 +779,7 @@
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
- LOGI("Application is not ready for input: name=%s, window=%s,"
- "%01.1fms since event, %01.1fms since wait started",
- application ? application->name.string() : "<unknown>",
- window ? window->inputChannel->getName().string() : "<unknown>",
- (currentTime - entry->eventTime) / 1000000.0,
- (currentTime - mInputTargetWaitStartTime) / 1000000.0);
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
- if (application) {
- commandEntry->inputApplicationHandle = application->handle;
- }
- if (window) {
- commandEntry->inputChannel = window->inputChannel;
- }
+ onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
@@ -818,17 +794,30 @@
}
}
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel) {
if (newTimeout > 0) {
// Extend the timeout.
mInputTargetWaitTimeoutTime = now() + newTimeout;
} else {
// Give up.
mInputTargetWaitTimeoutExpired = true;
+
+ // Release the touch target.
+ releaseTouchedWindowLocked();
+
+ // Input state will not be realistic. Mark it out of sync.
+ if (inputChannel.get()) {
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ connection->inputState.setOutOfSync();
+ }
+ }
}
}
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
nsecs_t currentTime) {
if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
return currentTime - mInputTargetWaitStartTime;
@@ -841,13 +830,6 @@
LOGD("Resetting ANR timeouts.");
#endif
- // Reset timeouts for all active connections.
- nsecs_t currentTime = now();
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections[i];
- connection->resetTimeout(currentTime);
- }
-
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
@@ -865,8 +847,8 @@
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no focused window but there is a "
- "focused application that may eventually add a window: '%s'.",
- mFocusedApplication->name.string());
+ "focused application that may eventually add a window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
@@ -894,19 +876,31 @@
goto Unresponsive;
}
+ // If the currently focused window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
*outWindow = mFocusedWindow;
- addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
- getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
// Done.
Failed:
Unresponsive:
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
- LOGD("findFocusedWindow finished: injectionResult=%d",
- injectionResult);
- logDispatchStateLocked();
+ LOGD("findFocusedWindow finished: injectionResult=%d, "
+ "timeSpendWaitingForApplication=%0.1fms",
+ injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
@@ -1018,8 +1012,8 @@
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no touched window but there is a "
- "focused application that may eventually add a new window: '%s'.",
- mFocusedApplication->name.string());
+ "focused application that may eventually add a new window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
@@ -1051,6 +1045,17 @@
goto Unresponsive;
}
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, newTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
+
// Success! Update the touch dispatch state for real.
releaseTouchedWindowLocked();
@@ -1098,6 +1103,17 @@
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
+
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, mTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
}
// Success! Output targets.
@@ -1108,7 +1124,7 @@
size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
for (size_t i = 0; i < numWallpaperWindows; i++) {
addWindowTargetLocked(mTouchedWallpaperWindows[i],
- InputTarget::FLAG_WINDOW_IS_OBSCURED, 0);
+ InputTarget::FLAG_WINDOW_IS_OBSCURED);
}
size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
@@ -1118,16 +1134,15 @@
if (outsideTarget.obscured) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
- addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0);
+ addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
}
mTempTouchedOutsideTargets.clear();
- int32_t targetFlags = InputTarget::FLAG_SYNC;
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
if (mTouchedWindowIsObscured) {
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
- addWindowTargetLocked(mTouchedWindow, targetFlags,
- getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ addWindowTargetLocked(mTouchedWindow, targetFlags);
*outWindow = mTouchedWindow;
}
@@ -1166,10 +1181,13 @@
}
Unresponsive:
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
- LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
- injectionResult, injectionPermission);
- logDispatchStateLocked();
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
+ "timeSpendWaitingForApplication=%0.1fms",
+ injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
@@ -1180,15 +1198,12 @@
mTouchedWallpaperWindows.clear();
}
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = window->inputChannel;
target.flags = targetFlags;
- target.timeout = window->dispatchingTimeout;
- target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
}
@@ -1200,8 +1215,6 @@
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
target.flags = 0;
- target.timeout = -1;
- target.timeSpentWaitingForApplication = 0;
target.xOffset = 0;
target.yOffset = 0;
}
@@ -1241,6 +1254,34 @@
return false;
}
+bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) {
+ ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ return connection->outboundQueue.isEmpty();
+ } else {
+ return true;
+ }
+}
+
+String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application,
+ const InputWindow* window) {
+ if (application) {
+ if (window) {
+ String8 label(application->name);
+ label.append(" - ");
+ label.append(window->name);
+ return label;
+ } else {
+ return application->name;
+ }
+ } else if (window) {
+ return window->name;
+ } else {
+ return String8("<unknown application or window>");
+ }
+}
+
void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
int32_t windowType, int32_t eventType) {
CommandEntry* commandEntry = postCommandLocked(
@@ -1254,25 +1295,18 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
+ LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
"xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
- connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
+ connection->getInputChannelName(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
toString(resumeWithAppendedMotionSample));
#endif
// Skip this event if the connection status is not normal.
- // We don't want to enqueue additional outbound events if the connection is broken or
- // not responding.
+ // We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::STATUS_NORMAL) {
LOGW("channel '%s' ~ Dropping event because the channel status is %s",
connection->getInputChannelName(), connection->getStatusLabel());
-
- // If the connection is not responding but the user is poking the application anyways,
- // retrigger the original timeout.
- if (connection->status == Connection::STATUS_NOT_RESPONDING) {
- timeoutDispatchCycleLocked(currentTime, connection);
- }
return;
}
@@ -1379,7 +1413,7 @@
DispatchEntry* cancelationDispatchEntry =
mAllocator.obtainDispatchEntry(cancelationEventEntry,
- 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+ 0, inputTarget->xOffset, inputTarget->yOffset); // increments ref
connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
mAllocator.releaseEventEntry(cancelationEventEntry);
@@ -1390,10 +1424,9 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
- inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->timeout);
- if (dispatchEntry->isSyncTarget()) {
- eventEntry->pendingSyncDispatches += 1;
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
+ if (dispatchEntry->hasForegroundTarget()) {
+ eventEntry->pendingForegroundDispatches += 1;
}
// Handle the case where we could not stream a new motion sample because the consumer has
@@ -1416,13 +1449,12 @@
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty) {
activateConnectionLocked(connection.get());
- startDispatchCycleLocked(currentTime, connection,
- inputTarget->timeSpentWaitingForApplication);
+ startDispatchCycleLocked(currentTime, connection);
}
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
+ const sp<Connection>& connection) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ startDispatchCycle",
connection->getInputChannelName());
@@ -1588,9 +1620,6 @@
connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
connection->lastDispatchTime = currentTime;
- nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
- connection->setNextTimeoutTime(currentTime, timeout);
-
// Notify other system components.
onDispatchCycleStartedLocked(currentTime, connection);
}
@@ -1610,21 +1639,8 @@
return;
}
- // Clear the pending timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
- if (connection->status == Connection::STATUS_NOT_RESPONDING) {
- // Recovering from an ANR.
- connection->status = Connection::STATUS_NORMAL;
-
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/);
- } else {
- // Normal finish. Not much to do here.
-
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/);
- }
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection);
// Reset the publisher since the event has been consumed.
// We do this now so that the publisher can release some of its internal resources
@@ -1653,20 +1669,20 @@
dispatchEntry->inProgress = false;
dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
dispatchEntry->tailMotionSample = NULL;
- startDispatchCycleLocked(currentTime, connection, 0);
+ startDispatchCycleLocked(currentTime, connection);
return;
}
// Finished.
connection->outboundQueue.dequeueAtHead();
- if (dispatchEntry->isSyncTarget()) {
- decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
mAllocator.releaseDispatchEntry(dispatchEntry);
} else {
// If the head is not in progress, then we must have already dequeued the in
- // progress event, which means we actually aborted it (due to ANR).
+ // progress event, which means we actually aborted it.
// So just start the next event for this connection.
- startDispatchCycleLocked(currentTime, connection, 0);
+ startDispatchCycleLocked(currentTime, connection);
return;
}
}
@@ -1675,66 +1691,6 @@
deactivateConnectionLocked(connection.get());
}
-void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ timeoutDispatchCycle",
- connection->getInputChannelName());
-#endif
-
- if (connection->status == Connection::STATUS_NORMAL) {
- // Enter the not responding state.
- connection->status = Connection::STATUS_NOT_RESPONDING;
- connection->lastANRTime = currentTime;
- } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
- // Connection is broken or dead.
- return;
- }
-
- // Notify other system components.
- // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
- onDispatchCycleANRLocked(currentTime, connection);
-}
-
-void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t newTimeout) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
- connection->getInputChannelName(), newTimeout);
-#endif
-
- if (connection->status != Connection::STATUS_NOT_RESPONDING) {
- return;
- }
-
- if (newTimeout > 0) {
- // The system has decided to give the application some more time.
- // Keep waiting synchronously and resume normal dispatch.
- connection->status = Connection::STATUS_NORMAL;
- connection->setNextTimeoutTime(currentTime, newTimeout);
- } else {
- // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
- // Reset the timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
- // Input state will no longer be realistic.
- connection->inputState.setOutOfSync();
-
- if (! connection->outboundQueue.isEmpty()) {
- // Make the current pending dispatch asynchronous (if it isn't already) so that
- // subsequent events can be delivered to the ANR dialog or to another application.
- DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
- currentDispatchEntry->preemptSyncTarget();
-
- // Drain all but the first entry in the outbound queue. We keep the first entry
- // since that is the one that dispatch is stuck on. We throw away the others
- // so that we don't spam the application with stale messages if it eventually
- // wakes up and recovers from the ANR.
- drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
- }
- }
-}
-
void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool broken) {
#if DEBUG_DISPATCH_CYCLE
@@ -1742,20 +1698,16 @@
connection->getInputChannelName(), toString(broken));
#endif
- // Clear the pending timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
// Input state will no longer be realistic.
connection->inputState.setOutOfSync();
// Clear the outbound queue.
- drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
+ drainOutboundQueueLocked(connection.get());
// Handle the case where the connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
if (broken) {
- if (connection->status == Connection::STATUS_NORMAL
- || connection->status == Connection::STATUS_NOT_RESPONDING) {
+ if (connection->status == Connection::STATUS_NORMAL) {
connection->status = Connection::STATUS_BROKEN;
// Notify other system components.
@@ -1764,24 +1716,16 @@
}
}
-void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
- DispatchEntry* firstDispatchEntryToDrain) {
- for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
- dispatchEntry != & connection->outboundQueue.tailSentinel;) {
- DispatchEntry* next = dispatchEntry->next;
- connection->outboundQueue.dequeue(dispatchEntry);
-
- if (dispatchEntry->isSyncTarget()) {
- decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
mAllocator.releaseDispatchEntry(dispatchEntry);
-
- dispatchEntry = next;
}
- if (connection->outboundQueue.isEmpty()) {
- deactivateConnectionLocked(connection);
- }
+ deactivateConnectionLocked(connection);
}
int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -1941,56 +1885,63 @@
// STREAMING CASE
//
// There is no pending motion event (of any kind) for this device in the inbound queue.
- // Search the outbound queues for a synchronously dispatched motion event for this
- // device. If found, then we append the new sample to that event and then try to
- // push it out to all current targets. It is possible that some targets will already
- // have consumed the motion event. This case is automatically handled by the
- // logic in prepareDispatchCycleLocked by tracking where resumption takes place.
- //
- // The reason we look for a synchronously dispatched motion event is because we
- // want to be sure that no other motion events have been dispatched since the move.
- // It's also convenient because it means that the input targets are still valid.
- // This code could be improved to support streaming of asynchronously dispatched
- // motion events (which might be significantly more efficient) but it may become
- // a little more complicated as a result.
- //
- // Note: This code crucially depends on the invariant that an outbound queue always
- // contains at most one synchronous event and it is always last (but it might
- // not be first!).
+ // Search the outbound queue for the current foreground targets to find a dispatched
+ // motion event that is still in progress. If found, then, appen the new sample to
+ // that event and push it out to all current targets. The logic in
+ // prepareDispatchCycleLocked takes care of the case where some targets may
+ // already have consumed the motion event by starting a new dispatch cycle if needed.
if (mCurrentInputTargetsValid) {
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections.itemAt(i);
- if (! connection->outboundQueue.isEmpty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
- if (dispatchEntry->isSyncTarget()) {
- if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
- goto NoBatchingOrStreaming;
- }
-
- MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
- dispatchEntry->eventEntry);
- if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE
- || syncedMotionEntry->deviceId != deviceId
- || syncedMotionEntry->pointerCount != pointerCount
- || syncedMotionEntry->isInjected()) {
- goto NoBatchingOrStreaming;
- }
-
- // Found synced move entry. Append sample and resume dispatch.
- mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
- pointerCoords);
- #if DEBUG_BATCHING
- LOGD("Appended motion sample onto batch for most recent synchronously "
- "dispatched motion event for this device in the outbound queues.");
- #endif
- nsecs_t currentTime = now();
- dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
- true /*resumeWithAppendedMotionSample*/);
-
- runCommandsLockedInterruptible();
- return; // done!
- }
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets[i];
+ if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
+ // Skip non-foreground targets. We only want to stream if there is at
+ // least one foreground target whose dispatch is still in progress.
+ continue;
}
+
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
+ if (connectionIndex < 0) {
+ // Connection must no longer be valid.
+ continue;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->outboundQueue.isEmpty()) {
+ // This foreground target has an empty outbound queue.
+ continue;
+ }
+
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (! dispatchEntry->inProgress
+ || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+ // No motion event is being dispatched.
+ continue;
+ }
+
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(
+ dispatchEntry->eventEntry);
+ if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || motionEntry->deviceId != deviceId
+ || motionEntry->pointerCount != pointerCount
+ || motionEntry->isInjected()) {
+ // The motion event is not compatible with this move.
+ continue;
+ }
+
+ // Hurray! This foreground target is currently dispatching a move event
+ // that we can stream onto. Append the motion sample and resume dispatch.
+ mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recently dispatched "
+ "motion event for this device in the outbound queues. "
+ "Attempting to stream the motion sample.");
+#endif
+ nsecs_t currentTime = now();
+ dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
+ true /*resumeWithAppendedMotionSample*/);
+
+ runCommandsLockedInterruptible();
+ return; // done!
}
}
@@ -2074,15 +2025,15 @@
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
&& syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
- while (injectedEntry->pendingSyncDispatches != 0) {
+ while (injectedEntry->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
- LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
- injectedEntry->pendingSyncDispatches);
+ LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+ injectedEntry->pendingForegroundDispatches);
#endif
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
- LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+ LOGD("injectInputEvent - Timed out waiting for pending foreground "
"dispatches to finish.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
@@ -2137,10 +2088,10 @@
}
}
-void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
- entry->pendingSyncDispatches -= 1;
+void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+ entry->pendingForegroundDispatches -= 1;
- if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+ if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
mInjectionSyncFinishedCondition.broadcast();
}
}
@@ -2238,6 +2189,9 @@
{ // acquire lock
AutoMutex _l(mLock);
+ // Clear old window pointers but remember their associated channels.
+ mFocusedWindow = NULL;
+
sp<InputChannel> touchedWindowChannel;
if (mTouchedWindow) {
touchedWindowChannel = mTouchedWindow->inputChannel;
@@ -2250,13 +2204,11 @@
}
mTouchedWallpaperWindows.clear();
}
-
- bool hadFocusedWindow = mFocusedWindow != NULL;
-
- mFocusedWindow = NULL;
mWallpaperWindows.clear();
-
mWindows.clear();
+
+ // Loop over new windows and rebuild the necessary window pointers for
+ // tracking focus and touch.
mWindows.appendVector(inputWindows);
size_t numWindows = mWindows.size();
@@ -2280,14 +2232,8 @@
mTouchedWindow = window;
}
}
-
mTempTouchedWallpaperChannels.clear();
- if ((hadFocusedWindow && ! mFocusedWindow)
- || (mFocusedWindow && ! mFocusedWindow->visible)) {
- preemptInputDispatchInnerLocked();
- }
-
#if DEBUG_FOCUS
logDispatchStateLocked();
#endif
@@ -2359,43 +2305,20 @@
}
}
-void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_FOCUS
- LOGD("preemptInputDispatch");
-#endif
-
- bool preemptedOne;
- { // acquire lock
- AutoMutex _l(mLock);
- preemptedOne = preemptInputDispatchInnerLocked();
- } // release lock
-
- if (preemptedOne) {
- // Wake up the poll loop so it can get a head start dispatching the next event.
- mLooper->wake();
- }
-}
-
-bool InputDispatcher::preemptInputDispatchInnerLocked() {
- bool preemptedOne = false;
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections[i];
- if (connection->hasPendingSyncTarget()) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
- connection->getInputChannelName());
-#endif
- connection->preemptSyncTarget();
- preemptedOne = true;
- }
- }
- return preemptedOne;
-}
-
void InputDispatcher::logDispatchStateLocked() {
String8 dump;
dumpDispatchStateLocked(dump);
- LOGD("%s", dump.string());
+
+ char* text = dump.lockBuffer(dump.size());
+ char* start = text;
+ while (*start != '\0') {
+ char* end = strchr(start, '\n');
+ if (*end == '\n') {
+ *(end++) = '\0';
+ }
+ LOGD("%s", start);
+ start = end;
+ }
}
void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
@@ -2409,28 +2332,30 @@
} else {
dump.append(" focusedApplication: <null>\n");
}
- dump.appendFormat(" focusedWindow: '%s'\n",
- mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
- dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n",
- mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ dump.appendFormat(" focusedWindow: name='%s'\n",
+ mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
+ dump.appendFormat(" touchedWindow: name='%s', touchDown=%d\n",
+ mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
mTouchDown);
for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
- dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n",
- i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+ dump.appendFormat(" touchedWallpaperWindows[%d]: name='%s'\n",
+ i, mTouchedWallpaperWindows[i]->name.string());
}
for (size_t i = 0; i < mWindows.size(); i++) {
- dump.appendFormat(" windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
- "visible=%s, flags=0x%08x, type=0x%08x, "
+ dump.appendFormat(" windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
"frame=[%d,%d][%d,%d], "
"visibleFrame=[%d,%d][%d,%d], "
"touchableArea=[%d,%d][%d,%d], "
"ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
- i, mWindows[i].inputChannel->getName().string(),
+ i, mWindows[i].name.string(),
toString(mWindows[i].paused),
toString(mWindows[i].hasFocus),
toString(mWindows[i].hasWallpaper),
toString(mWindows[i].visible),
+ toString(mWindows[i].canReceiveKeys),
mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ mWindows[i].layer,
mWindows[i].frameLeft, mWindows[i].frameTop,
mWindows[i].frameRight, mWindows[i].frameBottom,
mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
@@ -2447,12 +2372,14 @@
i, channel->getName().string());
}
+ dump.appendFormat(" inboundQueue: length=%u", mInboundQueue.count());
+
for (size_t i = 0; i < mActiveConnections.size(); i++) {
const Connection* connection = mActiveConnections[i];
- dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+ dump.appendFormat(" activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
"inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
i, connection->getInputChannelName(), connection->getStatusLabel(),
- toString(connection->hasPendingSyncTarget()),
+ connection->outboundQueue.count(),
toString(connection->inputState.isNeutral()),
toString(connection->inputState.isOutOfSync()));
}
@@ -2474,7 +2401,7 @@
{ // acquire lock
AutoMutex _l(mLock);
- if (getConnectionIndex(inputChannel) >= 0) {
+ if (getConnectionIndexLocked(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
@@ -2510,7 +2437,7 @@
{ // acquire lock
AutoMutex _l(mLock);
- ssize_t connectionIndex = getConnectionIndex(inputChannel);
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
@@ -2543,7 +2470,7 @@
return OK;
}
-ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
@@ -2578,31 +2505,7 @@
}
void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
- if (recoveredFromANR) {
- LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, "
- "%01.1fms since dispatch, %01.1fms since ANR",
- connection->getInputChannelName(),
- connection->getEventLatencyMillis(currentTime),
- connection->getDispatchLatencyMillis(currentTime),
- connection->getANRLatencyMillis(currentTime));
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
- commandEntry->connection = connection;
- }
-}
-
-void InputDispatcher::onDispatchCycleANRLocked(
nsecs_t currentTime, const sp<Connection>& connection) {
- LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch",
- connection->getInputChannelName(),
- connection->getEventLatencyMillis(currentTime),
- connection->getDispatchLatencyMillis(currentTime));
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
- commandEntry->connection = connection;
}
void InputDispatcher::onDispatchCycleBrokenLocked(
@@ -2615,6 +2518,25 @@
commandEntry->connection = connection;
}
+void InputDispatcher::onANRLocked(
+ nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+ nsecs_t eventTime, nsecs_t waitStartTime) {
+ LOGI("Application is not responding: %s. "
+ "%01.1fms since event, %01.1fms since wait started",
+ getApplicationWindowLabelLocked(application, window).string(),
+ (currentTime - eventTime) / 1000000.0,
+ (currentTime - waitStartTime) / 1000000.0);
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyANRLockedInterruptible);
+ if (application) {
+ commandEntry->inputApplicationHandle = application->handle;
+ }
+ if (window) {
+ commandEntry->inputChannel = window->inputChannel;
+ }
+}
+
void InputDispatcher::doNotifyConfigurationChangedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
@@ -2637,33 +2559,16 @@
}
}
-void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
+ mLock.unlock();
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
+ nsecs_t newTimeout = mPolicy->notifyANR(
+ commandEntry->inputApplicationHandle, commandEntry->inputChannel);
- nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
+ mLock.lock();
- mLock.lock();
-
- nsecs_t currentTime = now();
- resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
- }
-}
-
-void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
- CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
-
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
-
- mLock.lock();
- }
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -2695,22 +2600,9 @@
mLock.lock();
}
-void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
- CommandEntry* commandEntry) {
- mLock.unlock();
-
- nsecs_t newTimeout;
- if (commandEntry->inputChannel.get()) {
- newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
- } else if (commandEntry->inputApplicationHandle.get()) {
- newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
- } else {
- newTimeout = 0;
- }
-
- mLock.lock();
-
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+ // TODO Write some statistics about how long we spend waiting.
}
void InputDispatcher::dump(String8& dump) {
@@ -2718,6 +2610,18 @@
}
+// --- InputDispatcher::Queue ---
+
+template <typename T>
+uint32_t InputDispatcher::Queue<T>::count() const {
+ uint32_t result = 0;
+ for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) {
+ result += 1;
+ }
+ return result;
+}
+
+
// --- InputDispatcher::Allocator ---
InputDispatcher::Allocator::Allocator() {
@@ -2733,7 +2637,7 @@
entry->injectionIsAsync = false;
entry->injectorPid = -1;
entry->injectorUid = -1;
- entry->pendingSyncDispatches = 0;
+ entry->pendingForegroundDispatches = 0;
}
InputDispatcher::ConfigurationChangedEntry*
@@ -2797,14 +2701,13 @@
InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
+ int32_t targetFlags, float xOffset, float yOffset) {
DispatchEntry* entry = mDispatchEntryPool.alloc();
entry->eventEntry = eventEntry;
eventEntry->refCount += 1;
entry->targetFlags = targetFlags;
entry->xOffset = xOffset;
entry->yOffset = yOffset;
- entry->timeout = timeout;
entry->inProgress = false;
entry->headMotionSample = NULL;
entry->tailMotionSample = NULL;
@@ -2896,7 +2799,7 @@
void InputDispatcher::EventEntry::recycle() {
injectionResult = INPUT_EVENT_INJECTION_PENDING;
dispatchInProgress = false;
- pendingSyncDispatches = 0;
+ pendingForegroundDispatches = 0;
}
@@ -3106,9 +3009,7 @@
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
- nextTimeoutTime(LONG_LONG_MAX),
- lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
- lastANRTime(LONG_LONG_MAX) {
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}
InputDispatcher::Connection::~Connection() {
@@ -3118,18 +3019,6 @@
return inputPublisher.initialize();
}
-void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
- nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
-}
-
-void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
- if (outboundQueue.isEmpty()) {
- nextTimeoutTime = LONG_LONG_MAX;
- } else {
- setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
- }
-}
-
const char* InputDispatcher::Connection::getStatusLabel() const {
switch (status) {
case STATUS_NORMAL:
@@ -3138,9 +3027,6 @@
case STATUS_BROKEN:
return "BROKEN";
- case STATUS_NOT_RESPONDING:
- return "NOT_RESPONDING";
-
case STATUS_ZOMBIE:
return "ZOMBIE";
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 88084c0..783cbc4 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -325,9 +325,6 @@
if (classes & INPUT_DEVICE_CLASS_DPAD) {
keyboardSources |= AINPUT_SOURCE_DPAD;
}
- if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
- keyboardSources |= AINPUT_SOURCE_GAMEPAD;
- }
if (keyboardSources != 0) {
device->addMapper(new KeyboardInputMapper(device,
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 4c402dc..2c6346e 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -131,7 +131,10 @@
}
status_t InputChannel::sendSignal(char signal) {
- ssize_t nWrite = ::write(mSendPipeFd, & signal, 1);
+ ssize_t nWrite;
+ do {
+ nWrite = ::write(mSendPipeFd, & signal, 1);
+ } while (nWrite == -1 && errno == EINTR);
if (nWrite == 1) {
#if DEBUG_CHANNEL_SIGNALS
@@ -147,7 +150,11 @@
}
status_t InputChannel::receiveSignal(char* outSignal) {
- ssize_t nRead = ::read(mReceivePipeFd, outSignal, 1);
+ ssize_t nRead;
+ do {
+ nRead = ::read(mReceivePipeFd, outSignal, 1);
+ } while (nRead == -1 && errno == EINTR);
+
if (nRead == 1) {
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index fd287da..4aa50d6 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -162,9 +162,11 @@
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (eventCount < 0) {
- if (errno != EINTR) {
- LOGW("Poll failed with an unexpected error, errno=%d", errno);
+ if (errno == EINTR) {
+ return ALOOPER_POLL_WAKE;
}
+
+ LOGW("Poll failed with an unexpected error, errno=%d", errno);
return ALOOPER_POLL_ERROR;
}
@@ -196,7 +198,7 @@
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while (nRead == sizeof(buffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
@@ -272,7 +274,11 @@
LOGD("%p ~ wake", this);
#endif
- ssize_t nWrite = write(mWakeWritePipeFd, "W", 1);
+ ssize_t nWrite;
+ do {
+ nWrite = write(mWakeWritePipeFd, "W", 1);
+ } while (nWrite == -1 && errno == EINTR);
+
if (nWrite != 1) {
if (errno != EAGAIN) {
LOGW("Could not write wake signal, errno=%d", errno);
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index daa976f..3e9429d 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -199,6 +199,10 @@
* E.g. if the device supports 480p, 720p, and 1080p, then low is 480p and high is
* 1080p.
*
+ * The same is true for time lapse quality levels, i.e. QUALITY_TIME_LAPSE_LOW,
+ * QUALITY_TIME_LAPSE_HIGH are guaranteed to be supported and have to match one of
+ * qcif, cif, 480p, 720p, or 1080p.
+ *
* A camcorder recording session with higher quality level usually has higher output
* bit rate, better video and/or audio recording quality, larger video frame
* resolution and higher audio sampling rate, etc, than those with lower quality
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
new file mode 100755
index 0000000..9d40a78
--- /dev/null
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -0,0 +1,326 @@
+/*
+ * 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.videoeditor;
+
+import java.io.IOException;
+
+/**
+ * This class allows to handle an audio track. This audio file is mixed with the
+ * audio samples of the MediaItems.
+ * {@hide}
+ */
+public class AudioTrack {
+ // Instance variables
+ private final String mUniqueId;
+ private final String mFilename;
+ private final long mDurationMs;
+ private long mStartTimeMs;
+ private long mTimelineDurationMs;
+ private int mVolumePercent;
+ private long mBeginBoundaryTimeMs;
+ private long mEndBoundaryTimeMs;
+ private boolean mLoop;
+
+ // Ducking variables
+ private int mDuckingThreshold;
+ private int mDuckingLowVolume;
+ private boolean mIsDuckingEnabled;
+
+ // The audio waveform filename
+ private String mAudioWaveformFilename;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private AudioTrack() throws IOException {
+ this(null, null);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param audioTrackId The AudioTrack id
+ * @param filename The absolute file name
+ *
+ * @throws IOException if file is not found
+ * @throws IllegalArgumentException if file format is not supported or if
+ * the codec is not supported
+ */
+ public AudioTrack(String audioTrackId, String filename) throws IOException {
+ mUniqueId = audioTrackId;
+ mFilename = filename;
+ mStartTimeMs = 0;
+ // TODO: This value represents to the duration of the audio file
+ mDurationMs = 300000;
+ mTimelineDurationMs = mDurationMs;
+ mVolumePercent = 100;
+
+ // Play the entire audio track
+ mBeginBoundaryTimeMs = 0;
+ mEndBoundaryTimeMs = mDurationMs;
+
+ // By default loop is disabled
+ mLoop = false;
+
+ // Ducking is enabled by default
+ mDuckingThreshold = 0;
+ mDuckingLowVolume = 0;
+ mIsDuckingEnabled = true;
+
+ // The audio waveform file is generated later
+ mAudioWaveformFilename = null;
+ }
+
+ /**
+ * @return The id of the media item
+ */
+ public String getId() {
+ return mUniqueId;
+ }
+
+ /**
+ * Get the filename source for this audio track.
+ *
+ * @return The filename as an absolute file name
+ */
+ public String getFilename() {
+ return mFilename;
+ }
+
+ /**
+ * Set the volume of this audio track as percentage of the volume in the
+ * original audio source file.
+ *
+ * @param volumePercent Percentage of the volume to apply. If it is set to
+ * 0, then volume becomes mute. It it is set to 100, then volume
+ * is same as original volume. It it is set to 200, then volume
+ * is doubled (provided that volume amplification is supported)
+ *
+ * @throws UnsupportedOperationException if volume amplification is requested
+ * and is not supported.
+ */
+ public void setVolume(int volumePercent) {
+ mVolumePercent = volumePercent;
+ }
+
+ /**
+ * Get the volume of the audio track as percentage of the volume in the
+ * original audio source file.
+ *
+ * @return The volume in percentage
+ */
+ public int getVolume() {
+ return mVolumePercent;
+ }
+
+ /**
+ * Set the start time of this audio track relative to the storyboard
+ * timeline. Default value is 0.
+ *
+ * @param startTimeMs the start time in milliseconds
+ */
+ public void setStartTime(long startTimeMs) {
+ mStartTimeMs = startTimeMs;
+ }
+
+ /**
+ * Get the start time of this audio track relative to the storyboard
+ * timeline.
+ *
+ * @return The start time in milliseconds
+ */
+ public long getStartTime() {
+ return mStartTimeMs;
+ }
+
+ /**
+ * @return The duration in milliseconds. This value represents the audio
+ * track duration (not looped)
+ */
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /**
+ * @return The timeline duration. If looping is enabled this value
+ * represents the duration of the looped audio track, otherwise it
+ * is the duration of the audio track (mDurationMs).
+ */
+ public long getTimelineDuration() {
+ return mTimelineDurationMs;
+ }
+
+ /**
+ * Sets the start and end marks for trimming an audio track
+ *
+ * @param beginMs start time in the audio track in milliseconds (relative to
+ * the beginning of the audio track)
+ * @param endMs end time in the audio track in milliseconds (relative to the
+ * beginning of the audio track)
+ */
+ public void setExtractBoundaries(long beginMs, long endMs) {
+ if (beginMs > mDurationMs) {
+ throw new IllegalArgumentException("Invalid start time");
+ }
+ if (endMs > mDurationMs) {
+ throw new IllegalArgumentException("Invalid end time");
+ }
+
+ mBeginBoundaryTimeMs = beginMs;
+ mEndBoundaryTimeMs = endMs;
+ if (mLoop) {
+ // TODO: Compute mDurationMs (from the beginning of the loop until
+ // the end of all the loops.
+ mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
+ } else {
+ mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
+ }
+ }
+
+ /**
+ * @return The boundary begin time
+ */
+ public long getBoundaryBeginTime() {
+ return mBeginBoundaryTimeMs;
+ }
+
+ /**
+ * @return The boundary end time
+ */
+ public long getBoundaryEndTime() {
+ return mEndBoundaryTimeMs;
+ }
+
+ /**
+ * Enable the loop mode for this audio track. Note that only one of the
+ * audio tracks in the timeline can have the loop mode enabled. When
+ * looping is enabled the samples between mBeginBoundaryTimeMs and
+ * mEndBoundaryTimeMs are looped.
+ */
+ public void enableLoop() {
+ mLoop = true;
+ }
+
+ /**
+ * Disable the loop mode
+ */
+ public void disableLoop() {
+ mLoop = false;
+ }
+
+ /**
+ * @return true if looping is enabled
+ */
+ public boolean isLooping() {
+ return mLoop;
+ }
+
+ /**
+ * Disable the audio duck effect
+ */
+ public void disableDucking() {
+ mIsDuckingEnabled = false;
+ }
+
+ /**
+ * TODO DEFINE
+ *
+ * @param threshold
+ * @param lowVolume
+ * @param volume
+ */
+ public void enableDucking(int threshold, int lowVolume, int volume) {
+ mDuckingThreshold = threshold;
+ mDuckingLowVolume = lowVolume;
+ mIsDuckingEnabled = true;
+ }
+
+ /**
+ * @return true if ducking is enabled
+ */
+ public boolean isDuckingEnabled() {
+ return mIsDuckingEnabled;
+ }
+
+ /**
+ * @return The ducking threshold
+ */
+ public int getDuckingThreshhold() {
+ return mDuckingThreshold;
+ }
+
+ /**
+ * @return The ducking low level
+ */
+ public int getDuckingLowVolume() {
+ return mDuckingLowVolume;
+ }
+
+ /**
+ * This API allows to generate a file containing the sample volume levels of
+ * this audio track object. This function may take significant time and is
+ * blocking. The filename can be retrieved using getAudioWaveformFilename().
+ *
+ * @param listener The progress listener
+ *
+ * @throws IOException if the output file cannot be created
+ * @throws IllegalArgumentException if the audio file does not have a valid
+ * audio track
+ */
+ public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
+ throws IOException {
+ // TODO: Set mAudioWaveformFilename at the end once the extract is complete
+ }
+
+ /**
+ * Get the audio waveform file name if extractAudioWaveform was successful.
+ * The file format is as following:
+ * <ul>
+ * <li>first 4 bytes provide the number of samples for each value, as
+ * big-endian signed</li>
+ * <li>4 following bytes is the total number of values in the file, as
+ * big-endian signed</li>
+ * <li>then, all values follow as bytes</li>
+ * </ul>
+ *
+ * @return the name of the file, null if the file does not exist
+ */
+ public String getAudioWaveformFilename() {
+ return mAudioWaveformFilename;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof AudioTrack)) {
+ return false;
+ }
+ return mUniqueId.equals(((AudioTrack)object).mUniqueId);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mUniqueId.hashCode();
+ }
+}
diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java
new file mode 100755
index 0000000..177b863
--- /dev/null
+++ b/media/java/android/media/videoeditor/Effect.java
@@ -0,0 +1,119 @@
+/*
+ * 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.videoeditor;
+
+/**
+ * This is the super class for all effects. An effect can only be applied to a
+ * single media item. If one wants to apply the same effect to multiple media
+ * items, multiple @{MediaItem.addEffect(Effect)} call must be invoked on each
+ * of the MediaItem objects.
+ * {@hide}
+ */
+public abstract class Effect {
+ // Instance variables
+ private final String mUniqueId;
+ protected long mDurationMs;
+ // The start time of the effect relative to the media item timeline
+ protected long mStartTimeMs;
+
+ /**
+ * Default constructor
+ */
+ @SuppressWarnings("unused")
+ private Effect() {
+ mUniqueId = null;
+ mStartTimeMs = 0;
+ mDurationMs = 0;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param effectId The effect id
+ * @param startTimeMs The start time relative to the media item to which it
+ * is applied
+ * @param durationMs The effect duration in milliseconds
+ */
+ public Effect(String effectId, long startTimeMs, long durationMs) {
+ mUniqueId = effectId;
+ mStartTimeMs = startTimeMs;
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * @return The id of the effect
+ */
+ public String getId() {
+ return mUniqueId;
+ }
+
+ /**
+ * Set the duration of the effect. If a preview or export is in progress,
+ * then this change is effective for next preview or export session. s
+ *
+ * @param durationMs of the effect in milliseconds
+ */
+ public void setDuration(long durationMs) {
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * Get the duration of the effect
+ *
+ * @return The duration of the effect in milliseconds
+ */
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /**
+ * Set start time of the effect. If a preview or export is in progress, then
+ * this change is effective for next preview or export session.
+ *
+ * @param startTimeMs The start time of the effect relative to the begining
+ * of the media item in milliseconds
+ */
+ public void setStartTime(long startTimeMs) {
+ mStartTimeMs = startTimeMs;
+ }
+
+ /**
+ * @return The start time in milliseconds
+ */
+ public long getStartTime() {
+ return mStartTimeMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Effect)) {
+ return false;
+ }
+ return mUniqueId.equals(((Effect)object).mUniqueId);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mUniqueId.hashCode();
+ }
+}
diff --git a/media/java/android/media/videoeditor/EffectColor.java b/media/java/android/media/videoeditor/EffectColor.java
new file mode 100755
index 0000000..7c61627
--- /dev/null
+++ b/media/java/android/media/videoeditor/EffectColor.java
@@ -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.
+ */
+
+package android.media.videoeditor;
+
+/**
+ * This class allows to apply color on a media item.
+ * {@hide}
+ */
+public class EffectColor extends Effect {
+
+ /**
+ * Change the video frame color to the RGB color value provided
+ */
+ public static final int TYPE_COLOR = 1; // color as 888 RGB
+ /**
+ * Change the video frame color to a gradation from RGB color (at the top of
+ * the frame) to black (at the bottom of the frame).
+ */
+ public static final int TYPE_GRADIENT = 2;
+ /**
+ * Change the video frame color to sepia
+ */
+ public static final int TYPE_SEPIA = 3;
+ /**
+ * Invert the video frame color
+ */
+ public static final int TYPE_NEGATIVE = 4;
+ /**
+ * Make the video look like as if it was recorded in 50's
+ */
+ public static final int TYPE_FIFTIES = 5;
+
+ // Colors for the color effect
+ public static final int GREEN = 0x0000ff00;
+ public static final int PINK = 0x00ff66cc;
+ public static final int GRAY = 0x007f7f7f;
+
+ // The effect type
+ private final int mType;
+
+ // The effect parameter
+ private final int mParam;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private EffectColor() {
+ this(null, 0, 0, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param effectId The effect id
+ * @param startTimeMs The start time relative to the media item to which it
+ * is applied
+ * @param durationMs The duration of this effect in milliseconds
+ * @param type type of the effect. type is one of: TYPE_COLOR,
+ * TYPE_GRADIENT, TYPE_SEPIA, TYPE_NEGATIVE, TYPE_FIFTIES. If
+ * type is not supported, the argument is ignored
+ * @param param if type is TYPE_COLOR, param is the RGB color as 888.
+ * Otherwise, param is ignored
+ */
+ public EffectColor(String effectId, long startTimeMs, long durationMs,
+ int type, int param) {
+ super(effectId, startTimeMs, durationMs);
+ mType = type;
+ mParam = param;
+ }
+
+ /**
+ * @return The type of this effect
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * @return the color as RGB 888 if type is TYPE_COLOR. Otherwise, ignore.
+ */
+ public int getParam() {
+ return mParam;
+ }
+}
diff --git a/media/java/android/media/videoeditor/EffectKenBurns.java b/media/java/android/media/videoeditor/EffectKenBurns.java
new file mode 100755
index 0000000..c6d22f4
--- /dev/null
+++ b/media/java/android/media/videoeditor/EffectKenBurns.java
@@ -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.
+ */
+
+package android.media.videoeditor;
+
+import java.io.IOException;
+
+import android.graphics.Rect;
+
+/**
+ * This class represents a Ken Burns effect.
+ * {@hide}
+ */
+public class EffectKenBurns extends Effect {
+ // Instance variables
+ private Rect mStartRect;
+ private Rect mEndRect;
+
+ /**
+ * Objects of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private EffectKenBurns() throws IOException {
+ this(null, null, null, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param effectId The effect id
+ * @param startRect The start rectangle
+ * @param endRect The end rectangle
+ * @param startTimeMs The start time
+ * @param durationMs The duration of the Ken Burns effect in milliseconds
+ */
+ public EffectKenBurns(String effectId, Rect startRect, Rect endRect, long startTime,
+ long durationMs)
+ throws IOException {
+ super(effectId, startTime, durationMs);
+
+ mStartRect = startRect;
+ mEndRect = endRect;
+ }
+
+ /**
+ * @param startRect The start rectangle
+ *
+ * @throws IllegalArgumentException if start rectangle is incorrectly set.
+ */
+ public void setStartRect(Rect startRect) {
+ mStartRect = startRect;
+ }
+
+ /**
+ * @return The start rectangle
+ */
+ public Rect getStartRect() {
+ return mStartRect;
+ }
+
+ /**
+ * @param endRect The end rectangle
+ *
+ * @throws IllegalArgumentException if end rectangle is incorrectly set.
+ */
+ public void setEndRect(Rect endRect) {
+ mEndRect = endRect;
+ }
+
+ /**
+ * @return The end rectangle
+ */
+ public Rect getEndRect() {
+ return mEndRect;
+ }
+}
diff --git a/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java b/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java
new file mode 100644
index 0000000..1cce148
--- /dev/null
+++ b/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+/**
+ * This listener interface is used by
+ * {@link MediaVideoItem#extractAudioWaveform(ExtractAudioWaveformProgressListener listener)}
+ * or
+ * {@link AudioTrack#extractAudioWaveform(ExtractAudioWaveformProgressListener listener)}
+ * {@hide}
+ */
+public interface ExtractAudioWaveformProgressListener {
+ /**
+ * This method notifies the listener of the progress status of
+ * an extractAudioWaveform operation.
+ * This method may be called maximum 100 times for one operation.
+ *
+ * @param progress The progress in %. At the beginning of the operation,
+ * this value is set to 0; at the end, the value is set to 100.
+ */
+ public void onProgress(int progress);
+}
+
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
new file mode 100755
index 0000000..db7585a
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -0,0 +1,227 @@
+/*
+ * 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.videoeditor;
+
+import java.io.IOException;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.Log;
+
+/**
+ * This class represents an image item on the storyboard.
+ * {@hide}
+ */
+public class MediaImageItem extends MediaItem {
+ // Logging
+ private static final String TAG = "MediaImageItem";
+
+ // The resize paint
+ private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+ // Instance variables
+ private final int mWidth;
+ private final int mHeight;
+ private final int mAspectRatio;
+ private long mDurationMs;
+
+ /**
+ * This class cannot be instantiated by using the default constructor
+ */
+ @SuppressWarnings("unused")
+ private MediaImageItem() throws IOException {
+ this(null, null, 0, RENDERING_MODE_BLACK_BORDER);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mediaItemId The MediaItem id
+ * @param filename The image file name
+ * @param durationMs The duration of the image on the storyboard
+ * @param renderingMode The rendering mode
+ *
+ * @throws IOException
+ */
+ public MediaImageItem(String mediaItemId, String filename, long durationMs, int renderingMode)
+ throws IOException {
+ super(mediaItemId, filename, renderingMode);
+
+ // Determine the size of the image
+ final BitmapFactory.Options dbo = new BitmapFactory.Options();
+ dbo.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filename, dbo);
+
+ mWidth = dbo.outWidth;
+ mHeight = dbo.outHeight;
+ mDurationMs = durationMs;
+
+ // TODO: Determine the aspect ratio from the width and height
+ mAspectRatio = MediaProperties.ASPECT_RATIO_4_3;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getFileType() {
+ if (mFilename.endsWith(".jpg") || mFilename.endsWith(".jpeg")) {
+ return MediaProperties.FILE_JPEG;
+ } else if (mFilename.endsWith(".png")) {
+ return MediaProperties.FILE_PNG;
+ } else {
+ return MediaProperties.FILE_UNSUPPORTED;
+ }
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getAspectRatio() {
+ return mAspectRatio;
+ }
+
+ /**
+ * @param durationMs The duration of the image in the storyboard timeline
+ */
+ public void setDuration(long durationMs) {
+ mDurationMs = durationMs;
+ // TODO: Validate/modify the start and the end time of effects and overlays
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public long getTimelineDuration() {
+ return mDurationMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException {
+ return generateImageThumbnail(mFilename, width, height);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,
+ int thumbnailCount) throws IOException {
+ final Bitmap thumbnail = generateImageThumbnail(mFilename, width, height);
+ final Bitmap[] thumbnailArray = new Bitmap[thumbnailCount];
+ for (int i = 0; i < thumbnailCount; i++) {
+ thumbnailArray[i] = thumbnail;
+ }
+ return thumbnailArray;
+ }
+
+ /**
+ * Resize a bitmap within an input stream
+ *
+ * @param filename The filename
+ * @param width The thumbnail width
+ * @param height The thumbnail height
+ *
+ * @return The resized bitmap
+ */
+ private Bitmap generateImageThumbnail(String filename, int width, int height)
+ throws IOException {
+ final BitmapFactory.Options dbo = new BitmapFactory.Options();
+ dbo.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filename, dbo);
+
+ final int nativeWidth = dbo.outWidth;
+ final int nativeHeight = dbo.outHeight;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "generateThumbnail: Input: " + nativeWidth + "x" + nativeHeight
+ + ", resize to: " + width + "x" + height);
+ }
+
+ final Bitmap srcBitmap;
+ float bitmapWidth, bitmapHeight;
+ if (nativeWidth > width || nativeHeight > height) {
+ float dx = ((float)nativeWidth) / ((float)width);
+ float dy = ((float)nativeHeight) / ((float)height);
+ if (dx > dy) {
+ bitmapWidth = width;
+ bitmapHeight = nativeHeight / dx;
+ } else {
+ bitmapWidth = nativeWidth / dy;
+ bitmapHeight = height;
+ }
+ // Create the bitmap from file
+ if (nativeWidth / bitmapWidth > 1) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = nativeWidth / (int)bitmapWidth;
+ srcBitmap = BitmapFactory.decodeFile(filename, options);
+ } else {
+ srcBitmap = BitmapFactory.decodeFile(filename);
+ }
+ } else {
+ bitmapWidth = width;
+ bitmapHeight = height;
+ srcBitmap = BitmapFactory.decodeFile(filename);
+ }
+
+ if (srcBitmap == null) {
+ Log.e(TAG, "generateThumbnail: Cannot decode image bytes");
+ throw new IOException("Cannot decode file: " + mFilename);
+ }
+
+ // Create the canvas bitmap
+ final Bitmap bitmap = Bitmap.createBitmap((int)bitmapWidth, (int)bitmapHeight,
+ Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+ canvas.drawBitmap(srcBitmap, new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),
+ new Rect(0, 0, (int)bitmapWidth, (int)bitmapHeight), sResizePaint);
+ // Release the source bitmap
+ srcBitmap.recycle();
+ return bitmap;
+ }
+}
diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
new file mode 100755
index 0000000..9e32744
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -0,0 +1,434 @@
+/*
+ * 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.videoeditor;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.graphics.Bitmap;
+
+/**
+ * This abstract class describes the base class for any MediaItem. Objects are
+ * defined with a file path as a source data.
+ * {@hide}
+s */
+public abstract class MediaItem {
+ // A constant which can be used to specify the end of the file (instead of
+ // providing the actual duration of the media item).
+ public final static int END_OF_FILE = -1;
+
+ // Rendering modes
+ /**
+ * When using the RENDERING_MODE_BLACK_BORDER rendering mode video frames
+ * are resized by preserving the aspect ratio until the movie matches one of
+ * the dimensions of the output movie. The areas outside the resized video
+ * clip are rendered black.
+ */
+ public static final int RENDERING_MODE_BLACK_BORDER = 0;
+ /**
+ * When using the RENDERING_MODE_STRETCH rendering mode video frames are
+ * stretched horizontally or vertically to match the current aspect ratio of
+ * the movie.
+ */
+ public static final int RENDERING_MODE_STRETCH = 1;
+
+
+ // The unique id of the MediaItem
+ private final String mUniqueId;
+
+ // The name of the file associated with the MediaItem
+ protected final String mFilename;
+
+ // List of effects
+ private final List<Effect> mEffects;
+
+ // List of overlays
+ private final List<Overlay> mOverlays;
+
+ // The rendering mode
+ private int mRenderingMode;
+
+ // Beginning and end transitions
+ private Transition mBeginTransition;
+ private Transition mEndTransition;
+
+ /**
+ * Constructor
+ *
+ * @param mediaItemId The MediaItem id
+ * @param filename name of the media file.
+ * @param renderingMode The rendering mode
+ *
+ * @throws IOException if file is not found
+ * @throws IllegalArgumentException if a capability such as file format is not
+ * supported the exception object contains the unsupported
+ * capability
+ */
+ protected MediaItem(String mediaItemId, String filename, int renderingMode) throws IOException {
+ mUniqueId = mediaItemId;
+ mFilename = filename;
+ mRenderingMode = renderingMode;
+ mEffects = new ArrayList<Effect>();
+ mOverlays = new ArrayList<Overlay>();
+ mBeginTransition = null;
+ mEndTransition = null;
+ }
+
+ /**
+ * @return The of the media item
+ */
+ public String getId() {
+ return mUniqueId;
+ }
+
+ /**
+ * @return The media source file name
+ */
+ public String getFilename() {
+ return mFilename;
+ }
+
+ /**
+ * If aspect ratio of the MediaItem is different from the aspect ratio of
+ * the editor then this API controls the rendering mode.
+ *
+ * @param renderingMode rendering mode. It is one of:
+ * {@link #RENDERING_MODE_BLACK_BORDER},
+ * {@link #RENDERING_MODE_STRETCH}
+ */
+ public void setRenderingMode(int renderingMode) {
+ mRenderingMode = renderingMode;
+ }
+
+ /**
+ * @return The rendering mode
+ */
+ public int getRenderingMode() {
+ return mRenderingMode;
+ }
+
+ /**
+ * @param transition The beginning transition
+ */
+ void setBeginTransition(Transition transition) {
+ mBeginTransition = transition;
+ }
+
+ /**
+ * @return The begin transition
+ */
+ public Transition getBeginTransition() {
+ return mBeginTransition;
+ }
+
+ /**
+ * @param transition The end transition
+ */
+ void setEndTransition(Transition transition) {
+ mEndTransition = transition;
+ }
+
+ /**
+ * @return The end transition
+ */
+ public Transition getEndTransition() {
+ return mEndTransition;
+ }
+
+ /**
+ * @return The duration of the media item
+ */
+ public abstract long getDuration();
+
+ /**
+ * @return The timeline duration. This is the actual duration in the
+ * timeline (trimmed duration)
+ */
+ public abstract long getTimelineDuration();
+
+ /**
+ * @return The source file type
+ */
+ public abstract int getFileType();
+
+ /**
+ * @return Get the native width of the media item
+ */
+ public abstract int getWidth();
+
+ /**
+ * @return Get the native height of the media item
+ */
+ public abstract int getHeight();
+
+ /**
+ * Get aspect ratio of the source media item.
+ *
+ * @return the aspect ratio as described in MediaProperties.
+ * MediaProperties.ASPECT_RATIO_UNDEFINED if aspect ratio is not
+ * supported as in MediaProperties
+ */
+ public abstract int getAspectRatio();
+
+ /**
+ * Add the specified effect to this media item.
+ *
+ * Note that certain types of effects cannot be applied to video and to
+ * image media items. For example in certain implementation a Ken Burns
+ * implementation cannot be applied to video media item.
+ *
+ * This method invalidates transition video clips if the
+ * effect overlaps with the beginning and/or the end transition.
+ *
+ * @param effect The effect to apply
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if the effect start and/or duration are
+ * invalid or if the effect cannot be applied to this type of media
+ * item or if the effect id is not unique across all the Effects
+ * added.
+ */
+ public void addEffect(Effect effect) {
+ if (mEffects.contains(effect)) {
+ throw new IllegalArgumentException("Effect already exists: " + effect.getId());
+ }
+
+ if (effect.getStartTime() + effect.getDuration() > getDuration()) {
+ throw new IllegalArgumentException(
+ "Effect start time + effect duration > media clip duration");
+ }
+
+ mEffects.add(effect);
+ invalidateTransitions(effect);
+ }
+
+ /**
+ * Remove the effect with the specified id.
+ *
+ * This method invalidates a transition video clip if the effect overlaps
+ * with a transition.
+ *
+ * @param effectId The id of the effect to be removed
+ *
+ * @return The effect that was removed
+ * @throws IllegalStateException if a preview or an export is in progress
+ */
+ public Effect removeEffect(String effectId) {
+ for (Effect effect : mEffects) {
+ if (effect.getId().equals(effectId)) {
+ mEffects.remove(effect);
+ invalidateTransitions(effect);
+ return effect;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Find the effect with the specified id
+ *
+ * @param effectId The effect id
+ *
+ * @return The effect with the specified id (null if it does not exist)
+ */
+ public Effect getEffect(String effectId) {
+ for (Effect effect : mEffects) {
+ if (effect.getId().equals(effectId)) {
+ return effect;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the list of effects.
+ *
+ * @return the effects list. If no effects exist an empty list will be returned.
+ */
+ public List<Effect> getAllEffects() {
+ return mEffects;
+ }
+
+ /**
+ * Add an overlay to the storyboard. This method invalidates a transition
+ * video clip if the overlay overlaps with a transition.
+ *
+ * @param overlay The overlay to add
+ * @throws IllegalStateException if a preview or an export is in progress or
+ * if the overlay id is not unique across all the overlays added.
+ */
+ public void addOverlay(Overlay overlay) {
+ if (mOverlays.contains(overlay)) {
+ throw new IllegalArgumentException("Overlay already exists: " + overlay.getId());
+ }
+
+ if (overlay.getStartTime() + overlay.getDuration() > getDuration()) {
+ throw new IllegalArgumentException(
+ "Overlay start time + overlay duration > media clip duration");
+ }
+
+ mOverlays.add(overlay);
+ invalidateTransitions(overlay);
+ }
+
+ /**
+ * Remove the overlay with the specified id.
+ *
+ * This method invalidates a transition video clip if the overlay overlaps
+ * with a transition.
+ *
+ * @param overlayId The id of the overlay to be removed
+ *
+ * @return The overlay that was removed
+ * @throws IllegalStateException if a preview or an export is in progress
+ */
+ public Overlay removeOverlay(String overlayId) {
+ for (Overlay overlay : mOverlays) {
+ if (overlay.getId().equals(overlayId)) {
+ mOverlays.remove(overlay);
+ invalidateTransitions(overlay);
+ return overlay;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Find the overlay with the specified id
+ *
+ * @param overlayId The overlay id
+ *
+ * @return The overlay with the specified id (null if it does not exist)
+ */
+ public Overlay getOverlay(String overlayId) {
+ for (Overlay overlay : mOverlays) {
+ if (overlay.getId().equals(overlayId)) {
+ return overlay;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the list of overlays associated with this media item
+ *
+ * Note that if any overlay source files are not accessible anymore,
+ * this method will still provide the full list of overlays.
+ *
+ * @return The list of overlays. If no overlays exist an empty list will
+ * be returned.
+ */
+ public List<Overlay> getAllOverlays() {
+ return mOverlays;
+ }
+
+ /**
+ * Create a thumbnail at specified time in a video stream in Bitmap format
+ *
+ * @param width width of the thumbnail in pixels
+ * @param height height of the thumbnail in pixels
+ * @param timeMs The time in the source video file at which the thumbnail is
+ * requested (even if trimmed).
+ *
+ * @return The thumbnail as a Bitmap.
+ *
+ * @throws IOException if a file error occurs
+ * @throws IllegalArgumentException if time is out of video duration
+ */
+ public abstract Bitmap getThumbnail(int width, int height, long timeMs) throws IOException;
+
+ /**
+ * Get the array of Bitmap thumbnails between start and end.
+ *
+ * @param width width of the thumbnail in pixels
+ * @param height height of the thumbnail in pixels
+ * @param startMs The start of time range in milliseconds
+ * @param endMs The end of the time range in milliseconds
+ * @param thumbnailCount The thumbnail count
+ *
+ * @return The array of Bitmaps
+ *
+ * @throws IOException if a file error occurs
+ */
+ public abstract Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,
+ int thumbnailCount) throws IOException;
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof MediaItem)) {
+ return false;
+ }
+ return mUniqueId.equals(((MediaItem)object).mUniqueId);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mUniqueId.hashCode();
+ }
+
+ /**
+ * Invalidate the start and end transitions if necessary
+ *
+ * @param effect The effect that was added or removed
+ */
+ private void invalidateTransitions(Effect effect) {
+ // Check if the effect overlaps with the beginning and end transitions
+ if (mBeginTransition != null) {
+ if (effect.getStartTime() < mBeginTransition.getDuration()) {
+ mBeginTransition.invalidate();
+ }
+ }
+
+ if (mEndTransition != null) {
+ if (effect.getStartTime() + effect.getDuration() > getDuration()
+ - mEndTransition.getDuration()) {
+ mEndTransition.invalidate();
+ }
+ }
+ }
+
+ /**
+ * Invalidate the start and end transitions if necessary
+ *
+ * @param overlay The effect that was added or removed
+ */
+ private void invalidateTransitions(Overlay overlay) {
+ // Check if the overlay overlaps with the beginning and end transitions
+ if (mBeginTransition != null) {
+ if (overlay.getStartTime() < mBeginTransition.getDuration()) {
+ mBeginTransition.invalidate();
+ }
+ }
+
+ if (mEndTransition != null) {
+ if (overlay.getStartTime() + overlay.getDuration() > getDuration()
+ - mEndTransition.getDuration()) {
+ mEndTransition.invalidate();
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/videoeditor/MediaProperties.java b/media/java/android/media/videoeditor/MediaProperties.java
new file mode 100755
index 0000000..c3f5ef7
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaProperties.java
@@ -0,0 +1,256 @@
+/*
+ * 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.videoeditor;
+
+import android.util.Pair;
+
+/**
+ * This class defines all properties of a media file such as supported height, aspect ratio,
+ * bitrate for export function.
+ * {@hide}
+ */
+public class MediaProperties {
+ // Supported heights
+ public static final int HEIGHT_144 = 144;
+ public static final int HEIGHT_360 = 360;
+ public static final int HEIGHT_480 = 480;
+ public static final int HEIGHT_720 = 720;
+
+ // Supported aspect ratios
+ public static final int ASPECT_RATIO_UNDEFINED = 0;
+ public static final int ASPECT_RATIO_3_2 = 1;
+ public static final int ASPECT_RATIO_16_9 = 2;
+ public static final int ASPECT_RATIO_4_3 = 3;
+ public static final int ASPECT_RATIO_5_3 = 4;
+ public static final int ASPECT_RATIO_11_9 = 5;
+
+ // The array of supported aspect ratios
+ private static final int[] ASPECT_RATIOS = new int[] {
+ ASPECT_RATIO_3_2,
+ ASPECT_RATIO_16_9,
+ ASPECT_RATIO_4_3,
+ ASPECT_RATIO_5_3,
+ ASPECT_RATIO_11_9
+ };
+
+ // Supported resolutions for specific aspect ratios
+ @SuppressWarnings({"unchecked"})
+ private static final Pair<Integer, Integer>[] ASPECT_RATIO_3_2_RESOLUTIONS =
+ new Pair[] {
+ new Pair<Integer, Integer>(720, HEIGHT_480),
+ new Pair<Integer, Integer>(1080, HEIGHT_720)
+ };
+
+ @SuppressWarnings({"unchecked"})
+ private static final Pair<Integer, Integer>[] ASPECT_RATIO_4_3_RESOLUTIONS =
+ new Pair[] {
+ new Pair<Integer, Integer>(640, HEIGHT_480),
+ new Pair<Integer, Integer>(960, HEIGHT_720)
+ };
+
+ @SuppressWarnings({"unchecked"})
+ private static final Pair<Integer, Integer>[] ASPECT_RATIO_5_3_RESOLUTIONS =
+ new Pair[] {
+ new Pair<Integer, Integer>(800, HEIGHT_480)
+ };
+
+ @SuppressWarnings({"unchecked"})
+ private static final Pair<Integer, Integer>[] ASPECT_RATIO_11_9_RESOLUTIONS =
+ new Pair[] {
+ new Pair<Integer, Integer>(176, HEIGHT_144)
+ };
+
+ @SuppressWarnings({"unchecked"})
+ private static final Pair<Integer, Integer>[] ASPECT_RATIO_16_9_RESOLUTIONS =
+ new Pair[] {
+ new Pair<Integer, Integer>(640, HEIGHT_360),
+ new Pair<Integer, Integer>(854, HEIGHT_480),
+ new Pair<Integer, Integer>(1280, HEIGHT_720),
+ };
+
+
+ // Bitrate values (in bits per second)
+ public static final int BITRATE_28K = 28000;
+ public static final int BITRATE_40K = 40000;
+ public static final int BITRATE_64K = 64000;
+ public static final int BITRATE_96K = 96000;
+ public static final int BITRATE_128K = 128000;
+ public static final int BITRATE_192K = 192000;
+ public static final int BITRATE_256K = 256000;
+ public static final int BITRATE_384K = 384000;
+ public static final int BITRATE_512K = 512000;
+ public static final int BITRATE_800K = 800000;
+
+ // The array of supported bitrates
+ private static final int[] SUPPORTED_BITRATES = new int[] {
+ BITRATE_28K,
+ BITRATE_40K,
+ BITRATE_64K,
+ BITRATE_96K,
+ BITRATE_128K,
+ BITRATE_192K,
+ BITRATE_256K,
+ BITRATE_384K,
+ BITRATE_512K,
+ BITRATE_800K
+ };
+
+ // Video codec types
+ public static final int VCODEC_H264BP = 1;
+ public static final int VCODEC_H264MP = 2;
+ public static final int VCODEC_H263 = 3;
+ public static final int VCODEC_MPEG4 = 4;
+
+ // The array of supported video codecs
+ private static final int[] SUPPORTED_VCODECS = new int[] {
+ VCODEC_H264BP,
+ VCODEC_H263,
+ VCODEC_MPEG4,
+ };
+
+ // Audio codec types
+ public static final int ACODEC_AAC_LC = 1;
+ public static final int ACODEC_AMRNB = 2;
+ public static final int ACODEC_AMRWB = 3;
+ public static final int ACODEC_MP3 = 4;
+ public static final int ACODEC_OGG = 5;
+
+ // The array of supported video codecs
+ private static final int[] SUPPORTED_ACODECS = new int[] {
+ ACODEC_AAC_LC,
+ ACODEC_AMRNB,
+ ACODEC_AMRWB
+ };
+
+ // File format types
+ public static final int FILE_UNSUPPORTED = 0;
+ public static final int FILE_3GP = 1;
+ public static final int FILE_MP4 = 2;
+ public static final int FILE_JPEG = 3;
+ public static final int FILE_PNG = 4;
+
+ // The array of the supported file formats
+ private static final int[] SUPPORTED_VIDEO_FILE_FORMATS = new int[] {
+ FILE_3GP,
+ FILE_MP4
+ };
+
+ // The maximum count of audio tracks supported
+ public static final int AUDIO_MAX_TRACK_COUNT = 1;
+
+ // The maximum volume supported (100 means that no amplification is
+ // supported, i.e. attenuation only)
+ public static final int AUDIO_MAX_VOLUME_PERCENT = 100;
+
+ /**
+ * This class cannot be instantiated
+ */
+ private MediaProperties() {
+ }
+
+ /**
+ * @return The array of supported aspect ratios
+ */
+ public static int[] getAllSupportedAspectRatios() {
+ return ASPECT_RATIOS;
+ }
+
+ /**
+ * Get the supported resolutions for the specified aspect ratio.
+ *
+ * @param aspectRatio The aspect ratio for which the resolutions are requested
+ *
+ * @return The array of width and height pairs
+ */
+ public static Pair<Integer, Integer>[] getSupportedResolutions(int aspectRatio) {
+ final Pair<Integer, Integer>[] resolutions;
+ switch(aspectRatio) {
+ case ASPECT_RATIO_3_2: {
+ resolutions = ASPECT_RATIO_3_2_RESOLUTIONS;
+ break;
+ }
+
+ case ASPECT_RATIO_4_3: {
+ resolutions = ASPECT_RATIO_4_3_RESOLUTIONS;
+ break;
+ }
+
+ case ASPECT_RATIO_5_3: {
+ resolutions = ASPECT_RATIO_5_3_RESOLUTIONS;
+ break;
+ }
+
+ case ASPECT_RATIO_11_9: {
+ resolutions = ASPECT_RATIO_11_9_RESOLUTIONS;
+ break;
+ }
+
+ case ASPECT_RATIO_16_9: {
+ resolutions = ASPECT_RATIO_16_9_RESOLUTIONS;
+ break;
+ }
+
+ default: {
+ throw new IllegalArgumentException("Unknown aspect ratio: " + aspectRatio);
+ }
+ }
+
+ return resolutions;
+ }
+
+ /**
+ * @return The array of supported video codecs
+ */
+ public static int[] getSupportedVideoCodecs() {
+ return SUPPORTED_VCODECS;
+ }
+
+ /**
+ * @return The array of supported audio codecs
+ */
+ public static int[] getSupportedAudioCodecs() {
+ return SUPPORTED_ACODECS;
+ }
+
+ /**
+ * @return The array of supported file formats
+ */
+ public static int[] getSupportedVideoFileFormat() {
+ return SUPPORTED_VIDEO_FILE_FORMATS;
+ }
+
+ /**
+ * @return The array of supported video bitrates
+ */
+ public static int[] getSupportedVideoBitrates() {
+ return SUPPORTED_BITRATES;
+ }
+
+ /**
+ * @return The maximum value for the audio volume
+ */
+ public static int getSupportedMaxVolume() {
+ return MediaProperties.AUDIO_MAX_VOLUME_PERCENT;
+ }
+
+ /**
+ * @return The maximum number of audio tracks supported
+ */
+ public static int getSupportedAudioTrackCount() {
+ return MediaProperties.AUDIO_MAX_TRACK_COUNT;
+ }
+}
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
new file mode 100755
index 0000000..87e9a22
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -0,0 +1,541 @@
+/*
+ * 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.videoeditor;
+
+import java.io.IOException;
+
+import android.graphics.Bitmap;
+import android.media.MediaRecorder;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * This class represents a video clip item on the storyboard
+ * {@hide}
+ */
+public class MediaVideoItem extends MediaItem {
+ // Logging
+ private static final String TAG = "MediaVideoItem";
+
+ // Instance variables
+ private final int mWidth;
+ private final int mHeight;
+ private final int mAspectRatio;
+ private final int mFileType;
+ private final int mVideoType;
+ private final int mVideoProfile;
+ private final int mVideoBitrate;
+ private final long mDurationMs;
+ private final int mAudioBitrate;
+ private final int mFps;
+ private final int mAudioType;
+ private final int mAudioChannels;
+ private final int mAudioSamplingFrequency;
+
+ private long mBeginBoundaryTimeMs;
+ private long mEndBoundaryTimeMs;
+ private int mVolumePercentage;
+ private String mAudioWaveformFilename;
+ private PlaybackThread mPlaybackThread;
+
+ /**
+ * This listener interface is used by the MediaVideoItem to emit playback
+ * progress notifications. This callback should be invoked after the
+ * number of frames specified by
+ * {@link #startPlayback(SurfaceHolder surfaceHolder, long fromMs,
+ * int callbackAfterFrameCount, PlaybackProgressListener listener)}
+ */
+ public interface PlaybackProgressListener {
+ /**
+ * This method notifies the listener of the current time position while
+ * playing a media item
+ *
+ * @param mediaItem The media item
+ * @param timeMs The current playback position (expressed in milliseconds
+ * since the beginning of the media item).
+ * @param end true if the end of the media item was reached
+ */
+ public void onProgress(MediaVideoItem mediaItem, long timeMs, boolean end);
+ }
+
+ /**
+ * The playback thread
+ */
+ private class PlaybackThread extends Thread {
+ // Instance variables
+ private final static long FRAME_DURATION = 33;
+ private final PlaybackProgressListener mListener;
+ private final int mCallbackAfterFrameCount;
+ private final long mFromMs, mToMs;
+ private boolean mRun, mLoop;
+ private long mPositionMs;
+
+ /**
+ * Constructor
+ *
+ * @param fromMs The time (relative to the beginning of the media item)
+ * at which the playback will start
+ * @param toMs The time (relative to the beginning of the media item) at
+ * which the playback will stop. Use -1 to play to the end of
+ * the media item
+ * @param loop true if the playback should be looped once it reaches the
+ * end
+ * @param callbackAfterFrameCount The listener interface should be
+ * invoked after the number of frames specified by this
+ * parameter.
+ * @param listener The listener which will be notified of the playback
+ * progress
+ */
+ public PlaybackThread(long fromMs, long toMs, boolean loop, int callbackAfterFrameCount,
+ PlaybackProgressListener listener) {
+ mPositionMs = mFromMs = fromMs;
+ if (toMs < 0) {
+ mToMs = mDurationMs;
+ } else {
+ mToMs = toMs;
+ }
+ mLoop = loop;
+ mCallbackAfterFrameCount = callbackAfterFrameCount;
+ mListener = listener;
+ mRun = true;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "===> PlaybackThread.run enter");
+ }
+ int frameCount = 0;
+ while (mRun) {
+ try {
+ sleep(FRAME_DURATION);
+ } catch (InterruptedException ex) {
+ break;
+ }
+ frameCount++;
+ mPositionMs += FRAME_DURATION;
+
+ if (mPositionMs >= mToMs) {
+ if (!mLoop) {
+ if (mListener != null) {
+ mListener.onProgress(MediaVideoItem.this, mPositionMs, true);
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "PlaybackThread.run playback complete");
+ }
+ break;
+ } else {
+ // Fire a notification for the end of the clip
+ if (mListener != null) {
+ mListener.onProgress(MediaVideoItem.this, mToMs, false);
+ }
+
+ // Rewind
+ mPositionMs = mFromMs;
+ if (mListener != null) {
+ mListener.onProgress(MediaVideoItem.this, mPositionMs, false);
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "PlaybackThread.run playback complete");
+ }
+ frameCount = 0;
+ }
+ } else {
+ if (frameCount == mCallbackAfterFrameCount) {
+ if (mListener != null) {
+ mListener.onProgress(MediaVideoItem.this, mPositionMs, false);
+ }
+ frameCount = 0;
+ }
+ }
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "===> PlaybackThread.run exit");
+ }
+ }
+
+ /**
+ * Stop the playback
+ *
+ * @return The stop position
+ */
+ public long stopPlayback() {
+ mRun = false;
+ try {
+ join();
+ } catch (InterruptedException ex) {
+ }
+ return mPositionMs;
+ }
+ };
+
+ /**
+ * An object of this type cannot be instantiated with a default constructor
+ */
+ @SuppressWarnings("unused")
+ private MediaVideoItem() throws IOException {
+ this(null, null, RENDERING_MODE_BLACK_BORDER);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mediaItemId The MediaItem id
+ * @param filename The image file name
+ * @param renderingMode The rendering mode
+ *
+ * @throws IOException if the file cannot be opened for reading
+ */
+ public MediaVideoItem(String mediaItemId, String filename, int renderingMode)
+ throws IOException {
+ this(mediaItemId, filename, renderingMode, null);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mediaItemId The MediaItem id
+ * @param filename The image file name
+ * @param renderingMode The rendering mode
+ * @param audioWaveformFilename The name of the audio waveform file
+ *
+ * @throws IOException if the file cannot be opened for reading
+ */
+ MediaVideoItem(String mediaItemId, String filename, int renderingMode,
+ String audioWaveformFilename) throws IOException {
+ super(mediaItemId, filename, renderingMode);
+ // TODO: Set these variables correctly
+ mWidth = 0;
+ mHeight = 0;
+ mAspectRatio = MediaProperties.ASPECT_RATIO_3_2;
+ mFileType = MediaProperties.FILE_MP4;
+ mVideoType = MediaRecorder.VideoEncoder.H264;
+ // Do we have predefined values for this variable?
+ mVideoProfile = 0;
+ // Can video and audio duration be different?
+ mDurationMs = 10000;
+ mVideoBitrate = 800000;
+ mAudioBitrate = 30000;
+ mFps = 30;
+ mAudioType = MediaProperties.ACODEC_AAC_LC;
+ mAudioChannels = 2;
+ mAudioSamplingFrequency = 16000;
+
+ mBeginBoundaryTimeMs = 0;
+ mEndBoundaryTimeMs = mDurationMs;
+ mVolumePercentage = 100;
+ mAudioWaveformFilename = audioWaveformFilename;
+ }
+
+ /**
+ * Sets the start and end marks for trimming a video media item
+ *
+ * @param beginMs Start time in milliseconds. Set to 0 to extract from the
+ * beginning
+ * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to
+ * extract until the end
+ *
+ * @throws IllegalArgumentException if the start time is greater or equal than
+ * end time, the end time is beyond the file duration, the start time
+ * is negative
+ */
+ public void setExtractBoundaries(long beginMs, long endMs) {
+ if (beginMs > mDurationMs) {
+ throw new IllegalArgumentException("Invalid start time");
+ }
+ if (endMs > mDurationMs) {
+ throw new IllegalArgumentException("Invalid end time");
+ }
+
+ mBeginBoundaryTimeMs = beginMs;
+ mEndBoundaryTimeMs = endMs;
+ // TODO: Validate/modify the start and the end time of effects and overlays
+ }
+
+ /**
+ * @return The boundary begin time
+ */
+ public long getBoundaryBeginTime() {
+ return mBeginBoundaryTimeMs;
+ }
+
+ /**
+ * @return The boundary end time
+ */
+ public long getBoundaryEndTime() {
+ return mEndBoundaryTimeMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void addEffect(Effect effect) {
+ if (effect instanceof EffectKenBurns) {
+ throw new IllegalArgumentException("Ken Burns effects cannot be applied to MediaVideoItem");
+ }
+ super.addEffect(effect);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public Bitmap getThumbnail(int width, int height, long timeMs) {
+ return null;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,
+ int thumbnailCount) throws IOException {
+ return null;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getAspectRatio() {
+ return mAspectRatio;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getFileType() {
+ return mFileType;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /**
+ * @return The timeline duration. This is the actual duration in the
+ * timeline (trimmed duration)
+ */
+ @Override
+ public long getTimelineDuration() {
+ return mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
+ }
+
+ /**
+ * Render a frame according to the playback (in the native aspect ratio) for
+ * the specified media item. All effects and overlays applied to the media
+ * item are ignored. The extract boundaries are also ignored. This method
+ * can be used to playback frames when implementing trimming functionality.
+ *
+ * @param surfaceHolder SurfaceHolder used by the application
+ * @param timeMs time corresponding to the frame to display (relative to the
+ * the beginning of the media item).
+ * @return The accurate time stamp of the frame that is rendered .
+ * @throws IllegalStateException if a playback, preview or an export is
+ * already in progress
+ * @throws IllegalArgumentException if time is negative or greater than the
+ * media item duration
+ */
+ public long renderFrame(SurfaceHolder surfaceHolder, long timeMs) {
+ return timeMs;
+ }
+
+ /**
+ * Start the playback of this media item. This method does not block (does
+ * not wait for the playback to complete). The PlaybackProgressListener
+ * allows to track the progress at the time interval determined by the
+ * callbackAfterFrameCount parameter. The SurfaceHolder has to be created
+ * and ready for use before calling this method.
+ *
+ * @param surfaceHolder SurfaceHolder where the frames are rendered.
+ * @param fromMs The time (relative to the beginning of the media item) at
+ * which the playback will start
+ * @param toMs The time (relative to the beginning of the media item) at
+ * which the playback will stop. Use -1 to play to the end of the
+ * media item
+ * @param loop true if the playback should be looped once it reaches the end
+ * @param callbackAfterFrameCount The listener interface should be invoked
+ * after the number of frames specified by this parameter.
+ * @param listener The listener which will be notified of the playback
+ * progress
+ * @throws IllegalArgumentException if fromMs or toMs is beyond the playback
+ * duration
+ * @throws IllegalStateException if a playback, preview or an export is
+ * already in progress
+ */
+ public void startPlayback(SurfaceHolder surfaceHolder, long fromMs, long toMs, boolean loop,
+ int callbackAfterFrameCount, PlaybackProgressListener listener) {
+ if (fromMs >= mDurationMs) {
+ return;
+ }
+ mPlaybackThread = new PlaybackThread(fromMs, toMs, loop, callbackAfterFrameCount,
+ listener);
+ mPlaybackThread.start();
+ }
+
+ /**
+ * Stop the media item playback. This method blocks until the ongoing
+ * playback is stopped.
+ *
+ * @return The accurate current time when stop is effective expressed in
+ * milliseconds
+ */
+ public long stopPlayback() {
+ final long stopTimeMs;
+ if (mPlaybackThread != null) {
+ stopTimeMs = mPlaybackThread.stopPlayback();
+ mPlaybackThread = null;
+ } else {
+ stopTimeMs = 0;
+ }
+ return stopTimeMs;
+ }
+
+ /**
+ * This API allows to generate a file containing the sample volume levels of
+ * the Audio track of this media item. This function may take significant
+ * time and is blocking. The file can be retrieved using
+ * getAudioWaveformFilename().
+ *
+ * @param listener The progress listener
+ *
+ * @throws IOException if the output file cannot be created
+ * @throws IllegalArgumentException if the mediaItem does not have a valid
+ * Audio track
+ */
+ public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
+ throws IOException {
+ // TODO: Set mAudioWaveformFilename at the end once the export is complete
+ }
+
+ /**
+ * Get the audio waveform file name if {@link #extractAudioWaveform()} was
+ * successful. The file format is as following:
+ * <ul>
+ * <li>first 4 bytes provide the number of samples for each value, as big-endian signed</li>
+ * <li>4 following bytes is the total number of values in the file, as big-endian signed</li>
+ * <li>all values follow as bytes Name is unique.</li>
+ *</ul>
+ * @return the name of the file, null if the file has not been computed or
+ * if there is no Audio track in the mediaItem
+ */
+ public String getAudioWaveformFilename() {
+ return mAudioWaveformFilename;
+ }
+
+ /**
+ * Set volume of the Audio track of this mediaItem
+ *
+ * @param volumePercent in %/. 100% means no change; 50% means half value, 200%
+ * means double, 0% means silent.
+ * @throws UsupportedOperationException if volume value is not supported
+ */
+ public void setVolume(int volumePercent) {
+ mVolumePercentage = volumePercent;
+ }
+
+ /**
+ * Get the volume value of the audio track as percentage. Call of this
+ * method before calling setVolume will always return 100%
+ *
+ * @return the volume in percentage
+ */
+ public int getVolume() {
+ return mVolumePercentage;
+ }
+
+ /**
+ * @return The video type
+ */
+ public int getVideoType() {
+ return mVideoType;
+ }
+
+ /**
+ * @return The video profile
+ */
+ public int getVideoProfile() {
+ return mVideoProfile;
+ }
+
+ /**
+ * @return The video bitrate
+ */
+ public int getVideoBitrate() {
+ return mVideoBitrate;
+ }
+
+ /**
+ * @return The audio bitrate
+ */
+ public int getAudioBitrate() {
+ return mAudioBitrate;
+ }
+
+ /**
+ * @return The number of frames per second
+ */
+ public int getFps() {
+ return mFps;
+ }
+
+ /**
+ * @return The audio codec
+ */
+ public int getAudioType() {
+ return mAudioType;
+ }
+
+ /**
+ * @return The number of audio channels
+ */
+ public int getAudioChannels() {
+ return mAudioChannels;
+ }
+
+ /**
+ * @return The audio sample frequency
+ */
+ public int getAudioSamplingFrequency() {
+ return mAudioSamplingFrequency;
+ }
+}
diff --git a/media/java/android/media/videoeditor/Overlay.java b/media/java/android/media/videoeditor/Overlay.java
new file mode 100755
index 0000000..5065636
--- /dev/null
+++ b/media/java/android/media/videoeditor/Overlay.java
@@ -0,0 +1,117 @@
+/*
+ * 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.videoeditor;
+
+
+/**
+ * This is the super class for all Overlay classes.
+ * {@hide}
+ */
+public abstract class Overlay {
+ // Instance variables
+ private final String mUniqueId;
+
+ protected long mStartTimeMs;
+ protected long mDurationMs;
+
+ /**
+ * Default constructor
+ */
+ @SuppressWarnings("unused")
+ private Overlay() {
+ mUniqueId = null;
+ mStartTimeMs = 0;
+ mDurationMs = 0;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param overlayId The overlay id
+ * @param startTimeMs The start time relative to the media item start time
+ * @param durationMs The duration
+ *
+ * @throws IllegalArgumentException if the file type is not PNG or the
+ * startTimeMs and durationMs are incorrect.
+ */
+ public Overlay(String overlayId, long startTimeMs, long durationMs) {
+ mUniqueId = overlayId;
+ mStartTimeMs = startTimeMs;
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * @return The of the overlay
+ */
+ public String getId() {
+ return mUniqueId;
+ }
+
+ /**
+ * @return The duration of the overlay effect
+ */
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /**
+ * If a preview or export is in progress, then this change is effective for
+ * next preview or export session.
+ *
+ * @param durationMs The duration in milliseconds
+ */
+ public void setDuration(long durationMs) {
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * @return the start time of the overlay
+ */
+ public long getStartTime() {
+ return mStartTimeMs;
+ }
+
+ /**
+ * Set the start time for the overlay. If a preview or export is in
+ * progress, then this change is effective for next preview or export
+ * session.
+ *
+ * @param startTimeMs start time in milliseconds
+ */
+ public void setStartTime(long startTimeMs) {
+ mStartTimeMs = startTimeMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Overlay)) {
+ return false;
+ }
+ return mUniqueId.equals(((Overlay)object).mUniqueId);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mUniqueId.hashCode();
+ }
+}
diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java
new file mode 100755
index 0000000..e5d9b81
--- /dev/null
+++ b/media/java/android/media/videoeditor/OverlayFrame.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 android.media.videoeditor;
+
+
+/**
+ * This class is used to overlay an image on top of a media item. This class
+ * does not manage deletion of the overlay file so application may use
+ * {@link #getFilename()} for this purpose.
+ * {@hide}
+ */
+public class OverlayFrame extends Overlay {
+ // Instance variables
+ private final String mFilename;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private OverlayFrame() {
+ this(null, null, 0, 0);
+ }
+
+ /**
+ * Constructor for an OverlayFrame
+ *
+ * @param overlayId The overlay id
+ * @param filename The file name that contains the overlay. Only PNG
+ * supported.
+ * @param startTimeMs The overlay start time in milliseconds
+ * @param durationMs The overlay duration in milliseconds
+ *
+ * @throws IllegalArgumentException if the file type is not PNG or the
+ * startTimeMs and durationMs are incorrect.
+ */
+ public OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {
+ super(overlayId, startTimeMs, durationMs);
+ mFilename = filename;
+ }
+
+ /**
+ * Get the file name of this overlay
+ */
+ public String getFilename() {
+ return mFilename;
+ }
+}
diff --git a/media/java/android/media/videoeditor/Transition.java b/media/java/android/media/videoeditor/Transition.java
new file mode 100755
index 0000000..e4bc9a4
--- /dev/null
+++ b/media/java/android/media/videoeditor/Transition.java
@@ -0,0 +1,182 @@
+/*
+ * 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.videoeditor;
+
+import java.io.File;
+
+/**
+ * This class is super class for all transitions. Transitions (with the
+ * exception of TransitionAtStart and TransitioAtEnd) can only be inserted
+ * between media items.
+ *
+ * Adding a transition between MediaItems makes the
+ * duration of the storyboard shorter by the duration of the Transition itself.
+ * As a result, if the duration of the transition is larger than the smaller
+ * duration of the two MediaItems associated with the Transition, an exception
+ * will be thrown.
+ *
+ * During a transition, the audio track are cross-fading
+ * automatically. {@hide}
+ */
+public abstract class Transition {
+ // The transition behavior
+ /** The transition starts slowly and speed up */
+ public static final int BEHAVIOR_SPEED_UP = 0;
+ /** The transition start fast and speed down */
+ public static final int BEHAVIOR_SPEED_DOWN = 1;
+ /** The transition speed is constant */
+ public static final int BEHAVIOR_LINEAR = 2;
+ /** The transition starts fast and ends fast with a slow middle */
+ public static final int BEHAVIOR_MIDDLE_SLOW = 3;
+ /** The transition starts slowly and ends slowly with a fast middle */
+ public static final int BEHAVIOR_MIDDLE_FAST = 4;
+
+ // The unique id of the transition
+ private final String mUniqueId;
+
+ // The transition is applied at the end of this media item
+ private final MediaItem mAfterMediaItem;
+ // The transition is applied at the beginning of this media item
+ private final MediaItem mBeforeMediaItem;
+
+ // The transition behavior
+ protected final int mBehavior;
+
+ // The transition duration
+ protected long mDurationMs;
+
+ // The transition filename
+ protected String mFilename;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private Transition() {
+ this(null, null, null, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs The duration of the transition in milliseconds
+ * @param behavior The transition behavior
+ */
+ protected Transition(String transitionId, MediaItem afterMediaItem, MediaItem beforeMediaItem,
+ long durationMs, int behavior) {
+ mUniqueId = transitionId;
+ mAfterMediaItem = afterMediaItem;
+ mBeforeMediaItem = beforeMediaItem;
+ mDurationMs = durationMs;
+ mBehavior = behavior;
+ }
+
+ /**
+ * @return The of the transition
+ */
+ public String getId() {
+ return mUniqueId;
+ }
+
+ /**
+ * @return The media item at the end of which the transition is applied
+ */
+ public MediaItem getAfterMediaItem() {
+ return mAfterMediaItem;
+ }
+
+ /**
+ * @return The media item at the beginning of which the transition is applied
+ */
+ public MediaItem getBeforeMediaItem() {
+ return mBeforeMediaItem;
+ }
+
+ /**
+ * Set the duration of the transition.
+ *
+ * @param durationMs the duration of the transition in milliseconds
+ */
+ public void setDuration(long durationMs) {
+ mDurationMs = durationMs;
+ }
+
+ /**
+ * @return the duration of the transition in milliseconds
+ */
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ /**
+ * @return The behavior
+ */
+ public int getBehavior() {
+ return mBehavior;
+ }
+
+ /**
+ * Generate the video clip for the specified transition.
+ * This method may block for a significant amount of time.
+ *
+ * Before the method completes execution it sets the mFilename to
+ * the name of the newly generated transition video clip file.
+ */
+ abstract void generate();
+
+ /**
+ * Remove any resources associated with this transition
+ */
+ void invalidate() {
+ if (mFilename != null) {
+ new File(mFilename).delete();
+ mFilename = null;
+ }
+ }
+
+ /**
+ * @return true if the transition is generated
+ */
+ boolean isGenerated() {
+ return (mFilename != null);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Transition)) {
+ return false;
+ }
+ return mUniqueId.equals(((Transition)object).mUniqueId);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return mUniqueId.hashCode();
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionAlpha.java b/media/java/android/media/videoeditor/TransitionAlpha.java
new file mode 100755
index 0000000..0a4a12f
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionAlpha.java
@@ -0,0 +1,112 @@
+/*
+ * 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.videoeditor;
+
+
+/**
+ * This class allows to render an "alpha blending" transition according to a
+ * bitmap mask. The mask shows the shape of the transition all along the
+ * duration of the transition: just before the transition, video 1 is fully
+ * displayed. When the transition starts, as the time goes on, pixels of video 2
+ * replace pixels of video 1 according to the gray scale pixel value of the
+ * mask.
+ * {@hide}
+ */
+public class TransitionAlpha extends Transition {
+ /** This is the input JPEG file for the mask */
+ private final String mMaskFilename;
+
+ /**
+ * This is percentage (between 0 and 100) of blending between video 1 and
+ * video 2 if this value equals 0, then the mask is strictly applied if this
+ * value equals 100, then the mask is not at all applied (no transition
+ * effect)
+ */
+ private final int mBlendingPercent;
+
+ /**
+ * If true, this value inverts the direction of the mask: white pixels of
+ * the mask show video 2 pixels first black pixels of the mask show video 2
+ * pixels last.
+ */
+ private final boolean mIsInvert;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionAlpha() {
+ this(null, null, null, 0, 0, null, 0, false);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs duration of the transition in milliseconds
+ * @param behavior behavior is one of the behavior defined in Transition
+ * class
+ * @param maskFilename JPEG file name
+ * @param blendingPercent The blending percent applied
+ * @param invert true to invert the direction of the alpha blending
+ *
+ * @throws IllegalArgumentException if behavior is not supported, or if
+ * direction are not supported.
+ */
+ public TransitionAlpha(String transitionId, MediaItem afterMediaItem,
+ MediaItem beforeMediaItem, long durationMs, int behavior, String maskFilename,
+ int blendingPercent, boolean invert) {
+ super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);
+
+ mMaskFilename = maskFilename;
+ mBlendingPercent = blendingPercent;
+ mIsInvert = invert;
+ }
+
+ /**
+ * @return The blending percentage
+ */
+ public int getBlendingPercent() {
+ return mBlendingPercent;
+ }
+
+ /**
+ * @return The mask filename
+ */
+ public String getMaskFilename() {
+ return mMaskFilename;
+ }
+
+ /**
+ * @return true if the direction of the alpha blending is inverted
+ */
+ public boolean isInvert() {
+ return mIsInvert;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void generate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionAtEnd.java b/media/java/android/media/videoeditor/TransitionAtEnd.java
new file mode 100755
index 0000000..7765bd4
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionAtEnd.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+
+/**
+ * TransitionAtEnd is a class useful to manage a predefined transition at the
+ * end of the movie.
+ * {@hide}
+ */
+public class TransitionAtEnd extends Transition {
+ /**
+ * This transition fades to black frame using fade out in a certain provided
+ * duration. This transition is always applied at the end of the movie.
+ */
+ public static final int TYPE_FADE_TO_BLACK = 0;
+
+ /**
+ * This transition fades to black frame using curtain closing: A black image is
+ * moved from top to bottom to cover the video. This transition is always
+ * applied at the end of the movie.
+ */
+ public static final int TYPE_CURTAIN_CLOSING = 1;
+
+ // The transition type
+ private final int mType;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionAtEnd() {
+ this(null, null, 0, 0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param durationMs duration of the transition in milliseconds
+ * @param type type of the transition to apply.
+ */
+ public TransitionAtEnd(String transitionId, MediaItem afterMediaItem, long duration,
+ int type) {
+ super(transitionId, afterMediaItem, null, duration, Transition.BEHAVIOR_LINEAR);
+ mType = type;
+ }
+
+ /**
+ * Get the type of this transition
+ *
+ * @return The type of the transition
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ void generate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionAtStart.java b/media/java/android/media/videoeditor/TransitionAtStart.java
new file mode 100755
index 0000000..65ebd01
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionAtStart.java
@@ -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.
+ */
+
+package android.media.videoeditor;
+
+
+/**
+ * TransitionAtStart is a class useful to manage a predefined transition at the
+ * beginning of the movie.
+ * {@hide}
+ */
+public class TransitionAtStart extends Transition {
+ /**
+ * This transition fades from black using fade-in in a certain provided
+ * duration. This transition is always applied at the beginning of the
+ * movie.
+ */
+ public static final int TYPE_FADE_FROM_BLACK = 0;
+
+ /**
+ * This transition fades from black frame using curtain opening: A black
+ * image is displayed and moves from bottom to top making the video visible.
+ * This transition is always applied at the beginning of the movie.
+ */
+ public static final int TYPE_CURTAIN_OPENING = 1;
+
+ // The transition type
+ private final int mType;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionAtStart() {
+ this(null, null, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs The duration of the transition in milliseconds
+ * @param type The type of the transition to apply.
+ */
+ public TransitionAtStart(String transitionId, MediaItem beforeMediaItem, long durationMs,
+ int type) {
+ super(transitionId, null, beforeMediaItem, durationMs,
+ Transition.BEHAVIOR_LINEAR);
+ mType = type;
+ }
+
+ /**
+ * Get the type of this transition
+ *
+ * @return The type of the transition
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void generate() {
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ void invalidate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionCrossfade.java b/media/java/android/media/videoeditor/TransitionCrossfade.java
new file mode 100755
index 0000000..f8223e8
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionCrossfade.java
@@ -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.
+ */
+
+package android.media.videoeditor;
+
+
+/**
+ * This class allows to render a crossfade (dissolve) effect transition between
+ * two videos
+ * {@hide}
+ */
+public class TransitionCrossfade extends Transition {
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionCrossfade() {
+ this(null, null, null, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs duration of the transition in milliseconds
+ * @param behavior behavior is one of the behavior defined in Transition
+ * class
+ *
+ * @throws IllegalArgumentException if behavior is not supported.
+ */
+ public TransitionCrossfade(String transitionId, MediaItem afterMediaItem,
+ MediaItem beforeMediaItem, long durationMs, int behavior) {
+ super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ void generate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionFadeToBlack.java b/media/java/android/media/videoeditor/TransitionFadeToBlack.java
new file mode 100755
index 0000000..9569a65
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionFadeToBlack.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+
+/**
+ * This class is used to render a fade to black transition between two videos.
+ * {@hide}
+ */
+public class TransitionFadeToBlack extends Transition {
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionFadeToBlack() {
+ this(null, null, null, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs duration of the transition
+ * @param behavior behavior is one of the behavior defined in Transition
+ * class
+ *
+ * @throws IllegalArgumentException if behavior is not supported.
+ */
+ public TransitionFadeToBlack(String transitionId, MediaItem afterMediaItem,
+ MediaItem beforeMediaItem, long durationMs, int behavior) {
+ super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ void generate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/TransitionSliding.java b/media/java/android/media/videoeditor/TransitionSliding.java
new file mode 100755
index 0000000..cc9f4b2
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionSliding.java
@@ -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.
+ */
+
+
+package android.media.videoeditor;
+
+/**
+ * This class allows to create sliding transitions
+ * {@hide}
+ */
+public class TransitionSliding extends Transition {
+
+ /** Video 1 is pushed to the right while video 2 is coming from left */
+ public final static int DIRECTION_RIGHT_OUT_LEFT_IN = 0;
+ /** Video 1 is pushed to the left while video 2 is coming from right */
+ public static final int DIRECTION_LEFT_OUT_RIGHT_IN = 1;
+ /** Video 1 is pushed to the top while video 2 is coming from bottom */
+ public static final int DIRECTION_TOP_OUT_BOTTOM_IN = 2;
+ /** Video 1 is pushed to the bottom while video 2 is coming from top */
+ public static final int DIRECTION_BOTTOM_OUT_TOP_IN = 3;
+
+ // The sliding transitions
+ private final int mSlidingDirection;
+
+ /**
+ * An object of this type cannot be instantiated by using the default
+ * constructor
+ */
+ @SuppressWarnings("unused")
+ private TransitionSliding() {
+ this(null, null, null, 0, 0, 0);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param transitionId The transition id
+ * @param afterMediaItem The transition is applied to the end of this
+ * media item
+ * @param beforeMediaItem The transition is applied to the beginning of
+ * this media item
+ * @param durationMs duration of the transition in milliseconds
+ * @param behavior behavior is one of the behavior defined in Transition
+ * class
+ * @param direction direction shall be one of the supported directions like
+ * RIGHT_OUT_LEFT_IN
+ *
+ * @throws IllegalArgumentException if behavior is not supported.
+ */
+ public TransitionSliding(String transitionId, MediaItem afterMediaItem,
+ MediaItem beforeMediaItem, long durationMs, int behavior, int direction) {
+ super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);
+ mSlidingDirection = direction;
+ }
+
+ /**
+ * @return The sliding direction
+ */
+ public int getDirection() {
+ return mSlidingDirection;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ void generate() {
+ }
+}
diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java
new file mode 100755
index 0000000..aa8f2cb
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditor.java
@@ -0,0 +1,493 @@
+/*
+ * 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.videoeditor;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+
+import android.view.SurfaceHolder;
+
+/**
+ * This is the interface implemented by classes which provide video editing
+ * functionality. The VideoEditor implementation class manages all input and
+ * output files. Unless specifically mentioned, methods are blocking. A
+ * typical editing session may consist of the following sequence of operations:
+ *
+ * <ul>
+ * <li>Add a set of MediaItems</li>
+ * <li>Apply a set of Transitions between MediaItems</li>
+ * <li>Add Effects and Overlays to media items</li>
+ * <li>Preview the movie at any time</li>
+ * <li>Save the VideoEditor implementation class internal state</li>
+ * <li>Release the VideoEditor implementation class instance by invoking
+ * {@link #release()}
+ * </ul>
+ * The internal VideoEditor state consists of the following elements:
+ * <ul>
+ * <li>Ordered & trimmed MediaItems</li>
+ * <li>Transition video clips</li>
+ * <li>Overlays</li>
+ * <li>Effects</li>
+ * <li>Audio waveform for the background audio and MediaItems</li>
+ * <li>Project thumbnail</li>
+ * <li>Last exported movie.</li>
+ * <li>Other project specific data such as the current aspect ratio.</li>
+ * </ul>
+ * {@hide}
+ */
+public interface VideoEditor {
+ // The file name of the project thumbnail
+ public static final String THUMBNAIL_FILENAME = "thumbnail.jpg";
+
+ // Use this value instead of the specific end of the storyboard timeline
+ // value.
+ public final static int DURATION_OF_STORYBOARD = -1;
+
+ /**
+ * This listener interface is used by the VideoEditor to emit preview
+ * progress notifications. This callback should be invoked after the
+ * number of frames specified by
+ * {@link #startPreview(SurfaceHolder surfaceHolder, long fromMs,
+ * int callbackAfterFrameCount, PreviewProgressListener listener)}
+ */
+ public interface PreviewProgressListener {
+ /**
+ * This method notifies the listener of the current time position while
+ * previewing a project.
+ *
+ * @param videoEditor The VideoEditor instance
+ * @param timeMs The current preview position (expressed in milliseconds
+ * since the beginning of the storyboard timeline).
+ * @param end true if the end of the timeline was reached
+ */
+ public void onProgress(VideoEditor videoEditor, long timeMs, boolean end);
+ }
+
+ /**
+ * This listener interface is used by the VideoEditor to emit export status
+ * notifications.
+ * {@link #export(String filename, ExportProgressListener listener, int height, int bitrate)}
+ */
+ public interface ExportProgressListener {
+ /**
+ * This method notifies the listener of the progress status of a export
+ * operation.
+ *
+ * @param videoEditor The VideoEditor instance
+ * @param filename The name of the file which is in the process of being
+ * exported.
+ * @param progress The progress in %. At the beginning of the export, this
+ * value is set to 0; at the end, the value is set to 100.
+ */
+ public void onProgress(VideoEditor videoEditor, String filename, int progress);
+ }
+
+ /**
+ * @return The path where the VideoEditor stores all files related to the
+ * project
+ */
+ public String getPath();
+
+ /**
+ * This method releases all in-memory resources used by the VideoEditor
+ * instance. All pending operations such as preview, export and extract
+ * audio waveform must be canceled.
+ */
+ public void release();
+
+ /**
+ * Persist the current internal state of VideoEditor to the project path.
+ * The VideoEditor state may be restored by invoking the
+ * {@link VideoEditorFactory#load(String)} method. This method does not
+ * release the internal in-memory state of the VideoEditor. To release
+ * the in-memory state of the VideoEditor the {@link #release()} method
+ * must be invoked.
+ *
+ * Pending transition generations must be allowed to complete before the
+ * state is saved.
+ * Pending audio waveform generations must be allowed to complete.
+ * Pending export operations must be allowed to continue.
+ */
+ public void save() throws IOException;
+
+ /**
+ * Create the output movie based on all media items added and the applied
+ * storyboard items. This method can take a long time to execute and is
+ * blocking. The application will receive progress notifications via the
+ * ExportProgressListener. Specific implementations may not support multiple
+ * simultaneous export operations.
+ *
+ * Note that invoking methods which would change the contents of the output
+ * movie throw an IllegalStateException while an export operation is
+ * pending.
+ *
+ * @param filename The output file name (including the full path)
+ * @param height The height of the output video file. The supported values
+ * for height are described in the MediaProperties class, for
+ * example: HEIGHT_480. The width will be automatically
+ * computed according to the aspect ratio provided by
+ * {@link #setAspectRatio(int)}
+ * @param bitrate The bitrate of the output video file. This is approximate
+ * value for the output movie. Supported bitrate values are
+ * described in the MediaProperties class for example:
+ * BITRATE_384K
+ * @param listener The listener for progress notifications. Use null if
+ * export progress notifications are not needed.
+ *
+ * @throws IllegalArgumentException if height or bitrate are not supported.
+ * @throws IOException if output file cannot be created
+ * @throws IllegalStateException if a preview or an export is in progress or
+ * if no MediaItem has been added
+ * @throws CancellationException if export is canceled by calling
+ * {@link #cancelExport()}
+ * @throws UnsupportOperationException if multiple simultaneous export()
+ * are not allowed
+ */
+ public void export(String filename, int height, int bitrate, ExportProgressListener listener)
+ throws IOException;
+
+ /**
+ * Cancel the running export operation. This method blocks until the
+ * export is canceled and the exported file (if any) is deleted. If the
+ * export completed by the time this method is invoked, the export file
+ * will be deleted.
+ *
+ * @param filename The filename which identifies the export operation to be
+ * canceled.
+ **/
+ public void cancelExport(String filename);
+
+ /**
+ * Add a media item at the end of the storyboard.
+ *
+ * @param mediaItem The media item object to add
+ * @throws IllegalStateException if a preview or an export is in progress or
+ * if the media item id is not unique across all the media items
+ * added.
+ */
+ public void addMediaItem(MediaItem mediaItem);
+
+ /**
+ * Insert a media item after the media item with the specified id.
+ *
+ * @param mediaItem The media item object to insert
+ * @param afterMediaItemId Insert the mediaItem after the media item
+ * identified by this id. If this parameter is null, the media
+ * item is inserted at the beginning of the timeline.
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if media item with the specified id does
+ * not exist (null is a valid value) or if the media item id is
+ * not unique across all the media items added.
+ */
+ public void insertMediaItem(MediaItem mediaItem, String afterMediaItemId);
+
+ /**
+ * Move a media item after the media item with the specified id.
+ *
+ * Note: The project thumbnail is regenerated if the media item is or
+ * becomes the first media item in the storyboard timeline.
+ *
+ * @param mediaItemId The id of the media item to move
+ * @param afterMediaItemId Move the media item identified by mediaItemId after
+ * the media item identified by this parameter. If this parameter
+ * is null, the media item is moved at the beginning of the
+ * timeline.
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if one of media item ids is invalid
+ * (null is a valid value)
+ */
+ public void moveMediaItem(String mediaItemId, String afterMediaItemId);
+
+ /**
+ * Remove the media item with the specified id. If there are transitions
+ * before or after this media item, then this/these transition(s) are
+ * removed from the storyboard. If the extraction of the audio waveform is
+ * in progress, the extraction is canceled and the file is deleted.
+ *
+ * Effects and overlays associated with the media item will also be
+ * removed.
+ *
+ * Note: The project thumbnail is regenerated if the media item which
+ * is removed is the first media item in the storyboard or if the
+ * media item is the only one in the storyboard. If the
+ * media item is the only one in the storyboard, the project thumbnail
+ * will be set to a black frame and the aspect ratio will revert to the
+ * default aspect ratio, and this method is equivalent to
+ * removeAllMediaItems() in this case.
+ *
+ * @param mediaItemId The unique id of the media item to be removed
+ *
+ * @return The media item that was removed
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if media item with the specified id
+ * does not exist
+ */
+ public MediaItem removeMediaItem(String mediaItemId);
+
+ /**
+ * Remove all media items in the storyboard. All effects, overlays and all
+ * transitions are also removed.
+ *
+ * Note: The project thumbnail will be set to a black frame and the aspect
+ * ratio will revert to the default aspect ratio.
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ */
+ public void removeAllMediaItems();
+
+ /**
+ * Get the list of media items in the order in which it they appear in the
+ * storyboard timeline.
+ *
+ * Note that if any media item source files are no longer
+ * accessible, this method will still provide the full list of media items.
+ *
+ * @return The list of media items. If no media item exist an empty list
+ * will be returned.
+ */
+ public List<MediaItem> getAllMediaItems();
+
+ /**
+ * Find the media item with the specified id
+ *
+ * @param mediaItemId The media item id
+ *
+ * @return The media item with the specified id (null if it does not exist)
+ */
+ public MediaItem getMediaItem(String mediaItemId);
+
+ /**
+ * Add a transition between the media items specified by the transition.
+ * If a transition existed at the same position it is invalidated and then
+ * the transition is replaced. Note that the new transition video clip is
+ * not automatically generated by this method. The
+ * {@link Transition#generate()} method must be invoked to generate
+ * the transition video clip.
+ *
+ * Note that the TransitionAtEnd and TransitionAtStart are special kinds
+ * that can not be applied between two media items.
+ *
+ * A crossfade audio transition will be automatically applied regardless of
+ * the video transition.
+ *
+ * @param transition The transition to apply
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if the transition duration is larger
+ * than the smallest duration of the two media item files or
+ * if the two media items specified in the transition are not
+ * adjacent
+ */
+ public void addTransition(Transition transition);
+
+ /**
+ * Remove the transition with the specified id.
+ *
+ * @param transitionId The id of the transition to be removed
+ *
+ * @return The transition that was removed
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if transition with the specified id does
+ * not exist
+ */
+ public Transition removeTransition(String transitionId);
+
+ /**
+ * Get the list of transitions
+ *
+ * @return The list of transitions. If no transitions exist an empty list
+ * will be returned.
+ */
+ public List<Transition> getAllTransitions();
+
+ /**
+ * Find the transition with the specified transition id.
+ *
+ * @param transitionId The transition id
+ *
+ * @return The transition
+ */
+ public Transition getTransition(String transitionId);
+
+ /**
+ * Add the specified AudioTrack to the storyboard. Note: Specific
+ * implementations may support a limited number of audio tracks (e.g. only
+ * one audio track)
+ *
+ * @param audioTrack The AudioTrack to add
+ * @throws UnsupportedOperationException if the implementation supports a
+ * limited number of audio tracks.
+ * @throws IllegalArgumentException if media item is not unique across all
+ * the audio tracks already added.
+ */
+ public void addAudioTrack(AudioTrack audioTrack);
+
+ /**
+ * Insert an audio track after the audio track with the specified id. Use
+ * addAudioTrack to add an audio track at the end of the storyboard
+ * timeline.
+ *
+ * @param audioTrack The audio track object to insert
+ * @param afterAudioTrackId Insert the audio track after the audio track
+ * identified by this parameter. If this parameter is null the
+ * audio track is added at the beginning of the timeline.
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if media item with the specified id does
+ * not exist (null is a valid value). if media item is not
+ * unique across all the audio tracks already added.
+ * @throws UnsupportedOperationException if the implementation supports a
+ * limited number of audio tracks
+ */
+ public void insertAudioTrack(AudioTrack audioTrack, String afterAudioTrackId);
+
+ /**
+ * Move an AudioTrack after the AudioTrack with the specified id.
+ *
+ * @param audioTrackId The id of the AudioTrack to move
+ * @param afterAudioTrackId Move the AudioTrack identified by audioTrackId
+ * after the AudioTrack identified by this parameter. If this
+ * parameter is null the audio track is added at the beginning of
+ * the timeline.
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if one of media item ids is invalid
+ * (null is a valid value)
+ */
+ public void moveAudioTrack(String audioTrackId, String afterAudioTrackId);
+
+ /**
+ * Remove the audio track with the specified id. If the extraction of the
+ * audio waveform is in progress, the extraction is canceled and the file is
+ * deleted.
+ *
+ * @param audioTrackId The id of the audio track to be removed
+ *
+ * @return The audio track that was removed
+ * @throws IllegalStateException if a preview or an export is in progress
+ */
+ public AudioTrack removeAudioTrack(String audioTrackId);
+
+ /**
+ * Get the list of AudioTracks in order in which they appear in the storyboard.
+ *
+ * Note that if any AudioTrack source files are not accessible anymore,
+ * this method will still provide the full list of audio tracks.
+ *
+ * @return The list of AudioTracks. If no audio tracks exist an empty list
+ * will be returned.
+ */
+ public List<AudioTrack> getAllAudioTracks();
+
+ /**
+ * Find the AudioTrack with the specified id
+ *
+ * @param audioTrackId The AudioTrack id
+ *
+ * @return The AudioTrack with the specified id (null if it does not exist)
+ */
+ public AudioTrack getAudioTrack(String audioTrackId);
+
+ /**
+ * Set the aspect ratio used in the preview and the export movie.
+ *
+ * The default aspect ratio is ASPECTRATIO_16_9 (16:9).
+ *
+ * @param aspectRatio to apply. If aspectRatio is the same as the current
+ * aspect ratio, then this function just returns. The supported
+ * aspect ratio are defined in the MediaProperties class for
+ * example: ASPECTRATIO_16_9
+ *
+ * @throws IllegalStateException if a preview or an export is in progress
+ * @throws IllegalArgumentException if aspect ratio is not supported
+ */
+ public void setAspectRatio(int aspectRatio);
+
+ /**
+ * Get current aspect ratio.
+ *
+ * @return The aspect ratio as described in MediaProperties
+ */
+ public int getAspectRatio();
+
+ /**
+ * Get the preview (and output movie) duration.
+ *
+ * @return The duration of the preview (and output movie)
+ */
+ public long getDuration();
+
+ /**
+ * Render a frame according to the preview aspect ratio and activating all
+ * storyboard items relative to the specified time.
+ *
+ * @param surfaceHolder SurfaceHolder used by the application
+ * @param timeMs time corresponding to the frame to display
+ *
+ * @return The accurate time stamp of the frame that is rendered
+ * .
+ * @throws IllegalStateException if a preview or an export is already
+ * in progress
+ * @throws IllegalArgumentException if time is negative or beyond the
+ * preview duration
+ */
+ public long renderPreviewFrame(SurfaceHolder surfaceHolder, long timeMs);
+
+ /**
+ * This method must be called after the aspect ratio of the project changes
+ * and before startPreview is called. Note that this method may block for
+ * an extensive period of time.
+ */
+ public void generatePreview();
+
+ /**
+ * Start the preview of all the storyboard items applied on all MediaItems
+ * This method does not block (does not wait for the preview to complete).
+ * The PreviewProgressListener allows to track the progress at the time
+ * interval determined by the callbackAfterFrameCount parameter. The
+ * SurfaceHolder has to be created and ready for use before calling this
+ * method. The method is a no-op if there are no MediaItems in the
+ * storyboard.
+ *
+ * @param surfaceHolder SurfaceHolder where the preview is rendered.
+ * @param fromMs The time (relative to the timeline) at which the preview
+ * will start
+ * @param toMs The time (relative to the timeline) at which the preview will
+ * stop. Use -1 to play to the end of the timeline
+ * @param loop true if the preview should be looped once it reaches the end
+ * @param callbackAfterFrameCount The listener interface should be invoked
+ * after the number of frames specified by this parameter.
+ * @param listener The listener which will be notified of the preview
+ * progress
+ * @throws IllegalArgumentException if fromMs is beyond the preview duration
+ * @throws IllegalStateException if a preview or an export is already in
+ * progress
+ */
+ public void startPreview(SurfaceHolder surfaceHolder, long fromMs, long toMs, boolean loop,
+ int callbackAfterFrameCount, PreviewProgressListener listener);
+
+ /**
+ * Stop the current preview. This method blocks until ongoing preview is
+ * stopped. Ignored if there is no preview running.
+ *
+ * @return The accurate current time when stop is effective expressed in
+ * milliseconds
+ */
+ public long stopPreview();
+}
diff --git a/media/java/android/media/videoeditor/VideoEditorFactory.java b/media/java/android/media/videoeditor/VideoEditorFactory.java
new file mode 100755
index 0000000..2c56fc2
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditorFactory.java
@@ -0,0 +1,83 @@
+/*
+ * 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.videoeditor;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+
+/**
+ * The VideoEditorFactory class must be used to instantiate VideoEditor objects
+ * by creating a new project {@link #create(String)} or by loading an
+ * existing project {@link #load(String)}.
+ * {@hide}
+ */
+public class VideoEditorFactory {
+ /**
+ * This is the factory method for creating a new VideoEditor instance.
+ *
+ * @param projectPath The path where all VideoEditor internal
+ * files are stored. When a project is deleted the application is
+ * responsible for deleting the path and its contents.
+ *
+ * @return The VideoEditor instance
+ *
+ * @throws IOException if path does not exist or if the path can
+ * not be accessed in read/write mode
+ * @throws IllegalStateException if a previous VideoEditor instance has not
+ * been released
+ */
+ public static VideoEditor create(String projectPath) throws IOException {
+ // If the project path does not exist create it
+ final File dir = new File(projectPath);
+ if (!dir.exists()) {
+ if (!dir.mkdirs()) {
+ throw new FileNotFoundException("Cannot create project path: " + projectPath);
+ }
+ }
+ return new VideoEditorTestImpl(projectPath);
+ }
+
+ /**
+ * This is the factory method for instantiating a VideoEditor from the
+ * internal state previously saved with the
+ * {@link VideoEditor#save(String)} method.
+ *
+ * @param projectPath The path where all VideoEditor internal files
+ * are stored. When a project is deleted the application is
+ * responsible for deleting the path and its contents.
+ * @param generatePreview if set to true the
+ * {@link MediaEditor#generatePreview()} will be called internally to
+ * generate any needed transitions.
+ *
+ * @return The VideoEditor instance
+ *
+ * @throws IOException if path does not exist or if the path can
+ * not be accessed in read/write mode or if one of the resource
+ * media files cannot be retrieved
+ * @throws IllegalStateException if a previous VideoEditor instance has not
+ * been released
+ */
+ public static VideoEditor load(String projectPath, boolean generatePreview) throws IOException {
+ final VideoEditorTestImpl videoEditor = new VideoEditorTestImpl(projectPath);
+ if (generatePreview) {
+ videoEditor.generatePreview();
+ }
+ return videoEditor;
+ }
+}
diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
new file mode 100644
index 0000000..a23c5c6
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -0,0 +1,777 @@
+/*
+ * 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.videoeditor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.util.Log;
+import android.util.Xml;
+import android.view.SurfaceHolder;
+
+/**
+ * The VideoEditor implementation
+ * {@hide}
+ */
+public class VideoEditorTestImpl implements VideoEditor {
+ // Logging
+ private static final String TAG = "VideoEditorImpl";
+
+ // The project filename
+ private static final String PROJECT_FILENAME = "videoeditor.xml";
+
+ // XML tags
+ private static final String TAG_PROJECT = "project";
+ private static final String TAG_MEDIA_ITEMS = "media_items";
+ private static final String TAG_MEDIA_ITEM = "media_item";
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_FILENAME = "filename";
+ private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem";
+ private static final String ATTR_RENDERING_MODE = "rendering_mode";
+ private static final String ATTR_ASPECT_RATIO = "aspect_ratio";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_DURATION = "duration";
+ private static final String ATTR_BEGIN_TIME = "start_time";
+ private static final String ATTR_END_TIME = "end_time";
+ private static final String ATTR_VOLUME = "volume";
+
+ private static long mDurationMs;
+ private final String mProjectPath;
+ private final List<MediaItem> mMediaItems = new ArrayList<MediaItem>();
+ private final List<AudioTrack> mAudioTracks = new ArrayList<AudioTrack>();
+ private final List<Transition> mTransitions = new ArrayList<Transition>();
+ private PreviewThread mPreviewThread;
+ private int mAspectRatio;
+
+ /**
+ * The preview thread
+ */
+ private class PreviewThread extends Thread {
+ // Instance variables
+ private final static long FRAME_DURATION = 33;
+ private final PreviewProgressListener mListener;
+ private final int mCallbackAfterFrameCount;
+ private final long mFromMs, mToMs;
+ private boolean mRun, mLoop;
+ private long mPositionMs;
+
+ /**
+ * Constructor
+ *
+ * @param fromMs Start preview at this position
+ * @param toMs The time (relative to the timeline) at which the preview
+ * will stop. Use -1 to play to the end of the timeline
+ * @param callbackAfterFrameCount The listener interface should be invoked
+ * after the number of frames specified by this parameter.
+ * @param loop true if the preview should be looped once it reaches the end
+ * @param listener The listener
+ */
+ public PreviewThread(long fromMs, long toMs, boolean loop, int callbackAfterFrameCount,
+ PreviewProgressListener listener) {
+ mPositionMs = mFromMs = fromMs;
+ if (toMs < 0) {
+ mToMs = mDurationMs;
+ } else {
+ mToMs = toMs;
+ }
+ mLoop = loop;
+ mCallbackAfterFrameCount = callbackAfterFrameCount;
+ mListener = listener;
+ mRun = true;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "===> PreviewThread.run enter");
+ }
+ int frameCount = 0;
+ while (mRun) {
+ try {
+ sleep(FRAME_DURATION);
+ } catch (InterruptedException ex) {
+ break;
+ }
+ frameCount++;
+ mPositionMs += FRAME_DURATION;
+
+ if (mPositionMs >= mToMs) {
+ if (!mLoop) {
+ if (mListener != null) {
+ mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, true);
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "PreviewThread.run playback complete");
+ }
+ break;
+ } else {
+ // Fire a notification for the end of the clip
+ if (mListener != null) {
+ mListener.onProgress(VideoEditorTestImpl.this, mToMs, false);
+ }
+
+ // Rewind
+ mPositionMs = mFromMs;
+ if (mListener != null) {
+ mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "PreviewThread.run playback complete");
+ }
+ frameCount = 0;
+ }
+ } else {
+ if (frameCount == mCallbackAfterFrameCount) {
+ if (mListener != null) {
+ mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+ }
+ frameCount = 0;
+ }
+ }
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "===> PreviewThread.run exit");
+ }
+ }
+
+ /**
+ * Stop the preview
+ *
+ * @return The stop position
+ */
+ public long stopPreview() {
+ mRun = false;
+ try {
+ join();
+ } catch (InterruptedException ex) {
+ }
+ return mPositionMs;
+ }
+ };
+
+ /**
+ * Constructor
+ *
+ * @param projectPath
+ */
+ public VideoEditorTestImpl(String projectPath) throws IOException {
+ mProjectPath = projectPath;
+ final File projectXml = new File(projectPath, PROJECT_FILENAME);
+ if (projectXml.exists()) {
+ try {
+ load();
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ } else {
+ mAspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+ mDurationMs = 0;
+ }
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public String getPath() {
+ return mProjectPath;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void addMediaItem(MediaItem mediaItem) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ if (mMediaItems.contains(mediaItem)) {
+ throw new IllegalArgumentException("Media item already exists: " + mediaItem.getId());
+ }
+
+ mMediaItems.add(mediaItem);
+ computeTimelineDuration();
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void insertMediaItem(MediaItem mediaItem, String afterMediaItemId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ if (mMediaItems.contains(mediaItem)) {
+ throw new IllegalArgumentException("Media item already exists: " + mediaItem.getId());
+ }
+
+ if (afterMediaItemId == null) {
+ if (mMediaItems.size() > 0) {
+ final MediaItem mi = mMediaItems.get(0);
+ // Invalidate the transition at the beginning of the timeline
+ removeTransitionBefore(mi);
+ }
+ mMediaItems.add(0, mediaItem);
+ computeTimelineDuration();
+ } else {
+ final int mediaItemCount = mMediaItems.size();
+ for (int i = 0; i < mediaItemCount; i++) {
+ final MediaItem mi = mMediaItems.get(i);
+ if (mi.getId().equals(afterMediaItemId)) {
+ // Invalidate the transition at this position
+ removeTransitionAfter(mi);
+ // Insert the new media item
+ mMediaItems.add(i+1, mediaItem);
+ computeTimelineDuration();
+ return;
+ }
+ }
+ throw new IllegalArgumentException("MediaItem not found: " + afterMediaItemId);
+ }
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void moveMediaItem(String mediaItemId, String afterMediaItemId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ final MediaItem moveMediaItem = removeMediaItem(mediaItemId);
+ if (moveMediaItem == null) {
+ throw new IllegalArgumentException("Target MediaItem not found: " + mediaItemId);
+ }
+
+ if (afterMediaItemId == null) {
+ if (mMediaItems.size() > 0) {
+ final MediaItem mi = mMediaItems.get(0);
+ // Invalidate adjacent transitions at the insertion point
+ removeTransitionBefore(mi);
+ // Insert the media item at the new position
+ mMediaItems.add(0, moveMediaItem);
+ computeTimelineDuration();
+ } else {
+ throw new IllegalStateException("Cannot move media item (it is the only item)");
+ }
+ } else {
+ final int mediaItemCount = mMediaItems.size();
+ for (int i = 0; i < mediaItemCount; i++) {
+ final MediaItem mi = mMediaItems.get(i);
+ if (mi.getId().equals(afterMediaItemId)) {
+ // Invalidate adjacent transitions at the insertion point
+ removeTransitionAfter(mi);
+ // Insert the media item at the new position
+ mMediaItems.add(i+1, moveMediaItem);
+ computeTimelineDuration();
+ return;
+ }
+ }
+
+ throw new IllegalArgumentException("MediaItem not found: " + afterMediaItemId);
+ }
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized MediaItem removeMediaItem(String mediaItemId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ final MediaItem mediaItem = getMediaItem(mediaItemId);
+ if (mediaItem != null) {
+ // Remove the media item
+ mMediaItems.remove(mediaItem);
+ // Remove the adjacent transitions
+ removeAdjacentTransitions(mediaItem);
+ computeTimelineDuration();
+ }
+
+ return mediaItem;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized MediaItem getMediaItem(String mediaItemId) {
+ for (MediaItem mediaItem : mMediaItems) {
+ if (mediaItem.getId().equals(mediaItemId)) {
+ return mediaItem;
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized List<MediaItem> getAllMediaItems() {
+ return mMediaItems;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void removeAllMediaItems() {
+ mMediaItems.clear();
+
+ // Invalidate all transitions
+ for (Transition transition : mTransitions) {
+ transition.invalidate();
+ }
+ mTransitions.clear();
+
+ mDurationMs = 0;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void addTransition(Transition transition) {
+ // If a transition already exists at the specified position then
+ // invalidate it.
+ final Iterator<Transition> it = mTransitions.iterator();
+ while (it.hasNext()) {
+ final Transition t = it.next();
+ if (t.getAfterMediaItem() == transition.getAfterMediaItem()
+ || t.getBeforeMediaItem() == transition.getBeforeMediaItem()) {
+ it.remove();
+ t.invalidate();
+ break;
+ }
+ }
+
+ mTransitions.add(transition);
+
+ // Cross reference the transitions
+ final MediaItem afterMediaItem = transition.getAfterMediaItem();
+ if (afterMediaItem != null) {
+ afterMediaItem.setEndTransition(transition);
+ }
+ final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+ if (beforeMediaItem != null) {
+ beforeMediaItem.setBeginTransition(transition);
+ }
+ computeTimelineDuration();
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized Transition removeTransition(String transitionId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ final Transition transition = getTransition(transitionId);
+ if (transition != null) {
+ mTransitions.remove(transition);
+ transition.invalidate();
+ computeTimelineDuration();
+ }
+
+ // Cross reference the transitions
+ final MediaItem afterMediaItem = transition.getAfterMediaItem();
+ if (afterMediaItem != null) {
+ afterMediaItem.setEndTransition(null);
+ }
+ final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+ if (beforeMediaItem != null) {
+ beforeMediaItem.setBeginTransition(null);
+ }
+
+ return transition;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public List<Transition> getAllTransitions() {
+ return mTransitions;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public Transition getTransition(String transitionId) {
+ for (Transition transition : mTransitions) {
+ if (transition.getId().equals(transitionId)) {
+ return transition;
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void addAudioTrack(AudioTrack audioTrack) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ mAudioTracks.add(audioTrack);
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void insertAudioTrack(AudioTrack audioTrack, String afterAudioTrackId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ if (afterAudioTrackId == null) {
+ mAudioTracks.add(0, audioTrack);
+ } else {
+ final int audioTrackCount = mAudioTracks.size();
+ for (int i = 0; i < audioTrackCount; i++) {
+ AudioTrack at = mAudioTracks.get(i);
+ if (at.getId().equals(afterAudioTrackId)) {
+ mAudioTracks.add(i+1, audioTrack);
+ return;
+ }
+ }
+
+ throw new IllegalArgumentException("AudioTrack not found: " + afterAudioTrackId);
+ }
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void moveAudioTrack(String audioTrackId, String afterAudioTrackId) {
+ throw new IllegalStateException("Not supported");
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized AudioTrack removeAudioTrack(String audioTrackId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ final AudioTrack audioTrack = getAudioTrack(audioTrackId);
+ if (audioTrack != null) {
+ mAudioTracks.remove(audioTrack);
+ }
+
+ return audioTrack;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public AudioTrack getAudioTrack(String audioTrackId) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+
+ final AudioTrack audioTrack = getAudioTrack(audioTrackId);
+ if (audioTrack != null) {
+ mAudioTracks.remove(audioTrack);
+ }
+
+ return audioTrack;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public List<AudioTrack> getAllAudioTracks() {
+ return mAudioTracks;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public void save() throws IOException {
+ final XmlSerializer serializer = Xml.newSerializer();
+ final StringWriter writer = new StringWriter();
+ serializer.setOutput(writer);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", TAG_PROJECT);
+ serializer.attribute("", ATTR_ASPECT_RATIO, Integer.toString(mAspectRatio));
+
+ serializer.startTag("", TAG_MEDIA_ITEMS);
+ for (MediaItem mediaItem : mMediaItems) {
+ serializer.startTag("", TAG_MEDIA_ITEM);
+ serializer.attribute("", ATTR_ID, mediaItem.getId());
+ serializer.attribute("", ATTR_TYPE, mediaItem.getClass().getSimpleName());
+ serializer.attribute("", ATTR_FILENAME, mediaItem.getFilename());
+ serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString(mediaItem.getRenderingMode()));
+ if (mediaItem instanceof MediaVideoItem) {
+ final MediaVideoItem mvi = (MediaVideoItem)mediaItem;
+ serializer.attribute("", ATTR_BEGIN_TIME, Long.toString(mvi.getBoundaryBeginTime()));
+ serializer.attribute("", ATTR_END_TIME, Long.toString(mvi.getBoundaryEndTime()));
+ serializer.attribute("", ATTR_VOLUME, Integer.toString(mvi.getVolume()));
+ if (mvi.getAudioWaveformFilename() != null) {
+ serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME, mvi.getAudioWaveformFilename());
+ }
+ } else if (mediaItem instanceof MediaImageItem) {
+ serializer.attribute("", ATTR_DURATION, Long.toString(mediaItem.getDuration()));
+ }
+ serializer.endTag("", TAG_MEDIA_ITEM);
+ }
+ serializer.endTag("", TAG_MEDIA_ITEMS);
+
+ serializer.endTag("", TAG_PROJECT);
+ serializer.endDocument();
+
+ // Save the metadata XML file
+ final FileOutputStream out = new FileOutputStream(new File(getPath(), PROJECT_FILENAME));
+ out.write(writer.toString().getBytes());
+ out.flush();
+ out.close();
+ }
+
+ /**
+ * Load the project form XML
+ */
+ private void load() throws FileNotFoundException, XmlPullParserException, IOException {
+ final File file = new File(mProjectPath, PROJECT_FILENAME);
+ // Load the metadata
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new FileInputStream(file), "UTF-8");
+ int eventType = parser.getEventType();
+ String name;
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG: {
+ name = parser.getName();
+ if (name.equals(TAG_PROJECT)) {
+ mAspectRatio = Integer.parseInt(parser.getAttributeValue("",
+ ATTR_ASPECT_RATIO));
+ } else if (name.equals(TAG_MEDIA_ITEM)) {
+ final String mediaItemId = parser.getAttributeValue("", ATTR_ID);
+ final String type = parser.getAttributeValue("", ATTR_TYPE);
+ final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+ final int renderingMode = Integer.parseInt(parser.getAttributeValue("", ATTR_RENDERING_MODE));
+ final MediaItem mediaItem;
+ if (MediaImageItem.class.getSimpleName().equals(type)) {
+ final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+ mediaItem = new MediaImageItem(mediaItemId, filename, durationMs,
+ renderingMode);
+ } else if (MediaVideoItem.class.getSimpleName().equals(type)) {
+ final String audioWaveformFilename = parser.getAttributeValue("", ATTR_AUDIO_WAVEFORM_FILENAME);
+ mediaItem = new MediaVideoItem(mediaItemId, filename, renderingMode, audioWaveformFilename);
+
+ final long beginTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+ final long endTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_END_TIME));
+ ((MediaVideoItem)mediaItem).setExtractBoundaries(beginTimeMs, endTimeMs);
+
+ final int volumePercent = Integer.parseInt(parser.getAttributeValue("", ATTR_VOLUME));
+ ((MediaVideoItem)mediaItem).setVolume(volumePercent);
+ } else {
+ Log.e(TAG, "Unknown media item type: " + type);
+ mediaItem = null;
+ }
+ mMediaItems.add(mediaItem);
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+
+ computeTimelineDuration();
+ }
+
+ public void cancelExport(String filename) {
+ }
+
+ public void export(String filename, int height, int bitrate, ExportProgressListener listener)
+ throws IOException {
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public void generatePreview() {
+ // Generate all the needed transitions
+ for (Transition transition : mTransitions) {
+ if (!transition.isGenerated()) {
+ transition.generate();
+ }
+ }
+
+ // This is necessary because the user may had called setDuration on MediaImageItems
+ computeTimelineDuration();
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public void release() {
+ stopPreview();
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public long getDuration() {
+ // Since MediaImageItem can change duration we need to compute the duration here
+ computeTimelineDuration();
+ return mDurationMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public int getAspectRatio() {
+ return mAspectRatio;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public void setAspectRatio(int aspectRatio) {
+ mAspectRatio = aspectRatio;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public long renderPreviewFrame(SurfaceHolder surfaceHolder, long timeMs) {
+ if (mPreviewThread != null) {
+ throw new IllegalStateException("Previewing is in progress");
+ }
+ return timeMs;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized void startPreview(SurfaceHolder surfaceHolder, long fromMs,
+ long toMs, boolean loop, int callbackAfterFrameCount,
+ PreviewProgressListener listener) {
+ if (fromMs >= mDurationMs) {
+ return;
+ }
+ mPreviewThread = new PreviewThread(fromMs, toMs, loop, callbackAfterFrameCount, listener);
+ mPreviewThread.start();
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ public synchronized long stopPreview() {
+ final long stopTimeMs;
+ if (mPreviewThread != null) {
+ stopTimeMs = mPreviewThread.stopPreview();
+ mPreviewThread = null;
+ } else {
+ stopTimeMs = 0;
+ }
+ return stopTimeMs;
+ }
+
+ /**
+ * Compute the duration
+ */
+ private void computeTimelineDuration() {
+ mDurationMs = 0;
+ for (MediaItem mediaItem : mMediaItems) {
+ mDurationMs += mediaItem.getTimelineDuration();
+ }
+
+ // Subtract the transition times
+ for (Transition transition : mTransitions) {
+ if (!(transition instanceof TransitionAtStart) && !(transition instanceof TransitionAtEnd)) {
+ mDurationMs -= transition.getDuration();
+ }
+ }
+ }
+
+ /**
+ * Remove transitions associated with the specified media item
+ *
+ * @param mediaItem The media item
+ */
+ private void removeAdjacentTransitions(MediaItem mediaItem) {
+ final Iterator<Transition> it = mTransitions.iterator();
+ while (it.hasNext()) {
+ Transition t = it.next();
+ if (t.getAfterMediaItem() == mediaItem || t.getBeforeMediaItem() == mediaItem) {
+ it.remove();
+ t.invalidate();
+ mediaItem.setBeginTransition(null);
+ mediaItem.setEndTransition(null);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Remove the transition before this media item
+ *
+ * @param mediaItem The media item
+ */
+ private void removeTransitionBefore(MediaItem mediaItem) {
+ final Iterator<Transition> it = mTransitions.iterator();
+ while (it.hasNext()) {
+ Transition t = it.next();
+ if (t.getBeforeMediaItem() == mediaItem) {
+ it.remove();
+ t.invalidate();
+ mediaItem.setBeginTransition(null);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Remove the transition after this media item
+ *
+ * @param mediaItem The media item
+ */
+ private void removeTransitionAfter(MediaItem mediaItem) {
+ final Iterator<Transition> it = mTransitions.iterator();
+ while (it.hasNext()) {
+ Transition t = it.next();
+ if (t.getAfterMediaItem() == mediaItem) {
+ it.remove();
+ t.invalidate();
+ mediaItem.setEndTransition(null);
+ break;
+ }
+ }
+ }
+}
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 90756d0..8f40130 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -145,6 +145,7 @@
void *pParam,
size_t *pValueSize,
void *pValue);
+int Effect_setEnabled(EffectContext *pContext, bool enabled);
/* Effect Library Interface Implementation */
extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){
@@ -303,32 +304,34 @@
}
LOGV("\tEffectCreate - pBundledContext is %p", pContext->pBundledContext);
+ SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
+
// Create each Effect
if (memcmp(uuid, &gBassBoostDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
// Create Bass Boost
LOGV("\tEffectCreate - Effect to be created is LVM_BASS_BOOST");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBassInstantiated = LVM_TRUE;
+ pSessionContext->bBassInstantiated = LVM_TRUE;
pContext->itfe = &gLvmEffectInterface;
pContext->EffectType = LVM_BASS_BOOST;
} else if (memcmp(uuid, &gVirtualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
// Create Virtualizer
LOGV("\tEffectCreate - Effect to be created is LVM_VIRTUALIZER");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVirtualizerInstantiated=LVM_TRUE;
+ pSessionContext->bVirtualizerInstantiated=LVM_TRUE;
pContext->itfe = &gLvmEffectInterface;
pContext->EffectType = LVM_VIRTUALIZER;
} else if (memcmp(uuid, &gEqualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
// Create Equalizer
LOGV("\tEffectCreate - Effect to be created is LVM_EQUALIZER");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bEqualizerInstantiated = LVM_TRUE;
+ pSessionContext->bEqualizerInstantiated = LVM_TRUE;
pContext->itfe = &gLvmEffectInterface;
pContext->EffectType = LVM_EQUALIZER;
} else if (memcmp(uuid, &gVolumeDescriptor.uuid, sizeof(effect_uuid_t)) == 0){
// Create Volume
LOGV("\tEffectCreate - Effect to be created is LVM_VOLUME");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVolumeInstantiated = LVM_TRUE;
+ pSessionContext->bVolumeInstantiated = LVM_TRUE;
pContext->itfe = &gLvmEffectInterface;
pContext->EffectType = LVM_VOLUME;
@@ -353,29 +356,33 @@
return -EINVAL;
}
+
+ Effect_setEnabled(pContext, LVM_FALSE);
+
+ SessionContext *pSessionContext = &GlobalSessionMemory[pContext->pBundledContext->SessionNo];
+
// Clear the instantiated flag for the effect
if(pContext->EffectType == LVM_BASS_BOOST) {
LOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBassInstantiated = LVM_FALSE;
+ pSessionContext->bBassInstantiated = LVM_FALSE;
} else if(pContext->EffectType == LVM_VIRTUALIZER) {
LOGV("\tEffectRelease LVM_VIRTUALIZER Clearing global intstantiated flag");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVirtualizerInstantiated
- = LVM_FALSE;
+ pSessionContext->bVirtualizerInstantiated = LVM_FALSE;
} else if(pContext->EffectType == LVM_EQUALIZER) {
LOGV("\tEffectRelease LVM_EQUALIZER Clearing global intstantiated flag");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bEqualizerInstantiated =LVM_FALSE;
+ pSessionContext->bEqualizerInstantiated =LVM_FALSE;
} else if(pContext->EffectType == LVM_VOLUME) {
LOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVolumeInstantiated = LVM_FALSE;
+ pSessionContext->bVolumeInstantiated = LVM_FALSE;
} else {
LOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n");
}
// if all effects are no longer instantiaed free the lvm memory and delete BundledEffectContext
- if((GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBassInstantiated == LVM_FALSE)&&
- (GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVolumeInstantiated == LVM_FALSE)&&
- (GlobalSessionMemory[pContext->pBundledContext->SessionNo].bEqualizerInstantiated ==LVM_FALSE)&&
- (GlobalSessionMemory[pContext->pBundledContext->SessionNo].bVirtualizerInstantiated==LVM_FALSE))
+ if ((pSessionContext->bBassInstantiated == LVM_FALSE) &&
+ (pSessionContext->bVolumeInstantiated == LVM_FALSE) &&
+ (pSessionContext->bEqualizerInstantiated ==LVM_FALSE) &&
+ (pSessionContext->bVirtualizerInstantiated==LVM_FALSE))
{
#ifdef LVM_PCM
if (pContext->pBundledContext->PcmInPtr != NULL) {
@@ -402,8 +409,8 @@
}
LOGV("\tEffectRelease: All effects are no longer instantiated\n");
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].bBundledEffectsEnabled =LVM_FALSE;
- GlobalSessionMemory[pContext->pBundledContext->SessionNo].pBundledContext = LVM_NULL;
+ pSessionContext->bBundledEffectsEnabled =LVM_FALSE;
+ pSessionContext->pBundledContext = LVM_NULL;
LOGV("\tEffectRelease: Freeing LVM Bundle memory\n");
LvmEffect_free(pContext);
LOGV("\tEffectRelease: Deleting LVM Bundle context %p\n", pContext->pBundledContext);
@@ -2377,6 +2384,107 @@
return db_fix;
}
+//----------------------------------------------------------------------------
+// Effect_setEnabled()
+//----------------------------------------------------------------------------
+// Purpose:
+// Enable or disable effect
+//
+// Inputs:
+// pContext - pointer to effect context
+// enabled - true if enabling the effect, false otherwise
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Effect_setEnabled(EffectContext *pContext, bool enabled)
+{
+ LOGV("\tEffect_setEnabled() type %d, enabled %d", pContext->EffectType, enabled);
+
+ if (enabled) {
+ switch (pContext->EffectType) {
+ case LVM_BASS_BOOST:
+ if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already enabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->SamplesToExitCountBb =
+ (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
+ pContext->pBundledContext->bBassEnabled = LVM_TRUE;
+ break;
+ case LVM_EQUALIZER:
+ if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already enabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->SamplesToExitCountEq =
+ (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
+ pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
+ break;
+ case LVM_VIRTUALIZER:
+ if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already enabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->SamplesToExitCountVirt =
+ (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
+ pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
+ break;
+ case LVM_VOLUME:
+ if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already enabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
+ break;
+ default:
+ LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->NumberEffectsEnabled++;
+ LvmEffect_enable(pContext);
+ } else {
+ switch (pContext->EffectType) {
+ case LVM_BASS_BOOST:
+ if (pContext->pBundledContext->bBassEnabled == LVM_FALSE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_BASS_BOOST is already disabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->bBassEnabled = LVM_FALSE;
+ break;
+ case LVM_EQUALIZER:
+ if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_EQUALIZER is already disabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
+ break;
+ case LVM_VIRTUALIZER:
+ if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VIRTUALIZER is already disabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
+ break;
+ case LVM_VOLUME:
+ if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) {
+ LOGV("\tLVM_ERROR : Effect_setEnabled() LVM_VOLUME is already disabled");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
+ break;
+ default:
+ LOGV("\tLVM_ERROR : Effect_setEnabled() invalid effect type");
+ return -EINVAL;
+ }
+ pContext->pBundledContext->NumberEffectsEnabled--;
+ LvmEffect_disable(pContext);
+ }
+
+ return 0;
+}
+
} // namespace
} // namespace
@@ -2426,7 +2534,7 @@
//LOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
// pContext->pBundledContext->SamplesToExitCountBb);
} else {
- status = -ENODATA;
+ status = -ENODATA;
}
}
if ((pContext->pBundledContext->bVolumeEnabled == LVM_FALSE)&&
@@ -2843,62 +2951,8 @@
LOGV("\tLVM_ERROR : Effect_command cmdCode Case: EFFECT_CMD_ENABLE: ERROR");
return -EINVAL;
}
- switch (pContext->EffectType){
- case LVM_BASS_BOOST:
- if(pContext->pBundledContext->bBassEnabled == LVM_TRUE){
- LOGV("\tLVM_ERROR : BassBoost_command cmdCode Case: "
- "EFFECT_CMD_ENABLE: ERROR-Effect is already enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bBassEnabled = LVM_TRUE;
- //LOGV("\tEffect_command cmdCode Case:EFFECT_CMD_ENABLE LVM_BASS_BOOSTenabled");
- break;
- case LVM_EQUALIZER:
- if(pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE){
- LOGV("\tLVM_ERROR : Equalizer_command cmdCode Case: "
- "EFFECT_CMD_ENABLE: ERROR-Effect is already enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
- //LOGV("\tEffect_command cmdCode Case:EFFECT_CMD_ENABLE LVM_EQUALIZER enabled");
- break;
- case LVM_VIRTUALIZER:
- if(pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE){
- LOGV("\tLVM_ERROR : Virtualizer_command cmdCode Case: "
- "EFFECT_CMD_ENABLE: ERROR-Effect is already enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
- //LOGV("\tEffect_command cmdCode :EFFECT_CMD_ENABLE LVM_VIRTUALIZER enabled");
- break;
- case LVM_VOLUME:
- if(pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){
- LOGV("\tLVM_ERROR : Volume_command cmdCode Case: "
- "EFFECT_CMD_ENABLE: ERROR-Effect is already enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
- LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE LVM_VOLUME enabled");
- break;
- default:
- LOGV("\tLVM_ERROR : Effect_command cmdCode Case: "
- "EFFECT_CMD_ENABLE: ERROR, invalid Effect Type");
- return -EINVAL;
- }
- *(int *)pReplyData = 0;
- pContext->pBundledContext->NumberEffectsEnabled++;
- android::LvmEffect_enable(pContext);
- pContext->pBundledContext->SamplesToExitCountEq =
- (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
- pContext->pBundledContext->SamplesToExitCountBb =
- (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
- pContext->pBundledContext->SamplesToExitCountVirt =
- (LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1); // 0.1 secs Stereo
- LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE Samples to Exit = %d",
- pContext->pBundledContext->SamplesToExitCountBb);
- //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE NumberEffectsEnabled = %d",
- // pContext->pBundledContext->NumberEffectsEnabled);
- //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_ENABLE end");
+
+ *(int *)pReplyData = android::Effect_setEnabled(pContext, LVM_TRUE);
break;
case EFFECT_CMD_DISABLE:
@@ -2907,57 +2961,7 @@
LOGV("\tLVM_ERROR : Effect_command cmdCode Case: EFFECT_CMD_DISABLE: ERROR");
return -EINVAL;
}
- switch (pContext->EffectType){
- case LVM_BASS_BOOST:
- if(pContext->pBundledContext->bBassEnabled == LVM_FALSE){
- LOGV("\tLVM_ERROR : BassBoost_command cmdCode Case: "
- "EFFECT_CMD_DISABLE: ERROR-Effect is not yet enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bBassEnabled = LVM_FALSE;
- //LOGV("\tEffect_command cmdCode Case: "
- // "EFFECT_CMD_DISABLE LVM_BASS_BOOST disabled");
- break;
- case LVM_EQUALIZER:
- if(pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE){
- LOGV("\tLVM_ERROR : Equalizer_command cmdCode Case: "
- "EFFECT_CMD_DISABLE: ERROR-Effect is not yet enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
- //LOGV("\tEffect_command cmdCode Case: "
- // "EFFECT_CMD_DISABLE LVM_EQUALIZER disabled");
- break;
- case LVM_VIRTUALIZER:
- if(pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE){
- LOGV("\tLVM_ERROR : Virtualizer_command cmdCode Case: "
- "EFFECT_CMD_DISABLE: ERROR-Effect is not yet enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
- //LOGV("\tEffect_command cmdCode Case: "
- // "EFFECT_CMD_DISABLE LVM_VIRTUALIZER disabled");
- break;
- case LVM_VOLUME:
- if(pContext->pBundledContext->bVolumeEnabled == LVM_FALSE){
- LOGV("\tLVM_ERROR : Volume_command cmdCode Case: "
- "EFFECT_CMD_DISABLE: ERROR-Effect is not yet enabled");
- return -EINVAL;
- }
- pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
- //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_DISABLE LVM_VOLUME disabled");
- break;
- default:
- LOGV("\tLVM_ERROR : Effect_command cmdCode Case: "
- "EFFECT_CMD_DISABLE: ERROR, invalid Effect Type");
- return -EINVAL;
- }
- *(int *)pReplyData = 0;
- pContext->pBundledContext->NumberEffectsEnabled--;
- android::LvmEffect_disable(pContext);
- //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_DISABLE NumberEffectsEnabled = %d",
- // pContext->pBundledContext->NumberEffectsEnabled);
- //LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_DISABLE end");
+ *(int *)pReplyData = android::Effect_setEnabled(pContext, LVM_FALSE);
break;
case EFFECT_CMD_SET_DEVICE:
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 645c163..9ad63f0 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -424,24 +424,7 @@
}
/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderTimeLapseHighProfile()
-{
- MediaProfiles::VideoCodec *videoCodec =
- new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 20000000, 720, 480, 20);
-
- AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
- CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
- profile->mCameraId = 0;
- profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
- profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_HIGH;
- profile->mDuration = 60;
- profile->mVideoCodec = videoCodec;
- profile->mAudioCodec = audioCodec;
- return profile;
-}
-
-/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderTimeLapseLowProfile()
+MediaProfiles::createDefaultCamcorderTimeLapseQcifProfile(camcorder_quality quality)
{
MediaProfiles::VideoCodec *videoCodec =
new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 1000000, 176, 144, 20);
@@ -450,7 +433,7 @@
CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
profile->mCameraId = 0;
profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
- profile->mQuality = CAMCORDER_QUALITY_TIME_LAPSE_LOW;
+ profile->mQuality = quality;
profile->mDuration = 60;
profile->mVideoCodec = videoCodec;
profile->mAudioCodec = audioCodec;
@@ -458,24 +441,40 @@
}
/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderHighProfile()
+MediaProfiles::createDefaultCamcorderTimeLapse480pProfile(camcorder_quality quality)
{
MediaProfiles::VideoCodec *videoCodec =
- new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
+ new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 20000000, 720, 480, 20);
AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
profile->mCameraId = 0;
profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
- profile->mQuality = CAMCORDER_QUALITY_HIGH;
+ profile->mQuality = quality;
profile->mDuration = 60;
profile->mVideoCodec = videoCodec;
profile->mAudioCodec = audioCodec;
return profile;
}
+/*static*/ void
+MediaProfiles::createDefaultCamcorderTimeLapseLowProfiles(
+ MediaProfiles::CamcorderProfile **lowTimeLapseProfile,
+ MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile) {
+ *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_LOW);
+ *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_QCIF);
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderTimeLapseHighProfiles(
+ MediaProfiles::CamcorderProfile **highTimeLapseProfile,
+ MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile) {
+ *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_HIGH);
+ *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_480P);
+}
+
/*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderLowProfile()
+MediaProfiles::createDefaultCamcorderQcifProfile(camcorder_quality quality)
{
MediaProfiles::VideoCodec *videoCodec =
new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 192000, 176, 144, 20);
@@ -486,20 +485,72 @@
MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
profile->mCameraId = 0;
profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
- profile->mQuality = CAMCORDER_QUALITY_LOW;
+ profile->mQuality = quality;
profile->mDuration = 30;
profile->mVideoCodec = videoCodec;
profile->mAudioCodec = audioCodec;
return profile;
}
+/*static*/ MediaProfiles::CamcorderProfile*
+MediaProfiles::createDefaultCamcorderCifProfile(camcorder_quality quality)
+{
+ MediaProfiles::VideoCodec *videoCodec =
+ new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
+
+ AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
+ CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+ profile->mCameraId = 0;
+ profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
+ profile->mQuality = quality;
+ profile->mDuration = 60;
+ profile->mVideoCodec = videoCodec;
+ profile->mAudioCodec = audioCodec;
+ return profile;
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderLowProfiles(
+ MediaProfiles::CamcorderProfile **lowProfile,
+ MediaProfiles::CamcorderProfile **lowSpecificProfile) {
+ *lowProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_LOW);
+ *lowSpecificProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_QCIF);
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderHighProfiles(
+ MediaProfiles::CamcorderProfile **highProfile,
+ MediaProfiles::CamcorderProfile **highSpecificProfile) {
+ *highProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_HIGH);
+ *highSpecificProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_CIF);
+}
+
/*static*/ void
MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles)
{
- profiles->mCamcorderProfiles.add(createDefaultCamcorderTimeLapseHighProfile());
- profiles->mCamcorderProfiles.add(createDefaultCamcorderTimeLapseLowProfile());
- profiles->mCamcorderProfiles.add(createDefaultCamcorderHighProfile());
- profiles->mCamcorderProfiles.add(createDefaultCamcorderLowProfile());
+ // low camcorder profiles.
+ MediaProfiles::CamcorderProfile *lowProfile, *lowSpecificProfile;
+ createDefaultCamcorderLowProfiles(&lowProfile, &lowSpecificProfile);
+ profiles->mCamcorderProfiles.add(lowProfile);
+ profiles->mCamcorderProfiles.add(lowSpecificProfile);
+
+ // high camcorder profiles.
+ MediaProfiles::CamcorderProfile* highProfile, *highSpecificProfile;
+ createDefaultCamcorderHighProfiles(&highProfile, &highSpecificProfile);
+ profiles->mCamcorderProfiles.add(highProfile);
+ profiles->mCamcorderProfiles.add(highSpecificProfile);
+
+ // low camcorder time lapse profiles.
+ MediaProfiles::CamcorderProfile *lowTimeLapseProfile, *lowSpecificTimeLapseProfile;
+ createDefaultCamcorderTimeLapseLowProfiles(&lowTimeLapseProfile, &lowSpecificTimeLapseProfile);
+ profiles->mCamcorderProfiles.add(lowTimeLapseProfile);
+ profiles->mCamcorderProfiles.add(lowSpecificTimeLapseProfile);
+
+ // high camcorder time lapse profiles.
+ MediaProfiles::CamcorderProfile *highTimeLapseProfile, *highSpecificTimeLapseProfile;
+ createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile);
+ profiles->mCamcorderProfiles.add(highTimeLapseProfile);
+ profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile);
}
/*static*/ void
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index fe5074b..bf0c6e17 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1053,10 +1053,13 @@
status_t err = setupCamera();
if (err != OK) return err;
- *cameraSource = (mCaptureTimeLapse) ?
- CameraSourceTimeLapse::CreateFromCamera(mCamera,
- mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate):
- CameraSource::CreateFromCamera(mCamera);
+ if (mCaptureTimeLapse) {
+ mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(mCamera,
+ mTimeBetweenTimeLapseFrameCaptureUs, mVideoWidth, mVideoHeight, mFrameRate);
+ *cameraSource = mCameraSourceTimeLapse;
+ } else {
+ *cameraSource = CameraSource::CreateFromCamera(mCamera);
+ }
CHECK(*cameraSource != NULL);
return OK;
@@ -1325,6 +1328,11 @@
LOGV("stop");
status_t err = OK;
+ if (mCaptureTimeLapse && mCameraSourceTimeLapse != NULL) {
+ mCameraSourceTimeLapse->startQuickReadReturns();
+ mCameraSourceTimeLapse = NULL;
+ }
+
if (mCaptureAuxVideo) {
if (mWriterAux != NULL) {
mWriterAux->stop();
@@ -1411,6 +1419,7 @@
mTimeBetweenTimeLapseFrameCaptureUs = -1;
mCaptureAuxVideo = false;
mCameraSourceSplitter = NULL;
+ mCameraSourceTimeLapse = NULL;
mEncoderProfiles = MediaProfiles::getInstance();
mOutputFd = -1;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index f1dc9e6..02d9a01 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -25,6 +25,7 @@
class Camera;
class CameraSource;
+class CameraSourceTimeLapse;
class MediaSourceSplitter;
struct MediaSource;
struct MediaWriter;
@@ -101,6 +102,7 @@
int64_t mTimeBetweenTimeLapseFrameCaptureUs;
bool mCaptureAuxVideo;
sp<MediaSourceSplitter> mCameraSourceSplitter;
+ sp<CameraSourceTimeLapse> mCameraSourceTimeLapse;
String8 mParams;
int mOutputFd, mOutputFdAux;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 0d11b33..3bf8ae7 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -37,6 +37,7 @@
ShoutcastSource.cpp \
StagefrightMediaScanner.cpp \
StagefrightMetadataRetriever.cpp \
+ ThreadedSource.cpp \
ThrottledSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 8d7ada3..29f16d8 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -256,6 +256,8 @@
if (numLostBytes > kMaxBufferSize) {
mPrevLostBytes = numLostBytes - kMaxBufferSize;
numLostBytes = kMaxBufferSize;
+ } else {
+ mPrevLostBytes = 0;
}
CHECK_EQ(numLostBytes & 1, 0);
@@ -275,8 +277,9 @@
memset(buffer->data(), 0, numLostBytes);
buffer->set_range(0, numLostBytes);
if (numFramesRecorded == 0) {
- buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs);
+ buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
}
+ buffer->meta_data()->setInt64(kKeyTime, mStartTimeUs + mPrevSampleTimeUs);
buffer->meta_data()->setInt64(kKeyDriftTime, readTimeUs - mInitialReadTimeUs);
mPrevSampleTimeUs = timestampUs;
*out = buffer;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 9ccd140..0c9eef4 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -82,6 +82,10 @@
}
static int32_t getColorFormat(const char* colorFormat) {
+ if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
+ return OMX_COLOR_FormatYUV420Planar;
+ }
+
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
return OMX_COLOR_FormatYUV422SemiPlanar;
}
@@ -98,6 +102,9 @@
return OMX_COLOR_Format16bitRGB565;
}
+ LOGE("Uknown color format (%s), please add it to "
+ "CameraSource::getColorFormat", colorFormat);
+
CHECK_EQ(0, "Unknown color format");
}
@@ -203,6 +210,7 @@
}
void CameraSource::stopCameraRecording() {
+ mCamera->setListener(NULL);
mCamera->stopRecording();
}
@@ -213,7 +221,6 @@
mFrameAvailableCondition.signal();
int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mCamera->setListener(NULL);
stopCameraRecording();
releaseQueuedFrames();
while (!mFramesBeingEncoded.empty()) {
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 227e090..c1bc433 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -89,11 +89,34 @@
mMeta->setInt32(kKeyWidth, width);
mMeta->setInt32(kKeyHeight, height);
}
+
+ // Initialize quick stop variables.
+ mQuickStop = false;
+ mForceRead = false;
+ mLastReadBufferCopy = NULL;
+ mStopWaitingForIdleCamera = false;
}
CameraSourceTimeLapse::~CameraSourceTimeLapse() {
}
+void CameraSourceTimeLapse::startQuickReadReturns() {
+ Mutex::Autolock autoLock(mQuickStopLock);
+ LOGV("Enabling quick read returns");
+
+ // Enable quick stop mode.
+ mQuickStop = true;
+
+ if (mUseStillCameraForTimeLapse) {
+ // wake up the thread right away.
+ mTakePictureCondition.signal();
+ } else {
+ // Force dataCallbackTimestamp() coming from the video camera to not skip the
+ // next frame as we want read() to get a get a frame right away.
+ mForceRead = true;
+ }
+}
+
bool CameraSourceTimeLapse::trySettingPreviewSize(int32_t width, int32_t height) {
int64_t token = IPCThreadState::self()->clearCallingIdentity();
String8 s = mCamera->getParameters();
@@ -168,6 +191,53 @@
return true;
}
+void CameraSourceTimeLapse::signalBufferReturned(MediaBuffer* buffer) {
+ Mutex::Autolock autoLock(mQuickStopLock);
+ if (mQuickStop && (buffer == mLastReadBufferCopy)) {
+ buffer->setObserver(NULL);
+ buffer->release();
+ } else {
+ return CameraSource::signalBufferReturned(buffer);
+ }
+}
+
+void createMediaBufferCopy(const MediaBuffer& sourceBuffer, int64_t frameTime, MediaBuffer **newBuffer) {
+ size_t sourceSize = sourceBuffer.size();
+ void* sourcePointer = sourceBuffer.data();
+
+ (*newBuffer) = new MediaBuffer(sourceSize);
+ memcpy((*newBuffer)->data(), sourcePointer, sourceSize);
+
+ (*newBuffer)->meta_data()->setInt64(kKeyTime, frameTime);
+}
+
+void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBuffer& sourceBuffer) {
+ int64_t frameTime;
+ CHECK(sourceBuffer.meta_data()->findInt64(kKeyTime, &frameTime));
+ createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy);
+ mLastReadBufferCopy->add_ref();
+ mLastReadBufferCopy->setObserver(this);
+}
+
+status_t CameraSourceTimeLapse::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ if (mLastReadBufferCopy == NULL) {
+ mLastReadStatus = CameraSource::read(buffer, options);
+
+ // mQuickStop may have turned to true while read was blocked. Make a copy of
+ // the buffer in that case.
+ Mutex::Autolock autoLock(mQuickStopLock);
+ if (mQuickStop && *buffer) {
+ fillLastReadBufferCopy(**buffer);
+ }
+ return mLastReadStatus;
+ } else {
+ (*buffer) = mLastReadBufferCopy;
+ (*buffer)->add_ref();
+ return mLastReadStatus;
+ }
+}
+
// static
void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) {
CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
@@ -176,17 +246,31 @@
}
void CameraSourceTimeLapse::threadTimeLapseEntry() {
- while(mStarted) {
- if (mCameraIdle) {
- LOGV("threadTimeLapseEntry: taking picture");
- CHECK_EQ(OK, mCamera->takePicture());
+ while (mStarted) {
+ {
+ Mutex::Autolock autoLock(mCameraIdleLock);
+ if (!mCameraIdle) {
+ mCameraIdleCondition.wait(mCameraIdleLock);
+ }
+ CHECK(mCameraIdle);
mCameraIdle = false;
- usleep(mTimeBetweenTimeLapseFrameCaptureUs);
- } else {
- LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little.");
- usleep(1E4);
}
+
+ // Even if mQuickStop == true we need to take one more picture
+ // as a read() may be blocked, waiting for a frame to get available.
+ // After this takePicture, if mQuickStop == true, we can safely exit
+ // this thread as read() will make a copy of this last frame and keep
+ // returning it in the quick stop mode.
+ Mutex::Autolock autoLock(mQuickStopLock);
+ CHECK_EQ(OK, mCamera->takePicture());
+ if (mQuickStop) {
+ LOGV("threadTimeLapseEntry: Exiting due to mQuickStop = true");
+ return;
+ }
+ mTakePictureCondition.waitRelative(mQuickStopLock,
+ mTimeBetweenTimeLapseFrameCaptureUs * 1000);
}
+ LOGV("threadTimeLapseEntry: Exiting due to mStarted = false");
}
void CameraSourceTimeLapse::startCameraRecording() {
@@ -201,6 +285,7 @@
params.setPictureSize(mPictureWidth, mPictureHeight);
mCamera->setParameters(params.flatten());
mCameraIdle = true;
+ mStopWaitingForIdleCamera = false;
// disable shutter sound and play the recording sound.
mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
@@ -224,12 +309,26 @@
void *dummy;
pthread_join(mThreadTimeLapse, &dummy);
- // play the recording sound and restart preview.
+ // Last takePicture may still be underway. Wait for the camera to get
+ // idle.
+ Mutex::Autolock autoLock(mCameraIdleLock);
+ mStopWaitingForIdleCamera = true;
+ if (!mCameraIdle) {
+ mCameraIdleCondition.wait(mCameraIdleLock);
+ }
+ CHECK(mCameraIdle);
+ mCamera->setListener(NULL);
+
+ // play the recording sound.
mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
- CHECK_EQ(OK, mCamera->startPreview());
} else {
+ mCamera->setListener(NULL);
mCamera->stopRecording();
}
+ if (mLastReadBufferCopy) {
+ mLastReadBufferCopy->release();
+ mLastReadBufferCopy = NULL;
+ }
}
void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) {
@@ -264,7 +363,9 @@
void CameraSourceTimeLapse::threadStartPreview() {
CHECK_EQ(OK, mCamera->startPreview());
+ Mutex::Autolock autoLock(mCameraIdleLock);
mCameraIdle = true;
+ mCameraIdleCondition.signal();
}
void CameraSourceTimeLapse::restartPreview() {
@@ -358,7 +459,23 @@
LOGV("dataCallbackTimestamp timelapse: initial frame");
mLastTimeLapseFrameRealTimestampUs = *timestampUs;
- } else if (*timestampUs <
+ return false;
+ }
+
+ {
+ Mutex::Autolock autoLock(mQuickStopLock);
+
+ // mForceRead may be set to true by startQuickReadReturns(). In that
+ // case don't skip this frame.
+ if (mForceRead) {
+ LOGV("dataCallbackTimestamp timelapse: forced read");
+ mForceRead = false;
+ *timestampUs = mLastFrameTimestampUs;
+ return false;
+ }
+ }
+
+ if (*timestampUs <
(mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
// Skip all frames from last encoded frame until
// sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
@@ -374,6 +491,7 @@
mLastTimeLapseFrameRealTimestampUs = *timestampUs;
*timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
+ return false;
}
}
return false;
@@ -383,6 +501,18 @@
const sp<IMemory> &data) {
if (!mUseStillCameraForTimeLapse) {
mSkipCurrentFrame = skipFrameAndModifyTimeStamp(×tampUs);
+ } else {
+ Mutex::Autolock autoLock(mCameraIdleLock);
+ // If we are using the still camera and stop() has been called, it may
+ // be waiting for the camera to get idle. In that case return
+ // immediately. Calling CameraSource::dataCallbackTimestamp() will lead
+ // to a deadlock since it tries to access CameraSource::mLock which in
+ // this case is held by CameraSource::stop() currently waiting for the
+ // camera to get idle. And camera will not get idle until this call
+ // returns.
+ if (mStopWaitingForIdleCamera) {
+ return;
+ }
}
CameraSource::dataCallbackTimestamp(timestampUs, msgType, data);
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index de4233d..f0d8943 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1675,9 +1675,9 @@
}
- if (mSampleSizes.empty()) {
- err = ERROR_MALFORMED;
- } else if (OK != checkCodecSpecificData()) {
+ if (mSampleSizes.empty() || // no samples written
+ (!mIsAudio && mNumStssTableEntries == 0) || // no sync frames for video
+ (OK != checkCodecSpecificData())) { // no codec specific data
err = ERROR_MALFORMED;
}
mOwner->trackProgressStatus(this, -1, err);
@@ -1794,13 +1794,13 @@
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
if (!mCodecSpecificData ||
mCodecSpecificDataSize <= 0) {
- // Missing codec specific data
+ LOGE("Missing codec specific data");
return ERROR_MALFORMED;
}
} else {
if (mCodecSpecificData ||
mCodecSpecificDataSize > 0) {
- // Unexepected codec specific data found
+ LOGE("Unexepected codec specific data found");
return ERROR_MALFORMED;
}
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index f68c08a..2464efd 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -52,6 +52,8 @@
#include <OMX_Audio.h>
#include <OMX_Component.h>
+#include "include/ThreadedSource.h"
+
namespace android {
static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
@@ -134,6 +136,10 @@
for (size_t i = 0;
i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) {
if (!strcmp(name, kFactoryInfo[i].name)) {
+ if (!strcmp(name, "VPXDecoder")) {
+ return new ThreadedSource(
+ (*kFactoryInfo[i].CreateFunc)(source));
+ }
return (*kFactoryInfo[i].CreateFunc)(source);
}
}
@@ -826,8 +832,7 @@
if (OMX_ErrorNone != mOMX->getParameter(
mNode, OMX_IndexParamVideoPortFormat,
&portFormat, sizeof(portFormat))) {
-
- return UNKNOWN_ERROR;
+ break;
}
// Make sure that omx component does not overwrite
// the incremented index (bug 2897413).
@@ -847,6 +852,8 @@
break;
}
}
+
+ LOGE("color format %d is not supported", colorFormat);
return UNKNOWN_ERROR;
}
@@ -1047,7 +1054,7 @@
mNode, OMX_IndexParamVideoProfileLevelQuerySupported,
¶m, sizeof(param));
- if (err != OK) return err;
+ if (err != OK) break;
int32_t supportedProfile = static_cast<int32_t>(param.eProfile);
int32_t supportedLevel = static_cast<int32_t>(param.eLevel);
@@ -1055,7 +1062,10 @@
supportedProfile, supportedLevel);
if (profile == supportedProfile &&
- level == supportedLevel) {
+ level <= supportedLevel) {
+ // We can further check whether the level is a valid
+ // value; but we will leave that to the omx encoder component
+ // via OMX_SetParameter call.
profileLevel.mProfile = profile;
profileLevel.mLevel = level;
return OK;
diff --git a/media/libstagefright/ThreadedSource.cpp b/media/libstagefright/ThreadedSource.cpp
new file mode 100644
index 0000000..5add2a5
--- /dev/null
+++ b/media/libstagefright/ThreadedSource.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 "include/ThreadedSource.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+static const size_t kMaxQueueSize = 2;
+
+ThreadedSource::ThreadedSource(const sp<MediaSource> &source)
+ : mSource(source),
+ mReflector(new AHandlerReflector<ThreadedSource>(this)),
+ mLooper(new ALooper),
+ mStarted(false) {
+ mLooper->registerHandler(mReflector);
+}
+
+ThreadedSource::~ThreadedSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t ThreadedSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ status_t err = mSource->start(params);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mFinalResult = OK;
+ mSeekTimeUs = -1;
+ mDecodePending = false;
+
+ Mutex::Autolock autoLock(mLock);
+ postDecodeMore_l();
+
+ CHECK_EQ(mLooper->start(), (status_t)OK);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t ThreadedSource::stop() {
+ CHECK(mStarted);
+
+ CHECK_EQ(mLooper->stop(), (status_t)OK);
+
+ Mutex::Autolock autoLock(mLock);
+ clearQueue_l();
+
+ status_t err = mSource->stop();
+
+ mStarted = false;
+
+ return err;
+}
+
+sp<MetaData> ThreadedSource::getFormat() {
+ return mSource->getFormat();
+}
+
+status_t ThreadedSource::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ *buffer = NULL;
+
+ Mutex::Autolock autoLock(mLock);
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ int32_t seekComplete = 0;
+
+ sp<AMessage> msg = new AMessage(kWhatSeek, mReflector->id());
+ msg->setInt64("timeUs", seekTimeUs);
+ msg->setInt32("mode", seekMode);
+ msg->setPointer("complete", &seekComplete);
+ msg->post();
+
+ while (!seekComplete) {
+ mCondition.wait(mLock);
+ }
+ }
+
+ while (mQueue.empty() && mFinalResult == OK) {
+ mCondition.wait(mLock);
+ }
+
+ if (!mQueue.empty()) {
+ *buffer = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+
+ if (mFinalResult == OK) {
+ postDecodeMore_l();
+ }
+
+ return OK;
+ }
+
+ return mFinalResult;
+}
+
+void ThreadedSource::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSeek:
+ {
+ CHECK(msg->findInt64("timeUs", &mSeekTimeUs));
+ CHECK_GE(mSeekTimeUs, 0ll);
+
+ int32_t x;
+ CHECK(msg->findInt32("mode", &x));
+ mSeekMode = (ReadOptions::SeekMode)x;
+
+ int32_t *seekComplete;
+ CHECK(msg->findPointer("complete", (void **)&seekComplete));
+
+ Mutex::Autolock autoLock(mLock);
+ clearQueue_l();
+ mFinalResult = OK;
+
+ *seekComplete = 1;
+ mCondition.signal();
+
+ postDecodeMore_l();
+ break;
+ }
+
+ case kWhatDecodeMore:
+ {
+ {
+ Mutex::Autolock autoLock(mLock);
+ mDecodePending = false;
+
+ if (mQueue.size() == kMaxQueueSize) {
+ break;
+ }
+ }
+
+ MediaBuffer *buffer;
+ ReadOptions options;
+ if (mSeekTimeUs >= 0) {
+ options.setSeekTo(mSeekTimeUs, mSeekMode);
+ mSeekTimeUs = -1ll;
+ }
+ status_t err = mSource->read(&buffer, &options);
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (err != OK) {
+ mFinalResult = err;
+ } else {
+ mQueue.push_back(buffer);
+
+ if (mQueue.size() < kMaxQueueSize) {
+ postDecodeMore_l();
+ }
+ }
+
+ mCondition.signal();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void ThreadedSource::postDecodeMore_l() {
+ if (mDecodePending) {
+ return;
+ }
+
+ mDecodePending = true;
+ (new AMessage(kWhatDecodeMore, mReflector->id()))->post();
+}
+
+void ThreadedSource::clearQueue_l() {
+ while (!mQueue.empty()) {
+ MediaBuffer *buffer = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+
+ buffer->release();
+ buffer = NULL;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 57c1075..aff06bc 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -331,9 +331,20 @@
return err;
}
+ size_t maxBytesToRead =
+ mBitsPerSample == 8 ? kMaxFrameSize / 2 : kMaxFrameSize;
+
+ size_t maxBytesAvailable =
+ (mCurrentPos - mOffset >= (off_t)mSize)
+ ? 0 : mSize - (mCurrentPos - mOffset);
+
+ if (maxBytesToRead > maxBytesAvailable) {
+ maxBytesToRead = maxBytesAvailable;
+ }
+
ssize_t n = mDataSource->readAt(
mCurrentPos, buffer->data(),
- mBitsPerSample == 8 ? kMaxFrameSize / 2 : kMaxFrameSize);
+ maxBytesToRead);
if (n <= 0) {
buffer->release();
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 7154ba5..7483d60 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -317,7 +317,7 @@
&nalType, &nalRefIdc);
if (res != AVCDEC_SUCCESS) {
- LOGE("cannot determine nal type");
+ LOGV("cannot determine nal type");
} else if (nalType == AVC_NALTYPE_SPS || nalType == AVC_NALTYPE_PPS
|| (mSPSSeen && mPPSSeen)) {
switch (nalType) {
@@ -330,6 +330,7 @@
fragSize);
if (res != AVCDEC_SUCCESS) {
+ LOGV("PVAVCDecSeqParamSet returned error %d", res);
break;
}
@@ -396,6 +397,7 @@
fragSize);
if (res != AVCDEC_SUCCESS) {
+ LOGV("PVAVCDecPicParamSet returned error %d", res);
break;
}
@@ -418,8 +420,13 @@
AVCFrameIO Output;
Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
- CHECK_EQ(PVAVCDecGetOutput(mHandle, &index, &Release, &Output),
- AVCDEC_SUCCESS);
+ AVCDec_Status status =
+ PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+ if (status != AVCDEC_SUCCESS) {
+ LOGV("PVAVCDecGetOutput returned error %d", status);
+ break;
+ }
CHECK(index >= 0);
CHECK(index < (int32_t)mFrames.size());
@@ -466,7 +473,7 @@
err = OK;
} else {
- LOGV("failed to decode frame (res = %d)", res);
+ LOGV("PVAVCDecodeSlice returned error %d", res);
}
break;
}
diff --git a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
index fbc97f4..9433178 100644
--- a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
+++ b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp
@@ -29,8 +29,9 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
-#include "vpx_codec/vpx_decoder.h"
-#include "vp8/vp8dx.h"
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vp8dx.h"
namespace android {
@@ -82,9 +83,10 @@
}
mCtx = new vpx_codec_ctx_t;
- if (vpx_codec_dec_init(
- (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0)) {
- LOGE("on2 decoder failed to initialize.");
+ vpx_codec_err_t vpx_err;
+ if ((vpx_err = vpx_codec_dec_init(
+ (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+ LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
mSource->stop();
diff --git a/media/libstagefright/include/ThreadedSource.h b/media/libstagefright/include/ThreadedSource.h
new file mode 100644
index 0000000..c67295c
--- /dev/null
+++ b/media/libstagefright/include/ThreadedSource.h
@@ -0,0 +1,73 @@
+/*
+ * 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 THREADED_SOURCE_H_
+
+#define THREADED_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct ThreadedSource : public MediaSource {
+ ThreadedSource(const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+ virtual ~ThreadedSource();
+
+private:
+ enum {
+ kWhatDecodeMore = 'deco',
+ kWhatSeek = 'seek',
+ };
+
+ sp<MediaSource> mSource;
+ sp<AHandlerReflector<ThreadedSource> > mReflector;
+ sp<ALooper> mLooper;
+
+ Mutex mLock;
+ Condition mCondition;
+ List<MediaBuffer *> mQueue;
+ status_t mFinalResult;
+ bool mDecodePending;
+ bool mStarted;
+
+ int64_t mSeekTimeUs;
+ ReadOptions::SeekMode mSeekMode;
+
+ void postDecodeMore_l();
+ void clearQueue_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadedSource);
+};
+
+} // namespace android
+
+#endif // THREADED_SOURCE_H_
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index b63798f..b3e86eb 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -764,7 +764,11 @@
int64_t lastTimeUs;
CHECK(last->meta()->findInt64("timeUs", &lastTimeUs));
- CHECK_GE(lastTimeUs, firstTimeUs);
+ if (lastTimeUs < firstTimeUs) {
+ LOG(ERROR) << "Huh? Time moving backwards? "
+ << firstTimeUs << " > " << lastTimeUs;
+ return 0;
+ }
return lastTimeUs - firstTimeUs;
}
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index fd6aa62..dcff64c 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -175,19 +175,38 @@
mState = CONNECTING;
- mSocket = socket(AF_INET, SOCK_STREAM, 0);
-
- MakeSocketBlocking(mSocket, false);
-
AString url;
CHECK(msg->findString("url", &url));
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
AString host, path;
unsigned port;
- CHECK(ParseURL(url.c_str(), &host, &port, &path));
+ if (!ParseURL(url.c_str(), &host, &port, &path)) {
+ LOG(ERROR) << "Malformed rtsp url " << url;
+
+ reply->setInt32("result", ERROR_MALFORMED);
+ reply->post();
+
+ mState = DISCONNECTED;
+ return;
+ }
struct hostent *ent = gethostbyname(host.c_str());
- CHECK(ent != NULL);
+ if (ent == NULL) {
+ LOG(ERROR) << "Unknown host " << host;
+
+ reply->setInt32("result", -ENOENT);
+ reply->post();
+
+ mState = DISCONNECTED;
+ return;
+ }
+
+ mSocket = socket(AF_INET, SOCK_STREAM, 0);
+
+ MakeSocketBlocking(mSocket, false);
struct sockaddr_in remote;
memset(remote.sin_zero, 0, sizeof(remote.sin_zero));
@@ -198,9 +217,6 @@
int err = ::connect(
mSocket, (const struct sockaddr *)&remote, sizeof(remote));
- sp<AMessage> reply;
- CHECK(msg->findMessage("reply", &reply));
-
reply->setInt32("server-ip", ntohl(remote.sin_addr.s_addr));
if (err < 0) {
@@ -337,13 +353,20 @@
if (n == 0) {
// Server closed the connection.
- TRESPASS();
+ LOG(ERROR) << "Server unexpectedly closed the connection.";
+
+ reply->setInt32("result", ERROR_IO);
+ reply->post();
+ return;
} else if (n < 0) {
if (errno == EINTR) {
continue;
}
- TRESPASS();
+ LOG(ERROR) << "Error sending rtsp request.";
+ reply->setInt32("result", -errno);
+ reply->post();
+ return;
}
numBytesSent += (size_t)n;
@@ -415,13 +438,15 @@
ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
if (n == 0) {
// Server closed the connection.
+ LOG(ERROR) << "Server unexpectedly closed the connection.";
return ERROR_IO;
} else if (n < 0) {
if (errno == EINTR) {
continue;
}
- TRESPASS();
+ LOG(ERROR) << "Error reading rtsp response.";
+ return -errno;
}
offset += (size_t)n;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 0aefd58..a3864f1 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -209,22 +209,24 @@
response->mContent->data(),
response->mContent->size());
- CHECK(mSessionDesc->isValid());
-
- ssize_t i = response->mHeaders.indexOfKey("content-base");
- if (i >= 0) {
- mBaseURL = response->mHeaders.valueAt(i);
+ if (!mSessionDesc->isValid()) {
+ result = ERROR_MALFORMED;
} else {
- i = response->mHeaders.indexOfKey("content-location");
+ ssize_t i = response->mHeaders.indexOfKey("content-base");
if (i >= 0) {
mBaseURL = response->mHeaders.valueAt(i);
} else {
- mBaseURL = mSessionURL;
+ i = response->mHeaders.indexOfKey("content-location");
+ if (i >= 0) {
+ mBaseURL = response->mHeaders.valueAt(i);
+ } else {
+ mBaseURL = mSessionURL;
+ }
}
- }
- CHECK_GT(mSessionDesc->countTracks(), 1u);
- setupTrack(1);
+ CHECK_GT(mSessionDesc->countTracks(), 1u);
+ setupTrack(1);
+ }
}
}
@@ -333,13 +335,17 @@
sp<ARTSPResponse> response =
static_cast<ARTSPResponse *>(obj.get());
- CHECK_EQ(response->mStatusCode, 200u);
+ if (response->mStatusCode != 200) {
+ result = UNKNOWN_ERROR;
+ } else {
+ parsePlayResponse(response);
- parsePlayResponse(response);
+ sp<AMessage> timeout = new AMessage('tiou', id());
+ timeout->post(kStartupTimeoutUs);
+ }
+ }
- sp<AMessage> timeout = new AMessage('tiou', id());
- timeout->post(kStartupTimeoutUs);
- } else {
+ if (result != OK) {
sp<AMessage> reply = new AMessage('disc', id());
mConn->disconnect(reply);
}
@@ -477,6 +483,11 @@
uint32_t seqNum = (uint32_t)accessUnit->int32Data();
+ if (mSeekPending) {
+ LOG(INFO) << "we're seeking, dropping stale packet.";
+ break;
+ }
+
if (seqNum < track->mFirstSeqNumInSegment) {
LOG(INFO) << "dropping stale access-unit "
<< "(" << seqNum << " < "
@@ -600,18 +611,26 @@
LOG(INFO) << "PLAY completed with result "
<< result << " (" << strerror(-result) << ")";
- CHECK_EQ(result, (status_t)OK);
+ if (result == OK) {
+ sp<RefBase> obj;
+ CHECK(msg->findObject("response", &obj));
+ sp<ARTSPResponse> response =
+ static_cast<ARTSPResponse *>(obj.get());
- sp<RefBase> obj;
- CHECK(msg->findObject("response", &obj));
- sp<ARTSPResponse> response =
- static_cast<ARTSPResponse *>(obj.get());
+ if (response->mStatusCode != 200) {
+ result = UNKNOWN_ERROR;
+ } else {
+ parsePlayResponse(response);
- CHECK_EQ(response->mStatusCode, 200u);
+ LOG(INFO) << "seek completed.";
+ }
+ }
- parsePlayResponse(response);
+ if (result != OK) {
+ LOG(ERROR) << "seek failed, aborting.";
+ (new AMessage('abor', id()))->post();
+ }
- LOG(INFO) << "seek completed.";
mSeekPending = false;
break;
}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index c1134bf..5580700 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -295,7 +295,6 @@
AINPUT_SOURCE_CLASS_POINTER = 0x00000002,
AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004,
AINPUT_SOURCE_CLASS_POSITION = 0x00000008,
- AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010,
};
enum {
@@ -303,13 +302,10 @@
AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON,
- AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON,
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
- AINPUT_SOURCE_JOYSTICK_LEFT = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
- AINPUT_SOURCE_JOYSTICK_RIGHT = 0x02000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
};
/*
diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h
index a5b3ead..65ab5e4 100644
--- a/opengl/include/GLES/glext.h
+++ b/opengl/include/GLES/glext.h
@@ -211,9 +211,12 @@
#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5
#endif
-/* GL_OES_texture_external */
-#ifndef GL_TEXTURE_EXTERNAL_OES
+/* GL_OES_EGL_image_external */
+#ifndef GL_OES_EGL_image_external
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#define GL_SAMPLER_EXTERNAL_OES 0x8D66
+#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67
+#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68
#endif
/*------------------------------------------------------------------------*
@@ -782,9 +785,9 @@
typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array);
#endif
-/* GL_OES_texture_external */
-#ifndef GL_OES_texture_external
-#define GL_OES_texture_external 1
+/* GL_OES_EGL_image_external */
+#ifndef GL_OES_EGL_image_external
+#define GL_OES_EGL_image_external 1
#endif
/*------------------------------------------------------------------------*
diff --git a/opengl/include/GLES2/gl2ext.h b/opengl/include/GLES2/gl2ext.h
index de5d65a..9db4e252c 100644
--- a/opengl/include/GLES2/gl2ext.h
+++ b/opengl/include/GLES2/gl2ext.h
@@ -146,9 +146,12 @@
#define GL_INT_10_10_10_2_OES 0x8DF7
#endif
-/* GL_OES_texture_external */
-#ifndef GL_TEXTURE_EXTERNAL_OES
+/* GL_OES_EGL_image_external */
+#ifndef GL_OES_EGL_image_external
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#define GL_SAMPLER_EXTERNAL_OES 0x8D66
+#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67
+#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68
#endif
/*------------------------------------------------------------------------*
@@ -546,9 +549,9 @@
#define GL_OES_vertex_type_10_10_10_2 1
#endif
-/* GL_OES_texture_external */
-#ifndef GL_OES_texture_external
-#define GL_OES_texture_external 1
+/* GL_OES_EGL_image_external */
+#ifndef GL_OES_EGL_image_external
+#define GL_OES_EGL_image_external 1
#endif
/*------------------------------------------------------------------------*
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png
new file mode 100644
index 0000000..95ba181
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
new file mode 100644
index 0000000..adf668d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
new file mode 100644
index 0000000..7bf6b51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
new file mode 100644
index 0000000..78738ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
new file mode 100644
index 0000000..ac88143
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png
new file mode 100644
index 0000000..3e317dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png
new file mode 100644
index 0000000..72329f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png
new file mode 100644
index 0000000..558c49c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png
new file mode 100644
index 0000000..6440bdd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png
new file mode 100644
index 0000000..fe20423
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
deleted file mode 100644
index bfbf18e..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
deleted file mode 100644
index 896ba4d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
deleted file mode 100644
index af79eff..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
deleted file mode 100644
index 92c09c8..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
deleted file mode 100644
index f04fb11..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
deleted file mode 100644
index cb7b7b3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
deleted file mode 100644
index 5376e92..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
deleted file mode 100644
index fd54363..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
deleted file mode 100644
index 6c4873a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
deleted file mode 100644
index a3320cb..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
deleted file mode 100755
index 2f4fd4f..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
deleted file mode 100644
index 5aa23f6..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 5dedcc9..7ccf210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -120,19 +120,29 @@
//***** Signal strength icons
//GSM/UMTS
- private static final int[] sSignalImages = new int[] {
- R.drawable.stat_sys_signal_0,
- R.drawable.stat_sys_signal_1,
- R.drawable.stat_sys_signal_2,
- R.drawable.stat_sys_signal_3,
- R.drawable.stat_sys_signal_4
+ private static final int[][] sSignalImages = {
+ { R.drawable.stat_sys_signal_0,
+ R.drawable.stat_sys_signal_1,
+ R.drawable.stat_sys_signal_2,
+ R.drawable.stat_sys_signal_3,
+ R.drawable.stat_sys_signal_4 },
+ { R.drawable.stat_sys_signal_0_fully,
+ R.drawable.stat_sys_signal_1_fully,
+ R.drawable.stat_sys_signal_2_fully,
+ R.drawable.stat_sys_signal_3_fully,
+ R.drawable.stat_sys_signal_4_fully }
};
- private static final int[] sSignalImages_r = new int[] {
- R.drawable.stat_sys_r_signal_0,
- R.drawable.stat_sys_r_signal_1,
- R.drawable.stat_sys_r_signal_2,
- R.drawable.stat_sys_r_signal_3,
- R.drawable.stat_sys_r_signal_4
+ private static final int[][] sSignalImages_r = {
+ { R.drawable.stat_sys_r_signal_0,
+ R.drawable.stat_sys_r_signal_1,
+ R.drawable.stat_sys_r_signal_2,
+ R.drawable.stat_sys_r_signal_3,
+ R.drawable.stat_sys_r_signal_4 },
+ { R.drawable.stat_sys_r_signal_0_fully,
+ R.drawable.stat_sys_r_signal_1_fully,
+ R.drawable.stat_sys_r_signal_2_fully,
+ R.drawable.stat_sys_r_signal_3_fully,
+ R.drawable.stat_sys_r_signal_4_fully }
};
private static final int[] sRoamingIndicatorImages_cdma = new int[] {
R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
@@ -330,7 +340,9 @@
private int mLastWifiSignalLevel = -1;
private boolean mIsWifiConnected = false;
- private int mLastWifiInetConnectivityState = 0;
+
+ // state of inet connection - 0 not connected, 100 connected
+ private int mInetCondition = 0;
// sync state
// If sync is active the SyncActive icon is displayed. If sync is not active but
@@ -381,7 +393,8 @@
else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
updateTTY(intent);
}
- else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
+ action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
// TODO - stop using other means to get wifi/mobile info
updateConnectivity(intent);
}
@@ -489,6 +502,7 @@
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
// load config to determine if to distinguish Hspa data icon
@@ -704,19 +718,19 @@
if (info.isConnected()) {
updateDataNetType(info.getSubtype(), connectionStatus);
updateDataIcon();
+ updateSignalStrength(); // apply any change in connectionStatus
}
break;
case ConnectivityManager.TYPE_WIFI:
if (info.isConnected()) {
mIsWifiConnected = true;
- mLastWifiInetConnectivityState =
+ mInetCondition =
(connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
int iconId;
if (mLastWifiSignalLevel == -1) {
- iconId = sWifiSignalImages[mLastWifiInetConnectivityState][0];
+ iconId = sWifiSignalImages[mInetCondition][0];
} else {
- iconId = sWifiSignalImages[mLastWifiInetConnectivityState]
- [mLastWifiSignalLevel];
+ iconId = sWifiSignalImages[mInetCondition][mLastWifiSignalLevel];
}
mService.setIcon("wifi", iconId, 0);
// Show the icon since wi-fi is connected
@@ -724,13 +738,14 @@
} else {
mLastWifiSignalLevel = -1;
mIsWifiConnected = false;
- mLastWifiInetConnectivityState = 0;
+ mInetCondition = 0;
int iconId = sWifiSignalImages[0][0];
mService.setIcon("wifi", iconId, 0);
// Hide the icon since we're not connected
mService.setIconVisibility("wifi", false);
}
+ updateSignalStrength(); // apply any change in mInetCondition
break;
}
}
@@ -764,6 +779,7 @@
mDataState = state;
updateDataNetType(networkType, 0);
updateDataIcon();
+ updateSignalStrength(); // apply the change in connection status
}
@Override
@@ -858,12 +874,12 @@
// Though mPhone is a Manager, this call is not an IPC
if (mPhone.isNetworkRoaming()) {
- iconList = sSignalImages_r;
+ iconList = sSignalImages_r[mInetCondition];
} else {
- iconList = sSignalImages;
+ iconList = sSignalImages[mInetCondition];
}
} else {
- iconList = this.sSignalImages;
+ iconList = sSignalImages[mInetCondition];
// If 3G(EV) and 1x network are available than 3G should be
// displayed, displayed RSSI should be from the EV side.
@@ -925,37 +941,37 @@
}
private final void updateDataNetType(int net, int inetCondition) {
- int connected = (inetCondition > INET_CONDITION_THRESHOLD ? 1 : 0);
+ mInetCondition = (inetCondition > INET_CONDITION_THRESHOLD ? 1 : 0);
switch (net) {
case TelephonyManager.NETWORK_TYPE_EDGE:
- mDataIconList = sDataNetType_e[connected];
+ mDataIconList = sDataNetType_e[mInetCondition];
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
- mDataIconList = sDataNetType_3g[connected];
+ mDataIconList = sDataNetType_3g[mInetCondition];
break;
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
if (mHspaDataDistinguishable) {
- mDataIconList = sDataNetType_h[connected];
+ mDataIconList = sDataNetType_h[mInetCondition];
} else {
- mDataIconList = sDataNetType_3g[connected];
+ mDataIconList = sDataNetType_3g[mInetCondition];
}
break;
case TelephonyManager.NETWORK_TYPE_CDMA:
// display 1xRTT for IS95A/B
- mDataIconList = sDataNetType_1x[connected];
+ mDataIconList = sDataNetType_1x[mInetCondition];
break;
case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = sDataNetType_1x[connected];
+ mDataIconList = sDataNetType_1x[mInetCondition];
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
- mDataIconList = sDataNetType_3g[connected];
+ mDataIconList = sDataNetType_3g[mInetCondition];
break;
default:
- mDataIconList = sDataNetType_g[connected];
+ mDataIconList = sDataNetType_g[mInetCondition];
break;
}
}
@@ -1104,7 +1120,7 @@
if (newSignalLevel != mLastWifiSignalLevel) {
mLastWifiSignalLevel = newSignalLevel;
if (mIsWifiConnected) {
- iconId = sWifiSignalImages[mLastWifiInetConnectivityState][newSignalLevel];
+ iconId = sWifiSignalImages[mInetCondition][newSignalLevel];
} else {
iconId = sWifiTemporarilyNotConnectedImage;
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 96a239d..6935e92 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1069,7 +1069,15 @@
}
private void sendConnectedBroadcast(NetworkInfo info) {
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION);
+ }
+
+ private void sendInetConditionBroadcast(NetworkInfo info) {
+ sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
+ }
+
+ private void sendGeneralBroadcast(NetworkInfo info, String bcastType) {
+ Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1713,7 +1721,7 @@
break;
}
mDefaultInetConditionPublished = mDefaultInetCondition;
- sendConnectedBroadcast(networkInfo);
+ sendInetConditionBroadcast(networkInfo);
break;
}
}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 90d036a..024aec5 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -24,18 +24,13 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Environment;
-import android.os.LocalPowerManager;
-import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.Surface;
-import android.view.WindowManagerPolicy;
import java.io.BufferedReader;
import java.io.File;
@@ -83,7 +78,6 @@
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetFocusedApplication(InputApplication application);
- private static native void nativePreemptInputDispatch();
private static native InputDevice nativeGetInputDevice(int deviceId);
private static native int[] nativeGetInputDeviceIds();
private static native String nativeDump();
@@ -331,10 +325,6 @@
nativeSetFocusedApplication(application);
}
- public void preemptInputDispatch() {
- nativePreemptInputDispatch();
- }
-
public void setInputDispatchMode(boolean enabled, boolean frozen) {
nativeSetInputDispatchMode(enabled, frozen);
}
@@ -395,20 +385,10 @@
public void notifyInputChannelBroken(InputChannel inputChannel) {
mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
}
-
- @SuppressWarnings("unused")
- public long notifyInputChannelANR(InputChannel inputChannel) {
- return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
- }
-
- @SuppressWarnings("unused")
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
- }
@SuppressWarnings("unused")
- public long notifyANR(Object token) {
- return mWindowManagerService.mInputMonitor.notifyANR(token);
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
}
@SuppressWarnings("unused")
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index dbc59ef..befc770 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -27,6 +27,9 @@
// The input channel associated with the window.
public InputChannel inputChannel;
+ // The window name.
+ public String name;
+
// Window layout params attributes. (WindowManager.LayoutParams)
public int layoutParamsFlags;
public int layoutParamsType;
@@ -55,6 +58,9 @@
// Window is visible.
public boolean visible;
+ // Window can receive keys.
+ public boolean canReceiveKeys;
+
// Window has focus.
public boolean hasFocus;
@@ -64,6 +70,9 @@
// Input event dispatching is paused.
public boolean paused;
+ // Window layer.
+ public int layer;
+
// Id of process and user that owns the window.
public int ownerPid;
public int ownerUid;
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 0a90a4c..a33b7c294 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.LinkCapabilities;
import android.net.LinkProperties;
import android.os.Binder;
import android.os.Bundle;
@@ -92,7 +93,9 @@
private ArrayList<String> mConnectedApns;
- private LinkProperties mDataConnectionProperties;
+ private LinkProperties mDataConnectionLinkProperties;
+
+ private LinkCapabilities mDataConnectionLinkCapabilities;
private Bundle mCellLocation = new Bundle();
@@ -356,7 +359,7 @@
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
- int networkType) {
+ LinkCapabilities linkCapabilities, int networkType) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -382,8 +385,8 @@
}
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
- mDataConnectionApn = apn;
- mDataConnectionProperties = linkProperties;
+ mDataConnectionLinkProperties = linkProperties;
+ mDataConnectionLinkCapabilities = linkCapabilities;
if (mDataConnectionNetworkType != networkType) {
mDataConnectionNetworkType = networkType;
modified = true;
@@ -403,7 +406,7 @@
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnType, linkProperties);
+ apnType, linkProperties, linkCapabilities);
}
public void notifyDataConnectionFailed(String reason, String apnType) {
@@ -490,7 +493,8 @@
pw.println(" mDataConnectionPossible=" + mDataConnectionPossible);
pw.println(" mDataConnectionReason=" + mDataConnectionReason);
pw.println(" mDataConnectionApn=" + mDataConnectionApn);
- pw.println(" mDataConnectionProperties=" + mDataConnectionProperties);
+ pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
+ pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
pw.println(" mCellLocation=" + mCellLocation);
pw.println("registrations: count=" + recordCount);
for (Record r : mRecords) {
@@ -564,7 +568,8 @@
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
- String reason, String apn, String apnType, LinkProperties linkProperties) {
+ String reason, String apn, String apnType, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities) {
// 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.
@@ -584,6 +589,9 @@
intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface.getName());
}
}
+ if (linkCapabilities != null) {
+ intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
+ }
intent.putExtra(Phone.DATA_APN_KEY, apn);
intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
mContext.sendStickyBroadcast(intent);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index dacdd7d..fb8e888 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5088,61 +5088,39 @@
}
}
- /* Notifies the window manager about an input channel that is not responding.
+ /* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
- public long notifyInputChannelANR(InputChannel inputChannel) {
- AppWindowToken token;
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return 0; // window is unknown, abort dispatching
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ AppWindowToken appWindowToken = null;
+ if (inputChannel != null) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState != null) {
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ appWindowToken = windowState.mAppToken;
+ }
}
-
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- token = windowState.mAppToken;
}
- return notifyANRInternal(token);
- }
-
- /* Notifies the window manager about an input channel spontaneously recovering from ANR
- * by successfully delivering the event that originally timed out.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- // Nothing to do just now.
- // Just wait for the user to dismiss the ANR dialog.
- }
-
- /* Notifies the window manager about an application that is not responding
- * in general rather than with respect to a particular input channel.
- * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
- *
- * Called by the InputManager.
- */
- public long notifyANR(Object token) {
- AppWindowToken appWindowToken = (AppWindowToken) token;
+ if (appWindowToken == null && token != null) {
+ appWindowToken = (AppWindowToken) token;
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ }
- Slog.i(TAG, "Input event dispatching timed out sending to application "
- + appWindowToken.stringName);
- return notifyANRInternal(appWindowToken);
- }
-
- private long notifyANRInternal(AppWindowToken token) {
- if (token != null && token.appToken != null) {
+ if (appWindowToken != null && appWindowToken.appToken != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- boolean abort = token.appToken.keyDispatchingTimedOut();
+ boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
if (! abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return token.inputDispatchingTimeoutNanos;
+ return appWindowToken.inputDispatchingTimeoutNanos;
}
} catch (RemoteException ex) {
}
@@ -5195,13 +5173,16 @@
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.name = child.toString();
inputWindow.layoutParamsFlags = flags;
inputWindow.layoutParamsType = type;
inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
inputWindow.visible = isVisible;
+ inputWindow.canReceiveKeys = child.canReceiveKeys();
inputWindow.hasFocus = hasFocus;
inputWindow.hasWallpaper = hasWallpaper;
inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.layer = child.mLayer;
inputWindow.ownerPid = child.mSession.mPid;
inputWindow.ownerUid = child.mSession.mUid;
@@ -5294,23 +5275,6 @@
if (newWindow != mInputFocus) {
if (newWindow != null && newWindow.canReceiveKeys()) {
- // If the new input focus is an error window or appears above the current
- // input focus, preempt any pending synchronous dispatch so that we can
- // start delivering events to the new input focus as soon as possible.
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
- }
- preemptInputDispatchLw();
- } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Transferring focus to new window at higher layer: "
- + "old win layer=" + mInputFocus.mLayer
- + ", new win layer=" + newWindow.mLayer);
- }
- preemptInputDispatchLw();
- }
-
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
@@ -5322,14 +5286,6 @@
}
}
- /* Tells the dispatcher to stop waiting for its current synchronous event targets.
- * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
- * can begin.
- */
- private void preemptInputDispatchLw() {
- mInputManager.preemptInputDispatch();
- }
-
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {
diff --git a/services/java/com/android/server/sip/SipHelper.java b/services/java/com/android/server/sip/SipHelper.java
index d9a1bbf..050eddc 100644
--- a/services/java/com/android/server/sip/SipHelper.java
+++ b/services/java/com/android/server/sip/SipHelper.java
@@ -68,6 +68,7 @@
*/
class SipHelper {
private static final String TAG = SipHelper.class.getSimpleName();
+ private static final boolean DEBUG = true;
private SipStack mSipStack;
private SipProvider mSipProvider;
@@ -264,6 +265,7 @@
ClientTransaction clientTransaction =
mSipProvider.getNewClientTransaction(request);
+ if (DEBUG) Log.d(TAG, "send INVITE: " + request);
clientTransaction.sendRequest();
return clientTransaction;
} catch (ParseException e) {
@@ -281,6 +283,7 @@
ClientTransaction clientTransaction =
mSipProvider.getNewClientTransaction(request);
+ if (DEBUG) Log.d(TAG, "send RE-INVITE: " + request);
dialog.sendRequest(clientTransaction);
return clientTransaction;
} catch (ParseException e) {
@@ -314,6 +317,7 @@
ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
toHeader.setTag(tag);
response.addHeader(toHeader);
+ if (DEBUG) Log.d(TAG, "send RINGING: " + response);
transaction.sendResponse(response);
return transaction;
} catch (ParseException e) {
@@ -340,7 +344,9 @@
if (inviteTransaction == null) {
inviteTransaction = getServerTransaction(event);
}
+
if (inviteTransaction.getState() != TransactionState.COMPLETED) {
+ if (DEBUG) Log.d(TAG, "send OK: " + response);
inviteTransaction.sendResponse(response);
}
@@ -358,6 +364,7 @@
Response.BUSY_HERE, request);
if (inviteTransaction.getState() != TransactionState.COMPLETED) {
+ if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
inviteTransaction.sendResponse(response);
}
} catch (ParseException e) {
@@ -373,27 +380,31 @@
Response response = event.getResponse();
long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
.getSeqNumber();
- dialog.sendAck(dialog.createAck(cseq));
+ Request ack = dialog.createAck(cseq);
+ if (DEBUG) Log.d(TAG, "send ACK: " + ack);
+ dialog.sendAck(ack);
}
public void sendBye(Dialog dialog) throws SipException {
Request byeRequest = dialog.createRequest(Request.BYE);
- Log.d(TAG, "send BYE: " + byeRequest);
+ if (DEBUG) Log.d(TAG, "send BYE: " + byeRequest);
dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
}
public void sendCancel(ClientTransaction inviteTransaction)
throws SipException {
Request cancelRequest = inviteTransaction.createCancel();
+ if (DEBUG) Log.d(TAG, "send CANCEL: " + cancelRequest);
mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
}
public void sendResponse(RequestEvent event, int responseCode)
throws SipException {
try {
- getServerTransaction(event).sendResponse(
- mMessageFactory.createResponse(
- responseCode, event.getRequest()));
+ Response response = mMessageFactory.createResponse(
+ responseCode, event.getRequest());
+ if (DEBUG) Log.d(TAG, "send response: " + response);
+ getServerTransaction(event).sendResponse(response);
} catch (ParseException e) {
throw new SipException("sendResponse()", e);
}
@@ -402,8 +413,10 @@
public void sendInviteRequestTerminated(Request inviteRequest,
ServerTransaction inviteTransaction) throws SipException {
try {
- inviteTransaction.sendResponse(mMessageFactory.createResponse(
- Response.REQUEST_TERMINATED, inviteRequest));
+ Response response = mMessageFactory.createResponse(
+ Response.REQUEST_TERMINATED, inviteRequest);
+ if (DEBUG) Log.d(TAG, "send response: " + response);
+ inviteTransaction.sendResponse(response);
} catch (ParseException e) {
throw new SipException("sendInviteRequestTerminated()", e);
}
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index c1841f6..d7747fb 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -59,6 +59,8 @@
*/
public final class SipService extends ISipService.Stub {
private static final String TAG = "SipService";
+ private static final boolean DEBUG = true;
+ private static final boolean DEBUG_TIMER = DEBUG && false;
private static final int EXPIRY_TIME = 3600;
private static final int SHORT_EXPIRY_TIME = 10;
private static final int MIN_EXPIRY_TIME = 60;
@@ -90,7 +92,7 @@
}
private SipService(Context context) {
- Log.v(TAG, " service started!");
+ if (DEBUG) Log.d(TAG, " service started!");
mContext = context;
mConnectivityReceiver = new ConnectivityReceiver();
context.registerReceiver(mConnectivityReceiver,
@@ -137,7 +139,7 @@
throw new RuntimeException(
"empty broadcast action for incoming call");
}
- Log.v(TAG, "open3: " + localProfile.getUriString() + ": "
+ if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
+ incomingCallBroadcastAction + ": " + listener);
try {
SipSessionGroupExt group = createGroup(localProfile,
@@ -238,14 +240,14 @@
}
private void notifyProfileAdded(SipProfile localProfile) {
- Log.d(TAG, "notify: profile added: " + localProfile);
+ if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
mContext.sendBroadcast(intent);
}
private void notifyProfileRemoved(SipProfile localProfile) {
- Log.d(TAG, "notify: profile removed: " + localProfile);
+ if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
mContext.sendBroadcast(intent);
@@ -260,7 +262,7 @@
private void grabWifiLock() {
if (mWifiLock == null) {
- Log.v(TAG, "acquire wifi lock");
+ if (DEBUG) Log.d(TAG, "acquire wifi lock");
mWifiLock = ((WifiManager)
mContext.getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
@@ -270,7 +272,7 @@
private void releaseWifiLock() {
if (mWifiLock != null) {
- Log.v(TAG, "release wifi lock");
+ if (DEBUG) Log.d(TAG, "release wifi lock");
mWifiLock.release();
mWifiLock = null;
}
@@ -283,7 +285,7 @@
private synchronized void onConnectivityChanged(
String type, boolean connected) {
- Log.v(TAG, "onConnectivityChanged(): "
+ if (DEBUG) Log.d(TAG, "onConnectivityChanged(): "
+ mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
+ " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
@@ -398,7 +400,7 @@
mSipGroup.openToReceiveCalls(this);
mAutoRegistration.start(mSipGroup);
}
- Log.v(TAG, " openToReceiveCalls: " + getUri() + ": "
+ if (DEBUG) Log.d(TAG, " openToReceiveCalls: " + getUri() + ": "
+ mIncomingCallBroadcastAction);
}
@@ -410,8 +412,8 @@
if (mOpened) openToReceiveCalls();
} else {
// close mSipGroup but remember mOpened
- Log.v(TAG, " close auto reg temporarily: " + getUri() + ": "
- + mIncomingCallBroadcastAction);
+ if (DEBUG) Log.d(TAG, " close auto reg temporarily: "
+ + getUri() + ": " + mIncomingCallBroadcastAction);
mSipGroup.close();
mAutoRegistration.stop();
}
@@ -437,7 +439,7 @@
mOpened = false;
mSipGroup.closeToNotReceiveCalls();
mAutoRegistration.stop();
- Log.v(TAG, " close: " + getUri() + ": "
+ if (DEBUG) Log.d(TAG, " close: " + getUri() + ": "
+ mIncomingCallBroadcastAction);
}
@@ -456,13 +458,13 @@
}
// send out incoming call broadcast
- Log.d(TAG, " ringing~~ " + getUri() + ": " + caller.getUri()
- + ": " + session.getCallId());
addPendingSession(session);
Intent intent = SipManager.createIncomingCallBroadcast(
mIncomingCallBroadcastAction, session.getCallId(),
sessionDescription);
- Log.d(TAG, " send out intent: " + intent);
+ if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
+ + caller.getUri() + ": " + session.getCallId()
+ + " " + mIncomingCallBroadcastAction);
mContext.sendBroadcast(intent);
} catch (RemoteException e) {
// should never happen with a local call
@@ -474,7 +476,8 @@
@Override
public void onError(ISipSession session, String errorClass,
String message) {
- Log.v(TAG, "sip session error: " + errorClass + ": " + message);
+ if (DEBUG) Log.d(TAG, "sip session error: " + errorClass + ": "
+ + message);
}
public boolean isOpened() {
@@ -506,7 +509,7 @@
public void run() {
synchronized (SipService.this) {
SipSessionGroup.SipSessionImpl session = mSession.duplicate();
- Log.d(TAG, " ~~~ keepalive");
+ if (DEBUG) Log.d(TAG, "~~~ keepalive");
mTimer.cancel(this);
session.sendKeepAlive();
if (session.isReRegisterRequired()) {
@@ -549,7 +552,7 @@
// TODO: when rfc5626 is deployed, use reg-id and sip.instance
// in registration to avoid adding duplicate entries to server
mSession.unregister();
- Log.v(TAG, "start AutoRegistrationProcess for "
+ if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for "
+ mSession.getLocalProfile().getUriString());
}
}
@@ -581,7 +584,6 @@
}
public void setListener(ISipSessionListener listener) {
- Log.v(TAG, "setListener(): " + listener);
synchronized (SipService.this) {
mProxy.setListener(listener);
if (mSession == null) return;
@@ -619,7 +621,7 @@
public void run() {
mErrorCode = null;
mErrorMessage = null;
- Log.v(TAG, " ~~~ registering");
+ if (DEBUG) Log.d(TAG, "~~~ registering");
synchronized (SipService.this) {
if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
}
@@ -642,7 +644,7 @@
}
private void restart(int duration) {
- Log.v(TAG, "Refresh registration " + duration + "s later.");
+ if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
mTimer.cancel(this);
mTimer.set(duration * 1000, this);
}
@@ -659,7 +661,7 @@
@Override
public void onRegistering(ISipSession session) {
- Log.v(TAG, "onRegistering(): " + session + ": " + mSession);
+ if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
mRegistered = false;
@@ -669,7 +671,7 @@
@Override
public void onRegistrationDone(ISipSession session, int duration) {
- Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession);
+ if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
@@ -703,7 +705,7 @@
} else {
mRegistered = false;
mExpiryTime = -1L;
- Log.v(TAG, "Refresh registration immediately");
+ if (DEBUG) Log.d(TAG, "Refresh registration immediately");
run();
}
}
@@ -714,8 +716,8 @@
String errorCodeString, String message) {
SipErrorCode errorCode =
Enum.valueOf(SipErrorCode.class, errorCodeString);
- Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession
- + ": " + errorCode + ": " + message);
+ if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
+ + errorCode + ": " + message);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
mErrorCode = errorCode;
@@ -724,7 +726,7 @@
message);
if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
- Log.d(TAG, " pause auto-registration");
+ if (DEBUG) Log.d(TAG, " pause auto-registration");
stopButKeepStates();
} else if (!isStopped()) {
onError();
@@ -734,7 +736,7 @@
@Override
public void onRegistrationTimeout(ISipSession session) {
- Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession);
+ if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
mErrorCode = SipErrorCode.TIME_OUT;
@@ -773,30 +775,33 @@
NetworkInfo.State state = netInfo.getState();
NetworkInfo activeNetInfo = getActiveNetworkInfo();
- if (activeNetInfo != null) {
- Log.v(TAG, "active network: " + activeNetInfo.getTypeName()
- + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
- ? " CONNECTED" : " DISCONNECTED"));
- } else {
- Log.v(TAG, "active network: null");
+ if (DEBUG) {
+ if (activeNetInfo != null) {
+ Log.d(TAG, "active network: "
+ + activeNetInfo.getTypeName()
+ + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
+ ? " CONNECTED" : " DISCONNECTED"));
+ } else {
+ Log.d(TAG, "active network: null");
+ }
}
if ((state == NetworkInfo.State.CONNECTED)
&& (activeNetInfo != null)
&& (activeNetInfo.getType() != netInfo.getType())) {
- Log.d(TAG, "ignore connect event: " + type
+ if (DEBUG) Log.d(TAG, "ignore connect event: " + type
+ ", active: " + activeNetInfo.getTypeName());
return;
}
if (state == NetworkInfo.State.CONNECTED) {
- Log.v(TAG, "Connectivity alert: CONNECTED " + type);
+ if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type);
onChanged(type, true);
} else if (state == NetworkInfo.State.DISCONNECTED) {
- Log.v(TAG, "Connectivity alert: DISCONNECTED " + type);
+ if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type);
onChanged(type, false);
} else {
- Log.d(TAG, "Connectivity alert not processed: " + state
- + " " + type);
+ if (DEBUG) Log.d(TAG, "Connectivity alert not processed: "
+ + state + " " + type);
}
}
}
@@ -847,7 +852,7 @@
return;
}
mTask = null;
- Log.v(TAG, " deliver change for " + mNetworkType
+ if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
+ (mConnected ? " CONNECTED" : "DISCONNECTED"));
onConnectivityChanged(mNetworkType, mConnected);
}
@@ -929,8 +934,10 @@
newQueue.addAll((Collection<MyEvent>) mEventQueue);
mEventQueue.clear();
mEventQueue = newQueue;
- Log.v(TAG, "queue re-calculated");
- printQueue();
+ if (DEBUG_TIMER) {
+ Log.d(TAG, "queue re-calculated");
+ printQueue();
+ }
}
// Determines the period and the trigger time of the new event and insert it
@@ -984,10 +991,12 @@
}
long triggerTime = event.mTriggerTime;
- Log.v(TAG, " add event " + event + " scheduled at "
- + showTime(triggerTime) + " at " + showTime(now)
- + ", #events=" + mEventQueue.size());
- printQueue();
+ if (DEBUG_TIMER) {
+ Log.d(TAG, " add event " + event + " scheduled at "
+ + showTime(triggerTime) + " at " + showTime(now)
+ + ", #events=" + mEventQueue.size());
+ printQueue();
+ }
}
/**
@@ -997,7 +1006,7 @@
*/
public synchronized void cancel(Runnable callback) {
if (stopped() || mEventQueue.isEmpty()) return;
- Log.d(TAG, "cancel:" + callback);
+ if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback);
MyEvent firstEvent = mEventQueue.first();
for (Iterator<MyEvent> iter = mEventQueue.iterator();
@@ -1005,7 +1014,7 @@
MyEvent event = iter.next();
if (event.mCallback == callback) {
iter.remove();
- Log.d(TAG, " cancel found:" + event);
+ if (DEBUG_TIMER) Log.d(TAG, " cancel found:" + event);
}
}
if (mEventQueue.isEmpty()) {
@@ -1019,8 +1028,10 @@
recalculatePeriods();
scheduleNext();
}
- Log.d(TAG, "after cancel:");
- printQueue();
+ if (DEBUG_TIMER) {
+ Log.d(TAG, "after cancel:");
+ printQueue();
+ }
}
private void scheduleNext() {
@@ -1069,13 +1080,13 @@
}
private void execute(long triggerTime) {
- Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": "
- + mEventQueue.size());
+ if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
+ + showTime(triggerTime) + ": " + mEventQueue.size());
if (stopped() || mEventQueue.isEmpty()) return;
for (MyEvent event : mEventQueue) {
if (event.mTriggerTime != triggerTime) break;
- Log.d(TAG, "execute " + event);
+ if (DEBUG_TIMER) Log.d(TAG, "execute " + event);
event.mLastTriggerTime = event.mTriggerTime;
event.mTriggerTime += event.mPeriod;
@@ -1083,8 +1094,10 @@
// run the callback in a new thread to prevent deadlock
new Thread(event.mCallback).start();
}
- Log.d(TAG, "after timeout execution");
- printQueue();
+ if (DEBUG_TIMER) {
+ Log.d(TAG, "after timeout execution");
+ printQueue();
+ }
scheduleNext();
}
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index a3bf3eb..baf9a8e 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -80,9 +80,12 @@
*/
class SipSessionGroup implements SipListener {
private static final String TAG = "SipSession";
+ private static final boolean DEBUG = true;
+ private static final boolean DEBUG_PING = DEBUG && false;
private static final String ANONYMOUS = "anonymous";
private static final String SERVER_ERROR_PREFIX = "Response: ";
- private static final int EXPIRY_TIME = 3600;
+ private static final int EXPIRY_TIME = 3600; // in seconds
+ private static final int CANCEL_CALL_TIMER = 5; // in seconds
private static final EventObject DEREGISTER = new EventObject("Deregister");
private static final EventObject END_CALL = new EventObject("End call");
@@ -218,22 +221,26 @@
private synchronized SipSessionImpl getSipSession(EventObject event) {
String key = SipHelper.getCallId(event);
- Log.d(TAG, "sesssion key from event: " + key);
- Log.d(TAG, "active sessions:");
- for (String k : mSessionMap.keySet()) {
- Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
- }
SipSessionImpl session = mSessionMap.get(key);
+ if ((session != null) && isLoggable(session)) {
+ Log.d(TAG, "session key from event: " + key);
+ Log.d(TAG, "active sessions:");
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k));
+ }
+ }
return ((session != null) ? session : mCallReceiverSession);
}
private synchronized void addSipSession(SipSessionImpl newSession) {
removeSipSession(newSession);
String key = newSession.getCallId();
- Log.d(TAG, "+++ add a session with key: '" + key + "'");
mSessionMap.put(key, newSession);
- for (String k : mSessionMap.keySet()) {
- Log.d(TAG, " ..... " + k + ": " + mSessionMap.get(k));
+ if (isLoggable(newSession)) {
+ Log.d(TAG, "+++ add a session with key: '" + key + "'");
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " " + k + ": " + mSessionMap.get(k));
+ }
}
}
@@ -254,10 +261,12 @@
}
}
}
- Log.d(TAG, " remove session " + session + " with key '" + key + "'");
- for (String k : mSessionMap.keySet()) {
- Log.d(TAG, " ..... " + k + ": " + mSessionMap.get(k));
+ if ((s != null) && isLoggable(s)) {
+ Log.d(TAG, "remove session " + session + " @key '" + key + "'");
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " " + k + ": " + mSessionMap.get(k));
+ }
}
}
@@ -288,10 +297,10 @@
private synchronized void process(EventObject event) {
SipSessionImpl session = getSipSession(event);
try {
- if ((session != null) && session.process(event)) {
- Log.d(TAG, " ~~~~~ new state: " + session.mState);
- } else {
- Log.d(TAG, "event not processed: " + event);
+ boolean isLoggable = isLoggable(session, event);
+ boolean processed = (session != null) && session.process(event);
+ if (isLoggable && processed) {
+ Log.d(TAG, "new state after: " + session.mState);
}
} catch (Throwable e) {
Log.w(TAG, "event process error: " + event, e);
@@ -321,8 +330,8 @@
}
public boolean process(EventObject evt) throws SipException {
- Log.d(TAG, " ~~~~~ " + this + ": " + mState + ": processing "
- + log(evt));
+ if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ + mState + ": processing " + log(evt));
if (isRequestEvent(Request.INVITE, evt)) {
RequestEvent event = (RequestEvent) evt;
SipSessionImpl newSession = new SipSessionImpl(mProxy);
@@ -355,6 +364,40 @@
String mPeerSessionDescription;
boolean mInCall;
boolean mReRegisterFlag = false;
+ SessionTimer mTimer;
+
+ // lightweight timer
+ class SessionTimer {
+ private boolean mRunning = true;
+
+ void start(final int timeout) {
+ new Thread(new Runnable() {
+ public void run() {
+ sleep(timeout);
+ if (mRunning) timeout();
+ }
+ }).start();
+ }
+
+ synchronized void cancel() {
+ mRunning = false;
+ this.notify();
+ }
+
+ private void timeout() {
+ synchronized (SipSessionGroup.this) {
+ onError(SipErrorCode.TIME_OUT, "Session timed out!");
+ }
+ }
+
+ private synchronized void sleep(int timeout) {
+ try {
+ this.wait(timeout * 1000);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "session timer interrupted!");
+ }
+ }
+ }
public SipSessionImpl(ISipSessionListener listener) {
setListener(listener);
@@ -374,6 +417,8 @@
mServerTransaction = null;
mClientTransaction = null;
mPeerSessionDescription = null;
+
+ cancelSessionTimer();
}
public boolean isInCall() {
@@ -426,16 +471,16 @@
}).start();
}
- public void makeCall(SipProfile peerProfile,
- String sessionDescription) {
- doCommandAsync(
- new MakeCallCommand(peerProfile, sessionDescription));
+ public void makeCall(SipProfile peerProfile, String sessionDescription,
+ int timeout) {
+ doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
+ timeout));
}
- public void answerCall(String sessionDescription) {
+ public void answerCall(String sessionDescription, int timeout) {
try {
- processCommand(
- new MakeCallCommand(mPeerProfile, sessionDescription));
+ processCommand(new MakeCallCommand(mPeerProfile,
+ sessionDescription, timeout));
} catch (SipException e) {
onError(e);
}
@@ -445,9 +490,15 @@
doCommandAsync(END_CALL);
}
- public void changeCall(String sessionDescription) {
- doCommandAsync(
- new MakeCallCommand(mPeerProfile, sessionDescription));
+ public void changeCall(String sessionDescription, int timeout) {
+ doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
+ timeout));
+ }
+
+ public void changeCallWithTimeout(
+ String sessionDescription, int timeout) {
+ doCommandAsync(new MakeCallCommand(mPeerProfile, sessionDescription,
+ timeout));
}
public void register(int duration) {
@@ -503,8 +554,8 @@
}
public boolean process(EventObject evt) throws SipException {
- Log.d(TAG, " ~~~~~ " + this + ": " + mState + ": processing "
- + log(evt));
+ if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ + mState + ": processing " + log(evt));
synchronized (SipSessionGroup.this) {
if (isClosed()) return false;
@@ -672,14 +723,14 @@
if (mRPort == 0) mRPort = rPort;
if (mRPort != rPort) {
mReRegisterFlag = true;
- Log.w(TAG, String.format("rport is changed: %d <> %d",
- mRPort, rPort));
+ if (DEBUG) Log.w(TAG, String.format(
+ "rport is changed: %d <> %d", mRPort, rPort));
mRPort = rPort;
} else {
- Log.w(TAG, "rport is the same: " + rPort);
+ if (DEBUG_PING) Log.w(TAG, "rport is the same: " + rPort);
}
} else {
- Log.w(TAG, "peer did not respect our rport request");
+ if (DEBUG) Log.w(TAG, "peer did not respond rport");
}
reset();
return true;
@@ -792,6 +843,7 @@
addSipSession(this);
mState = SipSessionState.OUTGOING_CALL;
mProxy.onCalling(this);
+ startSessionTimer(cmd.getTimeout());
return true;
} else if (evt instanceof RegisterCommand) {
int duration = ((RegisterCommand) evt).getDuration();
@@ -823,6 +875,7 @@
((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction);
mState = SipSessionState.INCOMING_CALL_ANSWERING;
+ startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
} else if (END_CALL == evt) {
mSipHelper.sendInviteBusyHere(mInviteReceived,
@@ -865,6 +918,7 @@
if (mState == SipSessionState.OUTGOING_CALL) {
mState = SipSessionState.OUTGOING_CALL_RING_BACK;
mProxy.onRingingBack(this);
+ cancelSessionTimer();
}
return true;
case Response.OK:
@@ -872,14 +926,15 @@
mPeerSessionDescription = extractContent(response);
establishCall();
return true;
+ case Response.UNAUTHORIZED:
case Response.PROXY_AUTHENTICATION_REQUIRED:
if (handleAuthentication(event)) {
addSipSession(this);
} else if (mLastNonce == null) {
- endCallOnError(SipErrorCode.SERVER_ERROR,
+ onError(SipErrorCode.SERVER_ERROR,
"server does not provide challenge");
} else {
- endCallOnError(SipErrorCode.INVALID_CREDENTIALS,
+ onError(SipErrorCode.INVALID_CREDENTIALS,
"incorrect username or password");
}
return true;
@@ -905,6 +960,7 @@
// response.
mSipHelper.sendCancel(mClientTransaction);
mState = SipSessionState.OUTGOING_CALL_CANCELING;
+ startSessionTimer(CANCEL_CALL_TIMER);
return true;
}
return false;
@@ -917,9 +973,13 @@
Response response = event.getResponse();
int statusCode = response.getStatusCode();
if (expectResponse(Request.CANCEL, evt)) {
- if (statusCode == Response.OK) {
- // do nothing; wait for REQUEST_TERMINATED
- return true;
+ switch (statusCode) {
+ case Response.OK:
+ // do nothing; wait for REQUEST_TERMINATED
+ return true;
+ case Response.REQUEST_TERMINATED:
+ endCallNormally();
+ return true;
}
} else if (expectResponse(Request.INVITE, evt)) {
if (statusCode == Response.OK) {
@@ -969,11 +1029,27 @@
mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription());
mState = SipSessionState.OUTGOING_CALL;
+ startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
}
return false;
}
+ // timeout in seconds
+ private void startSessionTimer(int timeout) {
+ if (timeout > 0) {
+ mTimer = new SessionTimer();
+ mTimer.start(timeout);
+ }
+ }
+
+ private void cancelSessionTimer() {
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer = null;
+ }
+ }
+
private String createErrorMessage(Response response) {
return String.format(SERVER_ERROR_PREFIX + "%s (%d)",
response.getReasonPhrase(), response.getStatusCode());
@@ -982,15 +1058,10 @@
private void establishCall() {
mState = SipSessionState.IN_CALL;
mInCall = true;
+ cancelSessionTimer();
mProxy.onCallEstablished(this, mPeerSessionDescription);
}
- private void fallbackToPreviousInCall(Throwable exception) {
- exception = getRootCause(exception);
- fallbackToPreviousInCall(getErrorCode(exception),
- exception.toString());
- }
-
private void fallbackToPreviousInCall(SipErrorCode errorCode,
String message) {
mState = SipSessionState.IN_CALL;
@@ -1013,6 +1084,7 @@
}
private void onError(SipErrorCode errorCode, String message) {
+ cancelSessionTimer();
switch (mState) {
case REGISTERING:
case DEREGISTERING:
@@ -1187,6 +1259,34 @@
}
}
+ private static boolean isLoggable(SipSessionImpl s) {
+ if (s != null) {
+ switch (s.mState) {
+ case PINGING:
+ return DEBUG_PING;
+ }
+ }
+ return DEBUG;
+ }
+
+ private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
+ if (!isLoggable(s)) return false;
+ if (evt == null) return false;
+
+ if (evt instanceof OptionsCommand) {
+ return DEBUG_PING;
+ } else if (evt instanceof ResponseEvent) {
+ Response response = ((ResponseEvent) evt).getResponse();
+ if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
+ return DEBUG_PING;
+ }
+ return DEBUG;
+ } else if (evt instanceof RequestEvent) {
+ return DEBUG;
+ }
+ return false;
+ }
+
private static String log(EventObject evt) {
if (evt instanceof RequestEvent) {
return ((RequestEvent) evt).getRequest().toString();
@@ -1218,11 +1318,18 @@
private class MakeCallCommand extends EventObject {
private String mSessionDescription;
+ private int mTimeout; // in seconds
public MakeCallCommand(SipProfile peerProfile,
String sessionDescription) {
+ this(peerProfile, sessionDescription, -1);
+ }
+
+ public MakeCallCommand(SipProfile peerProfile,
+ String sessionDescription, int timeout) {
super(peerProfile);
mSessionDescription = sessionDescription;
+ mTimeout = timeout;
}
public SipProfile getPeerProfile() {
@@ -1232,6 +1339,9 @@
public String getSessionDescription() {
return mSessionDescription;
}
- }
+ public int getTimeout() {
+ return mTimeout;
+ }
+ }
}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 6dd619c5..d27c2c6 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -49,8 +49,6 @@
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
- jmethodID notifyInputChannelANR;
- jmethodID notifyInputChannelRecoveredFromANR;
jmethodID notifyANR;
jmethodID virtualKeyDownFeedback;
jmethodID interceptKeyBeforeQueueing;
@@ -85,6 +83,7 @@
jclass clazz;
jfieldID inputChannel;
+ jfieldID name;
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
jfieldID dispatchingTimeoutNanos;
@@ -101,9 +100,11 @@
jfieldID touchableAreaRight;
jfieldID touchableAreaBottom;
jfieldID visible;
+ jfieldID canReceiveKeys;
jfieldID hasFocus;
jfieldID hasWallpaper;
jfieldID paused;
+ jfieldID layer;
jfieldID ownerPid;
jfieldID ownerUid;
} gInputWindowClassInfo;
@@ -168,7 +169,6 @@
void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
void setFocusedApplication(JNIEnv* env, jobject applicationObj);
void setInputDispatchMode(bool enabled, bool frozen);
- void preemptInputDispatch();
/* --- InputReaderPolicyInterface implementation --- */
@@ -191,10 +191,9 @@
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle);
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel);
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
- virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel);
- virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
virtual int32_t getMaxEventsPerSecond();
@@ -409,10 +408,15 @@
jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
const sp<InputChannel>& inputChannel) {
+ InputChannel* inputChannelPtr = inputChannel.get();
+ if (! inputChannelPtr) {
+ return NULL;
+ }
+
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannelPtr);
if (index < 0) {
return NULL;
}
@@ -702,32 +706,34 @@
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}
-nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) {
+nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyANR");
#endif
JNIEnv* env = jniEnv();
- ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
- jweak tokenObjWeak = token->getTokenObj();
-
- jlong newTimeout;
- jobject tokenObjLocal = env->NewLocalRef(tokenObjWeak);
- if (tokenObjLocal) {
- newTimeout = env->CallLongMethod(mCallbacksObj,
- gCallbacksClassInfo.notifyANR, tokenObjLocal);
- if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
- newTimeout = 0; // abort dispatch
- } else {
- assert(newTimeout >= 0);
- }
-
- env->DeleteLocalRef(tokenObjLocal);
+ jobject tokenObjLocal;
+ if (inputApplicationHandle.get()) {
+ ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
+ jweak tokenObjWeak = token->getTokenObj();
+ tokenObjLocal = env->NewLocalRef(tokenObjWeak);
} else {
- newTimeout = 0; // abort dispatch
+ tokenObjLocal = NULL;
}
+ jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+ jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObjLocal, inputChannelObjLocal);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+ newTimeout = 0; // abort dispatch
+ } else {
+ assert(newTimeout >= 0);
+ }
+
+ env->DeleteLocalRef(tokenObjLocal);
+ env->DeleteLocalRef(inputChannelObjLocal);
return newTimeout;
}
@@ -748,51 +754,6 @@
}
}
-nsecs_t NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifyInputChannelANR - inputChannel='%s'",
- inputChannel->getName().string());
-#endif
-
- JNIEnv* env = jniEnv();
-
- jlong newTimeout;
- jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
- if (inputChannelObjLocal) {
- newTimeout = env->CallLongMethod(mCallbacksObj,
- gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
- if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
- newTimeout = 0; // abort dispatch
- } else {
- assert(newTimeout >= 0);
- }
-
- env->DeleteLocalRef(inputChannelObjLocal);
- } else {
- newTimeout = 0; // abort dispatch
- }
-
- return newTimeout;
-}
-
-void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
- inputChannel->getName().string());
-#endif
-
- JNIEnv* env = jniEnv();
-
- jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
- if (inputChannelObjLocal) {
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
- inputChannelObjLocal);
- checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
-
- env->DeleteLocalRef(inputChannelObjLocal);
- }
-}
-
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
if (! isScreenOn()) {
// Disable key repeat when the screen is off.
@@ -855,6 +816,8 @@
sp<InputChannel> inputChannel =
android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel != NULL) {
+ jstring name = jstring(env->GetObjectField(windowObj,
+ gInputWindowClassInfo.name));
jint layoutParamsFlags = env->GetIntField(windowObj,
gInputWindowClassInfo.layoutParamsFlags);
jint layoutParamsType = env->GetIntField(windowObj,
@@ -887,18 +850,25 @@
gInputWindowClassInfo.touchableAreaBottom);
jboolean visible = env->GetBooleanField(windowObj,
gInputWindowClassInfo.visible);
+ jboolean canReceiveKeys = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.canReceiveKeys);
jboolean hasFocus = env->GetBooleanField(windowObj,
gInputWindowClassInfo.hasFocus);
jboolean hasWallpaper = env->GetBooleanField(windowObj,
gInputWindowClassInfo.hasWallpaper);
jboolean paused = env->GetBooleanField(windowObj,
gInputWindowClassInfo.paused);
+ jint layer = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layer);
jint ownerPid = env->GetIntField(windowObj,
gInputWindowClassInfo.ownerPid);
jint ownerUid = env->GetIntField(windowObj,
gInputWindowClassInfo.ownerUid);
+ const char* nameStr = env->GetStringUTFChars(name, NULL);
+
outWindow.inputChannel = inputChannel;
+ outWindow.name.setTo(nameStr);
outWindow.layoutParamsFlags = layoutParamsFlags;
outWindow.layoutParamsType = layoutParamsType;
outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
@@ -915,11 +885,15 @@
outWindow.touchableAreaRight = touchableAreaRight;
outWindow.touchableAreaBottom = touchableAreaBottom;
outWindow.visible = visible;
+ outWindow.canReceiveKeys = canReceiveKeys;
outWindow.hasFocus = hasFocus;
outWindow.hasWallpaper = hasWallpaper;
outWindow.paused = paused;
+ outWindow.layer = layer;
outWindow.ownerPid = ownerPid;
outWindow.ownerUid = ownerUid;
+
+ env->ReleaseStringUTFChars(name, nameStr);
valid = true;
} else {
LOGW("Dropping input target because its input channel is not initialized.");
@@ -973,10 +947,6 @@
mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
-void NativeInputManager::preemptInputDispatch() {
- mInputManager->getDispatcher()->preemptInputDispatch();
-}
-
bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
bool isScreenOn = this->isScreenOn();
@@ -986,23 +956,17 @@
JNIEnv* env = jniEnv();
+ // Note: inputChannel may be null.
jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
- if (inputChannelObj) {
- jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeDispatching,
- inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
- keyEvent->getKeyCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), policyFlags);
- bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+ jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeDispatching,
+ inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
- env->DeleteLocalRef(inputChannelObj);
-
- return consumed && ! error;
- } else {
- LOGW("Could not apply key dispatch policy because input channel '%s' is "
- "no longer valid.", inputChannel->getName().string());
- return false;
- }
+ env->DeleteLocalRef(inputChannelObj);
+ return consumed && ! error;
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) {
@@ -1246,15 +1210,6 @@
gNativeInputManager->setInputDispatchMode(enabled, frozen);
}
-static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
- jclass clazz) {
- if (checkInputManagerUnitialized(env)) {
- return;
- }
-
- gNativeInputManager->preemptInputDispatch();
-}
-
static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
jclass clazz, jint deviceId) {
if (checkInputManagerUnitialized(env)) {
@@ -1357,8 +1312,6 @@
(void*) android_server_InputManager_nativeSetFocusedApplication },
{ "nativeSetInputDispatchMode", "(ZZ)V",
(void*) android_server_InputManager_nativeSetInputDispatchMode },
- { "nativePreemptInputDispatch", "()V",
- (void*) android_server_InputManager_nativePreemptInputDispatch },
{ "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
(void*) android_server_InputManager_nativeGetInputDevice },
{ "nativeGetInputDeviceIds", "()[I",
@@ -1398,14 +1351,8 @@
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
"notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
- GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
- "notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
-
- GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
- "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
-
GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
- "notifyANR", "(Ljava/lang/Object;)J");
+ "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz,
"virtualKeyDownFeedback", "()V");
@@ -1477,6 +1424,9 @@
GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
"inputChannel", "Landroid/view/InputChannel;");
+ GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz,
+ "name", "Ljava/lang/String;");
+
GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
"layoutParamsFlags", "I");
@@ -1525,6 +1475,9 @@
GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
"visible", "Z");
+ GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz,
+ "canReceiveKeys", "Z");
+
GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
"hasFocus", "Z");
@@ -1534,6 +1487,9 @@
GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
"paused", "Z");
+ GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz,
+ "layer", "I");
+
GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
"ownerPid", "I");
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
index 7f4f9fc..850866a 100644
--- a/services/surfaceflinger/GLExtensions.cpp
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -86,7 +86,7 @@
mHaveNpot = true;
}
- if (hasExtension("GL_OES_texture_external")) {
+ if (hasExtension("GL_OES_EGL_image_external")) {
mHaveTextureExternal = true;
} else if (strstr(mRenderer.string(), "Adreno")) {
// hack for Adreno 200
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 2ee21b9..4cfcfe3 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -146,7 +146,7 @@
Region::const_iterator it = clip.begin();
Region::const_iterator const end = clip.end();
if (it != end) {
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
glDisable(GL_TEXTURE_EXTERNAL_OES);
}
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index a1f339e..80cc52c 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -71,7 +71,7 @@
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0, 0, 0, alpha);
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
glDisable(GL_TEXTURE_EXTERNAL_OES);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5a27fc5..b45f6fe 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1003,7 +1003,7 @@
glVertexPointer(2, GL_SHORT, 0, vertices);
glTexCoordPointer(2, GL_SHORT, 0, tcoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
glDisable(GL_TEXTURE_EXTERNAL_OES);
}
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 76f6159..c9a15f5 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -43,7 +43,7 @@
}
GLenum TextureManager::getTextureTarget(const Image* image) {
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
switch (image->target) {
case Texture::TEXTURE_EXTERNAL:
return GL_TEXTURE_EXTERNAL_OES;
@@ -85,7 +85,7 @@
pImage->height = 0;
GLenum target = GL_TEXTURE_2D;
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
if (format && isYuvFormat(format)) {
target = GL_TEXTURE_EXTERNAL_OES;
@@ -306,7 +306,7 @@
if (target == GL_TEXTURE_2D) {
glBindTexture(GL_TEXTURE_2D, texture.name);
glEnable(GL_TEXTURE_2D);
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
glDisable(GL_TEXTURE_EXTERNAL_OES);
}
@@ -329,7 +329,7 @@
void TextureManager::deactivateTextures()
{
glDisable(GL_TEXTURE_2D);
-#if defined(GL_OES_texture_external)
+#if defined(GL_OES_EGL_image_external)
if (GLExtensions::getInstance().haveTextureExternal()) {
glDisable(GL_TEXTURE_EXTERNAL_OES);
}
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 8bd789c..83496dc 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -197,10 +197,12 @@
Phone.State s = Phone.State.IDLE;
for (Phone phone : mPhones) {
- if (phone.getState() == Phone.State.RINGING) {
- return Phone.State.RINGING;
+ if (phone.getState() == Phone.State.ANSWERING) {
+ return Phone.State.ANSWERING;
+ } else if (phone.getState() == Phone.State.RINGING) {
+ s = Phone.State.RINGING;
} else if (phone.getState() == Phone.State.OFFHOOK) {
- s = Phone.State.OFFHOOK;
+ if (s == Phone.State.IDLE) s = Phone.State.OFFHOOK;
}
}
return s;
@@ -290,6 +292,18 @@
}
/**
+ * @return the first answering call
+ */
+ public Call getFirstAnsweringCall() {
+ for (Phone phone : mPhones) {
+ if (phone.getState() == Phone.State.ANSWERING) {
+ return phone.getForegroundCall();
+ }
+ }
+ return null;
+ }
+
+ /**
* unregister phone from CallManager
* @param phone
*/
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 521d90c..3030481 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -21,6 +21,7 @@
import com.android.internal.util.HierarchicalState;
import com.android.internal.util.HierarchicalStateMachine;
+import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.os.AsyncResult;
import android.os.Message;
@@ -31,6 +32,7 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
+import java.util.HashMap;
/**
* {@hide}
@@ -262,6 +264,7 @@
protected PhoneBase phone;
protected int cid;
protected LinkProperties mLinkProperties = new LinkProperties();
+ protected LinkCapabilities mCapabilities = new LinkCapabilities();
protected long createTime;
protected long lastFailTime;
protected FailCause lastFailCause;
@@ -912,13 +915,25 @@
}
/**
- * @return the connections LinkProperties
+ * Return the LinkProperties for the connection.
+ *
+ * @return a copy of the LinkProperties, is never null.
*/
public LinkProperties getLinkProperties() {
return new LinkProperties(mLinkProperties);
}
/**
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
+ *
+ * @return a copy of this connections capabilities, may be empty but never null.
+ */
+ public LinkCapabilities getLinkCapabilities() {
+ return new LinkCapabilities(mCapabilities);
+ }
+
+ /**
* @return the current state as a string.
*/
public String getStateAsString() {
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 765f64b..d753973 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.LinkCapabilities;
import android.net.LinkProperties;
import android.os.AsyncResult;
import android.os.Handler;
@@ -192,8 +193,11 @@
/** indication of our availability (preconditions to trysetupData are met) **/
protected boolean mAvailability = false;
- /** all our link properties (dns, gateway, ip, etc) */
- protected LinkProperties mLinkProperties;
+ /** The link properties (dns, gateway, ip, etc) */
+ protected LinkProperties mLinkProperties = new LinkProperties();
+
+ /** The link capabilities */
+ protected LinkCapabilities mLinkCapabilities = new LinkCapabilities();
/**
* Default constructor
@@ -425,10 +429,40 @@
if (isApnIdEnabled(id)) {
return new LinkProperties(mLinkProperties);
} else {
- return null;
+ return new LinkProperties();
}
}
+ protected LinkCapabilities getLinkCapabilities(String apnType) {
+ int id = apnTypeToId(apnType);
+ if (isApnIdEnabled(id)) {
+ return new LinkCapabilities(mLinkCapabilities);
+ } else {
+ return new LinkCapabilities();
+ }
+ }
+
+ /**
+ * Return the LinkProperties for the connection.
+ *
+ * @param connection
+ * @return a copy of the LinkProperties, is never null.
+ */
+ protected LinkProperties getLinkProperties(DataConnection connection) {
+ return connection.getLinkProperties();
+ }
+
+ /**
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
+ *
+ * @param connection
+ * @return a copy of this connections capabilities, may be empty but never null.
+ */
+ protected LinkCapabilities getLinkCapabilities(DataConnection connection) {
+ return connection.getLinkCapabilities();
+ }
+
// tell all active apns of the current condition
protected void notifyDataConnection(String reason) {
for (int id = 0; id < APN_NUM_TYPES; id++) {
@@ -672,8 +706,4 @@
}
}
}
-
- protected LinkProperties getLinkProperties(DataConnection connection) {
- return connection.getLinkProperties();
- }
}
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index bf3c4d1..6a163dd 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.LinkCapabilities;
import android.net.LinkProperties;
import android.os.Bundle;
import android.os.RemoteException;
@@ -109,8 +110,10 @@
// pass apnType back up to fetch particular for this one.
TelephonyManager telephony = TelephonyManager.getDefault();
LinkProperties linkProperties = null;
+ LinkCapabilities linkCapabilities = null;
if (state == Phone.DataState.CONNECTED) {
linkProperties = sender.getLinkProperties(apnType);
+ linkCapabilities = sender.getLinkCapabilities(apnType);
}
try {
mRegistry.notifyDataConnection(
@@ -119,6 +122,7 @@
sender.getActiveApn(),
apnType,
linkProperties,
+ linkCapabilities,
((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 eb7e566..6407a4e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.net.LinkProperties;
+import android.net.LinkCapabilities;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -34,7 +35,7 @@
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, in LinkProperties linkProperties,
- int networkType);
+ in LinkCapabilities linkCapabilities, int networkType);
void notifyDataConnectionFailed(String reason, String apnType);
void notifyCellLocation(in Bundle cellLocation);
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index fffe057..e5736ca 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.content.Context;
+import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
@@ -53,10 +54,12 @@
* <li>OFFHOOK = The phone is off hook. At least one call
* exists that is dialing, active or holding and no calls are
* ringing or waiting.</li>
+ * <li>ANSWERING = The incoming call is picked up but the
+ * call is not established yet.</li>
* </ul>
*/
enum State {
- IDLE, RINGING, OFFHOOK;
+ IDLE, RINGING, OFFHOOK, ANSWERING;
};
/**
@@ -100,6 +103,7 @@
static final String DATA_APN_TYPE_KEY = "apnType";
static final String DATA_APN_KEY = "apn";
static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
+ static final String DATA_LINK_CAPABILITIES_KEY = "linkCapabilities";
static final String DATA_IFACE_NAME_KEY = "iface";
static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
@@ -324,6 +328,11 @@
LinkProperties getLinkProperties(String apnType);
/**
+ * Return the LinkCapabilities
+ */
+ LinkCapabilities getLinkCapabilities(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 78ce473..5412768 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.LinkCapabilities;
import android.net.LinkProperties;
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
@@ -942,6 +943,10 @@
return mDataConnection.getLinkProperties(apnType);
}
+ public LinkCapabilities getLinkCapabilities(String apnType) {
+ return mDataConnection.getLinkCapabilities(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 b6e4cda..9e0c087 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -20,6 +20,7 @@
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
+import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.os.Handler;
import android.os.Message;
@@ -212,6 +213,10 @@
return mActivePhone.getLinkProperties(apnType);
}
+ public LinkCapabilities getLinkCapabilities(String apnType) {
+ return mActivePhone.getLinkCapabilities(apnType);
+ }
+
public String getActiveApn() {
return mActivePhone.getActiveApn();
}
diff --git a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
index 30d06d8..4c4e718 100644
--- a/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/SipPhoneNotifier.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.os.Bundle;
import android.os.RemoteException;
@@ -111,8 +112,10 @@
// pass apnType back up to fetch particular for this one.
TelephonyManager telephony = TelephonyManager.getDefault();
LinkProperties linkProperties = null;
+ LinkCapabilities linkCapabilities = null;
if (state == Phone.DataState.CONNECTED) {
linkProperties = sender.getLinkProperties(apnType);
+ linkCapabilities = sender.getLinkCapabilities(apnType);
}
try {
mRegistry.notifyDataConnection(
@@ -121,6 +124,7 @@
sender.getActiveApn(),
apnType,
linkProperties,
+ linkCapabilities,
((telephony!=null) ? telephony.getNetworkType() :
TelephonyManager.NETWORK_TYPE_UNKNOWN));
} catch (RemoteException ex) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 5918245..e499e95 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -732,7 +732,9 @@
}
if (ar.exception == null) {
+ // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
mLinkProperties = getLinkProperties(mActiveDataConnection);
+ mLinkCapabilities = getLinkCapabilities(mActiveDataConnection);
// everything is setup
notifyDefaultData(reason);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index f6887eb..66da6e8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1099,7 +1099,9 @@
}
if (ar.exception == null) {
+ // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
mLinkProperties = getLinkProperties(mActivePdp);
+ mLinkCapabilities = getLinkCapabilities(mActivePdp);
ApnSetting apn = mActivePdp.getApn();
if (apn.proxy != null && apn.proxy.length() != 0) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 5ef61a8..4af35a6 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -73,6 +73,7 @@
public class SipPhone extends SipPhoneBase {
private static final String LOG_TAG = "SipPhone";
private static final boolean LOCAL_DEBUG = true;
+ private static final int SESSION_TIMEOUT = 8; // in seconds
// A call that is ringing or (call) waiting
private SipCall ringingCall = new SipCall();
@@ -165,6 +166,7 @@
} else {
throw new CallStateException("phone not ringing");
}
+ updatePhoneState();
}
}
@@ -395,25 +397,45 @@
}
}
- private CallerInfo getCallerInfo(String number) {
+ private CallerInfo createCallerInfo(String number, SipProfile callee) {
+ SipProfile p = callee;
+ String name = p.getDisplayName();
+ if (TextUtils.isEmpty(name)) name = p.getUserName();
+ CallerInfo info = new CallerInfo();
+ info.name = name;
+ info.phoneNumber = number;
+ Log.v(LOG_TAG, "create caller info from scratch:");
+ Log.v(LOG_TAG, " name: " + info.name);
+ Log.v(LOG_TAG, " numb: " + info.phoneNumber);
+ return info;
+ }
+
+ // from contacts
+ private CallerInfo findCallerInfo(String number) {
CallerInfo info = CallerInfo.getCallerInfo(mContext, number);
if ((info == null) || (info.name == null)) return null;
- Log.v(LOG_TAG, "++******++ got info from contact:");
+ Log.v(LOG_TAG, "got caller info from contact:");
Log.v(LOG_TAG, " name: " + info.name);
Log.v(LOG_TAG, " numb: " + info.phoneNumber);
Log.v(LOG_TAG, " pres: " + info.numberPresentation);
return info;
}
- Connection dial(String calleeSipUri) throws SipException {
- CallerInfo info = getCallerInfo(calleeSipUri);
+ private CallerInfo getCallerInfo(String number, SipProfile callee) {
+ CallerInfo info = findCallerInfo(number);
+ if (info == null) info = createCallerInfo(number, callee);
+ return info;
+ }
+
+ Connection dial(String originalNumber) throws SipException {
+ String calleeSipUri = originalNumber;
if (!calleeSipUri.contains("@")) {
calleeSipUri += "@" + getSipDomain(mProfile);
- if (info != null) info.phoneNumber = calleeSipUri;
}
try {
SipProfile callee =
new SipProfile.Builder(calleeSipUri).build();
+ CallerInfo info = getCallerInfo(originalNumber, callee);
SipConnection c = new SipConnection(this, callee, info);
connections.add(c);
c.dial();
@@ -445,9 +467,9 @@
void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) {
SipProfile callee = sipAudioCall.getPeerProfile();
- CallerInfo info = getCallerInfo(getUriString(callee));
- if (info == null) info = getCallerInfo(callee.getUserName());
- if (info == null) info = getCallerInfo(callee.getDisplayName());
+ CallerInfo info = findCallerInfo(getUriString(callee));
+ if (info == null) info = findCallerInfo(callee.getUserName());
+ if (info == null) info = findCallerInfo(callee.getDisplayName());
SipConnection c = new SipConnection(this, callee, info);
connections.add(c);
@@ -627,6 +649,7 @@
if (newState == Call.State.INCOMING) {
setState(mOwner.getState()); // INCOMING or WAITING
} else {
+ if (newState == Call.State.ACTIVE) call.startAudio();
setState(newState);
}
mOwner.onConnectionStateChanged(SipConnection.this);
@@ -654,20 +677,9 @@
super(getUriString(callee));
mOwner = owner;
mPeer = callee;
- if (info == null) info = createCallerInfo();
setUserData(info);
}
- private CallerInfo createCallerInfo() {
- SipProfile p = mPeer;
- String name = p.getDisplayName();
- if (TextUtils.isEmpty(name)) name = p.getUserName();
- CallerInfo info = new CallerInfo();
- info.name = name;
- info.phoneNumber = getUriString(p);
- return info;
- }
-
void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) {
setState(newState);
mSipAudioCall = sipAudioCall;
@@ -677,7 +689,7 @@
void acceptCall() throws CallStateException {
try {
- mSipAudioCall.answerCall();
+ mSipAudioCall.answerCall(SESSION_TIMEOUT);
} catch (SipException e) {
throw new CallStateException("acceptCall(): " + e);
}
@@ -695,7 +707,7 @@
void dial() throws SipException {
setState(Call.State.DIALING);
mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
- mPeer, null);
+ mPeer, null, SESSION_TIMEOUT);
mSipAudioCall.setRingbackToneEnabled(false);
mSipAudioCall.setListener(mAdapter);
}
@@ -703,7 +715,7 @@
void hold() throws CallStateException {
setState(Call.State.HOLDING);
try {
- mSipAudioCall.holdCall();
+ mSipAudioCall.holdCall(SESSION_TIMEOUT);
} catch (SipException e) {
throw new CallStateException("hold(): " + e);
}
@@ -713,7 +725,7 @@
mSipAudioCall.setAudioGroup(audioGroup);
setState(Call.State.ACTIVE);
try {
- mSipAudioCall.continueCall();
+ mSipAudioCall.continueCall(SESSION_TIMEOUT);
} catch (SipException e) {
throw new CallStateException("unhold(): " + e);
}
@@ -757,7 +769,7 @@
Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+ ": on phone " + getPhone().getPhoneName());
try {
- mSipAudioCall.endCall();
+ if (mSipAudioCall != null) mSipAudioCall.endCall();
setState(Call.State.DISCONNECTING);
setDisconnectCause(DisconnectCause.LOCAL);
} catch (SipException e) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 5f26af4..fec83d6 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -549,6 +549,8 @@
if (getRingingCall().isRinging()) {
state = State.RINGING;
+ } else if (getForegroundCall().isRinging()) {
+ state = State.ANSWERING;
} else if (getForegroundCall().isIdle()
&& getBackgroundCall().isIdle()) {
state = State.IDLE;
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index 5233677..ea6571e 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -23,7 +23,7 @@
android:configChanges="orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
@@ -57,4 +57,4 @@
<uses-permission android:name="android.permission.WRITE_SDCARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index 5d1e66c..c799b5c 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -26,12 +26,14 @@
import os
import subprocess
import logging
+import optparse
+import time
-def main():
- if len(sys.argv) < 2:
+def main(options, args):
+ if len(args) < 1:
run_cmd = ""
else:
- run_cmd = sys.argv[1]
+ run_cmd = args[0]
# Setup logging class
logging.basicConfig(level=logging.INFO, format='%(message)s')
@@ -53,9 +55,13 @@
android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
android_tree_root = os.path.normpath(android_tree_root)
- # Paths relative to android_tree_root
+ # If any of these is relative, then it's relative to ServerRoot (in our case android_tree_root)
webkit_path = os.path.join("external", "webkit")
- layout_tests_path = os.path.join(webkit_path, "LayoutTests")
+ if (options.tests_root_directory != None):
+ # if options.tests_root_directory is absolute, os.getcwd() is discarded!
+ layout_tests_path = os.path.normpath(os.path.join(os.getcwd(), options.tests_root_directory))
+ else:
+ layout_tests_path = os.path.join(webkit_path, "LayoutTests")
http_conf_path = os.path.join(layout_tests_path, "http", "conf")
# Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
@@ -66,9 +72,13 @@
custom_log_path = os.path.join(tmp_WebKit, "apache2-access.log")
# Prepare the command to (re)start/stop the server with specified settings
- apache2_restart_cmd = "apache2 -k " + run_cmd
+ apache2_restart_template = "apache2 -k %s"
directives = " -c \"ServerRoot " + android_tree_root + "\""
+ # The default config in apache2-debian-httpd.conf listens on ports 8080 and
+ # 8443. We also need to listen on port 8000 for HTTP tests.
+ directives += " -c \"Listen 8000\""
+
# We use http/tests as the document root as the HTTP tests use hardcoded
# resources at the server root. We then use aliases to make available the
# complete set of tests and the required scripts.
@@ -99,7 +109,20 @@
# Try to execute the commands
logging.info("Will " + run_cmd + " apache2 server.")
- cmd = export_envvars_cmd + " && " + apache2_restart_cmd + directives + conf_file_cmd
+ cmd_template = export_envvars_cmd + " && " + apache2_restart_template + directives + conf_file_cmd
+
+ # It is worth noting here that if the configuration file with which we restart the server points
+ # to a different PidFile it will not work and result in second apache2 instance.
+ if (run_cmd == 'restart'):
+ logging.info("First will stop...")
+ execute_cmd(cmd_template % ('stop'))
+ logging.info("Stopped. Will start now...")
+ # We need to sleep breifly to avoid errors with apache being stopped and started too quickly
+ time.sleep(0.5)
+
+ execute_cmd(cmd_template % (run_cmd))
+
+def execute_cmd(cmd):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = p.communicate()
@@ -121,4 +144,8 @@
logging.info("OK")
if __name__ == "__main__":
- main();
+ option_parser = optparse.OptionParser(usage="Usage: %prog [options] start|stop|restart")
+ option_parser.add_option("", "--tests-root-directory",
+ help="The directory from which to take the tests, default is external/webkit/LayoutTests in this checkout of the Android tree")
+ options, args = option_parser.parse_args();
+ main(options, args);
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index d5bf8b3..fd76e4a9 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -32,8 +32,15 @@
tmpdir = tempfile.gettempdir()
+ if options.tests_root_directory != None:
+ # if options.tests_root_directory is absolute, os.getcwd() is discarded!
+ tests_root_directory = os.path.normpath(os.path.join(os.getcwd(), options.tests_root_directory))
+ server_options = " --tests-root-directory=" + tests_root_directory
+ else:
+ server_options = "";
+
# Restart the server
- cmd = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "run_apache2.py") + " restart"
+ cmd = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "run_apache2.py") + server_options + " restart"
os.system(cmd);
# Run the tests in path
@@ -73,5 +80,7 @@
option_parser = optparse.OptionParser(usage="Usage: %prog [options] test-relative-path")
option_parser.add_option("", "--show-results-in-browser", default="true",
help="Show the results the host's default web browser, default=true")
+ option_parser.add_option("", "--tests-root-directory",
+ help="The directory from which to take the tests, default is external/webkit/LayoutTests in this checkout of the Android tree")
options, args = option_parser.parse_args();
main(options, args);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
index 7cbb397..96c1e5e 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
@@ -18,8 +18,16 @@
import android.os.Bundle;
import android.os.Message;
+import android.util.Log;
import android.webkit.WebView;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
/**
* A class that represent a result of the test. It is responsible for returning the result's
* raw data and generating its own diff in HTML format.
@@ -46,12 +54,14 @@
public abstract AbstractResult createResult(Bundle bundle);
}
- public enum ResultCode {
- PASS("Passed"),
- FAIL_RESULT_DIFFERS("Result differs"),
- FAIL_NO_EXPECTED_RESULT("No expected result"),
- FAIL_TIMED_OUT("Timed out"),
- FAIL_CRASHED("Crashed");
+ /**
+ * A code representing the result of comparing actual and expected results.
+ */
+ public enum ResultCode implements Serializable {
+ RESULTS_MATCH("Results match"),
+ RESULTS_DIFFER("Results differ"),
+ NO_EXPECTED_RESULT("No expected result"),
+ NO_ACTUAL_RESULT("No actual result");
private String mTitle;
@@ -79,6 +89,58 @@
return mAdditionalTextOutputString;
}
+ public byte[] getBytes() {
+ ByteArrayOutputStream baos = null;
+ ObjectOutputStream oos = null;
+ try {
+ try {
+ baos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(this);
+ } finally {
+ if (baos != null) {
+ baos.close();
+ }
+ if (oos != null) {
+ oos.close();
+ }
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Unable to serialize result: " + getRelativePath(), e);
+ }
+
+ return baos == null ? null : baos.toByteArray();
+ }
+
+ public static AbstractResult create(byte[] bytes) {
+ ByteArrayInputStream bais = null;
+ ObjectInputStream ois = null;
+ AbstractResult result = null;
+ try {
+ try {
+ bais = new ByteArrayInputStream(bytes);
+ ois = new ObjectInputStream(bais);
+ result = (AbstractResult)ois.readObject();
+ } finally {
+ if (bais != null) {
+ bais.close();
+ }
+ if (ois != null) {
+ ois.close();
+ }
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Unable to deserialize result!", e);
+ } catch (ClassNotFoundException e) {
+ Log.e(LOG_TAG, "Unable to deserialize result!", e);
+ }
+ return result;
+ }
+
+ public void clearResults() {
+ mAdditionalTextOutputString = null;
+ }
+
/**
* Makes the result object obtain the results of the test from the webview
* and store them in the format that suits itself bests. This method is asynchronous.
@@ -123,14 +185,48 @@
public abstract String getActualTextResult();
/**
- * Returns the code of this result.
+ * Returns the status code representing the result of comparing actual and expected results.
*
* @return
- * the code of this result
+ * the status code from comparing actual and expected results
*/
public abstract ResultCode getResultCode();
/**
+ * Returns whether this test crashed.
+ *
+ * @return
+ * whether this test crashed
+ */
+ public abstract boolean didCrash();
+
+ /**
+ * Returns whether this test timed out.
+ *
+ * @return
+ * whether this test timed out
+ */
+ public abstract boolean didTimeOut();
+
+ /**
+ * Sets that this test timed out.
+ */
+ public abstract void setDidTimeOut();
+
+ /**
+ * Returns whether the test passed.
+ *
+ * @return
+ * whether the test passed
+ */
+ public boolean didPass() {
+ // Tests that crash can't have timed out or have an actual result.
+ assert !(didCrash() && didTimeOut());
+ assert !(didCrash() && getResultCode() != ResultCode.NO_ACTUAL_RESULT);
+ return !didCrash() && !didTimeOut() && getResultCode() == ResultCode.RESULTS_MATCH;
+ }
+
+ /**
* Return the type of the result data.
*
* @return
@@ -150,4 +246,4 @@
public abstract String getDiffAsHtml();
public abstract Bundle getBundle();
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
index 31da776..4831168 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
@@ -61,7 +61,23 @@
@Override
public ResultCode getResultCode() {
- return ResultCode.FAIL_CRASHED;
+ return ResultCode.NO_ACTUAL_RESULT;
+ }
+
+ @Override
+ public boolean didCrash() {
+ return true;
+ }
+
+ @Override
+ public boolean didTimeOut() {
+ return false;
+ }
+
+ @Override
+ public void setDidTimeOut() {
+ /** This method is not applicable for this type of result */
+ assert false;
}
@Override
@@ -106,4 +122,4 @@
public void setExpectedTextResultPath(String relativePath) {
/** TODO */
}
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index 9a82f84..b9fc274 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -35,6 +35,7 @@
import android.util.Log;
import android.view.Window;
import android.webkit.ConsoleMessage;
+import android.webkit.HttpAuthHandler;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
@@ -166,6 +167,19 @@
onTestFinished();
}
}
+
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+ String host, String realm) {
+ if (handler.useHttpAuthUsernamePassword() && view != null) {
+ String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
+ if (credentials != null && credentials.length == 2) {
+ handler.proceed(credentials[0], credentials[1]);
+ return;
+ }
+ }
+ handler.cancel();
+ }
};
private WebChromeClient mWebChromeClient = new WebChromeClient() {
@@ -412,6 +426,7 @@
assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
+ mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
obtainActualResultsFromWebView();
}
@@ -427,6 +442,9 @@
mCurrentState = CurrentState.OBTAINING_RESULT;
+ if (mCurrentTestTimedOut) {
+ mCurrentResult.setDidTimeOut();
+ }
mCurrentResult.obtainActualResults(mCurrentWebView,
mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
}
@@ -438,7 +456,6 @@
Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
mCurrentState = CurrentState.IDLE;
- mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
reportResultToService();
mCurrentTestIndex++;
updateProgressBar();
@@ -456,9 +473,6 @@
Bundle bundle = mCurrentResult.getBundle();
bundle.putInt("testIndex", mCurrentTestIndex);
- if (mCurrentTestTimedOut) {
- bundle.putString("resultCode", AbstractResult.ResultCode.FAIL_TIMED_OUT.name());
- }
if (!mTestsList.isEmpty()) {
bundle.putString("nextTest", mTestsList.get(0));
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 9caabdb..17e19d0 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -39,6 +39,7 @@
private static final String LOG_TAG = "ManagerService";
private static final int MSG_CRASH_TIMEOUT_EXPIRED = 0;
+ private static final int MSG_SUMMARIZER_DONE = 1;
private static final int CRASH_TIMEOUT_MS = 20 * 1000;
@@ -86,30 +87,42 @@
break;
case MSG_CURRENT_TEST_CRASHED:
- mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
+ mInternalMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
onTestCrashed();
break;
case MSG_ALL_TESTS_FINISHED:
- mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
- mSummarizer.summarize();
- Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
- intent.setAction(Intent.ACTION_SHUTDOWN);
- /** This flag is needed because we send the intent from the service */
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- break;
+ /** We run it in a separate thread to avoid ANR */
+ new Thread() {
+ @Override
+ public void run() {
+ mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
+ Message msg = Message.obtain(mInternalMessagesHandler,
+ MSG_SUMMARIZER_DONE);
+ mSummarizer.summarize(msg);
+ }
+ }.start();
}
}
};
private Messenger mMessenger = new Messenger(mIncomingHandler);
- private Handler mCrashMessagesHandler = new Handler() {
+ private Handler mInternalMessagesHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (msg.what == MSG_CRASH_TIMEOUT_EXPIRED) {
- onTestCrashed();
+ switch (msg.what) {
+ case MSG_CRASH_TIMEOUT_EXPIRED:
+ onTestCrashed();
+ break;
+
+ case MSG_SUMMARIZER_DONE:
+ Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_SHUTDOWN);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ break;
}
}
};
@@ -134,7 +147,7 @@
super.onCreate();
mFileFilter = new FileFilter();
- mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);
+ mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH, getApplicationContext());
}
@Override
@@ -150,7 +163,7 @@
}
private void onActualResultsObtained(Bundle bundle) {
- mCrashMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
+ mInternalMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
AbstractResult results =
@@ -168,7 +181,7 @@
mCurrentlyRunningTest = nextTest;
mCurrentlyRunningTestIndex = index;
- mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_CRASH_TIMEOUT_EXPIRED, CRASH_TIMEOUT_MS);
+ mInternalMessagesHandler.sendEmptyMessageDelayed(MSG_CRASH_TIMEOUT_EXPIRED, CRASH_TIMEOUT_MS);
}
/**
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 2cde296..25c5ad5 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -16,10 +16,13 @@
package com.android.dumprendertree2;
+import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.Cursor;
import android.os.Build;
+import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -31,7 +34,6 @@
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
@@ -75,9 +77,12 @@
" width: 20px;}" +
"h3 span.sqr {" +
" text-decoration: none;" +
- " color: #8ee100;" +
" float: left;" +
" width: 20px;}" +
+ "h3 span.sqr_pass {" +
+ " color: #8ee100;}" +
+ "h3 span.sqr_fail {" +
+ " color: #c30000;}" +
"span.source {" +
" display: block;" +
" font-size: 10px;" +
@@ -134,12 +139,6 @@
" background-color: #ff8888; }" +
"span.ins {" +
" background-color: #88ff88; }" +
- "span.fail {" +
- " color: red;}" +
- "span.pass {" +
- " color: green;}" +
- "span.time_out {" +
- " color: orange;}" +
"table.summary {" +
" border: 1px solid black;" +
" margin-top: 20px;}" +
@@ -151,19 +150,16 @@
" text-transform: uppercase;" +
" padding: 3px;" +
" -webkit-border-radius: 4px;}" +
- "span." + AbstractResult.ResultCode.PASS.name() + "{" +
- " background-color: #8ee100;" +
- " color: black;}" +
- "span." + AbstractResult.ResultCode.FAIL_RESULT_DIFFERS.name() + "{" +
+ "span." + AbstractResult.ResultCode.RESULTS_DIFFER.name() + "{" +
" background-color: #ccc;" +
" color: black;}" +
- "span." + AbstractResult.ResultCode.FAIL_NO_EXPECTED_RESULT.name() + "{" +
+ "span." + AbstractResult.ResultCode.NO_EXPECTED_RESULT.name() + "{" +
" background-color: #a700e4;" +
" color: #fff;}" +
- "span." + AbstractResult.ResultCode.FAIL_TIMED_OUT.name() + "{" +
+ "span.timed_out {" +
" background-color: #f3cb00;" +
" color: black;}" +
- "span." + AbstractResult.ResultCode.FAIL_CRASHED.name() + "{" +
+ "span.crashed {" +
" background-color: #c30000;" +
" color: #fff;}" +
"span.noLtc {" +
@@ -193,20 +189,40 @@
private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
+ private static final int RESULTS_PER_DUMP = 500;
+ private static final int RESULTS_PER_DB_ACCESS = 50;
+
private int mCrashedTestsCount = 0;
private List<AbstractResult> mUnexpectedFailures = new ArrayList<AbstractResult>();
private List<AbstractResult> mExpectedFailures = new ArrayList<AbstractResult>();
private List<AbstractResult> mExpectedPasses = new ArrayList<AbstractResult>();
private List<AbstractResult> mUnexpectedPasses = new ArrayList<AbstractResult>();
+ private Cursor mUnexpectedFailuresCursor;
+ private Cursor mExpectedFailuresCursor;
+ private Cursor mUnexpectedPassesCursor;
+ private Cursor mExpectedPassesCursor;
+
private FileFilter mFileFilter;
private String mResultsRootDirPath;
private String mTestsRelativePath;
private Date mDate;
- public Summarizer(FileFilter fileFilter, String resultsRootDirPath) {
+ private int mResultsSinceLastHtmlDump = 0;
+ private int mResultsSinceLastDbAccess = 0;
+
+ private SummarizerDBHelper mDbHelper;
+
+ public Summarizer(FileFilter fileFilter, String resultsRootDirPath, Context context) {
mFileFilter = fileFilter;
mResultsRootDirPath = resultsRootDirPath;
+
+ /**
+ * We don't run the database I/O in a separate thread to avoid consumer/producer problem
+ * and to simplify code.
+ */
+ mDbHelper = new SummarizerDBHelper(context);
+ mDbHelper.open();
}
public static URI getDetailsUri() {
@@ -217,11 +233,12 @@
public void appendTest(AbstractResult result) {
String relativePath = result.getRelativePath();
- if (result.getResultCode() == AbstractResult.ResultCode.FAIL_CRASHED) {
+ if (result.didCrash()) {
mCrashedTestsCount++;
}
- if (result.getResultCode() == AbstractResult.ResultCode.PASS) {
+ if (result.didPass()) {
+ result.clearResults();
if (mFileFilter.isFail(relativePath)) {
mUnexpectedPasses.add(result);
} else {
@@ -234,26 +251,78 @@
mUnexpectedFailures.add(result);
}
}
+
+ if (++mResultsSinceLastDbAccess == RESULTS_PER_DB_ACCESS) {
+ persistLists();
+ clearLists();
+ }
+ }
+
+ private void clearLists() {
+ mUnexpectedFailures.clear();
+ mExpectedFailures.clear();
+ mUnexpectedPasses.clear();
+ mExpectedPasses.clear();
+ }
+
+ private void persistLists() {
+ persistListToTable(mUnexpectedFailures, SummarizerDBHelper.UNEXPECTED_FAILURES_TABLE);
+ persistListToTable(mExpectedFailures, SummarizerDBHelper.EXPECTED_FAILURES_TABLE);
+ persistListToTable(mUnexpectedPasses, SummarizerDBHelper.UNEXPECTED_PASSES_TABLE);
+ persistListToTable(mExpectedPasses, SummarizerDBHelper.EXPECTED_PASSES_TABLE);
+ mResultsSinceLastDbAccess = 0;
+ }
+
+ private void persistListToTable(List<AbstractResult> results, String table) {
+ for (AbstractResult abstractResult : results) {
+ mDbHelper.insertAbstractResult(abstractResult, table);
+ }
}
public void setTestsRelativePath(String testsRelativePath) {
mTestsRelativePath = testsRelativePath;
}
- public void summarize() {
+ public void summarize(Message onFinishMessage) {
+ persistLists();
+ clearLists();
+
+ mUnexpectedFailuresCursor =
+ mDbHelper.getAbstractResults(SummarizerDBHelper.UNEXPECTED_FAILURES_TABLE);
+ mUnexpectedPassesCursor =
+ mDbHelper.getAbstractResults(SummarizerDBHelper.UNEXPECTED_PASSES_TABLE);
+ mExpectedFailuresCursor =
+ mDbHelper.getAbstractResults(SummarizerDBHelper.EXPECTED_FAILURES_TABLE);
+ mExpectedPassesCursor =
+ mDbHelper.getAbstractResults(SummarizerDBHelper.EXPECTED_PASSES_TABLE);
+
String webKitRevision = getWebKitRevision();
createHtmlDetails(webKitRevision);
createTxtSummary(webKitRevision);
+
+ clearLists();
+ mUnexpectedFailuresCursor.close();
+ mUnexpectedPassesCursor.close();
+ mExpectedFailuresCursor.close();
+ mExpectedPassesCursor.close();
+
+ onFinishMessage.sendToTarget();
}
public void reset() {
mCrashedTestsCount = 0;
- mUnexpectedFailures.clear();
- mExpectedFailures.clear();
- mExpectedPasses.clear();
+ clearLists();
+ mDbHelper.reset();
mDate = new Date();
}
+ private void dumpHtmlToFile(StringBuilder html, boolean append) {
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
+ html.toString().getBytes(), append);
+ html.setLength(0);
+ mResultsSinceLastHtmlDump = 0;
+ }
+
private void createTxtSummary(String webKitRevision) {
StringBuilder txt = new StringBuilder();
@@ -266,10 +335,10 @@
txt.append("TOTAL: " + getTotalTestCount() + "\n");
txt.append("CRASHED (among all tests): " + mCrashedTestsCount + "\n");
- txt.append("UNEXPECTED FAILURES: " + mUnexpectedFailures.size() + "\n");
- txt.append("UNEXPECTED PASSES: " + mUnexpectedPasses.size() + "\n");
- txt.append("EXPECTED FAILURES: " + mExpectedFailures.size() + "\n");
- txt.append("EXPECTED PASSES: " + mExpectedPasses.size() + "\n");
+ txt.append("UNEXPECTED FAILURES: " + mUnexpectedFailuresCursor.getCount() + "\n");
+ txt.append("UNEXPECTED PASSES: " + mUnexpectedPassesCursor.getCount() + "\n");
+ txt.append("EXPECTED FAILURES: " + mExpectedFailuresCursor.getCount() + "\n");
+ txt.append("EXPECTED PASSES: " + mExpectedPassesCursor.getCount() + "\n");
FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
txt.toString().getBytes(), false);
@@ -284,26 +353,22 @@
html.append("</head><body>");
createTopSummaryTable(webKitRevision, html);
+ dumpHtmlToFile(html, false);
- createResultsListWithDiff(html, "Unexpected failures", mUnexpectedFailures);
-
- createResultsListNoDiff(html, "Unexpected passes", mUnexpectedPasses);
-
- createResultsListWithDiff(html, "Expected failures", mExpectedFailures);
-
- createResultsListNoDiff(html, "Expected passes", mExpectedPasses);
+ createResultsList(html, "Unexpected failures", mUnexpectedFailuresCursor);
+ createResultsList(html, "Unexpected passes", mUnexpectedPassesCursor);
+ createResultsList(html, "Expected failures", mExpectedFailuresCursor);
+ createResultsList(html, "Expected passes", mExpectedPassesCursor);
html.append("</body></html>");
-
- FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
- html.toString().getBytes(), false);
+ dumpHtmlToFile(html, true);
}
private int getTotalTestCount() {
- return mUnexpectedFailures.size() +
- mUnexpectedPasses.size() +
- mExpectedPasses.size() +
- mExpectedFailures.size();
+ return mUnexpectedFailuresCursor.getCount() +
+ mUnexpectedPassesCursor.getCount() +
+ mExpectedPassesCursor.getCount() +
+ mExpectedFailuresCursor.getCount();
}
private String getWebKitVersionFromUserAgentString() {
@@ -352,10 +417,10 @@
html.append("<table class=\"summary\">");
createSummaryTableRow(html, "TOTAL", getTotalTestCount());
createSummaryTableRow(html, "CRASHED (among all tests)", mCrashedTestsCount);
- createSummaryTableRow(html, "UNEXPECTED FAILURES", mUnexpectedFailures.size());
- createSummaryTableRow(html, "UNEXPECTED PASSES", mUnexpectedPasses.size());
- createSummaryTableRow(html, "EXPECTED FAILURES", mExpectedFailures.size());
- createSummaryTableRow(html, "EXPECTED PASSES", mExpectedPasses.size());
+ createSummaryTableRow(html, "UNEXPECTED FAILURES", mUnexpectedFailuresCursor.getCount());
+ createSummaryTableRow(html, "UNEXPECTED PASSES", mUnexpectedPassesCursor.getCount());
+ createSummaryTableRow(html, "EXPECTED FAILURES", mExpectedFailuresCursor.getCount());
+ createSummaryTableRow(html, "EXPECTED PASSES", mExpectedPassesCursor.getCount());
html.append("</table>");
}
@@ -366,18 +431,24 @@
html.append("</tr>");
}
- private void createResultsListWithDiff(StringBuilder html, String title,
- List<AbstractResult> resultsList) {
+ private void createResultsList(
+ StringBuilder html, String title, Cursor cursor) {
String relativePath;
String id = "";
AbstractResult.ResultCode resultCode;
- Collections.sort(resultsList);
- html.append("<h2>" + title + " [" + resultsList.size() + "]</h2>");
- for (AbstractResult result : resultsList) {
+ html.append("<h2>" + title + " [" + cursor.getCount() + "]</h2>");
+
+ if (!cursor.moveToFirst()) {
+ return;
+ }
+
+ AbstractResult result;
+ do {
+ result = SummarizerDBHelper.getAbstractResult(cursor);
+
relativePath = result.getRelativePath();
resultCode = result.getResultCode();
- assert resultCode != AbstractResult.ResultCode.PASS : "resultCode=" + resultCode;
html.append("<h3>");
@@ -387,61 +458,79 @@
* to cause any problems in this case
*/
id = relativePath.replace(File.separator, ":");
- html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
- html.append("return false;\">");
- html.append("<span class=\"tri\" id=\"tri." + id + "\">▶ </span>");
- html.append("<span class=\"path\">" + relativePath + "</span>");
- html.append("</a>");
- html.append(" <span class=\"listItem " + resultCode.name() + "\">");
- html.append(resultCode.toString());
- html.append("</span>");
+ /** Write the test name */
+ if (resultCode == AbstractResult.ResultCode.RESULTS_DIFFER) {
+ html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+ html.append("return false;\">");
+ html.append("<span class=\"tri\" id=\"tri." + id + "\">▶ </span>");
+ html.append("<span class=\"path\">" + relativePath + "</span>");
+ html.append("</a>");
+ } else {
+ html.append("<a href=\"" + getViewSourceUrl(result.getRelativePath()).toString() + "\"");
+ html.append(" target=\"_blank\">");
+ html.append("<span class=\"sqr sqr_" + (result.didPass() ? "pass" : "fail"));
+ html.append("\">■ </span>");
+ html.append("<span class=\"path\">" + result.getRelativePath() + "</span>");
+ html.append("</a>");
+ }
- /** Detect missing LTC function */
- String additionalTextOutputString = result.getAdditionalTextOutputString();
- if (additionalTextOutputString != null &&
- additionalTextOutputString.contains("com.android.dumprendertree") &&
- additionalTextOutputString.contains("has no method")) {
- if (additionalTextOutputString.contains("LayoutTestController")) {
- html.append(" <span class=\"listItem noLtc\">LTC function missing</span>");
- }
- if (additionalTextOutputString.contains("EventSender")) {
- html.append(" <span class=\"listItem noEventSender\">");
- html.append("ES function missing</span>");
- }
+ if (!result.didPass()) {
+ appendTags(html, result);
}
html.append("</h3>");
appendExpectedResultsSources(result, html);
- html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
- html.append(result.getDiffAsHtml());
- html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
- html.append("return false;\">Hide</a>");
- html.append(" | ");
- html.append("<a href=\"" + getViewSourceUrl(relativePath).toString() + "\"");
- html.append(" target=\"_blank\">Show source</a>");
- html.append("</div>");
+ if (resultCode == AbstractResult.ResultCode.RESULTS_DIFFER) {
+ html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
+ html.append(result.getDiffAsHtml());
+ html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+ html.append("return false;\">Hide</a>");
+ html.append(" | ");
+ html.append("<a href=\"" + getViewSourceUrl(relativePath).toString() + "\"");
+ html.append(" target=\"_blank\">Show source</a>");
+ html.append("</div>");
+ }
html.append("<div class=\"space\"></div>");
- }
+
+ if (++mResultsSinceLastHtmlDump == RESULTS_PER_DUMP) {
+ dumpHtmlToFile(html, true);
+ }
+
+ cursor.moveToNext();
+ } while (!cursor.isAfterLast());
}
- private void createResultsListNoDiff(StringBuilder html, String title,
- List<AbstractResult> resultsList) {
- Collections.sort(resultsList);
- html.append("<h2>" + title + " [" + resultsList.size() + "]</h2>");
- for (AbstractResult result : resultsList) {
- html.append("<h3>");
- html.append("<a href=\"" + getViewSourceUrl(result.getRelativePath()).toString() +
- "\"");
- html.append(" target=\"_blank\">");
- html.append("<span class=\"sqr\">■ </span>");
- html.append("<span class=\"path\">" + result.getRelativePath() + "</span>");
- html.append("</a>");
- html.append("</h3>");
- appendExpectedResultsSources(result, html);
- html.append("<div class=\"space\"></div>");
+ private void appendTags(StringBuilder html, AbstractResult result) {
+ /** Tag tests which crash, time out or where results don't match */
+ if (result.didCrash()) {
+ html.append(" <span class=\"listItem crashed\">Crashed</span>");
+ } else {
+ if (result.didTimeOut()) {
+ html.append(" <span class=\"listItem timed_out\">Timed out</span>");
+ }
+ AbstractResult.ResultCode resultCode = result.getResultCode();
+ if (resultCode != AbstractResult.ResultCode.RESULTS_MATCH) {
+ html.append(" <span class=\"listItem " + resultCode.name() + "\">");
+ html.append(resultCode.toString());
+ html.append("</span>");
+ }
+ }
+
+ /** Detect missing LTC function */
+ String additionalTextOutputString = result.getAdditionalTextOutputString();
+ if (additionalTextOutputString != null &&
+ additionalTextOutputString.contains("com.android.dumprendertree") &&
+ additionalTextOutputString.contains("has no method")) {
+ if (additionalTextOutputString.contains("LayoutTestController")) {
+ html.append(" <span class=\"listItem noLtc\">LTC function missing</span>");
+ }
+ if (additionalTextOutputString.contains("EventSender")) {
+ html.append(" <span class=\"listItem noEventSender\">");
+ html.append("ES function missing</span>");
+ }
}
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java
new file mode 100644
index 0000000..23e13ec
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A basic class that wraps database accesses inside itself and provides functionality to
+ * store and retrieve AbstractResults.
+ */
+public class SummarizerDBHelper {
+ private static final String KEY_ID = "id";
+ private static final String KEY_PATH = "path";
+ private static final String KEY_BYTES = "bytes";
+
+ private static final String DATABASE_NAME = "SummarizerDB";
+ private static final int DATABASE_VERSION = 1;
+
+ static final String EXPECTED_FAILURES_TABLE = "expectedFailures";
+ static final String UNEXPECTED_FAILURES_TABLE = "unexpectedFailures";
+ static final String EXPECTED_PASSES_TABLE = "expextedPasses";
+ static final String UNEXPECTED_PASSES_TABLE = "unexpextedPasses";
+ private static final Set<String> TABLES_NAMES = new HashSet<String>();
+ {
+ TABLES_NAMES.add(EXPECTED_FAILURES_TABLE);
+ TABLES_NAMES.add(EXPECTED_PASSES_TABLE);
+ TABLES_NAMES.add(UNEXPECTED_FAILURES_TABLE);
+ TABLES_NAMES.add(UNEXPECTED_PASSES_TABLE);
+ }
+
+ private static final void createTables(SQLiteDatabase db) {
+ String cmd;
+ for (String tableName : TABLES_NAMES) {
+ cmd = "create table " + tableName + " ("
+ + KEY_ID + " integer primary key autoincrement, "
+ + KEY_PATH + " text not null, "
+ + KEY_BYTES + " blob not null);";
+ db.execSQL(cmd);
+ }
+ }
+
+ private static final void dropTables(SQLiteDatabase db) {
+ for (String tableName : TABLES_NAMES) {
+ db.execSQL("DROP TABLE IF EXISTS " + tableName);
+ }
+ }
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ dropTables(db);
+ createTables(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ /** NOOP for now, because we will never upgrade the db */
+ }
+
+ public void reset(SQLiteDatabase db) {
+ dropTables(db);
+ createTables(db);
+ }
+ }
+
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ private final Context mContext;
+
+ public SummarizerDBHelper(Context ctx) {
+ mContext = ctx;
+ mDbHelper = new DatabaseHelper(mContext);
+ }
+
+ public void reset() {
+ mDbHelper.reset(this.mDb);
+ }
+
+ public void open() throws SQLException {
+ mDb = mDbHelper.getWritableDatabase();
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+ public void insertAbstractResult(AbstractResult result, String table) {
+ ContentValues cv = new ContentValues();
+ cv.put(KEY_PATH, result.getRelativePath());
+ cv.put(KEY_BYTES, result.getBytes());
+ mDb.insert(table, null, cv);
+ }
+
+ public Cursor getAbstractResults(String table) throws SQLException {
+ return mDb.query(false, table, new String[] {KEY_BYTES}, null, null, null, null,
+ KEY_PATH + " ASC", null);
+ }
+
+ public static AbstractResult getAbstractResult(Cursor cursor) {
+ return AbstractResult.create(cursor.getBlob(cursor.getColumnIndex(KEY_BYTES)));
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
index 9664efe..f835b6a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -39,12 +39,13 @@
private String mExpectedResultPath;
private String mActualResult;
private String mRelativePath;
+ private boolean mDidTimeOut;
private ResultCode mResultCode;
- private Message mResultObtainedMsg;
+ transient private Message mResultObtainedMsg;
private boolean mDumpChildFramesAsText;
- private Handler mHandler = new Handler() {
+ transient private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_DOCUMENT_AS_TEXT) {
@@ -74,10 +75,14 @@
mActualResult = bundle.getString("actualTextualResult");
setAdditionalTextOutputString(bundle.getString("additionalTextOutputString"));
mRelativePath = bundle.getString("relativePath");
- String resultCode = bundle.getString("resultCode");
- if (resultCode != null) {
- mResultCode = ResultCode.valueOf(resultCode);
- }
+ mDidTimeOut = bundle.getBoolean("didTimeOut");
+ }
+
+ @Override
+ public void clearResults() {
+ super.clearResults();
+ mExpectedResult = null;
+ mActualResult = null;
}
@Override
@@ -87,17 +92,31 @@
}
if (mExpectedResult == null) {
- mResultCode = AbstractResult.ResultCode.FAIL_NO_EXPECTED_RESULT;
+ mResultCode = AbstractResult.ResultCode.NO_EXPECTED_RESULT;
} else if (!mExpectedResult.equals(mActualResult)) {
- mResultCode = AbstractResult.ResultCode.FAIL_RESULT_DIFFERS;
+ mResultCode = AbstractResult.ResultCode.RESULTS_DIFFER;
} else {
- mResultCode = AbstractResult.ResultCode.PASS;
+ mResultCode = AbstractResult.ResultCode.RESULTS_MATCH;
}
-
return mResultCode;
}
@Override
+ public boolean didCrash() {
+ return false;
+ }
+
+ @Override
+ public boolean didTimeOut() {
+ return mDidTimeOut;
+ }
+
+ @Override
+ public void setDidTimeOut() {
+ mDidTimeOut = true;
+ }
+
+ @Override
public byte[] getActualImageResult() {
return null;
}
@@ -239,9 +258,7 @@
bundle.putString("actualTextualResult", getActualTextResult());
bundle.putString("additionalTextOutputString", getAdditionalTextOutputString());
bundle.putString("relativePath", mRelativePath);
- if (mResultCode != null) {
- bundle.putString("resultCode", mResultCode.name());
- }
+ bundle.putBoolean("didTimeOut", mDidTimeOut);
bundle.putString("type", getType().name());
return bundle;
}
@@ -250,4 +267,4 @@
public String getRelativePath() {
return mRelativePath;
}
-}
\ No newline at end of file
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
index 7317a27..cff436ff 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
@@ -40,11 +40,9 @@
* We use these ports because other webkit platforms do. They are set up in
* external/webkit/LayoutTests/http/conf/apache2-debian-httpd.conf
*/
- public static final int HTTP_PORT = 8080;
+ public static final int HTTP_PORT = 8000;
public static final int HTTPS_PORT = 8443;
- public static final String HOST = "localhost";
-
private static ForwarderManager forwarderManager;
private Set<Forwarder> mForwarders;
@@ -75,7 +73,7 @@
URL url = null;
try {
- url = new URL(protocol, HOST, port, "/");
+ url = new URL(protocol, HOST_IP, port, "/");
} catch (MalformedURLException e) {
assert false : "isHttps=" + isHttps;
}
@@ -122,4 +120,4 @@
mIsStarted = false;
Log.i(LOG_TAG, "ForwarderManager stopped.");
}
-}
\ No newline at end of file
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 5597415..8a08c48 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -214,7 +214,7 @@
<activity
android:name="LinesActivity"
android:label="_Lines"
- android:hardwareAccelerated="false">
+ android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/HwAccelerationTest/res/drawable/patch.9.png b/tests/HwAccelerationTest/res/drawable/patch.9.png
new file mode 100644
index 0000000..e3b3639
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/patch.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index 208dd88..4430533 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -97,6 +97,10 @@
mLargePaint.setShader(null);
canvas.drawLines(mPoints, mAlphaPaint);
+
+ mSmallPaint.setAntiAlias(false);
+ canvas.drawLine(0.0f, 0.0f, 400.0f, 0.0f, mSmallPaint);
+ mSmallPaint.setAntiAlias(true);
canvas.translate(120.0f, 0.0f);
mAlphaPaint.setShader(mShader);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
index d374c32..4f605fa 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
@@ -41,13 +41,14 @@
}
private class PatchView extends View {
- private Drawable mPatch;
+ private Drawable mPatch1, mPatch2;
public PatchView(Activity activity) {
super(activity);
final Resources resources = activity.getResources();
- mPatch = resources.getDrawable(R.drawable.btn_toggle_on);
+ mPatch1 = resources.getDrawable(R.drawable.patch);
+ mPatch2 = resources.getDrawable(R.drawable.btn_toggle_on);
}
@Override
@@ -58,8 +59,13 @@
final int left = (getWidth() - width) / 2;
final int top = (getHeight() - height) / 2;
- mPatch.setBounds(left, top, left + width, top + height);
- mPatch.draw(canvas);
+ mPatch1.setBounds(left, top, left + width, top + height);
+ mPatch1.draw(canvas);
+
+ canvas.translate(0.0f, height + 20.0f);
+
+ mPatch2.setBounds(left, top, left + width, top + height);
+ mPatch2.draw(canvas);
}
}
}
diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl
index 29fcb32..5661b8f 100644
--- a/voip/java/android/net/sip/ISipSession.aidl
+++ b/voip/java/android/net/sip/ISipSession.aidl
@@ -20,8 +20,8 @@
import android.net.sip.SipProfile;
/**
- * A SIP session that is associated with a SIP dialog or a transaction
- * (e.g., registration) not within a dialog.
+ * A SIP session that is associated with a SIP dialog or a transaction that is
+ * not within a dialog.
* @hide
*/
interface ISipSession {
@@ -112,9 +112,11 @@
*
* @param callee the SIP profile to make the call to
* @param sessionDescription the session description of this call
+ * @param timeout the session will be timed out if the call is not
+ * established within {@code timeout} seconds
* @see ISipSessionListener
*/
- void makeCall(in SipProfile callee, String sessionDescription);
+ void makeCall(in SipProfile callee, String sessionDescription, int timeout);
/**
* Answers an incoming call with the specified session description. The
@@ -122,8 +124,10 @@
* {@link SipSessionState#INCOMING_CALL}.
*
* @param sessionDescription the session description to answer this call
+ * @param timeout the session will be timed out if the call is not
+ * established within {@code timeout} seconds
*/
- void answerCall(String sessionDescription);
+ void answerCall(String sessionDescription, int timeout);
/**
* Ends an established call, terminates an outgoing call or rejects an
@@ -140,6 +144,8 @@
* to call when the session state is in {@link SipSessionState#IN_CALL}.
*
* @param sessionDescription the new session description
+ * @param timeout the session will be timed out if the call is not
+ * established within {@code timeout} seconds
*/
- void changeCall(String sessionDescription);
+ void changeCall(String sessionDescription, int timeout);
}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 1d1be69..4abea20 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -96,8 +96,8 @@
}
/**
- * The adapter class for {@link SipAudioCall#Listener}. The default
- * implementation of all callback methods is no-op.
+ * The adapter class for {@link Listener}. The default implementation of
+ * all callback methods is no-op.
*/
public class Adapter implements Listener {
protected void onChanged(SipAudioCall call) {
@@ -134,7 +134,7 @@
/**
* Sets the listener to listen to the audio call events. The method calls
- * {@link #setListener(Listener, false)}.
+ * {@code setListener(listener, false)}.
*
* @param listener to listen to the audio call events of this object
* @see #setListener(Listener, boolean)
@@ -153,17 +153,29 @@
void setListener(Listener listener, boolean callbackImmediately);
/**
- * Closes this object. The object is not usable after being closed.
+ * Closes this object. This object is not usable after being closed.
*/
void close();
/**
- * Initiates an audio call to the specified profile.
+ * Initiates an audio call to the specified profile. The attempt will be
+ * timed out if the call is not established within {@code timeout} seconds
+ * and {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
*
* @param callee the SIP profile to make the call to
* @param sipManager the {@link SipManager} object to help make call with
+ * @param timeout the timeout value in seconds
+ * @see Listener.onError
*/
- void makeCall(SipProfile callee, SipManager sipManager) throws SipException;
+ void makeCall(SipProfile callee, SipManager sipManager, int timeout)
+ throws SipException;
+
+ /**
+ * Starts the audio for the established call. This method should be called
+ * after {@link Listener#onCallEstablished} is called.
+ */
+ void startAudio();
/**
* Attaches an incoming call to this call object.
@@ -178,19 +190,39 @@
void endCall() throws SipException;
/**
- * Puts a call on hold. When succeeds,
- * {@link #Listener#onCallHeld(SipAudioCall)} is called.
+ * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is
+ * called. The attempt will be timed out if the call is not established
+ * within {@code timeout} seconds and
+ * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
+ *
+ * @param timeout the timeout value in seconds
+ * @see Listener.onError
*/
- void holdCall() throws SipException;
+ void holdCall(int timeout) throws SipException;
- /** Answers a call. */
- void answerCall() throws SipException;
+ /**
+ * Answers a call. The attempt will be timed out if the call is not
+ * established within {@code timeout} seconds and
+ * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
+ *
+ * @param timeout the timeout value in seconds
+ * @see Listener.onError
+ */
+ void answerCall(int timeout) throws SipException;
/**
* Continues a call that's on hold. When succeeds,
- * {@link #Listener#onCallEstablished(SipAudioCall)} is called.
+ * {@link Listener#onCallEstablished} is called. The attempt will be timed
+ * out if the call is not established within {@code timeout} seconds and
+ * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
+ *
+ * @param timeout the timeout value in seconds
+ * @see Listener.onError
*/
- void continueCall() throws SipException;
+ void continueCall(int timeout) throws SipException;
/** Puts the device to speaker mode. */
void setSpeakerMode(boolean speakerMode);
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index dd275da..fd32d4d 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -28,6 +28,7 @@
import android.net.rtp.AudioGroup;
import android.net.rtp.AudioStream;
import android.net.rtp.RtpStream;
+import android.net.wifi.WifiManager;
import android.os.Message;
import android.os.RemoteException;
import android.os.Vibrator;
@@ -36,6 +37,7 @@
import java.io.IOException;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -54,6 +56,7 @@
private static final boolean DONT_RELEASE_SOCKET = false;
private static final String AUDIO = "audio";
private static final int DTMF = 101;
+ private static final int SESSION_TIMEOUT = 5; // in seconds
private Context mContext;
private SipProfile mLocalProfile;
@@ -140,12 +143,21 @@
if (closeRtp) stopCall(RELEASE_SOCKET);
stopRingbackTone();
stopRinging();
- mSipSession = null;
+
mInCall = false;
mHold = false;
mSessionId = -1L;
mErrorCode = null;
mErrorMessage = null;
+
+ if (mSipSession != null) {
+ try {
+ mSipSession.setListener(null);
+ } catch (RemoteException e) {
+ // don't care
+ }
+ mSipSession = null;
+ }
}
public synchronized SipProfile getLocalProfile() {
@@ -215,7 +227,7 @@
// session changing request
try {
mPeerSd = new SdpSessionDescription(sessionDescription);
- answerCall();
+ answerCall(SESSION_TIMEOUT);
} catch (Throwable e) {
Log.e(TAG, "onRinging()", e);
session.endCall();
@@ -225,24 +237,18 @@
}
}
- private synchronized void establishCall(String sessionDescription) {
- stopRingbackTone();
- stopRinging();
- try {
- SdpSessionDescription sd =
- new SdpSessionDescription(sessionDescription);
- Log.d(TAG, "sip call established: " + sd);
- startCall(sd);
- mInCall = true;
- } catch (SdpException e) {
- Log.e(TAG, "createSessionDescription()", e);
- }
- }
-
@Override
public void onCallEstablished(ISipSession session,
String sessionDescription) {
- establishCall(sessionDescription);
+ stopRingbackTone();
+ stopRinging();
+ try {
+ mPeerSd = new SdpSessionDescription(sessionDescription);
+ Log.d(TAG, "sip call established: " + mPeerSd);
+ } catch (SdpException e) {
+ Log.e(TAG, "createSessionDescription()", e);
+ }
+
Listener listener = mListener;
if (listener != null) {
try {
@@ -342,14 +348,15 @@
}
public synchronized void makeCall(SipProfile peerProfile,
- SipManager sipManager) throws SipException {
+ SipManager sipManager, int timeout) throws SipException {
try {
mSipSession = sipManager.createSipSession(mLocalProfile, this);
if (mSipSession == null) {
throw new SipException(
"Failed to create SipSession; network available?");
}
- mSipSession.makeCall(peerProfile, createOfferSessionDescription());
+ mSipSession.makeCall(peerProfile, createOfferSessionDescription(),
+ timeout);
} catch (Throwable e) {
if (e instanceof SipException) {
throw (SipException) e;
@@ -372,10 +379,10 @@
}
}
- public synchronized void holdCall() throws SipException {
+ public synchronized void holdCall(int timeout) throws SipException {
if (mHold) return;
try {
- mSipSession.changeCall(createHoldSessionDescription());
+ mSipSession.changeCall(createHoldSessionDescription(), timeout);
mHold = true;
} catch (Throwable e) {
throwSipException(e);
@@ -385,21 +392,21 @@
if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
}
- public synchronized void answerCall() throws SipException {
+ public synchronized void answerCall(int timeout) throws SipException {
try {
stopRinging();
- mSipSession.answerCall(createAnswerSessionDescription());
+ mSipSession.answerCall(createAnswerSessionDescription(), timeout);
} catch (Throwable e) {
Log.e(TAG, "answerCall()", e);
throwSipException(e);
}
}
- public synchronized void continueCall() throws SipException {
+ public synchronized void continueCall(int timeout) throws SipException {
if (!mHold) return;
try {
mHold = false;
- mSipSession.changeCall(createContinueSessionDescription());
+ mSipSession.changeCall(createContinueSessionDescription(), timeout);
} catch (Throwable e) {
throwSipException(e);
}
@@ -572,10 +579,22 @@
return copies;
}
- private void startCall(SdpSessionDescription peerSd) {
- stopCall(DONT_RELEASE_SOCKET);
+ public void startAudio() {
+ try {
+ startAudioInternal();
+ } catch (UnknownHostException e) {
+ onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE.toString(),
+ e.getMessage());
+ } catch (Throwable e) {
+ onError(mSipSession, SipErrorCode.CLIENT_ERROR.toString(),
+ e.getMessage());
+ }
+ }
- mPeerSd = peerSd;
+ private synchronized void startAudioInternal() throws UnknownHostException {
+ stopCall(DONT_RELEASE_SOCKET);
+ mInCall = true;
+ SdpSessionDescription peerSd = mPeerSd;
String peerMediaAddress = peerSd.getPeerMediaAddress(AUDIO);
// TODO: handle multiple media fields
int peerMediaPort = peerSd.getPeerMediaPort(AUDIO);
@@ -584,58 +603,55 @@
int localPort = getLocalMediaPort();
int sampleRate = 8000;
int frameSize = sampleRate / 50; // 160
- try {
- // TODO: get sample rate from sdp
- mCodec = getCodec(peerSd);
- AudioStream audioStream = mAudioStream;
- audioStream.associate(InetAddress.getByName(peerMediaAddress),
- peerMediaPort);
- audioStream.setCodec(convert(mCodec), mCodec.payloadType);
- audioStream.setDtmfType(DTMF);
- Log.d(TAG, "start media: localPort=" + localPort + ", peer="
- + peerMediaAddress + ":" + peerMediaPort);
+ // TODO: get sample rate from sdp
+ mCodec = getCodec(peerSd);
- audioStream.setMode(RtpStream.MODE_NORMAL);
- if (!mHold) {
- // FIXME: won't work if peer is not sending nor receiving
- if (!peerSd.isSending(AUDIO)) {
- Log.d(TAG, " not receiving");
- audioStream.setMode(RtpStream.MODE_SEND_ONLY);
- }
- if (!peerSd.isReceiving(AUDIO)) {
- Log.d(TAG, " not sending");
- audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
- }
+ AudioStream audioStream = mAudioStream;
+ audioStream.associate(InetAddress.getByName(peerMediaAddress),
+ peerMediaPort);
+ audioStream.setCodec(convert(mCodec), mCodec.payloadType);
+ audioStream.setDtmfType(DTMF);
+ Log.d(TAG, "start media: localPort=" + localPort + ", peer="
+ + peerMediaAddress + ":" + peerMediaPort);
- /* The recorder volume will be very low if the device is in
- * IN_CALL mode. Therefore, we have to set the mode to NORMAL
- * in order to have the normal microphone level.
- */
- ((AudioManager) mContext.getSystemService
- (Context.AUDIO_SERVICE))
- .setMode(AudioManager.MODE_NORMAL);
+ audioStream.setMode(RtpStream.MODE_NORMAL);
+ if (!mHold) {
+ // FIXME: won't work if peer is not sending nor receiving
+ if (!peerSd.isSending(AUDIO)) {
+ Log.d(TAG, " not receiving");
+ audioStream.setMode(RtpStream.MODE_SEND_ONLY);
+ }
+ if (!peerSd.isReceiving(AUDIO)) {
+ Log.d(TAG, " not sending");
+ audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
}
- // AudioGroup logic:
- AudioGroup audioGroup = getAudioGroup();
- if (mHold) {
- if (audioGroup != null) {
- audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
- }
- // don't create an AudioGroup here; doing so will fail if
- // there's another AudioGroup out there that's active
+ /* The recorder volume will be very low if the device is in
+ * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+ * in order to have the normal microphone level.
+ */
+ ((AudioManager) mContext.getSystemService
+ (Context.AUDIO_SERVICE))
+ .setMode(AudioManager.MODE_NORMAL);
+ }
+
+ // AudioGroup logic:
+ AudioGroup audioGroup = getAudioGroup();
+ if (mHold) {
+ if (audioGroup != null) {
+ audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
+ }
+ // don't create an AudioGroup here; doing so will fail if
+ // there's another AudioGroup out there that's active
+ } else {
+ if (audioGroup == null) audioGroup = new AudioGroup();
+ audioStream.join(audioGroup);
+ if (mMuted) {
+ audioGroup.setMode(AudioGroup.MODE_MUTED);
} else {
- if (audioGroup == null) audioGroup = new AudioGroup();
- audioStream.join(audioGroup);
- if (mMuted) {
- audioGroup.setMode(AudioGroup.MODE_MUTED);
- } else {
- audioGroup.setMode(AudioGroup.MODE_NORMAL);
- }
+ audioGroup.setMode(AudioGroup.MODE_NORMAL);
}
- } catch (Exception e) {
- Log.e(TAG, "call()", e);
}
}
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index ee985de..a27f740 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -18,10 +18,10 @@
/**
* Defines error code returned in
- * {@link SipRegistrationListener#onRegistrationFailed(String, String, String)},
- * {@link ISipSessionListener#onError(ISipSession, String, String)},
- * {@link ISipSessionListener#onCallChangeFailed(ISipSession, String, String)} and
- * {@link ISipSessionListener#onRegistrationFailed(ISipSession, String, String)}.
+ * {@link SipRegistrationListener#onRegistrationFailed},
+ * {@link ISipSessionListener#onError},
+ * {@link ISipSessionListener#onCallChangeFailed} and
+ * {@link ISipSessionListener#onRegistrationFailed}.
* @hide
*/
public enum SipErrorCode {
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
index d615342..f0d846b 100644
--- a/voip/java/android/net/sip/SipException.java
+++ b/voip/java/android/net/sip/SipException.java
@@ -17,6 +17,7 @@
package android.net.sip;
/**
+ * General SIP-related exception class.
* @hide
*/
public class SipException extends Exception {
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 149053c..36895cd 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -28,25 +28,22 @@
/**
* The class provides API for various SIP related tasks. Specifically, the API
- * allows the application to:
+ * allows an application to:
* <ul>
* <li>register a {@link SipProfile} to have the background SIP service listen
* to incoming calls and broadcast them with registered command string. See
* {@link #open(SipProfile, String, SipRegistrationListener)},
- * {@link #open(SipProfile)}, {@link #close(String)},
- * {@link #isOpened(String)} and {@link isRegistered(String)}. It also
- * facilitates handling of the incoming call broadcast intent. See
- * {@link #isIncomingCallIntent(Intent)}, {@link #getCallId(Intent)},
- * {@link #getOfferSessionDescription(Intent)} and
- * {@link #takeAudioCall(Context, Intent, SipAudioCall.Listener)}.</li>
+ * {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
+ * {@link #isRegistered}. It also facilitates handling of the incoming call
+ * broadcast intent. See
+ * {@link #isIncomingCallIntent}, {@link #getCallId},
+ * {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
* <li>make/take SIP-based audio calls. See
- * {@link #makeAudioCall(Context, SipProfile, SipProfile, SipAudioCall.Listener)}
- * and {@link #takeAudioCall(Context, Intent, SipAudioCall.Listener}.</li>
+ * {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
* <li>register/unregister with a SIP service provider. See
- * {@link #register(SipProfile, int, ISipSessionListener)} and
- * {@link #unregister(SipProfile, ISipSessionListener)}.</li>
+ * {@link #register} and {@link #unregister}.</li>
* <li>process SIP events directly with a {@link ISipSession} created by
- * {@link createSipSession(SipProfile, ISipSessionListener)}.</li>
+ * {@link #createSipSession}.</li>
* </ul>
* @hide
*/
@@ -113,8 +110,7 @@
/**
* Opens the profile for making calls and/or receiving calls. Subsequent
* SIP calls can be made through the default phone UI. The caller may also
- * make subsequent calls through
- * {@link #makeAudioCall(Context, String, String, SipAudioCall.Listener)}.
+ * make subsequent calls through {@link #makeAudioCall}.
* If the receiving-call option is enabled in the profile, the SIP service
* will register the profile to the corresponding server periodically in
* order to receive calls from the server.
@@ -134,8 +130,7 @@
/**
* Opens the profile for making calls and/or receiving calls. Subsequent
* SIP calls can be made through the default phone UI. The caller may also
- * make subsequent calls through
- * {@link #makeAudioCall(Context, String, String, SipAudioCall.Listener)}.
+ * make subsequent calls through {@link #makeAudioCall}.
* If the receiving-call option is enabled in the profile, the SIP service
* will register the profile to the corresponding server periodically in
* order to receive calls from the server.
@@ -160,9 +155,7 @@
/**
* Sets the listener to listen to registration events. No effect if the
- * profile has not been opened to receive calls
- * (see {@link #open(SipProfile, String, SipRegistrationListener)} and
- * {@link #open(SipProfile)}).
+ * profile has not been opened to receive calls (see {@link #open}).
*
* @param localProfileUri the URI of the profile
* @param listener to listen to registration events; can be null
@@ -225,44 +218,55 @@
}
/**
- * Creates a {@link SipAudioCall} to make a call.
+ * Creates a {@link SipAudioCall} to make a call. The attempt will be timed
+ * out if the call is not established within {@code timeout} seconds and
+ * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
*
* @param context context to create a {@link SipAudioCall} object
* @param localProfile the SIP profile to make the call from
* @param peerProfile the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall};
* can be null
+ * @param timeout the timeout value in seconds
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error
+ * @see SipAudioCall.Listener.onError
*/
public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
- SipProfile peerProfile, SipAudioCall.Listener listener)
+ SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException {
SipAudioCall call = new SipAudioCallImpl(context, localProfile);
call.setListener(listener);
- call.makeCall(peerProfile, this);
+ call.makeCall(peerProfile, this, timeout);
return call;
}
/**
* Creates a {@link SipAudioCall} to make a call. To use this method, one
- * must call {@link #open(SipProfile)} first.
+ * must call {@link #open(SipProfile)} first. The attempt will be timed out
+ * if the call is not established within {@code timeout} seconds and
+ * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * will be called.
*
* @param context context to create a {@link SipAudioCall} object
* @param localProfileUri URI of the SIP profile to make the call from
* @param peerProfileUri URI of the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall};
* can be null
+ * @param timeout the timeout value in seconds
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error
+ * @see SipAudioCall.Listener.onError
*/
public SipAudioCall makeAudioCall(Context context, String localProfileUri,
- String peerProfileUri, SipAudioCall.Listener listener)
+ String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException {
try {
return makeAudioCall(context,
new SipProfile.Builder(localProfileUri).build(),
- new SipProfile.Builder(peerProfileUri).build(), listener);
+ new SipProfile.Builder(peerProfileUri).build(), listener,
+ timeout);
} catch (ParseException e) {
throw new SipException("build SipProfile", e);
}
@@ -283,7 +287,7 @@
/**
* Creates a {@link SipAudioCall} to take an incoming call. Before the call
* is returned, the listener will receive a
- * {@link SipAudioCall#Listener.onRinging(SipAudioCall, SipProfile)}
+ * {@link SipAudioCall.Listener#onRinging}
* callback.
*
* @param context context to create a {@link SipAudioCall} object
@@ -377,12 +381,11 @@
/**
* Registers the profile to the corresponding server for receiving calls.
- * {@link #open(SipProfile, String, SipRegistrationListener)} is still
- * needed to be called at least once in order for the SIP service to
- * broadcast an intent when an incoming call is received.
+ * {@link #open} is still needed to be called at least once in order for
+ * the SIP service to broadcast an intent when an incoming call is received.
*
* @param localProfile the SIP profile to register with
- * @param expiryTime registration expiration time (in second)
+ * @param expiryTime registration expiration time (in seconds)
* @param listener to listen to the registration events
* @throws SipException if calling the SIP service results in an error
*/
@@ -419,9 +422,9 @@
/**
* Gets the {@link ISipSession} that handles the incoming call. For audio
* calls, consider to use {@link SipAudioCall} to handle the incoming call.
- * See {@link #takeAudioCall(Context, Intent, SipAudioCall.Listener)}.
- * Note that the method may be called only once for the same intent. For
- * subsequent calls on the same intent, the method returns null.
+ * See {@link #takeAudioCall}. Note that the method may be called only once
+ * for the same intent. For subsequent calls on the same intent, the method
+ * returns null.
*
* @param incomingCallIntent the incoming call broadcast intent
* @return the session object that handles the incoming call
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index 1a7d8bf..88bfba9 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -61,7 +61,7 @@
};
/**
- * Class to help create a {@link SipProfile}.
+ * Class to help create a {@code SipProfile}.
*/
public static class Builder {
private AddressFactory mAddressFactory;
@@ -120,8 +120,8 @@
*
* @param username username of the SIP account
* @param serverDomain the SIP server domain; if the network address
- * is different from the domain, use
- * {@link #setOutboundProxy(String)} to set server address
+ * is different from the domain, use {@link #setOutboundProxy} to
+ * set server address
* @throws ParseException if the parameters are not valid
*/
public Builder(String username, String serverDomain)
@@ -309,6 +309,7 @@
* Gets the SIP URI of this profile.
*
* @return the SIP URI of this profile
+ * @hide
*/
public SipURI getUri() {
return (SipURI) mAddress.getURI();
@@ -327,6 +328,7 @@
* Gets the SIP address of this profile.
*
* @return the SIP address of this profile
+ * @hide
*/
public Address getSipAddress() {
return mAddress;
diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java
index e751e46..705f271 100644
--- a/voip/java/android/net/sip/SipRegistrationListener.java
+++ b/voip/java/android/net/sip/SipRegistrationListener.java
@@ -29,15 +29,15 @@
void onRegistering(String localProfileUri);
/**
- * Called when registration is successfully done.
+ * Called when the registration succeeded.
*
* @param localProfileUri the URI string of the SIP profile to register with
- * @param expiryTime duration in second before the registration expires
+ * @param expiryTime duration in seconds before the registration expires
*/
void onRegistrationDone(String localProfileUri, long expiryTime);
/**
- * Called when the registration fails.
+ * Called when the registration failed.
*
* @param localProfileUri the URI string of the SIP profile to register with
* @param errorCode error code of this error
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 26ed878..4435110 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -307,6 +307,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+
/**
* The lookup key for a {@link android.net.LinkProperties} object associated with the
* Wi-Fi network. Retrieve with
@@ -316,6 +317,14 @@
public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
/**
+ * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+
+ /**
* The network IDs of the configured networks could have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 9cc44b5..f8c9bd6 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.net.LinkCapabilities;
import android.net.NetworkInfo;
import android.net.LinkProperties;
import android.net.NetworkStateTracker;
@@ -48,6 +49,7 @@
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
private LinkProperties mLinkProperties;
+ private LinkCapabilities mLinkCapabilities;
private NetworkInfo mNetworkInfo;
/* For sending events to connectivity service handler */
@@ -59,9 +61,9 @@
public WifiStateTracker() {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mLinkProperties = new LinkProperties();
+ mLinkCapabilities = new LinkCapabilities();
mNetworkInfo.setIsAvailable(false);
- mLinkProperties.clear();
setTeardownRequested(false);
}
@@ -196,6 +198,16 @@
}
/**
+ * A capability is an Integer/String pair, the capabilities
+ * are defined in the class LinkSocket#Key.
+ *
+ * @return a copy of this connections capabilities, may be empty but never null.
+ */
+ public LinkCapabilities getLinkCapabilities() {
+ return new LinkCapabilities(mLinkCapabilities);
+ }
+
+ /**
* Fetch default gateway address for the network
*/
public int getDefaultGatewayAddr() {
@@ -230,8 +242,16 @@
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
- mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+ mLinkProperties = intent.getParcelableExtra(
WifiManager.EXTRA_LINK_PROPERTIES);
+ if (mLinkProperties == null) {
+ mLinkProperties = new LinkProperties();
+ }
+ mLinkCapabilities = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_CAPABILITIES);
+ if (mLinkCapabilities == null) {
+ mLinkCapabilities = new LinkCapabilities();
+ }
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
msg.sendToTarget();
} else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {