diff options
298 files changed, 10260 insertions, 4055 deletions
diff --git a/api/current.xml b/api/current.xml index 33a8020..e688ab4 100644 --- a/api/current.xml +++ b/api/current.xml @@ -6785,6 +6785,17 @@ visibility="public" > </field> +<field name="restoreAnyVersion" + type="int" + transient="false" + volatile="false" + value="16843451" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="restoreNeedsApplication" type="int" transient="false" @@ -14630,7 +14641,7 @@ </parameter> <parameter name="features" type="java.lang.String[]"> </parameter> -<parameter name="activityForPrompting" type="android.app.Activity"> +<parameter name="activity" type="android.app.Activity"> </parameter> <parameter name="addAccountOptions" type="android.os.Bundle"> </parameter> @@ -26277,6 +26288,83 @@ </parameter> </method> </interface> +<class name="UiModeManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="disableCarMode" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getNightMode" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="setNightMode" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mode" type="int"> +</parameter> +</method> +<field name="MODE_AUTO" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MODE_NIGHT" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MODE_NOTNIGHT" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="WallpaperInfo" extends="java.lang.Object" abstract="false" @@ -34549,6 +34637,17 @@ visibility="public" > </field> +<field name="UI_MODE_SERVICE" + type="java.lang.String" + transient="false" + volatile="false" + value=""uimode"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="VIBRATOR_SERVICE" type="java.lang.String" transient="false" @@ -89879,6 +89978,356 @@ </package> <package name="android.net.http" > +<class name="AndroidHttpClient" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="org.apache.http.client.HttpClient"> +</implements> +<method name="close" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="disableCurlLogging" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="enableCurlLogging" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +<parameter name="level" type="int"> +</parameter> +</method> +<method name="execute" + return="org.apache.http.HttpResponse" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="org.apache.http.client.methods.HttpUriRequest"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="org.apache.http.HttpResponse" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="org.apache.http.client.methods.HttpUriRequest"> +</parameter> +<parameter name="context" type="org.apache.http.protocol.HttpContext"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="org.apache.http.HttpResponse" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="target" type="org.apache.http.HttpHost"> +</parameter> +<parameter name="request" type="org.apache.http.HttpRequest"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="org.apache.http.HttpResponse" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="target" type="org.apache.http.HttpHost"> +</parameter> +<parameter name="request" type="org.apache.http.HttpRequest"> +</parameter> +<parameter name="context" type="org.apache.http.protocol.HttpContext"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="T" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="org.apache.http.client.methods.HttpUriRequest"> +</parameter> +<parameter name="responseHandler" type="org.apache.http.client.ResponseHandler<? extends T>"> +</parameter> +<exception name="ClientProtocolException" type="org.apache.http.client.ClientProtocolException"> +</exception> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="T" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="org.apache.http.client.methods.HttpUriRequest"> +</parameter> +<parameter name="responseHandler" type="org.apache.http.client.ResponseHandler<? extends T>"> +</parameter> +<parameter name="context" type="org.apache.http.protocol.HttpContext"> +</parameter> +<exception name="ClientProtocolException" type="org.apache.http.client.ClientProtocolException"> +</exception> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="T" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="target" type="org.apache.http.HttpHost"> +</parameter> +<parameter name="request" type="org.apache.http.HttpRequest"> +</parameter> +<parameter name="responseHandler" type="org.apache.http.client.ResponseHandler<? extends T>"> +</parameter> +<exception name="ClientProtocolException" type="org.apache.http.client.ClientProtocolException"> +</exception> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="execute" + return="T" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="target" type="org.apache.http.HttpHost"> +</parameter> +<parameter name="request" type="org.apache.http.HttpRequest"> +</parameter> +<parameter name="responseHandler" type="org.apache.http.client.ResponseHandler<? extends T>"> +</parameter> +<parameter name="context" type="org.apache.http.protocol.HttpContext"> +</parameter> +<exception name="ClientProtocolException" type="org.apache.http.client.ClientProtocolException"> +</exception> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="getCompressedEntity" + return="org.apache.http.entity.AbstractHttpEntity" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="data" type="byte[]"> +</parameter> +<parameter name="resolver" type="android.content.ContentResolver"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="getConnectionManager" + return="org.apache.http.conn.ClientConnectionManager" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMinGzipSize" + return="long" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="resolver" type="android.content.ContentResolver"> +</parameter> +</method> +<method name="getParams" + return="org.apache.http.params.HttpParams" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getUngzippedContent" + return="java.io.InputStream" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="entity" type="org.apache.http.HttpEntity"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +</method> +<method name="modifyRequestToAcceptGzipResponse" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="org.apache.http.HttpRequest"> +</parameter> +</method> +<method name="newInstance" + return="android.net.http.AndroidHttpClient" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="userAgent" type="java.lang.String"> +</parameter> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> +<method name="newInstance" + return="android.net.http.AndroidHttpClient" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="userAgent" type="java.lang.String"> +</parameter> +</method> +<field name="DEFAULT_SYNC_MIN_GZIP_BYTES" + type="long" + transient="false" + volatile="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="HttpDateTime" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="HttpDateTime" + type="android.net.http.HttpDateTime" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="parse" + return="java.lang.Long" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="timeString" type="java.lang.String"> +</parameter> +<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> +</exception> +</method> +</class> <class name="SslCertificate" extends="java.lang.Object" abstract="false" @@ -113095,6 +113544,28 @@ visibility="public" > </method> +<method name="getGlobalClassInitCount" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getGlobalClassInitTime" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getGlobalExternalAllocCount" return="int" abstract="false" @@ -113341,6 +113812,28 @@ visibility="public" > </method> +<method name="resetGlobalClassInitCount" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="resetGlobalClassInitTime" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="resetGlobalExternalAllocCount" return="void" abstract="false" @@ -133377,6 +133870,17 @@ visibility="public" > </field> +<field name="ACTION_ADD_ACCOUNT" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.settings.ADD_ACCOUNT_SETTINGS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_AIRPLANE_MODE_SETTINGS" type="java.lang.String" transient="false" @@ -133685,6 +134189,17 @@ visibility="public" > </field> +<field name="EXTRA_AUTHORITIES" + type="java.lang.String" + transient="false" + volatile="false" + value=""authorities"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Settings.NameValueTable" extends="java.lang.Object" @@ -137618,6 +138133,17 @@ visibility="public" > </field> +<field name="EXTRA_CALLING_PACKAGE" + type="java.lang.String" + transient="false" + volatile="false" + value=""calling_package"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_LANGUAGE" type="java.lang.String" transient="false" @@ -163356,6 +163882,134 @@ > </field> </class> +<class name="Patterns" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="concatGroups" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="matcher" type="java.util.regex.Matcher"> +</parameter> +</method> +<method name="digitsAndPlusOnly" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="matcher" type="java.util.regex.Matcher"> +</parameter> +</method> +<field name="DOMAIN_NAME" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EMAIL_ADDRESS" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="GOOD_IRI_CHAR" + type="java.lang.String" + transient="false" + volatile="false" + value=""a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="IP_ADDRESS" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PHONE" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TOP_LEVEL_DOMAIN" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TOP_LEVEL_DOMAIN_STR" + type="java.lang.String" + transient="false" + volatile="false" + value=""((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw])"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL" + type="java.lang.String" + transient="false" + volatile="false" + value=""(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw]))"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="WEB_URL" + type="java.util.regex.Pattern" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="PrintStreamPrinter" extends="java.lang.Object" abstract="false" @@ -211651,6 +212305,17 @@ <parameter name="measureSpec" type="int"> </parameter> </method> +<method name="resume" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="seekTo" return="void" abstract="false" @@ -211764,6 +212429,17 @@ visibility="public" > </method> +<method name="suspend" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> </class> <class name="ViewAnimator" extends="android.widget.FrameLayout" @@ -215715,7 +216391,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -215798,7 +216474,7 @@ value=""/sdcard/dmtrace.trace"" static="true" final="true" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </field> @@ -215835,6 +216511,28 @@ visibility="public" > </field> +<field name="KIND_GLOBAL_CLASS_INIT_COUNT" + type="int" + transient="false" + volatile="false" + value="32" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KIND_GLOBAL_CLASS_INIT_TIME" + type="int" + transient="false" + volatile="false" + value="64" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KIND_GLOBAL_EXT_ALLOCATED_BYTES" type="int" transient="false" @@ -215934,6 +216632,28 @@ visibility="public" > </field> +<field name="KIND_THREAD_CLASS_INIT_COUNT" + type="int" + transient="false" + volatile="false" + value="2097152" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KIND_THREAD_CLASS_INIT_TIME" + type="int" + transient="false" + volatile="false" + value="4194304" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KIND_THREAD_EXT_ALLOCATED_BYTES" type="int" transient="false" diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index d640de1..b6c9de4 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -88,6 +88,8 @@ public class Am { if (op.equals("start")) { runStart(); + } else if (op.equals("startservice")) { + runStartService(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { @@ -183,6 +185,15 @@ public class Am { return intent; } + private void runStartService() throws Exception { + Intent intent = makeIntent(); + System.out.println("Starting service: " + intent); + ComponentName cn = mAm.startService(null, intent, intent.getType()); + if (cn == null) { + System.err.println("Error: Not found; no service started."); + } + } + private void runStart() throws Exception { Intent intent = makeIntent(); System.out.println("Starting: " + intent); @@ -496,6 +507,8 @@ public class Am { " start an Activity: am start [-D] <INTENT>\n" + " -D: enable debugging\n" + "\n" + + " start a Service: am startservice <INTENT>\n" + + "\n" + " send a broadcast Intent: am broadcast <INTENT>\n" + "\n" + " start an Instrumentation: am instrument [flags] <COMPONENT>\n" + diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 78f90a1..adec5a4 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -96,6 +96,7 @@ static void dumpstate() { run_command("NETWORK INTERFACES", 10, "netcfg", NULL); dump_file("NETWORK ROUTES", "/proc/net/route"); + dump_file("ARP CACHE", "/proc/net/arp"); #ifdef FWDUMP_bcm4329 run_command("DUMP WIFI FIRMWARE LOG", 60, diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 92ae310..8e4adb1 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -68,7 +68,7 @@ /* other handy constants */ #define PROTECTED_DIR_PREFIX "/data/app-private/" -#define SDCARD_DIR_PREFIX "/asec/" +#define SDCARD_DIR_PREFIX getenv("ASEC_MOUNTPOINT") #define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" #define DALVIK_CACHE_POSTFIX "/classes.dex" diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 68d8bb0..52f767e 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -17,6 +17,8 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar +LOCAL_MODULE_TAGS := debug + LOCAL_MODULE:= stagefright include $(BUILD_EXECUTABLE) @@ -39,6 +41,8 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar +LOCAL_MODULE_TAGS := debug + LOCAL_MODULE:= record include $(BUILD_EXECUTABLE) @@ -61,6 +65,8 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar +LOCAL_MODULE_TAGS := debug + LOCAL_MODULE:= audioloop include $(BUILD_EXECUTABLE) diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java index 0c7ca83..0a48fe7 100644 --- a/common/java/com/android/common/OperationScheduler.java +++ b/common/java/com/android/common/OperationScheduler.java @@ -17,6 +17,7 @@ package com.android.common; import android.content.SharedPreferences; +import android.net.http.HttpDateTime; import android.text.format.Time; import java.util.Map; diff --git a/common/java/com/android/common/speech/Recognition.java b/common/java/com/android/common/speech/Recognition.java index 6f164a9..a79a19b 100644 --- a/common/java/com/android/common/speech/Recognition.java +++ b/common/java/com/android/common/speech/Recognition.java @@ -19,18 +19,42 @@ package com.android.common.speech; /** * Utilities for voice recognition implementations. * - * @see android.app.RecognitionService + * @see android.speech.RecognitionService + * @see android.speech.RecognizerIntent */ public class Recognition { - + + /** + * The key to the extra in the Bundle returned by + * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS + * which is an ArrayList of CharSequences which are hints that can be shown to + * the user for voice actions currently supported by voice search for the user's current + * language preference for voice search (i.e., the one defined in the extra + * android.speech.RecognizerIntent#EXTRA_LANGUAGE_PREFERENCE). + * + * If this is paired with EXTRA_HINT_CONTEXT, should return a set of hints that are + * appropriate for the provided context. + * + * The CharSequences are SpannedStrings and will contain segments wrapped in + * <annotation action="true"></annotation>. This is to indicate the section of the text + * which represents the voice action, to be highlighted in the UI if so desired. + */ + public static final String EXTRA_HINT_STRINGS = "android.speech.extra.HINT_STRINGS"; + + /** + * The key to an extra to be included in the request intent for + * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS. + * Should be an int of one of the values defined below. If an + * unknown int value is provided, it should be ignored. + */ + public static final String EXTRA_HINT_CONTEXT = "android.speech.extra.HINT_CONTEXT"; + /** - * The extra key used in an intent to the speech recognizer for voice search. Not - * generally to be used by developers. The system search dialog uses this, for example, - * to set a calling package for identification by a voice search API. If this extra - * is set by anyone but the system process, it should be overridden by the voice search - * implementation. + * A set of values for EXTRA_HINT_CONTEXT. */ - public final static String EXTRA_CALLING_PACKAGE = "calling_package"; + public static final int HINT_CONTEXT_UNKNOWN = 0; + public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1; + public static final int HINT_CONTEXT_CAR_HOME = 2; private Recognition() { } // don't instantiate } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 43a0f30..e2263fc 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -43,36 +43,92 @@ import java.util.Map; import com.google.android.collect.Maps; /** - * A class that helps with interactions with the AccountManager Service. It provides - * methods to allow for account, password, and authtoken management for all accounts on the - * device. One accesses the {@link AccountManager} by calling: - * <pre> - * AccountManager accountManager = AccountManager.get(context); - * </pre> + * This class provides access to a centralized registry of the user's + * online accounts. With this service, users only need to enter their + * credentials (username and password) once for any account, granting + * applications access to online resources with "one-click" approval. * - * <p> - * The AccountManager Service provides storage for the accounts known to the system, - * provides methods to manage them, and allows the registration of authenticators to - * which operations such as addAccount and getAuthToken are delegated. - * <p> - * Many of the calls take an {@link AccountManagerCallback} and {@link Handler} as parameters. - * These calls return immediately but run asynchronously. If a callback is provided then - * {@link AccountManagerCallback#run} will be invoked wen the request completes, successfully - * or not. An {@link AccountManagerFuture} is returned by these requests and also passed into the - * callback. The result if retrieved by calling {@link AccountManagerFuture#getResult()} which - * either returns the result or throws an exception as appropriate. - * <p> - * The asynchronous request can be made blocking by not providing a callback and instead - * calling {@link AccountManagerFuture#getResult()} on the future that is returned. This will - * cause the running thread to block until the result is returned. Keep in mind that one - * should not block the main thread in this way. Instead one should either use a callback, - * thus making the call asynchronous, or make the blocking call on a separate thread. - * getResult() will throw an {@link IllegalStateException} if you call it from the main thread - * before the request has completed, i.e. before the callback has been invoked. - * <p> - * If one wants to ensure that the callback is invoked from a specific handler then they should - * pass the handler to the request. This makes it easier to ensure thread-safety by running - * all of one's logic from a single handler. + * <p>Different online services have different ways of handling accounts and + * authentication, so the account manager uses pluggable <em>authenticator</em> + * modules for different <em>account types</em>. The authenticators (which + * may be written by third parties) handle the actual details of validating + * account credentials and storing account information. For example, Google, + * Facebook, and Microsoft Exchange each have their own authenticator. + * + * <p>Many servers support some notion of an <em>authentication token</em>, + * which can be used to authenticate a request to the server without sending + * the user's actual password. (Auth tokens are normally created with a + * separate request which does include the user's credentials.) AccountManager + * can generate these auth tokens for applications, so the application doesn't + * need to handle passwords directly. Auth tokens are normally reusable, and + * cached by AccountManager, but must be refreshed periodically. It's the + * responsibility of applications to <em>invalidate</em> auth tokens when they + * stop working so the AccountManager knows it needs to regenerate them. + * + * <p>Applications accessing a server normally go through these steps: + * + * <ul> + * <li>Get an instance of AccountManager using {@link #get(Context)}. + * + * <li>List the available accounts using {@link #getAccountsByType} or + * {@link #getAccountsByTypeAndFeatures}. Normally applications will only + * be interested in accounts with one particular <em>type</em>, which + * identifies the authenticator. Account <em>features</em> are used to + * identify particular account subtypes and capabilities. Both the account + * type and features are authenticator-specific strings, and must be known by + * the application in coordination with its preferred authenticators. + * + * <li>Select one or more of the available accounts, possibly by asking the + * user for their preference. If no suitable accounts are available, + * {@link #addAccount} may be called to prompt the user to create an + * account of the appropriate type. + * + * <li>Request an auth token for the selected account(s) using one of the + * {@link #getAuthToken} methods or related helpers. Refer to the description + * of each method for exact usage and error handling details. + * + * <li>Make the request using the auth token. The form of the auth token, + * the format of the request, and the protocol used are all specific to the + * service you are accessing. The application makes the request itself, using + * whatever network and protocol libraries are useful. + * + * <li><b>Important:</b> If the request fails with an authentication error, + * it could be that a cached auth token is stale and no longer honored by + * the server. The application must call {@link #invalidateAuthToken} to remove + * the token from the cache, otherwise requests will continue failing! After + * invalidating the auth token, immediately go back to the "Request an auth + * token" step above. If the process fails the second time, then it can be + * treated as a "genuine" authentication failure and the user notified or other + * appropriate actions taken. + * </ul> + * + * <p>Some AccountManager methods may require interaction with the user to + * prompt for credentials, present options, or ask the user to add an account. + * The caller may choose whether to allow AccountManager to directly launch the + * necessary user interface and wait for the user, or to return an Intent which + * the caller may use to launch the interface, or (in some cases) to install a + * notification which the user can select at any time to launch the interface. + * To have AccountManager launch the interface directly, the caller must supply + * the current foreground {@link Activity} context. + * + * <p>Many AccountManager methods take {@link AccountManagerCallback} and + * {@link Handler} as parameters. These methods return immediately but + * run asynchronously. If a callback is provided then + * {@link AccountManagerCallback#run} will be invoked on the Handler's + * thread when the request completes, successfully or not. + * An {@link AccountManagerFuture} is returned by these requests and also + * supplied to the callback (if any). The result is retrieved by calling + * {@link AccountManagerFuture#getResult()} which waits for the operation + * to complete (if necessary) and either returns the result or throws an + * exception if an error occurred during the operation. + * To make the request synchronously, call + * {@link AccountManagerFuture#getResult()} immediately on receiving the + * future from the method. No callback need be supplied. + * + * <p>Requests which may block, including + * {@link AccountManagerFuture#getResult()}, must never be called on + * the application's main event thread. These operations throw + * {@link IllegalStateException} if they are used on the main thread. */ public class AccountManager { private static final String TAG = "AccountManager"; @@ -85,34 +141,65 @@ public class AccountManager { public static final int ERROR_CODE_BAD_ARGUMENTS = 7; public static final int ERROR_CODE_BAD_REQUEST = 8; - public static final String KEY_ACCOUNTS = "accounts"; - public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types"; - public static final String KEY_USERDATA = "userdata"; - public static final String KEY_AUTHTOKEN = "authtoken"; - public static final String KEY_PASSWORD = "password"; + /** + * The Bundle key used for the {@link String} account name in results + * from methods which return information about a particular account. + */ public static final String KEY_ACCOUNT_NAME = "authAccount"; + + /** + * The Bundle key used for the {@link String} account type in results + * from methods which return information about a particular account. + */ public static final String KEY_ACCOUNT_TYPE = "accountType"; - public static final String KEY_ERROR_CODE = "errorCode"; - public static final String KEY_ERROR_MESSAGE = "errorMessage"; + + /** + * The Bundle key used for the auth token value in results + * from {@link #getAuthToken} and friends. + */ + public static final String KEY_AUTHTOKEN = "authtoken"; + + /** + * The Bundle key used for an {@link Intent} in results from methods that + * may require the caller to interact with the user. The Intent can + * be used to start the corresponding user interface activity. + */ public static final String KEY_INTENT = "intent"; - public static final String KEY_BOOLEAN_RESULT = "booleanResult"; + + /** + * The Bundle key used to supply the password directly in options to + * {@link #confirmCredentials}, rather than prompting the user with + * the standard password prompt. + */ + public static final String KEY_PASSWORD = "password"; + + public static final String KEY_ACCOUNTS = "accounts"; public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse"; public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse"; + public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types"; public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage"; public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey"; + public static final String KEY_BOOLEAN_RESULT = "booleanResult"; + public static final String KEY_ERROR_CODE = "errorCode"; + public static final String KEY_ERROR_MESSAGE = "errorMessage"; + public static final String KEY_USERDATA = "userdata"; + public static final String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_META_DATA_NAME = - "android.accounts.AccountAuthenticator"; + "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; private final Context mContext; private final IAccountManager mService; private final Handler mMainHandler; + /** * Action sent as a broadcast Intent by the AccountsService - * when accounts are added to and/or removed from the device's - * database. + * when accounts are added, accounts are removed, or an + * account's credentials (saved password, etc) are changed. + * + * @see #addOnAccountsUpdatedListener */ public static final String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED"; @@ -136,26 +223,36 @@ public class AccountManager { } /** - * Retrieve an AccountManager instance that is associated with the context that is passed in. - * Certain calls such as {@link #addOnAccountsUpdatedListener} use this context internally, - * so the caller must take care to use a {@link Context} whose lifetime is associated with - * the listener registration. + * Gets an AccountManager instance associated with a Context. + * The {@link Context} will be used as long as the AccountManager is + * active, so make sure to use a {@link Context} whose lifetime is + * commensurate with any listeners registered to + * {@link #addOnAccountsUpdatedListener} or similar methods. + * + * <p>It is safe to call this method from the main thread. + * + * <p>No permission is required to call this method. + * * @param context The {@link Context} to use when necessary - * @return an {@link AccountManager} instance that is associated with context + * @return An {@link AccountManager} instance */ public static AccountManager get(Context context) { return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); } /** - * Get the password that is associated with the account. Returns null if the account does - * not exist. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. + * Gets the saved password associated with the account. + * This is intended for authenticators and related code; applications + * should get an auth token instead. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the account's authenticator. + * + * @param account The account to query for a password + * @return The account's password, null if none or if the account doesn't exist */ public String getPassword(final Account account) { try { @@ -167,14 +264,19 @@ public class AccountManager { } /** - * Get the user data named by "key" that is associated with the account. - * Returns null if the account does not exist or if it does not have a value for key. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. + * Gets the user data named by "key" associated with the account. + * This is intended for authenticators and related code to store + * arbitrary metadata along with accounts. The meaning of the keys + * and values is up to the authenticator for the account. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the account's authenticator. + * + * @param account The account to query for user data + * @return The user data, null if the account or key doesn't exist */ public String getUserData(final Account account, final String key) { try { @@ -186,14 +288,15 @@ public class AccountManager { } /** - * Query the AccountManager Service for an array that contains a - * {@link AuthenticatorDescription} for each registered authenticator. - * @return an array that contains all the authenticators known to the AccountManager service. - * This array will be empty if there are no authenticators and will never return null. - * <p> - * It is safe to call this method from the main thread. - * <p> - * No permission is required to make this call. + * Lists the currently registered authenticators. + * + * <p>It is safe to call this method from the main thread. + * + * <p>No permission is required to call this method. + * + * @return An array of {@link AuthenticatorDescription} for every + * authenticator known to the AccountManager service. Empty (never + * null) if no authenticators are known. */ public AuthenticatorDescription[] getAuthenticatorTypes() { try { @@ -205,13 +308,16 @@ public class AccountManager { } /** - * Query the AccountManager Service for all accounts. - * @return an array that contains all the accounts known to the AccountManager service. - * This array will be empty if there are no accounts and will never return null. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS} + * Lists all accounts of any type registered on the device. + * Equivalent to getAccountsByType(null). + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. + * + * @return An array of {@link Account}, one for each account. Empty + * (never null) if no accounts have been added. */ public Account[] getAccounts() { try { @@ -223,15 +329,20 @@ public class AccountManager { } /** - * Query the AccountManager for the set of accounts that have a given type. If null - * is passed as the type than all accounts are returned. - * @param type the account type by which to filter, or null to get all accounts - * @return an array that contains the accounts that match the specified type. This array - * will be empty if no accounts match. It will never return null. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS} + * Lists all accounts of a particular type. The account type is a + * string token corresponding to the authenticator and useful domain + * of the account. For example, there are types corresponding to Google + * and Facebook. The exact string token to use will be published somewhere + * associated with the authenticator in question. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. + * + * @param type The type of accounts to return, null to retrieve all accounts + * @return An array of {@link Account}, one per matching account. Empty + * (never null) if no accounts of the specified type have been added. */ public Account[] getAccountsByType(String type) { try { @@ -243,45 +354,27 @@ public class AccountManager { } /** - * Tests that the given account has the specified features. If this account does not exist - * then this call returns false. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Boolean result = hasFeatures(account, features, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * hasFeatures(account, features, new AccountManagerCallback<Boolean>() { - * public void run(AccountManagerFuture<Boolean> future) { - * Boolean result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}. + * Finds out whether a particular account has all the specified features. + * Account features are authenticator-specific string tokens identifying + * boolean account properties. For example, features are used to tell + * whether Google accounts have a particular service (such as Google + * Calendar or Google Talk) enabled. The feature names and their meanings + * are published somewhere associated with the authenticator in question. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. * * @param account The {@link Account} to test - * @param features the features for which to test - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Boolean} that is true if the account exists and has the - * specified features. + * @param features An array of the account features to check + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Boolean, + * true if the account exists and has all of the specified features. */ public AccountManagerFuture<Boolean> hasFeatures(final Account account, final String[] features, @@ -300,18 +393,73 @@ public class AccountManager { } /** - * Add an account to the AccountManager's set of known accounts. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. - * @param account The account to add - * @param password The password to associate with the account. May be null. - * @param userdata A bundle of key/value pairs to set as the account's userdata. May be null. - * @return true if the account was sucessfully added, false otherwise, for example, - * if the account already exists or if the account is null + * Lists all accounts of a type which have certain features. The account + * type identifies the authenticator (see {@link #getAccountsByType}). + * Account features are authenticator-specific string tokens identifying + * boolean account properties (see {@link #hasFeatures}). + * + * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator, + * which may contact the server or do other work to check account features, + * so the method returns an {@link AccountManagerFuture}. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. + * + * @param type The type of accounts to return, must not be null + * @param features An array of the account features to require, + * may be null or empty + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to an array of + * {@link Account}, one per account of the specified type which + * matches the requested features. + */ + public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( + final String type, final String[] features, + AccountManagerCallback<Account[]> callback, Handler handler) { + return new Future2Task<Account[]>(handler, callback) { + public void doWork() throws RemoteException { + if (type == null) { + Log.e(TAG, "Type is null"); + set(new Account[0]); + return; + } + mService.getAccountsByFeatures(mResponse, type, features); + } + public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { + if (!bundle.containsKey(KEY_ACCOUNTS)) { + throw new AuthenticatorException("no result in response"); + } + final Parcelable[] parcelables = bundle.getParcelableArray(KEY_ACCOUNTS); + Account[] descs = new Account[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + descs[i] = (Account) parcelables[i]; + } + return descs; + } + }.start(); + } + + /** + * Adds an account directly to the AccountManager. Normally used by sign-up + * wizards associated with authenticators, not directly by applications. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the added account's authenticator. + * + * @param account The {@link Account} to add + * @param password The password to associate with the account, null for none + * @param userdata String values to use for the account's userdata, null for none + * @return Whether the account was successfully added. False if the account + * already exists, the account is null, or another error occurs. */ public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { try { @@ -323,43 +471,25 @@ public class AccountManager { } /** - * Removes the given account. If this account does not exist then this call has no effect. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Boolean result = removeAccount(account, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * removeAccount(account, new AccountManagerCallback<Boolean>() { - * public void run(AccountManagerFuture<Boolean> future) { - * Boolean result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * Removes an account from the AccountManager. Does nothing if the account + * does not exist. Does not delete the account from the server. + * The authenticator may have its own policies preventing account + * deletion, in which case the account will not be deleted. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. * * @param account The {@link Account} to remove - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Boolean} that is true if the account is successfully removed - * or false if the authenticator refuses to remove the account. + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Boolean, + * true if the account has been successfully removed, + * false if the authenticator forbids deleting this account. */ public AccountManagerFuture<Boolean> removeAccount(final Account account, AccountManagerCallback<Boolean> callback, Handler handler) { @@ -377,14 +507,20 @@ public class AccountManager { } /** - * Removes the given authtoken. If this authtoken does not exist for the given account type - * then this call has no effect. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * @param accountType the account type of the authtoken to invalidate - * @param authToken the authtoken to invalidate + * Removes an auth token from the AccountManager's cache. Does nothing if + * the auth token is not currently in the cache. Applications must call this + * method when the auth token is found to have expired or otherwise become + * invalid for authenticating requests. The AccountManager does not validate + * or expire cached auth tokens otherwise. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS} or + * {@link android.Manifest.permission#USE_CREDENTIALS} + * + * @param accountType The account type of the auth token to invalidate + * @param authToken The auth token to invalidate */ public void invalidateAuthToken(final String accountType, final String authToken) { try { @@ -396,20 +532,21 @@ public class AccountManager { } /** - * Gets the authtoken named by "authTokenType" for the specified account if it is cached - * by the AccountManager. If no authtoken is cached then null is returned rather than - * asking the authenticaticor to generate one. If the account or the - * authtoken do not exist then null is returned. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. - * @param account the account whose authtoken is to be retrieved, must not be null - * @param authTokenType the type of authtoken to retrieve - * @return an authtoken for the given account and authTokenType, if one is cached by the - * AccountManager, null otherwise. + * Gets an auth token from the AccountManager's cache. If no auth + * token is cached for this account, null will be returned -- a new + * auth token will not be generated, and the server will not be contacted. + * Intended for use by the authenticator, not directly by applications. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the account's authenticator. + * + * @param account The account to fetch an auth token for + * @param authTokenType The type of auth token to fetch, see {#getAuthToken} + * @return The cached auth token for this account and type, or null if + * no auth token is cached or the account does not exist. */ public String peekAuthToken(final Account account, final String authTokenType) { if (account == null) { @@ -428,16 +565,19 @@ public class AccountManager { } /** - * Sets the password for the account. The password may be null. If the account does not exist - * then this call has no affect. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. - * @param account the account whose password is to be set. Must not be null. - * @param password the password to set for the account. May be null. + * Sets or forgets a saved password. This modifies the local copy of the + * password used to automatically authenticate the user; it does + * not change the user's account password on the server. Intended for use + * by the authenticator, not directly by applications. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and have the same UID as the account's authenticator. + * + * @param account The account to set a password for + * @param password The password to set, null to clear the password */ public void setPassword(final Account account, final String password) { if (account == null) { @@ -453,13 +593,18 @@ public class AccountManager { } /** - * Sets the password for account to null. If the account does not exist then this call - * has no effect. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * @param account the account whose password is to be cleared. Must not be null. + * Forgets a saved password. This erases the local copy of the password; + * it does not change the user's account password on the server. + * Has the same effect as setPassword(account, null) but requires fewer + * permissions, and may be used by applications or management interfaces + * to "sign out" from an account. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS} + * + * @param account The account whose password to clear */ public void clearPassword(final Account account) { if (account == null) { @@ -475,17 +620,19 @@ public class AccountManager { } /** - * Sets account's userdata named "key" to the specified value. If the account does not - * exist then this call has no effect. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. - * @param account the account whose userdata is to be set. Must not be null. - * @param key the key of the userdata to set. Must not be null. - * @param value the value to set. May be null. + * Sets one userdata key for an account. Intended by use for the + * authenticator to stash state for itself, not directly by applications. + * The meaning of the keys and values is up to the authenticator. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the account's authenticator. + * + * @param account The account to set the userdata for + * @param key The userdata key to set. Must not be null + * @param value The value to set, null to clear this userdata key */ public void setUserData(final Account account, final String key, final String value) { if (account == null) { @@ -505,17 +652,20 @@ public class AccountManager { } /** - * Sets the authtoken named by "authTokenType" to the value specified by authToken. + * Adds an auth token to the AccountManager cache for an account. * If the account does not exist then this call has no effect. - * <p> - * It is safe to call this method from the main thread. - * <p> - * Requires that the caller has permission - * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running - * with the same UID as the Authenticator for the account. - * @param account the account whose authtoken is to be set. Must not be null. - * @param authTokenType the type of the authtoken to set. Must not be null. - * @param authToken the authToken to set. May be null. + * Replaces any previous auth token for this account and auth token type. + * Intended for use by the authenticator, not directly by applications. + * + * <p>It is safe to call this method from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} + * and to have the same UID as the account's authenticator. + * + * @param account The account to set an auth token for + * @param authTokenType The type of the auth token, see {#getAuthToken} + * @param authToken The auth token to add to the cache */ public void setAuthToken(Account account, final String authTokenType, final String authToken) { try { @@ -527,25 +677,27 @@ public class AccountManager { } /** - * Convenience method that makes a blocking call to - * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)} - * then extracts and returns the value of {@link #KEY_AUTHTOKEN} from its result. - * <p> - * It is not safe to call this method from the main thread. See {@link #getAuthToken}. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}. - * @param account the account whose authtoken is to be retrieved, must not be null - * @param authTokenType the type of authtoken to retrieve - * @param notifyAuthFailure if true, cause the AccountManager to put up a "sign-on" notification - * for the account if no authtoken is cached by the AccountManager and the the authenticator - * does not have valid credentials to get an authtoken. - * @return an authtoken for the given account and authTokenType, if one is cached by the - * AccountManager, null otherwise. - * @throws AuthenticatorException if the authenticator is not present, unreachable or returns - * an invalid response. - * @throws OperationCanceledException if the request is canceled for any reason - * @throws java.io.IOException if the authenticator experiences an IOException while attempting - * to communicate with its backend server. + * This convenience helper synchronously gets an auth token with + * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}. + * + * <p>This method may block while a network request completes, and must + * never be made from the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param account The account to fetch an auth token for + * @param authTokenType The auth token type, see {#link getAuthToken} + * @param notifyAuthFailure If true, display a notification and return null + * if authentication fails; if false, prompt and wait for the user to + * re-enter correct credentials before returning + * @return An auth token of the specified type for this account, or null + * if authentication fails or none can be fetched. + * @throws AuthenticatorException if the authenticator failed to respond + * @throws OperationCanceledException if the request was canceled for any + * reason, including the user canceling a credential request + * @throws java.io.IOException if the authenticator experienced an I/O problem + * creating a new auth token, usually because of network trouble */ public String blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure) @@ -556,64 +708,57 @@ public class AccountManager { } /** - * Request that an authtoken of the specified type be returned for an account. - * If the Account Manager has a cached authtoken of the requested type then it will - * service the request itself. Otherwise it will pass the request on to the authenticator. - * The authenticator can try to service this request with information it already has stored - * in the AccountManager but may need to launch an activity to prompt the - * user to enter credentials. If it is able to retrieve the authtoken it will be returned - * in the result. - * <p> - * If the authenticator needs to prompt the user for credentials it will return an intent to - * an activity that will do the prompting. The supplied activity will be used to launch the - * intent and the result will come from the launched activity. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = getAuthToken( - * account, authTokenType, options, activity, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * getAuthToken(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}. - * - * @param account The account whose credentials are to be updated. - * @param authTokenType the auth token to retrieve as part of updating the credentials. - * May be null. - * @param options authenticator specific options for the request - * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then - * the intent will be started with this activity. If you do not with to have the intent - * started automatically then use the other form, - * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, android.os.Handler)} - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains: + * Gets an auth token of the specified type for a particular account, + * prompting the user for credentials if necessary. This method is + * intended for applications running in the foreground where it makes + * sense to ask the user directly for a password. + * + * <p>If a previously generated auth token is cached for this account and + * type, then it will be returned. Otherwise, if we have a saved password + * the server accepts, it will be used to generate a new auth token. + * Otherwise, the user will be asked for a password, which will be sent to + * the server to generate a new auth token. + * + * <p>The value of the auth token type depends on the authenticator. + * Some services use different tokens to access different functionality -- + * for example, Google uses different auth tokens to access Gmail and + * Google Calendar for the same account. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param account The account to fetch an auth token for + * @param authTokenType The auth token type, an authenticator-dependent + * string token, must not be null + * @param options Authenticator-specific options for the request, + * may be null or empty + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user for a password + * if necessary; used only to call startActivity(); must not be null. + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with + * at least the following fields: + * <ul> + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted + * </ul> + * + * (Other authenticator-specific values may be returned.) If an auth token + * could not be fetched, {@link AccountManagerFuture#getResult()} throws: * <ul> - * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN} + * <li> {@link AuthenticatorException} if the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation is canceled for + * any reason, incluidng the user canceling a credential request + * <li> {@link IOException} if the authenticator experienced an I/O problem + * creating a new auth token, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> getAuthToken( final Account account, final String authTokenType, final Bundle options, @@ -630,67 +775,71 @@ public class AccountManager { } /** - * Request that an authtoken of the specified type be returned for an account. - * If the Account Manager has a cached authtoken of the requested type then it will - * service the request itself. Otherwise it will pass the request on to the authenticator. - * The authenticator can try to service this request with information it already has stored - * in the AccountManager but may need to launch an activity to prompt the - * user to enter credentials. If it is able to retrieve the authtoken it will be returned - * in the result. - * <p> - * If the authenticator needs to prompt the user for credentials, rather than returning the - * authtoken it will instead return an intent for - * an activity that will do the prompting. If an intent is returned and notifyAuthFailure - * is true then a notification will be created that launches this intent. This intent can be - * invoked by the caller directly to start the activity that prompts the user for the - * updated credentials. Otherwise this activity will not be run until the user activates - * the notification. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = getAuthToken( - * account, authTokenType, notifyAuthFailure, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * getAuthToken(account, authTokenType, notifyAuthFailure, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}. - * - * @param account The account whose credentials are to be updated. - * @param authTokenType the auth token to retrieve as part of updating the credentials. - * May be null. - * @param notifyAuthFailure if true and the authenticator returns a {@link #KEY_INTENT} in the - * result then a "sign-on needed" notification will be created that will launch this intent. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * Gets an auth token of the specified type for a particular account, + * optionally raising a notification if the user must enter credentials. + * This method is intended for background tasks and services where the + * user should not be immediately interrupted with a password prompt. + * + * <p>If a previously generated auth token is cached for this account and + * type, then it will be returned. Otherwise, if we have saved credentials + * the server accepts, it will be used to generate a new auth token. + * Otherwise, an Intent will be returned which, when started, will prompt + * the user for a password. If the notifyAuthFailure parameter is set, + * the same Intent will be associated with a status bar notification, + * alerting the user that they need to enter a password at some point. + * + * <p>If the intent is left in a notification, you will need to wait until + * the user gets around to entering a password before trying again, + * which could be hours or days or never. When it does happen, the + * account manager will broadcast the {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} + * {@link Intent}, which applications can use to trigger another attempt + * to fetch an auth token. + * + * <p>If notifications are not enabled, it is the application's + * responsibility to launch the returned intent at some point to let + * the user enter credentials. In either case, the result from this + * call will not wait for user action. + * + * <p>The value of the auth token type depends on the authenticator. + * Some services use different tokens to access different functionality -- + * for example, Google uses different auth tokens to access Gmail and + * Google Calendar for the same account. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param account The account to fetch an auth token for + * @param authTokenType The auth token type, an authenticator-dependent + * string token, must not be null + * @param notifyAuthFailure True to add a notification to prompt the + * user for a password if necessary, false to leave that to the caller + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with + * at least the following fields on success: * <ul> - * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials - * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN} - * if the authenticator is able to retrieve the auth token + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted + * </ul> + * + * (Other authenticator-specific values may be returned.) If the user + * must enter credentials, the returned Bundle contains only + * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt. + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li> {@link AuthenticatorException} if the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation is canceled for + * any reason, incluidng the user canceling a credential request + * <li> {@link IOException} if the authenticator experienced an I/O problem + * creating a new auth token, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> getAuthToken( final Account account, final String authTokenType, final boolean notifyAuthFailure, @@ -706,57 +855,52 @@ public class AccountManager { } /** - * Request that an account be added with the given accountType. This request - * is processed by the authenticator for the account type. If no authenticator is registered - * in the system then {@link AuthenticatorException} is thrown. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = addAccount( - * account, authTokenType, features, options, activity, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * addAccount(account, authTokenType, features, options, activity, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * - * @param accountType The type of account to add. This must not be null. - * @param authTokenType The account that is added should be able to service this auth token - * type. This may be null. - * @param requiredFeatures The account that is added should support these features. - * This array may be null or empty. - * @param addAccountOptions A bundle of authenticator-specific options that is passed on - * to the authenticator. This may be null. - * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then - * the intent will be started with this activity. If activity is null then the result will - * be returned as-is. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * Asks the user to add an account of a specified type. The authenticator + * for this account type processes this request with the appropriate user + * interface. If the user does elect to create a new account, the account + * name is returned. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * + * @param accountType The type of account to add; must not be null + * @param authTokenType The type of auth token (see {@link #getAuthToken}) + * this account will need to be able to generate, null for none + * @param requiredFeatures The features (see {@link #hasFeatures}) this + * account must have, null for none + * @param addAccountOptions Authenticator-specific options for the request, + * may be null or empty + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user to create an + * account; used only to call startActivity(); if null, the prompt + * will not be launched directly, but the necessary {@link Intent} + * will be returned to the caller instead + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with + * these fields if activity was specified and an account was created: * <ul> - * <li> {@link #KEY_INTENT}, or - * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * </ul> + * + * If no activity was specified, the returned Bundle contains only + * {@link #KEY_INTENT} with the {@link Intent} needed to launch the + * actual account creation process. + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li> {@link AuthenticatorException} if no authenticator was registered for + * this account type or the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation was canceled for + * any reason, including the user canceling the creation process + * <li> {@link IOException} if the authenticator experienced an I/O problem + * creating a new account, usually because of network trouble * </ul> */ public AccountManagerFuture<Bundle> addAccount(final String accountType, @@ -778,128 +922,59 @@ public class AccountManager { } /** - * Queries for accounts that match the given account type and feature set. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Account[] result = - * getAccountsByTypeAndFeatures(accountType, features, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * getAccountsByTypeAndFeatures(accountType, features, new AccountManagerCallback<Account[]>() { - * public void run(AccountManagerFuture<Account[]> future) { - * Account[] result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}. - * - * @param type The type of {@link Account} to return. If null is passed in then an empty - * array will be returned. - * @param features the features with which to filter the accounts list. Each returned account - * will have all specified features. This may be null, which will mean the account list will - * not be filtered by features, making this functionally identical to - * {@link #getAccountsByType(String)}. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a an {@link Account} array that contains accounts of the specified - * type that match all the requested features. - */ - public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( - final String type, final String[] features, - AccountManagerCallback<Account[]> callback, Handler handler) { - return new Future2Task<Account[]>(handler, callback) { - public void doWork() throws RemoteException { - if (type == null) { - Log.e(TAG, "Type is null"); - set(new Account[0]); - return; - } - mService.getAccountsByFeatures(mResponse, type, features); - } - public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { - if (!bundle.containsKey(KEY_ACCOUNTS)) { - throw new AuthenticatorException("no result in response"); - } - final Parcelable[] parcelables = bundle.getParcelableArray(KEY_ACCOUNTS); - Account[] descs = new Account[parcelables.length]; - for (int i = 0; i < parcelables.length; i++) { - descs[i] = (Account) parcelables[i]; - } - return descs; - } - }.start(); - } - - /** - * Requests that the authenticator checks that the user knows the credentials for the account. - * This is typically done by returning an intent to an activity that prompts the user to - * enter the credentials. This request - * is processed by the authenticator for the account. If no matching authenticator is - * registered in the system then {@link AuthenticatorException} is thrown. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = confirmCredentials( - * account, options, activity, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * confirmCredentials(account, options, activity, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * - * @param account The account whose credentials are to be checked - * @param options authenticator specific options for the request - * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then - * the intent will be started with this activity. If activity is null then the result will - * be returned as-is. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * Confirms that the user knows the password for an account to make extra + * sure they are the owner of the account. The user-entered password can + * be supplied directly, otherwise the authenticator for this account type + * prompts the user with the appropriate interface. This method is + * intended for applications which want extra assurance; for example, the + * phone lock screen uses this to let the user unlock the phone with an + * account password if they forget the lock pattern. + * + * <p>If the user-entered password matches a saved password for this + * account, the request is considered valid; otherwise the authenticator + * verifies the password (usually by contacting the server). + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * + * @param account The account to confirm password knowledge for + * @param options Authenticator-specific options for the request; + * if the {@link #KEY_PASSWORD} string field is present, the + * authenticator may use it directly rather than prompting the user; + * may be null or empty + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user to enter a + * password; used only to call startActivity(); if null, the prompt + * will not be launched directly, but the necessary {@link Intent} + * will be returned to the caller instead + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle + * with these fields if activity or password was supplied and + * the account was successfully verified: + * <ul> + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success + * </ul> + * + * If no activity or password was specified, the returned Bundle contains + * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the + * password prompt. + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: * <ul> - * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials - * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct - * credentials + * <li> {@link AuthenticatorException} if the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation was canceled for + * any reason, including the user canceling the password prompt + * <li> {@link IOException} if the authenticator experienced an I/O problem + * verifying the password, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> confirmCredentials(final Account account, final Bundle options, @@ -914,59 +989,52 @@ public class AccountManager { } /** - * Requests that the authenticator update the the credentials for a user. This is typically - * done by returning an intent to an activity that will prompt the user to update the stored - * credentials for the account. This request - * is processed by the authenticator for the account. If no matching authenticator is - * registered in the system then {@link AuthenticatorException} is thrown. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = updateCredentials( - * account, authTokenType, options, activity, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * updateCredentials(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * - * @param account The account whose credentials are to be updated. - * @param authTokenType the auth token to retrieve as part of updating the credentials. - * May be null. - * @param options authenticator specific options for the request - * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then - * the intent will be started with this activity. If activity is null then the result will - * be returned as-is. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * Asks the user to enter a new password for an account, updating the + * saved credentials for the account. Normally this happens automatically + * when the server rejects credentials during an auth token fetch, but this + * can be invoked directly to ensure we have the correct credentials stored. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * + * @param account The account to update credentials for + * @param authTokenType The credentials entered must allow an auth token + * of this type to be created (but no actual auth token is returned); + * may be null + * @param options Authenticator-specific options for the request; + * may be null or empty + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user to enter a + * password; used only to call startActivity(); if null, the prompt + * will not be launched directly, but the necessary {@link Intent} + * will be returned to the caller instead + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle + * with these fields if an activity was supplied and the account + * credentials were successfully updated: * <ul> - * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials - * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct - * credentials. + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * </ul> + * + * If no activity was specified, the returned Bundle contains only + * {@link #KEY_INTENT} with the {@link Intent} needed to launch the + * password prompt. + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li> {@link AuthenticatorException} if the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation was canceled for + * any reason, including the user canceling the password prompt + * <li> {@link IOException} if the authenticator experienced an I/O problem + * verifying the password, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> updateCredentials(final Account account, final String authTokenType, @@ -982,53 +1050,41 @@ public class AccountManager { } /** - * Request that the properties for an authenticator be updated. This is typically done by - * returning an intent to an activity that will allow the user to make changes. This request - * is processed by the authenticator for the account. If no matching authenticator is - * registered in the system then {@link AuthenticatorException} is thrown. - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Do not block the main thread waiting this method's result. - * <p> - * Not allowed from main thread (but allowed from other threads): - * <pre> - * Bundle result = editProperties(accountType, activity, callback, handler).getResult(); - * </pre> - * Allowed from main thread: - * <pre> - * editProperties(accountType, activity, new AccountManagerCallback<Bundle>() { - * public void run(AccountManagerFuture<Bundle> future) { - * Bundle result = future.getResult(); - * // use result - * } - * }, handler); - * </pre> - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * - * @param accountType The account type of the authenticator whose properties are to be edited. - * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then - * the intent will be started with this activity. If activity is null then the result will - * be returned as-is. - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * Offers the user an opportunity to change an authenticator's settings. + * These properties are for the authenticator in general, not a particular + * account. Not all authenticators support this method. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * + * @param accountType The account type associated with the authenticator + * to adjust + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to adjust authenticator settings; + * used only to call startActivity(); if null, the settings dialog will + * not be launched directly, but the necessary {@link Intent} will be + * returned to the caller instead + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle + * which is empty if properties were edited successfully, or + * if no activity was specified, contains only {@link #KEY_INTENT} + * needed to launch the authenticator's settings dialog. + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: * <ul> - * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials - * <li> nothing, returned if the edit completes successfully + * <li> {@link AuthenticatorException} if no authenticator was registered for + * this account type or the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation was canceled for + * any reason, including the user canceling the settings dialog + * <li> {@link IOException} if the authenticator experienced an I/O problem + * updating settings, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> editProperties(final String accountType, final Activity activity, final AccountManagerCallback<Bundle> callback, @@ -1475,57 +1531,68 @@ public class AccountManager { } /** - * Convenience method that combines the functionality of {@link #getAccountsByTypeAndFeatures}, - * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)}, - * and {@link #addAccount}. It first gets the list of accounts that match accountType and the - * feature set. If there are none then {@link #addAccount} is invoked with the authTokenType - * feature set, and addAccountOptions. If there is exactly one then - * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)} is - * called with that account. If there are more than one then a chooser activity is launched - * to prompt the user to select one of them and then the authtoken is retrieved for it, - * <p> - * This call returns immediately but runs asynchronously and the result is accessed via the - * {@link AccountManagerFuture} that is returned. This future is also passed as the sole - * parameter to the {@link AccountManagerCallback}. If the caller wished to use this - * method asynchronously then they will generally pass in a callback object that will get - * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then - * they will generally pass null for the callback and instead call - * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, - * which will then block until the request completes. - * <p> - * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}. - * - * @param accountType the accountType to query; this must be non-null - * @param authTokenType the type of authtoken to retrieve; this must be non-null - * @param features a filter for the accounts. See {@link #getAccountsByTypeAndFeatures}. - * @param activityForPrompting The activity used to start any account management - * activities that are required to fulfill this request. This may be null. - * @param addAccountOptions authenticator-specific options used if an account needs to be added - * @param getAuthTokenOptions authenticator-specific options passed to getAuthToken - * @param callback A callback to invoke when the request completes. If null then - * no callback is invoked. - * @param handler The {@link Handler} to use to invoke the callback. If null then the - * main thread's {@link Handler} is used. - * @return an {@link AccountManagerFuture} that represents the future result of the call. - * The future result is a {@link Bundle} that contains either: + * This convenience helper combines the functionality of + * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and + * {@link #addAccount}. + * + * <p>This method gets a list of the accounts matching the + * specified type and feature set; if there is exactly one, it is + * used; if there are more than one, the user is prompted to pick one; + * if there are none, the user is prompted to add one. Finally, + * an auth token is acquired for the chosen account. + * + * <p>This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#MANAGE_ACCOUNTS}. + * + * @param accountType The account type required + * (see {@link #getAccountsByType}), must not be null + * @param authTokenType The desired auth token type + * (see {@link #getAuthToken}), must not be null + * @param features Required features for the account + * (see {@link #getAccountsByTypeAndFeatures}), may be null or empty + * @param activity The {@link Activity} context to use for launching new + * sub-Activities to prompt to add an account, select an account, + * and/or enter a password, as necessary; used only to call + * startActivity(); should not be null + * @param addAccountOptions Authenticator-specific options to use for + * adding new accounts; may be null or empty + * @param getAuthTokenOptions Authenticator-specific options to use for + * getting auth tokens; may be null or empty + * @param callback Callback to invoke when the request completes, + * null for no callback + * @param handler {@link Handler} identifying the callback thread, + * null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with + * at least the following fields: * <ul> - * <li> {@link #KEY_INTENT}, if no activity is supplied yet an activity needs to launched to - * fulfill the request. - * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN} if the - * request completes successfully. + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account + * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account + * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted + * </ul> + * + * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li> {@link AuthenticatorException} if no authenticator was registered for + * this account type or the authenticator failed to respond + * <li> {@link OperationCanceledException} if the operation was canceled for + * any reason, including the user canceling any operation + * <li> {@link IOException} if the authenticator experienced an I/O problem + * updating settings, usually because of network trouble * </ul> - * If the user presses "back" then the request will be canceled. */ public AccountManagerFuture<Bundle> getAuthTokenByFeatures( final String accountType, final String authTokenType, final String[] features, - final Activity activityForPrompting, final Bundle addAccountOptions, + final Activity activity, final Bundle addAccountOptions, final Bundle getAuthTokenOptions, final AccountManagerCallback<Bundle> callback, final Handler handler) { if (accountType == null) throw new IllegalArgumentException("account type is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); final GetAuthTokenByTypeAndFeaturesTask task = new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features, - activityForPrompting, addAccountOptions, getAuthTokenOptions, callback, handler); + activity, addAccountOptions, getAuthTokenOptions, callback, handler); task.start(); return task; } @@ -1552,18 +1619,26 @@ public class AccountManager { }; /** - * Add a {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}. - * The listener is guaranteed to be invoked on the thread of the Handler that is passed - * in or the main thread's Handler if handler is null. - * <p> - * You must remove this listener before the context that was used to retrieve this - * {@link AccountManager} instance goes away. This generally means when the Activity - * or Service you are running is stopped. - * @param listener the listener to add - * @param handler the Handler whose thread will be used to invoke the listener. If null - * the AccountManager context's main thread will be used. - * @param updateImmediately if true then the listener will be invoked as a result of this - * call. + * Adds an {@link OnAccountsUpdateListener} to this instance of the + * {@link AccountManager}. This listener will be notified whenever the + * list of accounts on the device changes. + * + * <p>As long as this listener is present, the AccountManager instance + * will not be garbage-collected, and neither will the {@link Context} + * used to retrieve it, which may be a large Activity instance. To avoid + * memory leaks, you must remove this listener before then. Normally + * listeners are added in an Activity or Service's {@link Activity#onCreate} + * and removed in {@link Activity#onDestroy}. + * + * <p>It is safe to call this method from the main thread. + * + * <p>No permission is required to call this method. + * + * @param listener The listener to send notifications to + * @param handler {@link Handler} identifying the thread to use + * for notifications, null for the main thread + * @param updateImmediately If true, the listener will be invoked + * (on the handler thread) right away with the current account list * @throws IllegalArgumentException if listener is null * @throws IllegalStateException if listener was already added */ @@ -1596,9 +1671,15 @@ public class AccountManager { } /** - * Remove an {@link OnAccountsUpdateListener} that was previously registered with - * {@link #addOnAccountsUpdatedListener}. - * @param listener the listener to remove + * Removes an {@link OnAccountsUpdateListener} previously registered with + * {@link #addOnAccountsUpdatedListener}. The listener will no longer + * receive notifications of account changes. + * + * <p>It is safe to call this method from the main thread. + * + * <p>No permission is required to call this method. + * + * @param listener The previously added listener to remove * @throws IllegalArgumentException if listener is null * @throws IllegalStateException if listener was not already added */ diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 770554e..2aaf5b0 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -565,7 +565,7 @@ public class AccountManagerService } public void invalidateAuthToken(String accountType, String authToken) { - checkManageAccountsPermission(); + checkManageAccountsOrUseCredentialsPermissions(); long identityToken = clearCallingIdentity(); try { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -691,11 +691,21 @@ public class AccountManagerService if (account == null) { return; } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_PASSWORD, password); - mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + final ContentValues values = new ContentValues(); + values.put(ACCOUNTS_PASSWORD, password); + final long accountId = getAccountId(db, account); + if (accountId >= 0) { + final String[] argsAccountId = {String.valueOf(accountId)}; + db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); + db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); + db.setTransactionSuccessful(); + } + } finally { + db.endTransaction(); + } sendAccountsChangedBroadcast(); } @@ -1134,7 +1144,10 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { if (features == null || features.length == 0) { - getAccountsByType(type); + Account[] accounts = getAccountsByType(type); + Bundle result = new Bundle(); + result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); + onResult(response, result); return; } new GetAccountsByTypeAndFeatureSession(response, type, features).bind(); @@ -1734,17 +1747,22 @@ public class AccountManagerService } } - private void checkBinderPermission(String permission) { + /** Succeeds if any of the specified permissions are granted. */ + private void checkBinderPermission(String... permissions) { final int uid = Binder.getCallingUid(); - if (mContext.checkCallingOrSelfPermission(permission) != - PackageManager.PERMISSION_GRANTED) { - String msg = "caller uid " + uid + " lacks " + permission; - Log.w(TAG, msg); - throw new SecurityException(msg); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "caller uid " + uid + " has " + permission); + + for (String perm : permissions) { + if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "caller uid " + uid + " has " + perm); + } + return; + } } + + String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions); + Log.w(TAG, msg); + throw new SecurityException(msg); } private boolean inSystemImage(int callerUid) { @@ -1835,6 +1853,11 @@ public class AccountManagerService checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS); } + private void checkManageAccountsOrUseCredentialsPermissions() { + checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS, + Manifest.permission.USE_CREDENTIALS); + } + /** * Allow callers with the given uid permission to get credentials for account/authTokenType. * <p> diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 05bbf3b..b38aeda 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1308,14 +1308,16 @@ public class Activity extends ContextThemeWrapper } // close any cursors we are managing. - int numCursors = mManagedCursors.size(); - for (int i = 0; i < numCursors; i++) { - ManagedCursor c = mManagedCursors.get(i); - if (c != null) { - c.mCursor.close(); + synchronized (mManagedCursors) { + int numCursors = mManagedCursors.size(); + for (int i = 0; i < numCursors; i++) { + ManagedCursor c = mManagedCursors.get(i); + if (c != null) { + c.mCursor.close(); + } } + mManagedCursors.clear(); } - mManagedCursors.clear(); } /** @@ -3778,13 +3780,15 @@ public class Activity extends ContextThemeWrapper } final void performRestart() { - final int N = mManagedCursors.size(); - for (int i=0; i<N; i++) { - ManagedCursor mc = mManagedCursors.get(i); - if (mc.mReleased || mc.mUpdated) { - mc.mCursor.requery(); - mc.mReleased = false; - mc.mUpdated = false; + synchronized (mManagedCursors) { + final int N = mManagedCursors.size(); + for (int i=0; i<N; i++) { + ManagedCursor mc = mManagedCursors.get(i); + if (mc.mReleased || mc.mUpdated) { + mc.mCursor.requery(); + mc.mReleased = false; + mc.mUpdated = false; + } } } @@ -3850,12 +3854,14 @@ public class Activity extends ContextThemeWrapper " did not call through to super.onStop()"); } - final int N = mManagedCursors.size(); - for (int i=0; i<N; i++) { - ManagedCursor mc = mManagedCursors.get(i); - if (!mc.mReleased) { - mc.mCursor.deactivate(); - mc.mReleased = true; + synchronized (mManagedCursors) { + final int N = mManagedCursors.size(); + for (int i=0; i<N; i++) { + ManagedCursor mc = mManagedCursors.get(i); + if (!mc.mReleased) { + mc.mCursor.deactivate(); + mc.mReleased = true; + } } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 13cc3ba..0756c71 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -77,9 +77,12 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.net.URL; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -460,6 +463,7 @@ public final class ActivityThread { mClassLoader = ApplicationLoaders.getDefault().getClassLoader( zip, mDataDir, mBaseClassLoader); + initializeJavaContextClassLoader(); } else { if (mBaseClassLoader == null) { mClassLoader = ClassLoader.getSystemClassLoader(); @@ -471,6 +475,120 @@ public final class ActivityThread { } } + /** + * Setup value for Thread.getContextClassLoader(). If the + * package will not run in in a VM with other packages, we set + * the Java context ClassLoader to the + * PackageInfo.getClassLoader value. However, if this VM can + * contain multiple packages, we intead set the Java context + * ClassLoader to a proxy that will warn about the use of Java + * context ClassLoaders and then fall through to use the + * system ClassLoader. + * + * <p> Note that this is similar to but not the same as the + * android.content.Context.getClassLoader(). While both + * context class loaders are typically set to the + * PathClassLoader used to load the package archive in the + * single application per VM case, a single Android process + * may contain several Contexts executing on one thread with + * their own logical ClassLoaders while the Java context + * ClassLoader is a thread local. This is why in the case when + * we have multiple packages per VM we do not set the Java + * context ClassLoader to an arbitrary but instead warn the + * user to set their own if we detect that they are using a + * Java library that expects it to be set. + */ + private void initializeJavaContextClassLoader() { + IPackageManager pm = getPackageManager(); + android.content.pm.PackageInfo pi; + try { + pi = pm.getPackageInfo(mPackageName, 0); + } catch (RemoteException e) { + throw new AssertionError(e); + } + /* + * Two possible indications that this package could be + * sharing its virtual machine with other packages: + * + * 1.) the sharedUserId attribute is set in the manifest, + * indicating a request to share a VM with other + * packages with the same sharedUserId. + * + * 2.) the application element of the manifest has an + * attribute specifying a non-default process name, + * indicating the desire to run in another packages VM. + */ + boolean sharedUserIdSet = (pi.sharedUserId != null); + boolean processNameNotDefault = + (pi.applicationInfo != null && + !mPackageName.equals(pi.applicationInfo.processName)); + boolean sharable = (sharedUserIdSet || processNameNotDefault); + ClassLoader contextClassLoader = + (sharable) + ? new WarningContextClassLoader() + : mClassLoader; + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + + private static class WarningContextClassLoader extends ClassLoader { + + private static boolean warned = false; + + private void warn(String methodName) { + if (warned) { + return; + } + warned = true; + Thread.currentThread().setContextClassLoader(getParent()); + Log.w(TAG, "ClassLoader." + methodName + ": " + + "The class loader returned by " + + "Thread.getContextClassLoader() may fail for processes " + + "that host multiple applications. You should explicitly " + + "specify a context class loader. For example: " + + "Thread.setContextClassLoader(getClass().getClassLoader());"); + } + + @Override public URL getResource(String resName) { + warn("getResource"); + return getParent().getResource(resName); + } + + @Override public Enumeration<URL> getResources(String resName) throws IOException { + warn("getResources"); + return getParent().getResources(resName); + } + + @Override public InputStream getResourceAsStream(String resName) { + warn("getResourceAsStream"); + return getParent().getResourceAsStream(resName); + } + + @Override public Class<?> loadClass(String className) throws ClassNotFoundException { + warn("loadClass"); + return getParent().loadClass(className); + } + + @Override public void setClassAssertionStatus(String cname, boolean enable) { + warn("setClassAssertionStatus"); + getParent().setClassAssertionStatus(cname, enable); + } + + @Override public void setPackageAssertionStatus(String pname, boolean enable) { + warn("setPackageAssertionStatus"); + getParent().setPackageAssertionStatus(pname, enable); + } + + @Override public void setDefaultAssertionStatus(boolean enable) { + warn("setDefaultAssertionStatus"); + getParent().setDefaultAssertionStatus(enable); + } + + @Override public void clearAssertionStatus() { + warn("clearAssertionStatus"); + getParent().clearAssertionStatus(); + } + } + public String getAppDir() { return mAppDir; } @@ -2495,7 +2613,6 @@ public final class ActivityThread { " did not call through to super.onPostCreate()"); } } - r.state = null; } r.paused = true; @@ -2526,6 +2643,7 @@ public final class ActivityThread { if (a != null) { r.createdConfig = new Configuration(mConfiguration); + Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward); if (!r.activity.mFinished && r.startsNotResumed) { @@ -2541,6 +2659,9 @@ public final class ActivityThread { try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); + // We need to keep around the original state, in case + // we need to be created again. + r.state = oldState; if (!r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + diff --git a/core/java/android/app/AliasActivity.java b/core/java/android/app/AliasActivity.java index 7527a5b..3756529 100644 --- a/core/java/android/app/AliasActivity.java +++ b/core/java/android/app/AliasActivity.java @@ -26,7 +26,8 @@ import android.content.res.XmlResourceParser; import android.os.Bundle; import android.util.AttributeSet; import android.util.Xml; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import java.io.IOException; diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 832f599..fe81056 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -16,8 +16,16 @@ package android.app; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemProperties; +import android.provider.Settings; import android.util.Printer; import java.io.PrintWriter; import java.io.StringWriter; @@ -38,6 +46,13 @@ import java.io.StringWriter; */ public class ApplicationErrorReport implements Parcelable { + // System property defining error report receiver for system apps + static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; + + // System property defining default error report receiver + static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; + + /** * Uninitialized error report. */ @@ -54,8 +69,13 @@ public class ApplicationErrorReport implements Parcelable { public static final int TYPE_ANR = 2; /** + * An error report about an application that's consuming too much battery. + */ + public static final int TYPE_BATTERY = 3; + + /** * Type of this report. Can be one of {@link #TYPE_NONE}, - * {@link #TYPE_CRASH} or {@link #TYPE_ANR}. + * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}. */ public int type; @@ -99,6 +119,11 @@ public class ApplicationErrorReport implements Parcelable { public AnrInfo anrInfo; /** + * Text containing battery usage data. + */ + public String batteryText; + + /** * Create an uninitialized instance of {@link ApplicationErrorReport}. */ public ApplicationErrorReport() { @@ -112,6 +137,68 @@ public class ApplicationErrorReport implements Parcelable { readFromParcel(in); } + public static ComponentName getErrorReportReceiver(Context context, + String packageName, int appFlags) { + // check if error reporting is enabled in secure settings + int enabled = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.SEND_ACTION_APP_ERROR, 0); + if (enabled == 0) { + return null; + } + + PackageManager pm = context.getPackageManager(); + + // look for receiver in the installer package + String candidate = pm.getInstallerPackageName(packageName); + ComponentName result = getErrorReportReceiver(pm, packageName, candidate); + if (result != null) { + return result; + } + + // if the error app is on the system image, look for system apps + // error receiver + if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) { + candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); + result = getErrorReportReceiver(pm, packageName, candidate); + if (result != null) { + return result; + } + } + + // if there is a default receiver, try that + candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); + return getErrorReportReceiver(pm, packageName, candidate); + } + + /** + * Return activity in receiverPackage that handles ACTION_APP_ERROR. + * + * @param pm PackageManager isntance + * @param errorPackage package which caused the error + * @param receiverPackage candidate package to receive the error + * @return activity component within receiverPackage which handles + * ACTION_APP_ERROR, or null if not found + */ + static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage, + String receiverPackage) { + if (receiverPackage == null || receiverPackage.length() == 0) { + return null; + } + + // break the loop if it's the error report receiver package that crashed + if (receiverPackage.equals(errorPackage)) { + return null; + } + + Intent intent = new Intent(Intent.ACTION_APP_ERROR); + intent.setPackage(receiverPackage); + ResolveInfo info = pm.resolveActivity(intent, 0); + if (info == null || info.activityInfo == null) { + return null; + } + return new ComponentName(receiverPackage, info.activityInfo.name); + } + public void writeToParcel(Parcel dest, int flags) { dest.writeInt(type); dest.writeString(packageName); @@ -127,6 +214,9 @@ public class ApplicationErrorReport implements Parcelable { case TYPE_ANR: anrInfo.writeToParcel(dest, flags); break; + case TYPE_BATTERY: + dest.writeString(batteryText); + break; } } @@ -142,10 +232,17 @@ public class ApplicationErrorReport implements Parcelable { case TYPE_CRASH: crashInfo = new CrashInfo(in); anrInfo = null; + batteryText = null; break; case TYPE_ANR: anrInfo = new AnrInfo(in); crashInfo = null; + batteryText = null; + break; + case TYPE_BATTERY: + batteryText = in.readString(); + anrInfo = null; + crashInfo = null; break; } } @@ -347,6 +444,9 @@ public class ApplicationErrorReport implements Parcelable { case TYPE_ANR: anrInfo.dump(pw, prefix); break; + case TYPE_BATTERY: + pw.println(batteryText); + break; } } } diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java index a2bfc76..4695c21 100644 --- a/core/java/android/app/BackupAgent.java +++ b/core/java/android/app/BackupAgent.java @@ -31,10 +31,18 @@ import android.util.Log; import java.io.IOException; /** - * This is the central interface between an application and Android's - * settings backup mechanism. - * - * <p>STOPSHIP write more documentation about the backup process here. + * This is the central interface between an application and Android's settings + * backup mechanism. Any implementation of a backup agent should perform backup + * and restore actions in + * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} + * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)} + * respectively. + * <p> + * A backup agent based on convenient helper classes is available in + * {@link android.backup.BackupHelperAgent} for less complex implementation + * requirements. + * <p> + * STOPSHIP write more documentation about the backup process here. */ public abstract class BackupAgent extends ContextWrapper { private static final String TAG = "BackupAgent"; @@ -51,51 +59,58 @@ public abstract class BackupAgent extends ContextWrapper { } /** - * The application is being asked to write any data changed since the - * last time it performed a backup operation. The state data recorded - * during the last backup pass is provided in the oldState file descriptor. - * If oldState is null, no old state is available and the application should perform - * a full backup. In both cases, a representation of the final backup state after - * this pass should be written to the file pointed to by the newStateFd file descriptor. - * - * @param oldState An open, read-only ParcelFileDescriptor pointing to the last backup - * state provided by the application. May be null, in which - * case no prior state is being provided and the application should - * perform a full backup. - * @param data A structured wrapper around an open, read/write ParcelFileDescriptor - * pointing to the backup data destination. Typically the application will use - * backup helper classes to write to this file. - * @param newState An open, read/write ParcelFileDescriptor pointing to an empty - * file. The application should record the final backup state - * here after writing the requested data to dataFd. + * The application is being asked to write any data changed since the last + * time it performed a backup operation. The state data recorded during the + * last backup pass is provided in the <code>oldState</code> file + * descriptor. If <code>oldState</code> is <code>null</code>, no old state + * is available and the application should perform a full backup. In both + * cases, a representation of the final backup state after this pass should + * be written to the file pointed to by the file descriptor wrapped in + * <code>newState</code>. + * + * @param oldState An open, read-only ParcelFileDescriptor pointing to the + * last backup state provided by the application. May be + * <code>null</code>, in which case no prior state is being + * provided and the application should perform a full backup. + * @param data A structured wrapper around an open, read/write + * ParcelFileDescriptor pointing to the backup data destination. + * Typically the application will use backup helper classes to + * write to this file. + * @param newState An open, read/write ParcelFileDescriptor pointing to an + * empty file. The application should record the final backup + * state here after writing the requested data to dataFd. */ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException; - + /** - * The application is being restored from backup, and should replace any - * existing data with the contents of the backup. The backup data is - * provided in the file pointed to by the dataFd file descriptor. Once - * the restore is finished, the application should write a representation - * of the final state to the newStateFd file descriptor, - * - * <p>The application is responsible for properly erasing its old data and - * replacing it with the data supplied to this method. No "clear user data" - * operation will be performed automatically by the operating system. The - * exception to this is in the case of a failed restore attempt: if onRestore() - * throws an exception, the OS will assume that the application's data may now - * be in an incoherent state, and will clear it before proceeding. - * - * @param data A structured wrapper around an open, read-only ParcelFileDescriptor - * pointing to a full snapshot of the application's data. Typically the - * application will use helper classes to read this data. - * @param appVersionCode The android:versionCode value of the application that backed - * up this particular data set. This makes it easier for an application's - * agent to distinguish among several possible older data versions when - * asked to perform the restore operation. - * @param newState An open, read/write ParcelFileDescriptor pointing to an empty - * file. The application should record the final backup state - * here after restoring its data from dataFd. + * The application is being restored from backup and should replace any + * existing data with the contents of the backup. The backup data is + * provided in the file descriptor pointed to by the + * {@link android.backup.BackupDataInput} instance <code>data</code>. Once + * the restore is finished, the application should write a representation of + * the final state to the <code>newState</code> file descriptor. + * <p> + * The application is responsible for properly erasing its old data and + * replacing it with the data supplied to this method. No "clear user data" + * operation will be performed automatically by the operating system. The + * exception to this is in the case of a failed restore attempt: if + * onRestore() throws an exception, the OS will assume that the + * application's data may now be in an incoherent state, and will clear it + * before proceeding. + * + * @param data A structured wrapper around an open, read-only + * ParcelFileDescriptor pointing to a full snapshot of the + * application's data. Typically the application will use helper + * classes to read this data. + * @param appVersionCode The android:versionCode value of the application + * that backed up this particular data set. This makes it easier + * for an application's agent to distinguish among several + * possible older data versions when asked to perform the restore + * operation. + * @param newState An open, read/write ParcelFileDescriptor pointing to an + * empty file. The application should record the final backup + * state here after restoring its data from dataFd. */ public abstract void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 1d004ee..0e21936 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -17,7 +17,7 @@ package android.app; import com.android.internal.policy.PolicyManager; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import com.google.android.collect.Maps; import org.xmlpull.v1.XmlPullParserException; @@ -194,6 +194,7 @@ class ContextImpl extends Context { private AccountManager mAccountManager; // protected by mSync private DropBoxManager mDropBoxManager = null; private DevicePolicyManager mDevicePolicyManager = null; + private UiModeManager mUiModeManager = null; private final Object mSync = new Object(); @@ -960,6 +961,8 @@ class ContextImpl extends Context { return getDropBoxManager(); } else if (DEVICE_POLICY_SERVICE.equals(name)) { return getDevicePolicyManager(); + } else if (UI_MODE_SERVICE.equals(name)) { + return getUiModeManager(); } return null; @@ -1146,13 +1149,22 @@ class ContextImpl extends Context { private DevicePolicyManager getDevicePolicyManager() { synchronized (mSync) { if (mDevicePolicyManager == null) { - mDevicePolicyManager = new DevicePolicyManager(this, + mDevicePolicyManager = DevicePolicyManager.create(this, mMainThread.getHandler()); } } return mDevicePolicyManager; } + private UiModeManager getUiModeManager() { + synchronized (mSync) { + if (mUiModeManager == null) { + mUiModeManager = new UiModeManager(); + } + } + return mUiModeManager; + } + @Override public int checkPermission(String permission, int pid, int uid) { if (permission == null) { diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java index bedf4b4..61e1bb3 100644 --- a/core/java/android/app/DeviceAdminInfo.java +++ b/core/java/android/app/DeviceAdminInfo.java @@ -18,6 +18,7 @@ package android.app; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; import android.content.ComponentName; import android.content.Context; @@ -331,6 +332,19 @@ public final class DeviceAdminInfo implements Parcelable { return res; } + /** @hide */ + public void writePoliciesToXml(XmlSerializer out) + throws IllegalArgumentException, IllegalStateException, IOException { + out.attribute(null, "flags", Integer.toString(mUsesPolicies)); + } + + /** @hide */ + public void readPoliciesFromXml(XmlPullParser parser) + throws XmlPullParserException, IOException { + mUsesPolicies = Integer.parseInt( + parser.getAttributeValue(null, "flags")); + } + public void dump(Printer pw, String prefix) { pw.println(prefix + "Receiver:"); mReceiver.dump(pw, prefix + " "); diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java index d611807..0e8c1ab 100644 --- a/core/java/android/app/DevicePolicyManager.java +++ b/core/java/android/app/DevicePolicyManager.java @@ -49,13 +49,18 @@ public class DevicePolicyManager { private final Handler mHandler; - /*package*/ DevicePolicyManager(Context context, Handler handler) { + private DevicePolicyManager(Context context, Handler handler) { mContext = context; mHandler = handler; mService = IDevicePolicyManager.Stub.asInterface( ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); } + /*package*/ static DevicePolicyManager create(Context context, Handler handler) { + DevicePolicyManager me = new DevicePolicyManager(context, handler); + return me.mService != null ? me : null; + } + /** * Activity action: ask the user to add a new device administrator to the system. * The desired policy is the ComponentName of the policy in the @@ -133,6 +138,20 @@ public class DevicePolicyManager { } /** + * @hide + */ + public boolean packageHasActiveAdmins(String packageName) { + if (mService != null) { + try { + return mService.packageHasActiveAdmins(packageName); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } + + /** * Remove a current administration component. This can only be called * by the application that owns the administration component; if you * try to remove someone else's component, a security exception will be diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 4598bb5..0ed5eb8 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -822,13 +822,15 @@ public class Dialog implements DialogInterface, Window.Callback, final SearchManager searchManager = (SearchManager) mContext .getSystemService(Context.SEARCH_SERVICE); - // associate search with owner activity if possible (otherwise it will default to - // global search). + // associate search with owner activity final ComponentName appName = getAssociatedActivity(); - final boolean globalSearch = (appName == null); - searchManager.startSearch(null, false, appName, null, globalSearch); - dismiss(); - return true; + if (appName != null) { + searchManager.startSearch(null, false, appName, null, false); + dismiss(); + return true; + } else { + return false; + } } /** diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl index ae5c4bf..b138720 100644 --- a/core/java/android/app/IDevicePolicyManager.aidl +++ b/core/java/android/app/IDevicePolicyManager.aidl @@ -49,6 +49,7 @@ interface IDevicePolicyManager { void setActiveAdmin(in ComponentName policyReceiver); boolean isAdminActive(in ComponentName policyReceiver); List<ComponentName> getActiveAdmins(); + boolean packageHasActiveAdmins(String packageName); void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result); void removeActiveAdmin(in ComponentName policyReceiver); diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 9ba7863..cb03d2c 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -24,9 +24,8 @@ import android.os.Bundle; /** @hide */ interface ISearchManager { - SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); + SearchableInfo getSearchableInfo(in ComponentName launchActivity); List<SearchableInfo> getSearchablesInGlobalSearch(); - List<SearchableInfo> getSearchablesForWebSearch(); - SearchableInfo getDefaultSearchableForWebSearch(); - void setDefaultWebSearch(in ComponentName component); + ComponentName getGlobalSearchActivity(); + ComponentName getWebSearchActivity(); } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 581b436..af68689 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -16,8 +16,6 @@ package android.app; -import com.android.common.Patterns; -import com.android.common.speech.Recognition; import static android.app.SuggestionsAdapter.getColumnString; @@ -48,6 +46,7 @@ import android.text.TextWatcher; import android.util.AndroidRuntimeException; import android.util.AttributeSet; import android.util.Log; +import android.util.Patterns; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; @@ -274,7 +273,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); // Try to get the searchable info for the provided component. - mSearchable = searchManager.getSearchableInfo(componentName, false); + mSearchable = searchManager.getSearchableInfo(componentName); if (mSearchable == null) { return false; @@ -313,7 +312,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mLaunchComponent = null; mAppSearchData = null; mSearchable = null; - mActivityContext = null; mUserQuery = null; } @@ -411,7 +409,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS updateSearchAppIcon(); updateSearchBadge(); updateQueryHint(); - updateVoiceButton(); + updateVoiceButton(TextUtils.isEmpty(mUserQuery)); // In order to properly configure the input method (if one is being used), we // need to let it know if we'll be providing suggestions. Although it would be @@ -560,10 +558,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS /** * Update the visibility of the voice button. There are actually two voice search modes, * either of which will activate the button. + * @param empty whether the search query text field is empty. If it is, then the other + * criteria apply to make the voice button visible. Otherwise the voice button will not + * be visible - i.e., if the user has typed a query, remove the voice button. */ - private void updateVoiceButton() { + private void updateVoiceButton(boolean empty) { int visibility = View.GONE; - if (mSearchable.getVoiceSearchEnabled()) { + if (mSearchable.getVoiceSearchEnabled() && empty) { Intent testIntent = null; if (mSearchable.getVoiceSearchLaunchWebSearch()) { testIntent = mVoiceWebSearchIntent; @@ -666,6 +667,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // The user changed the query, remember it. mUserQuery = s == null ? "" : s.toString(); } + updateVoiceButton(TextUtils.isEmpty(s)); } public void afterTextChanged(Editable s) { @@ -746,9 +748,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return; } SearchableInfo searchable = mSearchable; - // First stop the existing search before starting voice search, or else we'll end - // up showing the search dialog again once we return to the app. - cancel(); try { if (searchable.getVoiceSearchLaunchWebSearch()) { getContext().startActivity(mVoiceWebSearchIntent); @@ -762,6 +761,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // voice search before showing the button. But just in case... Log.w(LOG_TAG, "Could not find voice search activity"); } + dismiss(); } }; @@ -819,7 +819,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); - voiceIntent.putExtra(Recognition.EXTRA_CALLING_PACKAGE, + voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null : searchActivity.toShortString()); // Add the values that configure forwarding the results diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index ce5f1bf..b54e53d 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -137,21 +137,11 @@ import java.util.List; * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre> * - * <p><b>How to enable global search with Quick Search Box.</b> In addition to searching within + * <p><b>How to start global search.</b> In addition to searching within * your activity or application, you can also use the Search Manager to invoke a platform-global - * search, which uses Quick Search Box to search across the device and the web. There are two ways - * to do this: - * <ul><li>You can simply define "search" within your application or activity to mean global search. - * This is described in more detail in the - * <a href="#SearchabilityMetadata">Searchability Metadata</a> section. Briefly, you will - * add a single meta-data entry to your manifest, declaring that the default search - * for your application is "*". This indicates to the system that no application-specific - * search activity is provided, and that it should launch web-based search instead.</li> - * <li>Simply do nothing and the default implementation of - * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered. - * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}. - * This is most useful if you wish to provide local searchability <i>and</i> access to global - * search.)</li></ul> + * search, which uses Quick Search Box to search across the device and the web. + * Override {@link android.app.Activity#onSearchRequested} and call + * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}. * * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users * will expect it to be available in all contexts. If your UI design absolutely precludes @@ -871,12 +861,8 @@ import java.util.List; * * <p>The simplest way to specify this is to add a <i>search reference</i> element to the * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file. - * The value of this reference can be either of: - * <ul><li>The name of your searchable activity. - * It is typically prefixed by '.' to indicate that it's in the same package.</li> - * <li>A "*" indicates that the system may select a default searchable activity, in which - * case it will typically select web-based search.</li> - * </ul> + * The value of this reference should be the name of your searchable activity. + * It is typically prefixed by '.' to indicate that it's in the same package. * * <p>Here is a snippet showing the necessary addition to the manifest entry for your * non-searchable activities. @@ -1639,6 +1625,7 @@ public class SearchManager return; } Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(globalSearchActivity); // TODO: Always pass name of calling package as an extra? if (appSearchData != null) { @@ -1661,32 +1648,15 @@ public class SearchManager /** * Gets the name of the global search activity. * - * This is currently implemented by returning the first activity that handles - * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow - * more than one global search acitivity to be installed, this code must be changed. - * - * TODO: Doing this every time we start global search is inefficient. Will fix that once - * we have settled on the right mechanism for finding the global search activity. - * * @hide */ public ComponentName getGlobalSearchActivity() { - Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH); - PackageManager pm = mContext.getPackageManager(); - List<ResolveInfo> activities = - pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - int count = activities.size(); - for (int i = 0; i < count; i++) { - ActivityInfo ai = activities.get(i).activityInfo; - if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, - ai.packageName) == PackageManager.PERMISSION_GRANTED) { - return new ComponentName(ai.packageName, ai.name); - } else { - Log.w(TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " - + "but does not have the GLOBAL_SEARCH permission."); - } + try { + return mService.getGlobalSearchActivity(); + } catch (RemoteException ex) { + Log.e(TAG, "getGlobalSearchActivity() failed: " + ex); + return null; } - return null; } /** @@ -1699,13 +1669,12 @@ public class SearchManager * @hide */ public ComponentName getWebSearchActivity() { - ComponentName globalSearch = getGlobalSearchActivity(); - if (globalSearch == null) { + try { + return mService.getWebSearchActivity(); + } catch (RemoteException ex) { + Log.e(TAG, "getWebSearchActivity() failed: " + ex); return null; } - Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); - intent.setPackage(globalSearch.getPackageName()); - return intent.resolveActivity(mContext.getPackageManager()); } /** @@ -1839,27 +1808,7 @@ public class SearchManager */ public SearchableInfo getSearchableInfo(ComponentName componentName) { try { - return mService.getSearchableInfo(componentName, false); - } catch (RemoteException ex) { - Log.e(TAG, "getSearchableInfo() failed: " + ex); - return null; - } - } - - /** - * Gets information about a searchable activity. - * - * @param componentName The activity to get searchable information for. - * @param globalSearch If <code>false</code>, return information about the given activity. - * If <code>true</code>, return information about the global search activity. - * @return Searchable information, or <code>null</code> if the activity is not searchable. - * - * @hide because SearchableInfo is not part of the API. - */ - public SearchableInfo getSearchableInfo(ComponentName componentName, - boolean globalSearch) { - try { - return mService.getSearchableInfo(componentName, globalSearch); + return mService.getSearchableInfo(componentName); } catch (RemoteException ex) { Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java new file mode 100644 index 0000000..aca8ab4 --- /dev/null +++ b/core/java/android/app/UiModeManager.java @@ -0,0 +1,83 @@ +package android.app; + +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +/** + * This class provides access to the system uimode services. These services + * allow applications to control UI modes of the device. + * It provides functionality to disable the car mode and it gives access to the + * night mode settings. + * + * <p>You do not instantiate this class directly; instead, retrieve it through + * {@link android.content.Context#getSystemService + * Context.getSystemService(Context.UI_MODE_SERVICE)}. + */ +public class UiModeManager { + private static final String TAG = "UiModeManager"; + + public static final int MODE_NOTNIGHT = 1; + public static final int MODE_NIGHT = 2; + public static final int MODE_AUTO = 3; + + private IUiModeManager mService; + + /*package*/ UiModeManager() { + mService = IUiModeManager.Stub.asInterface( + ServiceManager.getService("uimode")); + } + + /** + * Disables the car mode. + */ + public void disableCarMode() { + if (mService != null) { + try { + mService.disableCarMode(); + } catch (RemoteException e) { + Log.e(TAG, "disableCarMode: RemoteException", e); + } + } + } + + /** + * Sets the night mode. Changes to the night mode are only effective when + * the car mode is enabled on a device. + * + * <p>The mode can be one of: + * <ul> + * <li><em>{@link #MODE_NOTNIGHT}<em> - sets the device into notnight + * mode.</li> + * <li><em>{@link #MODE_NIGHT}</em> - sets the device into night mode. + * </li> + * <li><em>{@link #MODE_AUTO}</em> - automatic night/notnight switching + * depending on the location and certain other sensors.</li> + */ + public void setNightMode(int mode) { + if (mService != null) { + try { + mService.setNightMode(mode); + } catch (RemoteException e) { + Log.e(TAG, "setNightMode: RemoteException", e); + } + } + } + + /** + * Returns the currently configured night mode. + * + * @return {@link #MODE_NOTNIGHT}, {@link #MODE_NIGHT} or {@link #MODE_AUTO} + * When an error occurred -1 is returned. + */ + public int getNightMode() { + if (mService != null) { + try { + return mService.getNightMode(); + } catch (RemoteException e) { + Log.e(TAG, "getNightMode: RemoteException", e); + } + } + return -1; + } +} diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java index 6bf848f..5a8034b 100644 --- a/core/java/android/backup/AbsoluteFileBackupHelper.java +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -21,7 +21,6 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** * Like FileBackupHelper, but takes absolute paths for the files instead of diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java index 295dc66..a08ee75 100644 --- a/core/java/android/backup/BackupDataInput.java +++ b/core/java/android/backup/BackupDataInput.java @@ -16,8 +16,6 @@ package android.backup; -import android.content.Context; - import java.io.FileDescriptor; import java.io.IOException; diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 672d01f..34879d8 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -16,8 +16,6 @@ package android.backup; -import android.content.Context; - import java.io.FileDescriptor; import java.io.IOException; diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java index fc48cf2..7eedd01 100644 --- a/core/java/android/backup/BackupHelper.java +++ b/core/java/android/backup/BackupHelper.java @@ -18,16 +18,19 @@ package android.backup; import android.os.ParcelFileDescriptor; -import java.io.InputStream; - /** - * STOPSHIP: document! + * A convenient interface to be used with the + * {@link android.backup.BackupHelperAgent} to implement backup and restore of + * arbitrary data types. + * <p> + * STOPSHOP: document! */ public interface BackupHelper { /** - * Based on oldState, determine which of the files from the application's data directory - * need to be backed up, write them to the data stream, and fill in newState with the - * state as it exists now. + * Based on <code>oldState</code>, determine which of the files from the + * application's data directory need to be backed up, write them to + * <code>data</code>, and fill in <code>newState</code> with the state as it + * exists now. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState); diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java index dc17154f..7fb44f4 100644 --- a/core/java/android/backup/BackupHelperAgent.java +++ b/core/java/android/backup/BackupHelperAgent.java @@ -17,24 +17,19 @@ package android.backup; import android.app.BackupAgent; -import android.backup.BackupHelper; -import android.backup.BackupHelperDispatcher; -import android.backup.BackupDataInput; -import android.backup.BackupDataOutput; import android.os.ParcelFileDescriptor; -import android.util.Log; import java.io.IOException; /** - * A convenient BackupAgent wrapper class that automatically manages heterogeneous - * data sets within the backup data, each identified by a unique key prefix. An - * application will typically extend this class in their own backup agent. Then, - * within the agent's onBackup() and onRestore() methods, it will call - * {@link #addHelper(String, BackupHelper)} one or more times to specify the data - * sets, then invoke super.onBackup() or super.onRestore() to have the BackupHelperAgent - * implementation process the data. - * + * A convenient BackupAgent wrapper class that automatically manages + * heterogeneous data sets within the backup data, each identified by a unique + * key prefix. An application will typically extend this class in their own + * backup agent. Then, within the agent's onBackup() and onRestore() methods, it + * will call {@link #addHelper(String, BackupHelper)} one or more times to + * specify the data sets, then invoke super.onBackup() or super.onRestore() to + * have the BackupHelperAgent implementation process the data. + * <p> * STOPSHIP: document! */ public class BackupHelperAgent extends BackupAgent { diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java index bf2c44d..68076db 100644 --- a/core/java/android/backup/BackupHelperDispatcher.java +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -19,12 +19,10 @@ package android.backup; import android.os.ParcelFileDescriptor; import android.util.Log; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; import java.io.FileDescriptor; -import java.util.TreeMap; +import java.io.IOException; import java.util.Map; +import java.util.TreeMap; /** @hide */ public class BackupHelperDispatcher { diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 4bf59eb..2dff975 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -23,33 +23,40 @@ import android.os.ServiceManager; import android.util.Log; /** - * BackupManager is the interface to the system's backup service. - * Applications simply instantiate one, and then use that instance - * to communicate with the backup infrastructure. - * - * <p>When your application has made changes to data it wishes to have - * backed up, call {@link #dataChanged()} to notify the backup service. - * The system will then schedule a backup operation to occur in the near - * future. Repeated calls to {@link #dataChanged()} have no further effect - * until the backup operation actually occurs. - * - * <p>The backup operation itself begins with the system launching the - * {@link android.app.BackupAgent} subclass declared in your manifest. See the + * BackupManager is the interface to the system's backup service. Applications + * simply instantiate one, and then use that instance to communicate with the + * backup infrastructure. + * <p> + * When an application has made changes to data which should be backed up, a + * call to {@link #dataChanged()} will notify the backup service. The system + * will then schedule a backup operation to occur in the near future. Repeated + * calls to {@link #dataChanged()} have no further effect until the backup + * operation actually occurs. + * <p> + * The backup operation itself begins with the system launching the + * {@link android.app.BackupAgent} subclass declared in your manifest. See the * documentation for {@link android.app.BackupAgent} for a detailed description * of how the backup then proceeds. - * - * <p>STOPSHIP more documentation here! Include the attributes: - * android:backupAgent - * android:allowBackup - * android:restoreNeedsApplication - * android:killAfterRestore + * <p> + * A simple implementation of a BackupAgent useful for backing up Preferences + * and files is available by using {@link android.backup.BackupHelperAgent}. + * <p> + * STOPSHIP: more documentation! + * <p> + * <b>XML attributes</b> + * <p> + * See {@link android.R.styleable#AndroidManifestApplication + * AndroidManifest.xml's application attributes} + * + * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup + * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent + * @attr ref + * android.R.styleable#AndroidManifestApplication_restoreNeedsApplication + * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore */ public class BackupManager { private static final String TAG = "BackupManager"; - /** @hide TODO: REMOVE THIS */ - public static final boolean EVEN_THINK_ABOUT_DOING_RESTORE = true; - private Context mContext; private static IBackupManager sService; @@ -78,9 +85,6 @@ public class BackupManager { * {@link android.app.BackupAgent} subclass will be scheduled when you call this method. */ public void dataChanged() { - if (!EVEN_THINK_ABOUT_DOING_RESTORE) { - return; - } checkServiceBinder(); if (sService != null) { try { @@ -100,9 +104,6 @@ public class BackupManager { * permission if the package named in the argument is not the caller's own. */ public static void dataChanged(String packageName) { - if (!EVEN_THINK_ABOUT_DOING_RESTORE) { - return; - } checkServiceBinder(); if (sService != null) { try { @@ -118,9 +119,6 @@ public class BackupManager { * {@link android.backup.RestoreSession} class for documentation on that process. */ public RestoreSession beginRestoreSession() { - if (!EVEN_THINK_ABOUT_DOING_RESTORE) { - return null; - } RestoreSession session = null; checkServiceBinder(); if (sService != null) { diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index 68b4d42..cc859e2 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -21,10 +21,23 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** - * STOPSHIP: document! [manages backup of a set of files; restore is totally opaque] + * A helper class which can be used in conjunction with + * {@link android.backup.BackupHelperAgent} to manage the backup of a set of + * files. Whenever backup is performed, all files changed since the last backup + * will be saved in their entirety. During the first time the backup happens, + * all the files in the list will be backed up. Note that this should only be + * used with small configuration files and not with large binary files. + * <p> + * Any files not present in the list of files during the restore procedure will + * be ignored. If files present in a previous version of an application are + * removed in subsequent versions, it is the responsibility of the developer to + * design a mechanism to remove those files. Otherwise files no longer needed + * will linger and consume space on the device. + * <p> + * STOPSHIP: document! [manages backup of a set of files; restore is totally + * opaque] */ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "FileBackupHelper"; @@ -50,9 +63,16 @@ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelp } /** - * Based on oldState, determine which of the files from the application's data directory - * need to be backed up, write them to the data stream, and fill in newState with the - * state as it exists now. + * Based on <code>oldState</code>, determine which of the files from the + * application's data directory need to be backed up, write them to the data + * stream, and fill in <code>newState</code> with the state as it exists + * now. When <code>oldState</code> is <code>null</code>, all the files will + * be backed up. + * <p> + * This should be called from {@link android.backup.BackupHelperAgent} + * directly. See + * {@link android.app.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} + * for a description of parameter meanings. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/backup/FileBackupHelperBase.java index a0ff38b..7cb1ccc 100644 --- a/core/java/android/backup/FileBackupHelperBase.java +++ b/core/java/android/backup/FileBackupHelperBase.java @@ -22,10 +22,12 @@ import android.util.Log; import java.io.File; import java.io.FileDescriptor; -import java.io.FileOutputStream; +/** + * Base class for the {@link android.backup.FileBackupHelper} implementation. + */ class FileBackupHelperBase { - private static final String TAG = "RestoreHelperBase"; + private static final String TAG = "FileBackupHelperBase"; int mPtr; Context mContext; @@ -45,8 +47,8 @@ class FileBackupHelperBase { } /** - * Check the parameters so the native code doens't have to throw all the exceptions - * since it's easier to do that from java. + * Check the parameters so the native code doesn't have to throw all the exceptions + * since it's easier to do that from Java. */ static void performBackup_checked(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState, String[] files, String[] keys) { diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index f9c97a3..7ba80db 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -17,13 +17,19 @@ package android.backup; import android.content.Context; +import android.content.SharedPreferences; import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** + * A helper class which can be used in conjunction with + * {@link android.backup.BackupHelperAgent} to manage the backup of + * {@link android.content.SharedPreferences}. Whenever backup is performed it + * will back up all named shared preferences which have changed since the last + * backup. + * <p> * STOPSHIP: document! */ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper { diff --git a/core/java/android/backup/package.html b/core/java/android/backup/package.html new file mode 100644 index 0000000..13c0eb8 --- /dev/null +++ b/core/java/android/backup/package.html @@ -0,0 +1,22 @@ +<HTML> +<BODY> +<p>Package containing the backup and restore functionality available to +applications. All backup management is controlled through +{@link android.backup.BackupManager}. Individual backup functionality is +implemented through a subclass {@link android.app.BackupAgent} and a +simple and easy-to-use implementation is provided in +{@link android.backup.BackupHelperAgent}.</p> + +<p>STOPSHIP: add more documenation and remove Dev Guide link if not written!</p> + +<p>The backup APIs let applications:</p> +<ul> + <li>Perform backup of arbitrary data</li> + <li>Easily perform backup of Preferences and files</li> + <li>Handle restore of data</li> +</ul> + +<p>For a detailed guide to using the backup APIs, see the <a +href="{@docRoot}guide/topics/backup.html">Backup Dev Guide topic</a>.</p> +</BODY> +</HTML> diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index cf9c58f..e77e76f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -664,6 +664,10 @@ public final class BluetoothDevice implements Parcelable { * determine which channel to connect to. * <p>The remote device will be authenticated and communication on this * socket will be encrypted. + * <p>Hint: If you are connecting to a Bluetooth serial board then try + * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. + * However if you are connecting to an Android peer then please generate + * your own unique UUID. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index c4ba05d..7ca0f01 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -126,7 +126,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co } /** - * The samee as {@link #flattenToString()}, but abbreviates the class + * The same as {@link #flattenToString()}, but abbreviates the class * name if it is a suffix of the package. The result can still be used * with {@link #unflattenFromString(String)}. * diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 45f361e..29f388a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -167,9 +167,9 @@ public abstract class ContentResolver { /** @hide */ public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff; - // Always log queries which take 100ms+; shorter queries are + // Always log queries which take 500ms+; shorter queries are // sampled accordingly. - private static final int SLOW_THRESHOLD_MILLIS = 100; + private static final int SLOW_THRESHOLD_MILLIS = 500; private final Random mRandom = new Random(); // guarded by itself public ContentResolver(Context context) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 672e5f7..897d702 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1204,6 +1204,8 @@ public abstract class Context { * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method") * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager} * for management of input methods. + * <dt> {@link #UI_MODE_SERVICE} ("uimode") + * <dd> An {@link android.app.UiModeManager} for controlling UI modes. * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -1249,6 +1251,8 @@ public abstract class Context { * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager + * @see #UI_MODE_SERVICE + * @see android.app.UiModeManager */ public abstract Object getSystemService(String name); @@ -1511,6 +1515,14 @@ public abstract class Context { public static final String DEVICE_POLICY_SERVICE = "device_policy"; /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.app.UiModeManager} for controlling UI modes. + * + * @see #getSystemService + */ + public static final String UI_MODE_SERVICE = "uimode"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 1b0437c..fb3f646 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -34,7 +34,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import java.io.IOException; import java.io.Serializable; @@ -1316,6 +1317,21 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; /** + * @hide + * Broadcast Action: Ask system services if there is any reason to + * restart the given package. The data contains the name of the + * package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * <li> {@link #EXTRA_PACKAGES} String array of all packages to check. + * </ul> + * + * <p class="note">This is a protected intent that can only be sent + * by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; + /** * Broadcast Action: The user has restarted a package, and all of its * processes have been killed. All runtime state * associated with it (processes, alarms, notifications, etc) should @@ -2098,6 +2114,7 @@ public class Intent implements Parcelable, Cloneable { * number to call in a {@link android.content.Intent#ACTION_CALL}. */ public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"; + /** * Used as an int extra field in {@link android.content.Intent#ACTION_UID_REMOVED} * intents to supply the uid the package had been assigned. Also an optional @@ -2108,6 +2125,11 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_UID = "android.intent.extra.UID"; /** + * @hide String array of package names. + */ + public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; + + /** * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} * intents to indicate whether this represents a full uninstall (removing * both the code and its data) or a partial uninstall (leaving its data, diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 023c024..452fd8a 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -34,7 +34,8 @@ import android.util.AndroidException; import android.util.Config; import android.util.Log; import android.util.Printer; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; /** * Structured description of Intent values to be matched. An IntentFilter can diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index fcb910d..5aad3af 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -18,7 +18,7 @@ package android.content; import com.android.internal.os.AtomicFile; import com.android.internal.util.ArrayUtils; -import com.android.common.FastXmlSerializer; +import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2e405c1..8773f59 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -248,6 +248,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_NATIVE_DEBUGGABLE = 1<<21; /** + * Value for {@link #flags}: Set to true if the application's backup + * agent claims to be able to handle restore data even "from the future," + * i.e. from versions of the application with a versionCode greater than + * the one currently installed on the device. + * + * <p>If android:allowBackup is set to false or no android:backupAgent + * is specified, this flag will be ignored. + * + * {@hide} + */ + public static final int FLAG_RESTORE_ANY_VERSION = 1<<22; + + /** * Flags associated with the application. Any combination of * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE}, * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f793a00..399a87d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -309,7 +309,7 @@ interface IPackageManager { * MountService uses this to call into the package manager to update * status of sdcard. */ - void updateExternalMediaStatus(boolean mounted); + boolean updateExternalMediaStatus(boolean mounted); String nextPackageToClean(String lastPackage); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5823560..b07bafc 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -30,12 +30,14 @@ import android.content.res.XmlResourceParser; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Config; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import java.io.File; import java.io.IOException; @@ -710,9 +712,10 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); } sa.recycle(); + pkg.installLocation = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_installLocation, - PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); + PackageInfo.INSTALL_LOCATION_AUTO); // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; @@ -1369,8 +1372,8 @@ public class PackageParser { if (allowBackup) { ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; - // backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant - // if backup is possible for the given application. + // backupAgent, killAfterRestore, restoreNeedsApplication, and restoreAnyVersion + // are only relevant if backup is possible for the given application. String backupAgent = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestApplication_backupAgent); if (backupAgent != null) { @@ -1390,6 +1393,11 @@ public class PackageParser { false)) { ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion, + false)) { + ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION; + } } } diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index a885820..b74c073 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -43,7 +43,7 @@ import java.io.IOException; import java.io.FileInputStream; import com.android.internal.os.AtomicFile; -import com.android.common.FastXmlSerializer; +import com.android.internal.util.FastXmlSerializer; import com.google.android.collect.Maps; import com.google.android.collect.Lists; diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 7f9a5c6..1070f08 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -24,6 +24,7 @@ import android.util.TypedValue; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; /** * Provides access to an application's raw asset files; see {@link Resources} @@ -59,6 +60,8 @@ public final class AssetManager { private static final String TAG = "AssetManager"; private static final boolean localLOGV = Config.LOGV || false; + private static final boolean DEBUG_REFS = false; + private static final Object sSync = new Object(); private static AssetManager sSystem = null; @@ -72,6 +75,7 @@ public final class AssetManager { private int mNumRefs = 1; private boolean mOpen = true; + private HashMap<Integer, RuntimeException> mRefStacks; /** * Create a new AssetManager containing only the basic system assets. @@ -82,6 +86,10 @@ public final class AssetManager { */ public AssetManager() { synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); ensureSystemAssets(); @@ -99,6 +107,12 @@ public final class AssetManager { } private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + } init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); } @@ -122,7 +136,7 @@ public final class AssetManager { // + ", released=" + mReleased); if (mOpen) { mOpen = false; - decRefsLocked(); + decRefsLocked(this.hashCode()); } } } @@ -298,8 +312,9 @@ public final class AssetManager { } int asset = openAsset(fileName, accessMode); if (asset != 0) { - mNumRefs++; - return new AssetInputStream(asset); + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset file: " + fileName); @@ -389,8 +404,9 @@ public final class AssetManager { } int asset = openNonAssetNative(cookie, fileName, accessMode); if (asset != 0) { - mNumRefs++; - return new AssetInputStream(asset); + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset absolute file: " + fileName); @@ -468,16 +484,17 @@ public final class AssetManager { } int xmlBlock = openXmlAssetNative(cookie, fileName); if (xmlBlock != 0) { - mNumRefs++; - return new XmlBlock(this, xmlBlock); + XmlBlock res = new XmlBlock(this, xmlBlock); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset XML file: " + fileName); } - /*package*/ void xmlBlockGone() { + /*package*/ void xmlBlockGone(int id) { synchronized (this) { - decRefsLocked(); + decRefsLocked(id); } } @@ -486,20 +503,34 @@ public final class AssetManager { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - mNumRefs++; - return newTheme(); + int res = newTheme(); + incRefsLocked(res); + return res; } } /*package*/ final void releaseTheme(int theme) { synchronized (this) { deleteTheme(theme); - decRefsLocked(); + decRefsLocked(theme); } } protected void finalize() throws Throwable { - destroy(); + try { + if (DEBUG_REFS && mNumRefs != 0) { + Log.w(TAG, "AssetManager " + this + + " finalized with non-zero refs: " + mNumRefs); + if (mRefStacks != null) { + for (RuntimeException e : mRefStacks.values()) { + Log.w(TAG, "Reference from here", e); + } + } + } + destroy(); + } finally { + super.finalize(); + } } public final class AssetInputStream extends InputStream { @@ -526,7 +557,7 @@ public final class AssetManager { if (mAsset != 0) { destroyAsset(mAsset); mAsset = 0; - decRefsLocked(); + decRefsLocked(hashCode()); } } } @@ -710,7 +741,22 @@ public final class AssetManager { private native final void init(); private native final void destroy(); - private final void decRefsLocked() { + private final void incRefsLocked(int id) { + if (DEBUG_REFS) { + if (mRefStacks == null) { + mRefStacks = new HashMap<Integer, RuntimeException>(); + RuntimeException ex = new RuntimeException(); + ex.fillInStackTrace(); + mRefStacks.put(this.hashCode(), ex); + } + } + mNumRefs++; + } + + private final void decRefsLocked(int id) { + if (DEBUG_REFS && mRefStacks != null) { + mRefStacks.remove(id); + } mNumRefs--; //System.out.println("Dec streams: mNumRefs=" + mNumRefs // + " mReleased=" + mReleased); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index a5e39d4..0608cc0 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -17,7 +17,7 @@ package android.content.res; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 2411177..5e90b91 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -24,7 +24,8 @@ import android.util.SparseArray; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; /** * Conveniences for retrieving data out of a compiled string resource. diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 8f0003b..a7fb31d 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -5,7 +5,8 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import java.util.Arrays; diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index f800232..ad1bfb2 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -17,7 +17,8 @@ package android.content.res; import android.util.TypedValue; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParserException; @@ -59,7 +60,7 @@ final class XmlBlock { if (mOpenCount == 0) { nativeDestroy(mNative); if (mAssets != null) { - mAssets.xmlBlockGone(); + mAssets.xmlBlockGone(hashCode()); } } } diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java index eb85822..a7a1d9a 100644 --- a/core/java/android/database/sqlite/SQLiteCompiledSql.java +++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java @@ -95,12 +95,16 @@ import android.util.Log; } } - /* package */ synchronized boolean isInUse() { - return mInUse; - } - - /* package */ synchronized void acquire() { + /** + * returns true if acquire() succeeds. false otherwise. + */ + /* package */ synchronized boolean acquire() { + if (mInUse) { + // someone already has acquired it. + return false; + } mInUse = true; + return true; } /* package */ synchronized void release() { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 8fd8e28..c13dd23 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -208,11 +208,11 @@ public class SQLiteDatabase extends SQLiteClosable { // Things related to query logging/sampling for debugging // slow/frequent queries during development. Always log queries - // which take 100ms+; shorter queries are sampled accordingly. + // which take 500ms+; shorter queries are sampled accordingly. // Commit statements, which are typically slow, are logged // together with the most recently executed SQL statement, for // disambiguation. - private static final int QUERY_LOG_TIME_IN_MILLIS = 100; + private static final int QUERY_LOG_TIME_IN_MILLIS = 500; private static final int QUERY_LOG_SQL_LENGTH = 64; private static final String COMMIT_SQL = "COMMIT;"; private final Random mRandom = new Random(); diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 2bb2f5d..7a29cb4 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -57,20 +57,19 @@ public abstract class SQLiteProgram extends SQLiteClosable { mCompiledSql = new SQLiteCompiledSql(db, sql); // add it to the cache of compiled-sqls - db.addToCompiledQueries(sql, mCompiledSql); + // but before adding it and thus making it available for anyone else to use it, + // make sure it is acquired by me. mCompiledSql.acquire(); + db.addToCompiledQueries(sql, mCompiledSql); } else { // it is already in compiled-sql cache. - if (mCompiledSql.isInUse()) { - // but the CompiledSql in cache is in use by some other SQLiteProgram object. + // try to acquire the object. + if (!mCompiledSql.acquire()) { + // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object. // we can't have two different SQLiteProgam objects can't share the same // CompiledSql object. create a new one. // finalize it when I am done with it in "this" object. mCompiledSql = new SQLiteCompiledSql(db, sql); - } else { - // the CompiledSql in cache is NOT in use by any other SQLiteProgram object. - // it is safe to give it to this SQLIteProgram Object. - mCompiledSql.acquire(); } } nStatement = mCompiledSql.nStatement; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index badb767..c76aca1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -332,10 +332,10 @@ public class ConnectivityManager /** * Sets the value of the setting for background data usage. - * + * * @param allowBackgroundData Whether an application should use data while * it is in the background. - * + * * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING * @see #getBackgroundDataSetting() * @hide @@ -346,7 +346,35 @@ public class ConnectivityManager } catch (RemoteException e) { } } - + + /** + * Gets the value of the setting for enabling Mobile data. + * + * @return Whether mobile data is enabled. + * @hide + */ + public boolean getMobileDataEnabled() { + try { + return mService.getMobileDataEnabled(); + } catch (RemoteException e) { + return true; + } + } + + /** + * Sets the persisted value for enabling/disabling Mobile data. + * + * @param allowMobileData Whether the mobile data connection should be + * used or not. + * @hide + */ + public void setMobileDataEnabled(boolean enabled) { + try { + mService.setMobileDataEnabled(enabled); + } catch (RemoteException e) { + } + } + /** * Don't allow use of default constructor. */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 508e9c3..2514693 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -51,6 +51,10 @@ interface IConnectivityManager void setBackgroundDataSetting(boolean allowBackgroundData); + boolean getMobileDataEnabled(); + + void setMobileDataEnabled(boolean enabled); + boolean tether(String iface); boolean untether(String iface); diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 9aa23fe..915c5d7 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -45,10 +45,10 @@ public class InterfaceConfiguration implements Parcelable { } private static void putAddress(StringBuffer buf, int addr) { - buf.append(addr & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff).append('.'). - append((addr >>>= 8) & 0xff); + buf.append((addr >> 24) & 0xff).append('.'). + append((addr >> 16) & 0xff).append('.'). + append((addr >> 8) & 0xff).append('.'). + append(addr & 0xff); } /** Implement the Parcelable interface {@hide} */ diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index f959fee..6941e57 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,11 +16,12 @@ package android.net; +import com.android.internal.net.DomainNameValidator; + import android.os.SystemProperties; import android.util.Config; import android.util.Log; -import com.android.common.DomainNameValidator; import java.io.IOException; import java.net.InetAddress; diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java index fa13894..a572f60 100644 --- a/core/java/android/net/WebAddress.java +++ b/core/java/android/net/WebAddress.java @@ -16,7 +16,7 @@ package android.net; -import static com.android.common.Patterns.GOOD_IRI_CHAR; +import static android.util.Patterns.GOOD_IRI_CHAR; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/common/java/com/android/common/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 4c65eb0..3517737 100644 --- a/common/java/com/android/common/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.common; +package android.net.http; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -36,7 +36,6 @@ import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; @@ -55,7 +54,6 @@ import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.net.URI; -import java.security.KeyManagementException; import android.content.Context; import android.content.ContentResolver; diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index e1327dd..c527fe4 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -16,7 +16,8 @@ package android.net.http; -import com.android.common.DomainNameValidator; + +import com.android.internal.net.DomainNameValidator; import org.apache.harmony.xnet.provider.jsse.SSLParameters; diff --git a/common/java/com/android/common/HttpDateTime.java b/core/java/android/net/http/HttpDateTime.java index f4052cc..c7a31ee 100644 --- a/common/java/com/android/common/HttpDateTime.java +++ b/core/java/android/net/http/HttpDateTime.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.common; +package android.net.http; import android.text.format.Time; @@ -22,8 +22,9 @@ import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; - -/** {@hide} */ +/** + * Helper for parsing an HTTP date. + */ public final class HttpDateTime { /* diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index b706c5c..56a05ee 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -750,11 +750,8 @@ public abstract class BatteryStats implements Parcelable { * Checkin server version of dump to produce more compact, computer-readable log. * * NOTE: all times are expressed in 'ms'. - * @param fd - * @param pw - * @param which */ - private final void dumpCheckinLocked(PrintWriter pw, int which) { + public final void dumpCheckinLocked(PrintWriter pw, int which, int reqUid) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; final long batteryUptime = getBatteryUptime(rawUptime); @@ -856,19 +853,24 @@ public abstract class BatteryStats implements Parcelable { getDischargeCurrentLevel()); } - Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); - if (kernelWakelocks.size() > 0) { - for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { - sb.setLength(0); - printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, ""); - - dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), - sb.toString()); + if (reqUid < 0) { + Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); + if (kernelWakelocks.size() > 0) { + for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { + sb.setLength(0); + printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, ""); + + dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), + sb.toString()); + } } } for (int iu = 0; iu < NU; iu++) { final int uid = uidStats.keyAt(iu); + if (reqUid >= 0 && uid != reqUid) { + continue; + } Uid u = uidStats.valueAt(iu); // Dump Network stats per uid, if any long rx = u.getTcpBytesReceived(which); @@ -987,7 +989,7 @@ public abstract class BatteryStats implements Parcelable { } @SuppressWarnings("unused") - private final void dumpLocked(PrintWriter pw, String prefix, int which) { + public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; final long batteryUptime = getBatteryUptime(rawUptime); @@ -1063,23 +1065,25 @@ public abstract class BatteryStats implements Parcelable { long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; - Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); - if (kernelWakelocks.size() > 0) { - for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { - - String linePrefix = ": "; - sb.setLength(0); - sb.append(prefix); - sb.append(" Kernel Wake lock "); - sb.append(ent.getKey()); - linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, - linePrefix); - if (!linePrefix.equals(": ")) { - sb.append(" realtime"); - } else { - sb.append(": (nothing executed)"); + if (reqUid < 0) { + Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); + if (kernelWakelocks.size() > 0) { + for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { + + String linePrefix = ": "; + sb.setLength(0); + sb.append(prefix); + sb.append(" Kernel Wake lock "); + sb.append(ent.getKey()); + linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, + linePrefix); + if (!linePrefix.equals(": ")) { + sb.append(" realtime"); + } else { + sb.append(": (nothing executed)"); + } + pw.println(sb.toString()); } - pw.println(sb.toString()); } } @@ -1212,7 +1216,12 @@ public abstract class BatteryStats implements Parcelable { for (int iu=0; iu<NU; iu++) { final int uid = uidStats.keyAt(iu); + if (reqUid >= 0 && uid != reqUid) { + continue; + } + Uid u = uidStats.valueAt(iu); + pw.println(prefix + " #" + uid + ":"); boolean uidActivity = false; @@ -1421,16 +1430,16 @@ public abstract class BatteryStats implements Parcelable { pw.println("Total Statistics (Current and Historic):"); pw.println(" System starts: " + getStartCount() + ", currently on battery: " + getIsOnBattery()); - dumpLocked(pw, "", STATS_TOTAL); + dumpLocked(pw, "", STATS_TOTAL, -1); pw.println(""); pw.println("Last Run Statistics (Previous run of system):"); - dumpLocked(pw, "", STATS_LAST); + dumpLocked(pw, "", STATS_LAST, -1); pw.println(""); pw.println("Current Battery Statistics (Currently running system):"); - dumpLocked(pw, "", STATS_CURRENT); + dumpLocked(pw, "", STATS_CURRENT, -1); pw.println(""); pw.println("Unplugged Statistics (Since last unplugged from power):"); - dumpLocked(pw, "", STATS_UNPLUGGED); + dumpLocked(pw, "", STATS_UNPLUGGED, -1); } @SuppressWarnings("unused") @@ -1445,13 +1454,13 @@ public abstract class BatteryStats implements Parcelable { } if (isUnpluggedOnly) { - dumpCheckinLocked(pw, STATS_UNPLUGGED); + dumpCheckinLocked(pw, STATS_UNPLUGGED, -1); } else { - dumpCheckinLocked(pw, STATS_TOTAL); - dumpCheckinLocked(pw, STATS_LAST); - dumpCheckinLocked(pw, STATS_UNPLUGGED); - dumpCheckinLocked(pw, STATS_CURRENT); + dumpCheckinLocked(pw, STATS_TOTAL, -1); + dumpCheckinLocked(pw, STATS_LAST, -1); + dumpCheckinLocked(pw, STATS_UNPLUGGED, -1); + dumpCheckinLocked(pw, STATS_CURRENT, -1); } } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 9ee251e..a4c595d 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -541,6 +541,14 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo public static int getGlobalFreedSize() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); } + public static int getGlobalClassInitCount() { + /* number of classes that have been successfully initialized */ + return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT); + } + public static int getGlobalClassInitTime() { + /* cumulative elapsed time for class initialization, in usec */ + return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME); + } public static int getGlobalExternalAllocCount() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS); } @@ -584,6 +592,12 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo public static void resetGlobalFreedSize() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); } + public static void resetGlobalClassInitCount() { + VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT); + } + public static void resetGlobalClassInitTime() { + VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME); + } public static void resetGlobalExternalAllocCount() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS); } diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl index 79a6cfe..2b2dcf4 100644 --- a/core/java/android/os/storage/IMountService.aidl +++ b/core/java/android/os/storage/IMountService.aidl @@ -61,9 +61,12 @@ interface IMountService /** * Safely unmount external storage at given mount point. - * Returns an int consistent with MountServiceResultCode + * The unmount is an asynchronous operation. Applications + * should register StorageEventListener for storage related + * status changes. + * */ - int unmountVolume(String mountPoint, boolean force); + void unmountVolume(String mountPoint, boolean force); /** * Format external storage given a mount point. diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java index 5d09fb5..635323e 100644 --- a/core/java/android/pim/RecurrenceSet.java +++ b/core/java/android/pim/RecurrenceSet.java @@ -18,7 +18,6 @@ package android.pim; import android.content.ContentValues; import android.database.Cursor; -import android.os.Bundle; import android.provider.Calendar; import android.text.TextUtils; import android.text.format.Time; @@ -26,6 +25,7 @@ import android.util.Config; import android.util.Log; import java.util.List; +import java.util.regex.Pattern; /** * Basic information about a recurrence, following RFC 2445 Section 4.8.5. @@ -36,6 +36,7 @@ public class RecurrenceSet { private final static String TAG = "CalendarProvider"; private final static String RULE_SEPARATOR = "\n"; + private final static String FOLDING_SEPARATOR = "\n "; // TODO: make these final? public EventRecurrence[] rrules = null; @@ -309,7 +310,8 @@ public static boolean populateComponent(ContentValues values, String rdateStr = values.getAsString(Calendar.Events.RDATE); String exruleStr = values.getAsString(Calendar.Events.EXRULE); String exdateStr = values.getAsString(Calendar.Events.EXDATE); - boolean allDay = values.getAsInteger(Calendar.Events.ALL_DAY) == 1; + Integer allDayInteger = values.getAsInteger(Calendar.Events.ALL_DAY); + boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false; if ((dtstart == -1) || (TextUtils.isEmpty(duration))|| @@ -361,7 +363,7 @@ public static boolean populateComponent(ContentValues values, if (TextUtils.isEmpty(ruleStr)) { return; } - String[] rrules = ruleStr.split(RULE_SEPARATOR); + String[] rrules = getRuleStrings(ruleStr); for (String rrule : rrules) { ICalendar.Property prop = new ICalendar.Property(propertyName); prop.setValue(rrule); @@ -369,6 +371,52 @@ public static boolean populateComponent(ContentValues values, } } + private static String[] getRuleStrings(String ruleStr) { + if (null == ruleStr) { + return new String[0]; + } + String unfoldedRuleStr = unfold(ruleStr); + String[] split = unfoldedRuleStr.split(RULE_SEPARATOR); + int count = split.length; + for (int n = 0; n < count; n++) { + split[n] = fold(split[n]); + } + return split; + } + + + private static final Pattern IGNORABLE_ICAL_WHITESPACE_RE = + Pattern.compile("(?:\\r\\n?|\\n)[ \t]"); + + private static final Pattern FOLD_RE = Pattern.compile(".{75}"); + + /** + * fold and unfolds ical content lines as per RFC 2445 section 4.1. + * + * <h3>4.1 Content Lines</h3> + * + * <p>The iCalendar object is organized into individual lines of text, called + * content lines. Content lines are delimited by a line break, which is a CRLF + * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10). + * + * <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line + * break. Long content lines SHOULD be split into a multiple line + * representations using a line "folding" technique. That is, a long line can + * be split between any two characters by inserting a CRLF immediately + * followed by a single linear white space character (i.e., SPACE, US-ASCII + * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed + * immediately by a single linear white space character is ignored (i.e., + * removed) when processing the content type. + */ + public static String fold(String unfoldedIcalContent) { + return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n "); + } + + public static String unfold(String foldedIcalContent) { + return IGNORABLE_ICAL_WHITESPACE_RE.matcher( + foldedIcalContent).replaceAll(""); + } + private static void addPropertyForDateStr(ICalendar.Component component, String propertyName, String dateStr) { diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 2eb25954..194fe33 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -26,8 +26,6 @@ import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.RemoteException; import android.pim.vcard.exception.VCardException; -import android.provider.CallLog; -import android.provider.CallLog.Calls; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; @@ -44,8 +42,6 @@ import android.provider.ContactsContract.CommonDataKinds.Relation; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; -import android.text.TextUtils; -import android.text.format.Time; import android.util.CharsetUtils; import android.util.Log; @@ -60,7 +56,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -125,12 +120,6 @@ public class VCardComposer { public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; - // Property for call log entry - private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME"; - private static final String VCARD_PROPERTY_CALLTYPE_INCOMING = "INCOMING"; - private static final String VCARD_PROPERTY_CALLTYPE_OUTGOING = "OUTGOING"; - private static final String VCARD_PROPERTY_CALLTYPE_MISSED = "MISSED"; - private static final String SHIFT_JIS = "SHIFT_JIS"; private static final String UTF_8 = "UTF-8"; @@ -275,30 +264,14 @@ public class VCardComposer { private final String mCharsetString; private boolean mTerminateIsCalled; - final private List<OneEntryHandler> mHandlerList; + private final List<OneEntryHandler> mHandlerList; private String mErrorReason = NO_ERROR; - private boolean mIsCallLogComposer; - private static final String[] sContactsProjection = new String[] { Contacts._ID, }; - /** The projection to use when querying the call log table */ - private static final String[] sCallLogProjection = new String[] { - Calls.NUMBER, Calls.DATE, Calls.TYPE, Calls.CACHED_NAME, Calls.CACHED_NUMBER_TYPE, - Calls.CACHED_NUMBER_LABEL - }; - private static final int NUMBER_COLUMN_INDEX = 0; - private static final int DATE_COLUMN_INDEX = 1; - private static final int CALL_TYPE_COLUMN_INDEX = 2; - private static final int CALLER_NAME_COLUMN_INDEX = 3; - private static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 4; - private static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 5; - - private static final String FLAG_TIMEZONE_UTC = "Z"; - public VCardComposer(Context context) { this(context, VCardConfig.VCARD_TYPE_DEFAULT, true); } @@ -377,6 +350,7 @@ public class VCardComposer { if (contentUri == null) { return false; } + if (mCareHandlerErrors) { List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>( mHandlerList.size()); @@ -396,10 +370,7 @@ public class VCardComposer { } final String[] projection; - if (CallLog.Calls.CONTENT_URI.equals(contentUri)) { - projection = sCallLogProjection; - mIsCallLogComposer = true; - } else if (Contacts.CONTENT_URI.equals(contentUri) || + if (Contacts.CONTENT_URI.equals(contentUri) || CONTACTS_TEST_CONTENT_URI.equals(contentUri)) { projection = sContactsProjection; } else { @@ -426,11 +397,7 @@ public class VCardComposer { return false; } - if (mIsCallLogComposer) { - mIdColumn = -1; - } else { - mIdColumn = mCursor.getColumnIndex(Contacts._ID); - } + mIdColumn = mCursor.getColumnIndex(Contacts._ID); return true; } @@ -448,19 +415,14 @@ public class VCardComposer { mErrorReason = FAILURE_REASON_NOT_INITIALIZED; return false; } - String name = null; String vcard; try { - if (mIsCallLogComposer) { - vcard = createOneCallLogEntryInternal(); + if (mIdColumn >= 0) { + vcard = createOneEntryInternal(mCursor.getString(mIdColumn), + getEntityIteratorMethod); } else { - if (mIdColumn >= 0) { - vcard = createOneEntryInternal(mCursor.getString(mIdColumn), - getEntityIteratorMethod); - } else { - Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); - return true; - } + Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); + return true; } } catch (VCardException e) { Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage()); @@ -468,7 +430,7 @@ public class VCardComposer { } catch (OutOfMemoryError error) { // Maybe some data (e.g. photo) is too big to have in memory. But it // should be rare. - Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry: " + name); + Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry."); System.gc(); // TODO: should tell users what happened? return true; @@ -630,99 +592,4 @@ public class VCardComposer { public String getErrorReason() { return mErrorReason; } - - /** - * This static function is to compose vCard for phone own number - */ - public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName, - String phoneNumber, boolean vcardVer21) { - final int vcardType = (vcardVer21 ? - VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8 : - VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8); - final VCardBuilder builder = new VCardBuilder(vcardType); - boolean needCharset = false; - if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) { - needCharset = true; - } - builder.appendLine(VCardConstants.PROPERTY_FN, phoneName, needCharset, false); - builder.appendLine(VCardConstants.PROPERTY_N, phoneName, needCharset, false); - - if (!TextUtils.isEmpty(phoneNumber)) { - String label = Integer.toString(phonetype); - builder.appendTelLine(phonetype, label, phoneNumber, false); - } - - return builder.toString(); - } - - /** - * Format according to RFC 2445 DATETIME type. - * The format is: ("%Y%m%dT%H%M%SZ"). - */ - private final String toRfc2455Format(final long millSecs) { - Time startDate = new Time(); - startDate.set(millSecs); - String date = startDate.format2445(); - return date + FLAG_TIMEZONE_UTC; - } - - /** - * Try to append the property line for a call history time stamp field if possible. - * Do nothing if the call log type gotton from the database is invalid. - */ - private void tryAppendCallHistoryTimeStampField(final VCardBuilder builder) { - // Extension for call history as defined in - // in the Specification for Ic Mobile Communcation - ver 1.1, - // Oct 2000. This is used to send the details of the call - // history - missed, incoming, outgoing along with date and time - // to the requesting device (For example, transferring phone book - // when connected over bluetooth) - // - // e.g. "X-IRMC-CALL-DATETIME;MISSED:20050320T100000Z" - final int callLogType = mCursor.getInt(CALL_TYPE_COLUMN_INDEX); - final String callLogTypeStr; - switch (callLogType) { - case Calls.INCOMING_TYPE: { - callLogTypeStr = VCARD_PROPERTY_CALLTYPE_INCOMING; - break; - } - case Calls.OUTGOING_TYPE: { - callLogTypeStr = VCARD_PROPERTY_CALLTYPE_OUTGOING; - break; - } - case Calls.MISSED_TYPE: { - callLogTypeStr = VCARD_PROPERTY_CALLTYPE_MISSED; - break; - } - default: { - Log.w(LOG_TAG, "Call log type not correct."); - return; - } - } - - final long dateAsLong = mCursor.getLong(DATE_COLUMN_INDEX); - builder.appendLine(VCARD_PROPERTY_X_TIMESTAMP, - Arrays.asList(callLogTypeStr), toRfc2455Format(dateAsLong)); - } - - private String createOneCallLogEntryInternal() { - final VCardBuilder builder = new VCardBuilder(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8); - String name = mCursor.getString(CALLER_NAME_COLUMN_INDEX); - if (TextUtils.isEmpty(name)) { - name = mCursor.getString(NUMBER_COLUMN_INDEX); - } - final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name)); - builder.appendLine(VCardConstants.PROPERTY_FN, name, needCharset, false); - builder.appendLine(VCardConstants.PROPERTY_N, name, needCharset, false); - - final String number = mCursor.getString(NUMBER_COLUMN_INDEX); - final int type = mCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX); - String label = mCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX); - if (TextUtils.isEmpty(label)) { - label = Integer.toString(type); - } - builder.appendTelLine(type, label, number, false); - tryAppendCallHistoryTimeStampField(builder); - return builder.toString(); - } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index acb8473..1163106 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3176,6 +3176,56 @@ public final class ContactsContract { } /** + * Additional columns returned by the {@link Contacts#CONTENT_FILTER_URI} providing the + * explanation of why the filter matched the contact. Specifically, they contain the + * data type and element that was used for matching. + * <p> + * This is temporary API, it will need to change when we move to FTS. + * + * @hide + */ + public static class SearchSnippetColumns { + + /** + * The ID of the data row that was matched by the filter. + * + * @hide + */ + public static final String SNIPPET_DATA_ID = "snippet_data_id"; + + /** + * The type of data that was matched by the filter. + * + * @hide + */ + public static final String SNIPPET_MIMETYPE = "snippet_mimetype"; + + /** + * The {@link CommonDataKinds.CommonColumns#DATA} field of the data row + * that was matched by the filter. + * + * @hide + */ + public static final String SNIPPET_DATA = "snippet_data"; + + /** + * The {@link CommonDataKinds.CommonColumns#TYPE} field of the data row + * that was matched by the filter. + * + * @hide + */ + public static final String SNIPPET_TYPE = "snippet_type"; + + /** + * The {@link CommonDataKinds.CommonColumns#LABEL} field of the data row + * that was matched by the filter. + * + * @hide + */ + public static final String SNIPPET_LABEL = "snippet_label"; + } + + /** * Container for definitions of common data types stored in the {@link ContactsContract.Data} * table. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 14e27eb..b75a8cc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -374,6 +374,11 @@ public final class Settings { * In some cases, a matching Activity may not exist, so ensure you * safeguard against this. * <p> + * The account types available to add via the add account button may be restricted by adding an + * {@link #EXTRA_AUTHORITIES} extra to this Intent with one or more syncable content provider's + * authorities. Only account types which can sync with that content provider will be offered to + * the user. + * <p> * Input: Nothing. * <p> * Output: Nothing. @@ -383,6 +388,24 @@ public final class Settings { "android.settings.SYNC_SETTINGS"; /** + * Activity Action: Show add account screen for creating a new account. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * The account types available to add may be restricted by adding an {@link #EXTRA_AUTHORITIES} + * extra to the Intent with one or more syncable content provider's authorities. Only account + * types which can sync with that content provider will be offered to the user. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ADD_ACCOUNT = + "android.settings.ADD_ACCOUNT_SETTINGS"; + + /** * Activity Action: Show settings for selecting the network operator. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -468,6 +491,19 @@ public final class Settings { // End of Intent actions for Settings + /** + * Activity Extra: Limit available options in launched activity based on the given authority. + * <p> + * This can be passed as an extra field in an Activity Intent with one or more syncable content + * provider's authorities as a String[]. This field is used by some intents to alter the + * behavior of the called activity. + * <p> + * Example: The {@link #ACTION_ADD_ACCOUNT} intent restricts the account types available based + * on the authority given. + */ + public static final String EXTRA_AUTHORITIES = + "authorities"; + private static final String JID_RESOURCE_PREFIX = "android"; public static final String AUTHORITY = "settings"; @@ -2430,6 +2466,13 @@ public final class Settings { public static final String BACKGROUND_DATA = "background_data"; /** + * Whether mobile data connections are allowed by the user. See + * ConnectivityManager for more info. + * @hide + */ + public static final String MOBILE_DATA = "mobile_data"; + + /** * The CDMA roaming mode 0 = Home Networks, CDMA default * 1 = Roaming on Affiliated networks * 2 = Roaming on any networks @@ -3033,11 +3076,11 @@ public final class Settings { * @hide */ public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; - + /** * The {@link ComponentName} string of the service to be used as the voice recognition * service. - * + * * @hide */ public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service"; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index adeef54..d965962 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -29,8 +29,8 @@ import android.telephony.SmsMessage; import android.text.TextUtils; import android.util.Config; import android.util.Log; +import android.util.Patterns; -import com.android.common.Patterns; import java.util.HashSet; import java.util.Set; diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index fbc4a81..caa3144 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -16,20 +16,14 @@ package android.server.search; -import android.app.ActivityManagerNative; -import android.app.IActivityWatcher; +import com.android.internal.content.PackageMonitor; + import android.app.ISearchManager; -import android.app.ISearchManagerCallback; import android.app.SearchManager; import android.app.SearchableInfo; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.os.Handler; -import android.os.RemoteException; import android.util.Log; import java.util.List; @@ -42,13 +36,11 @@ public class SearchManagerService extends ISearchManager.Stub { // general debugging support private static final String TAG = "SearchManagerService"; - private static final boolean DBG = false; // Context that the service is running in. private final Context mContext; - // This field is initialized in ensureSearchablesCreated(), and then never modified. - // Only accessed by ensureSearchablesCreated() and getSearchables() + // This field is initialized lazily in getSearchables(), and then never modified. private Searchables mSearchables; /** @@ -61,58 +53,28 @@ public class SearchManagerService extends ISearchManager.Stub { mContext = context; } - private synchronized void ensureSearchablesCreated() { - if (mSearchables != null) return; // already created - - mSearchables = new Searchables(mContext); - mSearchables.buildSearchableList(); - - IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - packageFilter.addDataScheme("package"); - mContext.registerReceiver(mPackageChangedReceiver, packageFilter); - // Register for events related to sdcard installation. - IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(mPackageChangedReceiver, sdFilter); - } - private synchronized Searchables getSearchables() { - ensureSearchablesCreated(); + if (mSearchables == null) { + mSearchables = new Searchables(mContext); + mSearchables.buildSearchableList(); + new MyPackageMonitor().register(mContext, true); + } return mSearchables; } /** * Refreshes the "searchables" list when packages are added/removed. */ - private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() { + class MyPackageMonitor extends PackageMonitor { @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - if (Intent.ACTION_PACKAGE_ADDED.equals(action) || - Intent.ACTION_PACKAGE_REMOVED.equals(action) || - Intent.ACTION_PACKAGE_CHANGED.equals(action) || - Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) || - Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - if (DBG) Log.d(TAG, "Got " + action); - // Update list of searchable activities - getSearchables().buildSearchableList(); - broadcastSearchablesChanged(); - } + public void onSomePackagesChanged() { + // Update list of searchable activities + getSearchables().buildSearchableList(); + // Inform all listeners that the list of searchables has been updated. + Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + mContext.sendBroadcast(intent); } - }; - - /** - * Informs all listeners that the list of searchables has been updated. - */ - void broadcastSearchablesChanged() { - Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - mContext.sendBroadcast(intent); } // @@ -123,24 +85,15 @@ public class SearchManagerService extends ISearchManager.Stub { * Returns the SearchableInfo for a given activity. * * @param launchActivity The activity from which we're launching this search. - * @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 - * search is defined in the current application or activity, no search will be launched. - * If true, this will always launch a platform-global (e.g. web-based) search instead. * @return Returns a SearchableInfo record describing the parameters of the search, * or null if no searchable metadata was available. */ - public SearchableInfo getSearchableInfo(final ComponentName launchActivity, - final boolean globalSearch) { - if (globalSearch) { - return getSearchables().getDefaultSearchable(); - } else { - if (launchActivity == null) { - Log.e(TAG, "getSearchableInfo(), activity == null"); - return null; - } - return getSearchables().getSearchableInfo(launchActivity); + public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { + if (launchActivity == null) { + Log.e(TAG, "getSearchableInfo(), activity == null"); + return null; } + return getSearchables().getSearchableInfo(launchActivity); } /** @@ -151,27 +104,17 @@ public class SearchManagerService extends ISearchManager.Stub { } /** - * Returns a list of the searchable activities that handle web searches. - * Can be called from any thread. + * Gets the name of the global search activity. */ - public List<SearchableInfo> getSearchablesForWebSearch() { - return getSearchables().getSearchablesForWebSearchList(); + public ComponentName getGlobalSearchActivity() { + return getSearchables().getGlobalSearchActivity(); } /** - * Returns the default searchable activity for web searches. - * Can be called from any thread. + * Gets the name of the web search activity. */ - public SearchableInfo getDefaultSearchableForWebSearch() { - return getSearchables().getDefaultSearchableForWebSearch(); + public ComponentName getWebSearchActivity() { + return getSearchables().getWebSearchActivity(); } - /** - * Sets the default searchable activity for web searches. - * Can be called from any thread. - */ - public void setDefaultWebSearch(final ComponentName component) { - getSearchables().setDefaultWebSearch(component); - broadcastSearchablesChanged(); - } } diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index cbb63a5..279c17d 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,14 +16,12 @@ package android.server.search; -import com.android.internal.app.ResolverActivity; - +import android.Manifest; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -52,9 +50,8 @@ public class Searchables { private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; - private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null; - private SearchableInfo mDefaultSearchable = null; - private SearchableInfo mDefaultSearchableForWebSearch = null; + private ComponentName mGlobalSearchActivity = null; + private ComponentName mWebSearchActivity = null; public static String GOOGLE_SEARCH_COMPONENT_NAME = "com.android.googlesearch/.GoogleSearch"; @@ -131,10 +128,9 @@ public class Searchables { // Irrespective of source, if a reference was found, follow it. if (refActivityName != null) { - // An app or activity can declare that we should simply launch - // "system default search" if search is invoked. + // This value is deprecated, return null if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { - return getDefaultSearchable(); + return null; } String pkg = activity.getPackageName(); ComponentName referredActivity; @@ -164,20 +160,6 @@ public class Searchables { } /** - * Provides the system-default search activity, which you can use - * whenever getSearchableInfo() returns null; - * - * @return Returns the system-default search activity, null if never defined - */ - public synchronized SearchableInfo getDefaultSearchable() { - return mDefaultSearchable; - } - - public synchronized boolean isDefaultSearchable(SearchableInfo searchable) { - return searchable == mDefaultSearchable; - } - - /** * Builds an entire list (suitable for display) of * activities that are searchable, by iterating the entire set of * ACTION_SEARCH & ACTION_WEB_SEARCH intents. @@ -205,8 +187,6 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); - ArrayList<SearchableInfo> newSearchablesForWebSearchList - = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); @@ -244,127 +224,71 @@ public class Searchables { } } - if (webSearchInfoList != null) { - for (int i = 0; i < webSearchInfoList.size(); ++i) { - ActivityInfo ai = webSearchInfoList.get(i).activityInfo; - ComponentName component = new ComponentName(ai.packageName, ai.name); - SearchableInfo searchable = newSearchablesMap.get(component); - if (searchable == null) { - Log.w(LOG_TAG, "did not find component in searchables: " + component); - } else { - newSearchablesForWebSearchList.add(searchable); - } - } - } - - // Find the global search provider - Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); - SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); - - if (newDefaultSearchable == null) { - Log.w(LOG_TAG, "No searchable info found for new default searchable activity " - + globalSearchActivity); - } + // Find the global search activity + ComponentName newGlobalSearchActivity = findGlobalSearchActivity(); - // Find the default web search provider. - ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext); - SearchableInfo newDefaultSearchableForWebSearch = null; - if (webSearchActivity != null) { - newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity); - } - if (newDefaultSearchableForWebSearch == null) { - Log.w(LOG_TAG, "No searchable info found for new default web search activity " - + webSearchActivity); - } + // Find the web search activity + ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; - mSearchablesForWebSearchList = newSearchablesForWebSearchList; - mDefaultSearchable = newDefaultSearchable; - mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; + mGlobalSearchActivity = newGlobalSearchActivity; + mWebSearchActivity = newWebSearchActivity; } } /** - * Checks if the given activity component is present in the system and if so makes it the - * preferred activity for handling ACTION_WEB_SEARCH. - * @param component Name of the component to check and set as preferred. - * @param action Intent action for which this activity is to be set as preferred. - * @return true if component was detected and set as preferred activity, false if not. + * Finds the global search activity. + * + * This is currently implemented by returning the first activity that handles + * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow + * more than one global search activity to be installed, this code must be changed. */ - private static boolean setPreferredActivity(Context context, - ComponentName component, String action) { - Log.d(LOG_TAG, "Checking component " + component); - PackageManager pm = context.getPackageManager(); - ActivityInfo ai; - try { - ai = pm.getActivityInfo(component, 0); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - - // The code here to find the value for bestMatch is heavily inspired by the code - // in ResolverActivity where the preferred activity is set. - Intent intent = new Intent(action); - intent.addCategory(Intent.CATEGORY_DEFAULT); - List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0); - ComponentName set[] = new ComponentName[webSearchActivities.size()]; - int bestMatch = 0; - for (int i = 0; i < webSearchActivities.size(); ++i) { - ResolveInfo ri = webSearchActivities.get(i); - set[i] = new ComponentName(ri.activityInfo.packageName, - ri.activityInfo.name); - if (ri.match > bestMatch) bestMatch = ri.match; + private ComponentName findGlobalSearchActivity() { + Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> activities = + pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + int count = activities == null ? 0 : activities.size(); + for (int i = 0; i < count; i++) { + ActivityInfo ai = activities.get(i).activityInfo; + if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, + ai.packageName) == PackageManager.PERMISSION_GRANTED) { + return new ComponentName(ai.packageName, ai.name); + } else { + Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " + + "but does not have the GLOBAL_SEARCH permission."); + } } - - Log.d(LOG_TAG, "Setting preferred web search activity to " + component); - IntentFilter filter = new IntentFilter(action); - filter.addCategory(Intent.CATEGORY_DEFAULT); - pm.replacePreferredActivity(filter, bestMatch, set, component); - return true; + Log.w(LOG_TAG, "No global search activity found"); + return null; } - private static ComponentName getPreferredWebSearchActivity(Context context) { - // Check if we have a preferred web search activity. + /** + * Finds the web search activity. + * + * Only looks in the package of the global search activity. + */ + private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) { + if (globalSearchActivity == null) { + return null; + } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); - PackageManager pm = context.getPackageManager(); - ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - - if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) { - Log.d(LOG_TAG, "No preferred activity set for action web search."); - - // The components in the providers array are checked in the order of declaration so the - // first one has the highest priority. If the component exists in the system it is set - // as the preferred activity to handle intent action web search. - String[] preferredActivities = context.getResources().getStringArray( - com.android.internal.R.array.default_web_search_providers); - for (String componentName : preferredActivities) { - ComponentName component = ComponentName.unflattenFromString(componentName); - if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) { - return component; - } - } - } else { - // If the current preferred activity is GoogleSearch, and we detect - // EnhancedGoogleSearch installed as well, set the latter as preferred since that - // is a superset and provides more functionality. - ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); - if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) { - ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString( - ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); - if (setPreferredActivity(context, enhancedGoogleSearch, - Intent.ACTION_WEB_SEARCH)) { - return enhancedGoogleSearch; - } - } + intent.setPackage(globalSearchActivity.getPackageName()); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> activities = + pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + int count = activities == null ? 0 : activities.size(); + for (int i = 0; i < count; i++) { + ActivityInfo ai = activities.get(i).activityInfo; + // TODO: do some sanity checks here? + return new ComponentName(ai.packageName, ai.name); } - - if (ri == null) return null; - return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); + Log.w(LOG_TAG, "No web search activity found"); + return null; } /** @@ -383,24 +307,16 @@ public class Searchables { } /** - * Returns a list of the searchable activities that handle web searches. - */ - public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() { - return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList); - } - - /** - * Returns the default searchable activity for web searches. + * Gets the name of the global search activity. */ - public synchronized SearchableInfo getDefaultSearchableForWebSearch() { - return mDefaultSearchableForWebSearch; + public synchronized ComponentName getGlobalSearchActivity() { + return mGlobalSearchActivity; } /** - * Sets the default searchable activity for web searches. + * Gets the name of the web search activity. */ - public synchronized void setDefaultWebSearch(ComponentName component) { - setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH); - buildSearchableList(); + public synchronized ComponentName getWebSearchActivity() { + return mWebSearchActivity; } } diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 7c15cec..bf411e1 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -32,6 +32,15 @@ import android.os.Bundle; * Constants for supporting speech recognition through starting an {@link Intent} */ public class RecognizerIntent { + /** + * The extra key used in an intent to the speech recognizer for voice search. Not + * generally to be used by developers. The system search dialog uses this, for example, + * to set a calling package for identification by a voice search API. If this extra + * is set by anyone but the system process, it should be overridden by the voice search + * implementation. + */ + public final static String EXTRA_CALLING_PACKAGE = "calling_package"; + private RecognizerIntent() { // Not for instantiating. } diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java index 22d95d1..cd33d8a 100644 --- a/core/java/android/test/InstrumentationTestCase.java +++ b/core/java/android/test/InstrumentationTestCase.java @@ -16,8 +16,6 @@ package android.test; -import junit.framework.TestCase; - import android.app.Activity; import android.app.Instrumentation; import android.content.Intent; @@ -26,9 +24,11 @@ import android.util.Log; import android.view.KeyEvent; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.InvocationTargetException; + +import junit.framework.TestCase; /** * A test case that has access to {@link Instrumentation}. @@ -86,7 +86,6 @@ public class InstrumentationTestCase extends TestCase { * @param extras Optional extra stuff to pass to the activity. * @return The activity, or null if non launched. */ - @SuppressWarnings("unchecked") public final <T extends Activity> T launchActivity( String pkg, Class<T> activityCls, @@ -338,6 +337,7 @@ public class InstrumentationTestCase extends TestCase { * * @throws Exception */ + @Override protected void tearDown() throws Exception { Runtime.getRuntime().gc(); Runtime.getRuntime().runFinalization(); diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java new file mode 100644 index 0000000..e4f934e --- /dev/null +++ b/core/java/android/text/AndroidBidi.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +/** + * Access the ICU bidi implementation. + * @hide + */ +/* package */ class AndroidBidi { + + public static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { + if (chs == null || chInfo == null) { + throw new NullPointerException(); + } + + if (n < 0 || chs.length < n || chInfo.length < n) { + throw new IndexOutOfBoundsException(); + } + + switch(dir) { + case Layout.DIR_REQUEST_LTR: dir = 0; break; + case Layout.DIR_REQUEST_RTL: dir = 1; break; + case Layout.DIR_REQUEST_DEFAULT_LTR: dir = -2; break; + case Layout.DIR_REQUEST_DEFAULT_RTL: dir = -1; break; + default: dir = 0; break; + } + + int result = runBidi(dir, chs, chInfo, n, haveInfo); + result = (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT; + return result; + } + + private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo); +}
\ No newline at end of file diff --git a/core/java/android/text/AutoText.java b/core/java/android/text/AutoText.java index 862305b..04730ec 100644 --- a/core/java/android/text/AutoText.java +++ b/core/java/android/text/AutoText.java @@ -18,7 +18,9 @@ package android.text; import android.content.res.Resources; import android.content.res.XmlResourceParser; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; + import android.view.View; import org.xmlpull.v1.XmlPullParser; diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 33ecc01..07e71f9 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -46,7 +46,8 @@ import android.text.style.TypefaceSpan; import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import android.util.Log; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; import java.io.IOException; import java.io.StringReader; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 1023036..38ac9b7 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1936,6 +1936,11 @@ public abstract class Layout { public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; + + /* package */ static final int DIR_REQUEST_LTR = 1; + /* package */ static final int DIR_REQUEST_RTL = -1; + /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; + /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; public enum Alignment { ALIGN_NORMAL, diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 6c89f92..600ec7e 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -234,215 +234,9 @@ extends Layout } if (!easy) { - AndroidCharacter.getDirectionalities(chs, chdirs, end - start); - - /* - * Determine primary paragraph direction - */ - - for (int j = start; j < end; j++) { - int d = chdirs[j - start]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { - dir = DIR_LEFT_TO_RIGHT; - break; - } - if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - dir = DIR_RIGHT_TO_LEFT; - break; - } - } - - /* - * XXX Explicit overrides should go here - */ - - /* - * Weak type resolution - */ - - final byte SOR = dir == DIR_LEFT_TO_RIGHT ? - Character.DIRECTIONALITY_LEFT_TO_RIGHT : - Character.DIRECTIONALITY_RIGHT_TO_LEFT; - - // dump(chdirs, n, "initial"); - - // W1 non spacing marks - for (int j = 0; j < n; j++) { - if (chdirs[j] == Character.NON_SPACING_MARK) { - if (j == 0) - chdirs[j] = SOR; - else - chdirs[j] = chdirs[j - 1]; - } - } - - // dump(chdirs, n, "W1"); - - // W2 european numbers - byte cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - cur = d; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { - if (cur == - Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; - } - } - - // dump(chdirs, n, "W2"); - - // W3 arabic letters - for (int j = 0; j < n; j++) { - if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - } - - // dump(chdirs, n, "W3"); - - // W4 single separator between numbers - for (int j = 1; j < n - 1; j++) { - byte d = chdirs[j]; - byte prev = chdirs[j - 1]; - byte next = chdirs[j + 1]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { - if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && - next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { - if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && - next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && - next == Character.DIRECTIONALITY_ARABIC_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; - } - } - - // dump(chdirs, n, "W4"); - - // W5 european number terminators - boolean adjacent = false; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - adjacent = true; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - else - adjacent = false; - } - - //dump(chdirs, n, "W5"); - - // W5 european number terminators part 2, - // W6 separators and terminators - adjacent = false; - for (int j = n - 1; j >= 0; j--) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - adjacent = true; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { - if (adjacent) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - else - chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; - } - else { - adjacent = false; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || - d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || - d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || - d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) - chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; - } - } - - // dump(chdirs, n, "W6"); - - // W7 strong direction of european numbers - cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == SOR || - d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) - cur = d; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = cur; - } - - // dump(chdirs, n, "W7"); - - // N1, N2 neutrals - cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - cur = d; - } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - d == Character.DIRECTIONALITY_ARABIC_NUMBER) { - cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - } else { - byte dd = SOR; - int k; - - for (k = j + 1; k < n; k++) { - dd = chdirs[k]; - - if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - break; - } - if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { - dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - break; - } - } - - for (int y = j; y < k; y++) { - if (dd == cur) - chdirs[y] = cur; - else - chdirs[y] = SOR; - } - - j = k - 1; - } - } - - // dump(chdirs, n, "final"); - - // extra: enforce that all tabs and surrogate characters go the - // primary direction - // TODO: actually do directions right for surrogates - - for (int j = 0; j < n; j++) { - char c = chs[j]; - - if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { - chdirs[j] = SOR; - } - } - - // extra: enforce that object replacements go to the - // primary direction - // and that none of the underlying characters are treated - // as viable breakpoints + // Ensure that none of the underlying characters are treated + // as viable breakpoints, and that the entire run gets the + // same bidi direction. if (source instanceof Spanned) { Spanned sp = (Spanned) source; @@ -453,12 +247,14 @@ extends Layout int b = sp.getSpanEnd(spans[y]); for (int x = a; x < b; x++) { - chdirs[x - start] = SOR; chs[x - start] = '\uFFFC'; } } } + // XXX put override flags, etc. into chdirs + dir = bidi(dir, chs, chdirs, n, false); + // Do mirroring for right-to-left segments for (int i = 0; i < n; i++) { @@ -810,6 +606,239 @@ extends Layout } } + /** + * Runs the unicode bidi algorithm on the first n chars in chs, returning + * the char dirs in chInfo and the base line direction of the first + * paragraph. + * + * XXX change result from dirs to levels + * + * @param dir the direction flag, either DIR_REQUEST_LTR, + * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL. + * @param chs the text to examine + * @param chInfo on input, if hasInfo is true, override and other flags + * representing out-of-band embedding information. On output, the generated + * dirs of the text. + * @param n the length of the text/information in chs and chInfo + * @param hasInfo true if chInfo has input information, otherwise the + * input data in chInfo is ignored. + * @return the resolved direction level of the first paragraph, either + * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT. + */ + /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, + boolean hasInfo) { + + AndroidCharacter.getDirectionalities(chs, chInfo, n); + + /* + * Determine primary paragraph direction if not specified + */ + if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) { + // set up default + dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT; + for (int j = 0; j < n; j++) { + int d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { + dir = DIR_LEFT_TO_RIGHT; + break; + } + if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + dir = DIR_RIGHT_TO_LEFT; + break; + } + } + } + + final byte SOR = dir == DIR_LEFT_TO_RIGHT ? + Character.DIRECTIONALITY_LEFT_TO_RIGHT : + Character.DIRECTIONALITY_RIGHT_TO_LEFT; + + /* + * XXX Explicit overrides should go here + */ + + /* + * Weak type resolution + */ + + // dump(chdirs, n, "initial"); + + // W1 non spacing marks + for (int j = 0; j < n; j++) { + if (chInfo[j] == Character.NON_SPACING_MARK) { + if (j == 0) + chInfo[j] = SOR; + else + chInfo[j] = chInfo[j - 1]; + } + } + + // dump(chdirs, n, "W1"); + + // W2 european numbers + byte cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + cur = d; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { + if (cur == + Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + } + } + + // dump(chdirs, n, "W2"); + + // W3 arabic letters + for (int j = 0; j < n; j++) { + if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + } + + // dump(chdirs, n, "W3"); + + // W4 single separator between numbers + for (int j = 1; j < n - 1; j++) { + byte d = chInfo[j]; + byte prev = chInfo[j - 1]; + byte next = chInfo[j + 1]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { + if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && + next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { + if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && + next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && + next == Character.DIRECTIONALITY_ARABIC_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + } + } + + // dump(chdirs, n, "W4"); + + // W5 european number terminators + boolean adjacent = false; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + adjacent = true; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + else + adjacent = false; + } + + //dump(chdirs, n, "W5"); + + // W5 european number terminators part 2, + // W6 separators and terminators + adjacent = false; + for (int j = n - 1; j >= 0; j--) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + adjacent = true; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { + if (adjacent) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + else + chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + else { + adjacent = false; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || + d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || + d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || + d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) + chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + } + + // dump(chdirs, n, "W6"); + + // W7 strong direction of european numbers + cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == SOR || + d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) + cur = d; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = cur; + } + + // dump(chdirs, n, "W7"); + + // N1, N2 neutrals + cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + cur = d; + } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + d == Character.DIRECTIONALITY_ARABIC_NUMBER) { + cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + } else { + byte dd = SOR; + int k; + + for (k = j + 1; k < n; k++) { + dd = chInfo[k]; + + if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + break; + } + if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { + dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + break; + } + } + + for (int y = j; y < k; y++) { + if (dd == cur) + chInfo[y] = cur; + else + chInfo[y] = SOR; + } + + j = k - 1; + } + } + + // dump(chdirs, n, "final"); + + // extra: enforce that all tabs and surrogate characters go the + // primary direction + // TODO: actually do directions right for surrogates + + for (int j = 0; j < n; j++) { + char c = chs[j]; + + if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { + chInfo[j] = SOR; + } + } + + return dir; + } + private static final char FIRST_CJK = '\u2E80'; /** * Returns true if the specified character is one of those specified @@ -1012,12 +1041,12 @@ extends Layout int extra; if (needMultiply) { - // XXX: this looks like it is using the +0.5 and the cast to int - // to do rounding, but this I expect this isn't doing the intended - // thing when spacingmult < 1. An intended extra of, say, -1.2 - // will get 'rounded' to -.7 and then truncated to 0. - extra = (int) ((below - above) * (spacingmult - 1) - + spacingadd + 0.5); + double ex = (below - above) * (spacingmult - 1) + spacingadd; + if (ex >= 0) { + extra = (int)(ex + 0.5); + } else { + extra = -(int)(-ex + 0.5); + } } else { extra = 0; } diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index 7f87365..9860588 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -22,10 +22,10 @@ import android.text.style.URLSpan; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; +import android.util.Patterns; import android.webkit.WebView; import android.widget.TextView; -import com.android.common.Patterns; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; diff --git a/common/java/com/android/common/Patterns.java b/core/java/android/util/Patterns.java index 3b3b038..2ee6e8a 100644 --- a/common/java/com/android/common/Patterns.java +++ b/core/java/android/util/Patterns.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.common; +package android.util; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 4f496d7..0fc70d5 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -27,7 +27,7 @@ import java.io.IOException; import java.util.TimeZone; import java.util.Date; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; /** * A class containing utility methods related to time zones. diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java index 8f855cd..ecedbe1 100644 --- a/core/java/android/util/XmlPullAttributes.java +++ b/core/java/android/util/XmlPullAttributes.java @@ -19,7 +19,8 @@ package android.util; import org.xmlpull.v1.XmlPullParser; import android.util.AttributeSet; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; /** * Provides an implementation of AttributeSet on top of an XmlPullParser. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2eb633f..679206d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3650,14 +3650,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** - * This is called when a container is going to temporarily detach a child - * that currently has focus, with + * @hide + */ + public void dispatchStartTemporaryDetach() { + onStartTemporaryDetach(); + } + + /** + * This is called when a container is going to temporarily detach a child, with * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}. * It will either be followed by {@link #onFinishTemporaryDetach()} or - * {@link #onDetachedFromWindow()} when the container is done. Generally - * this is currently only done ListView for a view with focus. + * {@link #onDetachedFromWindow()} when the container is done. */ public void onStartTemporaryDetach() { + removeUnsetPressCallback(); + } + + /** + * @hide + */ + public void dispatchFinishTemporaryDetach() { + onFinishTemporaryDetach(); } /** @@ -4362,6 +4375,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Remove the prepress detection timer. + */ + private void removeUnsetPressCallback() { + if ((mPrivateFlags & PRESSED) != 0 && mUnsetPressedState != null) { + setPressed(false); + removeCallbacks(mUnsetPressedState); + } + } + + /** * Remove the tap detection timer. */ private void removeTapCallback() { @@ -5886,6 +5909,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #onAttachedToWindow() */ protected void onDetachedFromWindow() { + removeUnsetPressCallback(); removeLongPressCallback(); destroyDrawingCache(); } @@ -7312,6 +7336,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Sets the background color for this view. * @param color the color of the background */ + @RemotableViewMethod public void setBackgroundColor(int color) { setBackgroundDrawable(new ColorDrawable(color)); } @@ -7322,6 +7347,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @param resid The identifier of the resource. * @attr ref android.R.styleable#View_background */ + @RemotableViewMethod public void setBackgroundResource(int resid) { if (resid != 0 && resid == mBackgroundResource) { return; @@ -8731,7 +8757,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean clampedX, boolean clampedY) { // Intentionally empty. } - + /** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. @@ -9182,12 +9208,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean mRecomputeGlobalAttributes; /** - * Set to true when attributes (like mKeepScreenOn) need to be - * recomputed. - */ - boolean mAttributesChanged; - - /** * Set during a traveral if any views want to keep the screen on. */ boolean mKeepScreenOn; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0663215..d05416d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1065,6 +1065,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } return false; } + + /** + * {@inheritDoc} + * + * @hide + */ + @Override + public void dispatchStartTemporaryDetach() { + super.dispatchStartTemporaryDetach(); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].dispatchStartTemporaryDetach(); + } + } + + /** + * {@inheritDoc} + * + * @hide + */ + @Override + public void dispatchFinishTemporaryDetach() { + super.dispatchFinishTemporaryDetach(); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].dispatchFinishTemporaryDetach(); + } + } /** * {@inheritDoc} diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 2aba60b..6c58461 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -57,8 +57,12 @@ public abstract class WindowOrientationListener { * {@link android.hardware.SensorManager SensorManager}). Use the default * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. + * + * This constructor is private since no one uses it and making it public would complicate + * things, since the lowpass filtering code depends on the actual sampling period, and there's + * no way to get the period from SensorManager based on the rate constant. */ - public WindowOrientationListener(Context context, int rate) { + private WindowOrientationListener(Context context, int rate) { mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); @@ -107,94 +111,179 @@ public abstract class WindowOrientationListener { } class SensorEventListenerImpl implements SensorEventListener { + // We work with all angles in degrees in this class. + private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); + + // Indices into SensorEvent.values private static final int _DATA_X = 0; private static final int _DATA_Y = 1; private static final int _DATA_Z = 2; - // Angle around x-asis that's considered almost too vertical. Beyond - // this angle will not result in any orientation changes. f phone faces uses, - // the device is leaning backward. - private static final int PIVOT_UPPER = 65; - // Angle about x-axis that's considered negative vertical. Beyond this - // angle will not result in any orientation changes. If phone faces uses, - // the device is leaning forward. - private static final int PIVOT_LOWER = -10; - static final int ROTATION_0 = 0; - static final int ROTATION_90 = 1; - static final int ROTATION_180 = 2; - static final int ROTATION_270 = 3; - int mRotation = ROTATION_0; - - // Threshold values defined for device rotation positions - // follow order ROTATION_0 .. ROTATION_270 - final int THRESHOLDS[][][] = new int[][][] { - {{60, 135}, {135, 225}, {225, 300}}, - {{0, 45}, {45, 135}, {135, 210}, {330, 360}}, - {{0, 45}, {45, 120}, {240, 315}, {315, 360}}, - {{0, 30}, {150, 225}, {225, 315}, {315, 360}} + + // Internal aliases for the four orientation states. ROTATION_0 = default portrait mode, + // ROTATION_90 = left side of device facing the sky, etc. + private static final int ROTATION_0 = 0; + private static final int ROTATION_90 = 1; + private static final int ROTATION_180 = 2; + private static final int ROTATION_270 = 3; + + // Current orientation state + private int mRotation = ROTATION_0; + + // Mapping our internal aliases into actual Surface rotation values + private final int[] SURFACE_ROTATIONS = new int[] {Surface.ROTATION_0, Surface.ROTATION_90, + Surface.ROTATION_180, Surface.ROTATION_270}; + + // Threshold ranges of orientation angle to transition into other orientation states. + // The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc. + // ROTATE_TO defines the orientation each threshold range transitions to, and must be kept + // in sync with this. + // The thresholds are nearly regular -- we generally transition about the halfway point + // between two states with a swing of 30 degreees for hysteresis. For ROTATION_180, + // however, we enforce stricter thresholds, pushing the thresholds 15 degrees closer to 180. + private final int[][][] THRESHOLDS = new int[][][] { + {{60, 165}, {165, 195}, {195, 300}}, + {{0, 45}, {45, 165}, {165, 195}, {330, 360}}, + {{0, 45}, {45, 135}, {225, 315}, {315, 360}}, + {{0, 30}, {165, 195}, {195, 315}, {315, 360}} }; - // Transform rotation ranges based on THRESHOLDS. This - // has to be in step with THESHOLDS - final int ROTATE_TO[][] = new int[][] { - {ROTATION_270, ROTATION_180, ROTATION_90}, - {ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_0}, - {ROTATION_0, ROTATION_270, ROTATION_90, ROTATION_0}, - {ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0} + // See THRESHOLDS + private final int[][] ROTATE_TO = new int[][] { + {ROTATION_270, ROTATION_180, ROTATION_90}, + {ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_0}, + {ROTATION_0, ROTATION_270, ROTATION_90, ROTATION_0}, + {ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0} }; - // Mapping into actual Surface rotation values - final int TRANSFORM_ROTATIONS[] = new int[]{Surface.ROTATION_0, - Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270}; + // Maximum absolute tilt angle at which to consider orientation changes. Beyond this (i.e. + // when screen is facing the sky or ground), we refuse to make any orientation changes. + private static final int MAX_TILT = 65; + + // Additional limits on tilt angle to transition to each new orientation. We ignore all + // vectors with tilt beyond MAX_TILT, but we can set stricter limits on transition to a + // particular orientation here. + private final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, MAX_TILT, 40, MAX_TILT}; + + // Between this tilt angle and MAX_TILT, we'll allow orientation changes, but we'll filter + // with a higher time constant, making us less sensitive to change. This primarily helps + // prevent momentary orientation changes when placing a device on a table from the side (or + // picking one up). + private static final int PARTIAL_TILT = 45; + + // Maximum allowable deviation of the magnitude of the sensor vector from that of gravity, + // in m/s^2. Beyond this, we assume the phone is under external forces and we can't trust + // the sensor data. However, under constantly vibrating conditions (think car mount), we + // still want to pick up changes, so rather than ignore the data, we filter it with a very + // high time constant. + private static final int MAX_DEVIATION_FROM_GRAVITY = 1; + + // Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL. There's no + // way to get this information from SensorManager. + // Note the actual period is generally 3-30ms larger than this depending on the device, but + // that's not enough to significantly skew our results. + private static final int SAMPLING_PERIOD_MS = 200; + + // The following time constants are all used in low-pass filtering the accelerometer output. + // See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for + // background. + + // When device is near-vertical (screen approximately facing the horizon) + private static final int DEFAULT_TIME_CONSTANT_MS = 200; + // When device is partially tilted towards the sky or ground + private static final int TILTED_TIME_CONSTANT_MS = 600; + // When device is under external acceleration, i.e. not just gravity. We heavily distrust + // such readings. + private static final int ACCELERATING_TIME_CONSTANT_MS = 5000; + + private static final float DEFAULT_LOWPASS_ALPHA = + (float) SAMPLING_PERIOD_MS / (DEFAULT_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS); + private static final float TILTED_LOWPASS_ALPHA = + (float) SAMPLING_PERIOD_MS / (TILTED_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS); + private static final float ACCELERATING_LOWPASS_ALPHA = + (float) SAMPLING_PERIOD_MS / (ACCELERATING_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS); + + // The low-pass filtered accelerometer data + private float[] mFilteredVector = new float[] {0, 0, 0}; int getCurrentRotation() { - return TRANSFORM_ROTATIONS[mRotation]; + return SURFACE_ROTATIONS[mRotation]; } - - private void calculateNewRotation(int orientation, int zyangle) { - if (localLOGV) Log.i(TAG, orientation + ", " + zyangle + ", " + mRotation); - int rangeArr[][] = THRESHOLDS[mRotation]; + + private void calculateNewRotation(int orientation, int tiltAngle) { + if (localLOGV) Log.i(TAG, orientation + ", " + tiltAngle + ", " + mRotation); + int thresholdRanges[][] = THRESHOLDS[mRotation]; int row = -1; - for (int i = 0; i < rangeArr.length; i++) { - if ((orientation >= rangeArr[i][0]) && (orientation < rangeArr[i][1])) { + for (int i = 0; i < thresholdRanges.length; i++) { + if (orientation >= thresholdRanges[i][0] && orientation < thresholdRanges[i][1]) { row = i; break; } } - if (row != -1) { - // Find new rotation based on current rotation value. - // This also takes care of irregular rotations as well. - int rotation = ROTATE_TO[mRotation][row]; - if (localLOGV) Log.i(TAG, " new rotation = " + rotation); - if (rotation != mRotation) { - mRotation = rotation; - // Trigger orientation change - onOrientationChanged(TRANSFORM_ROTATIONS[rotation]); - } + if (row == -1) return; // no matching transition + + int rotation = ROTATE_TO[mRotation][row]; + if (tiltAngle > MAX_TRANSITION_TILT[rotation]) { + // tilted too far flat to go to this rotation + return; } + + if (localLOGV) Log.i(TAG, " new rotation = " + rotation); + mRotation = rotation; + onOrientationChanged(SURFACE_ROTATIONS[rotation]); + } + + private float lowpassFilter(float newValue, float oldValue, float alpha) { + return alpha * newValue + (1 - alpha) * oldValue; + } + + private float vectorMagnitude(float x, float y, float z) { + return (float) Math.sqrt(x*x + y*y + z*z); + } + + /** + * Absolute angle between upVector and the x-y plane (the plane of the screen), in [0, 90]. + * 90 degrees = screen facing the sky or ground. + */ + private float tiltAngle(float z, float magnitude) { + return Math.abs((float) Math.asin(z / magnitude) * RADIANS_TO_DEGREES); } public void onSensorChanged(SensorEvent event) { - float[] values = event.values; - float X = values[_DATA_X]; - float Y = values[_DATA_Y]; - float Z = values[_DATA_Z]; - float OneEightyOverPi = 57.29577957855f; - float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z); - float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi; - if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) { - // Check orientation only if the phone is flat enough - // Don't trust the angle if the magnitude is small compared to the y value - float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi; - int orientation = 90 - Math.round(angle); - // normalize to 0 - 359 range - while (orientation >= 360) { - orientation -= 360; - } - while (orientation < 0) { - orientation += 360; - } - calculateNewRotation(orientation, Math.round(zyangle)); + // the vector given in the SensorEvent points straight up (towards the sky) under ideal + // conditions (the phone is not accelerating). i'll call this upVector elsewhere. + float x = event.values[_DATA_X]; + float y = event.values[_DATA_Y]; + float z = event.values[_DATA_Z]; + float magnitude = vectorMagnitude(x, y, z); + float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY); + float tiltAngle = tiltAngle(z, magnitude); + + float alpha = DEFAULT_LOWPASS_ALPHA; + if (tiltAngle > MAX_TILT) { + return; + } else if (deviation > MAX_DEVIATION_FROM_GRAVITY) { + alpha = ACCELERATING_LOWPASS_ALPHA; + } else if (tiltAngle > PARTIAL_TILT) { + alpha = TILTED_LOWPASS_ALPHA; + } + + x = mFilteredVector[0] = lowpassFilter(x, mFilteredVector[0], alpha); + y = mFilteredVector[1] = lowpassFilter(y, mFilteredVector[1], alpha); + z = mFilteredVector[2] = lowpassFilter(z, mFilteredVector[2], alpha); + magnitude = vectorMagnitude(x, y, z); + tiltAngle = tiltAngle(z, magnitude); + + // Angle between the x-y projection of upVector and the +y-axis, increasing + // counter-clockwise. + // 0 degrees = speaker end towards the sky + // 90 degrees = left edge of device towards the sky + float orientationAngle = (float) Math.atan2(-x, y) * RADIANS_TO_DEGREES; + int orientation = Math.round(orientationAngle); + // atan2 returns (-180, 180]; normalize to [0, 360) + if (orientation < 0) { + orientation += 360; } + calculateNewRotation(orientation, Math.round(tiltAngle)); } public void onAccuracyChanged(Sensor sensor, int accuracy) { @@ -208,13 +297,12 @@ public abstract class WindowOrientationListener { public boolean canDetectOrientation() { return mSensor != null; } - + /** * Called when the rotation view of the device has changed. - * Can be either Surface.ROTATION_90 or Surface.ROTATION_0. - * @param rotation The new orientation of the device. * - * @see #ORIENTATION_UNKNOWN + * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. + * @see Surface */ abstract public void onOrientationChanged(int rotation); } diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index 87cab3c..647556b 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -18,6 +18,7 @@ package android.webkit; import android.content.Context; import android.net.http.Headers; +import android.net.http.HttpDateTime; import android.os.FileUtils; import android.util.Log; import java.io.File; @@ -30,7 +31,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Map; -import com.android.common.HttpDateTime; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 94bedde..1d28731 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -18,9 +18,9 @@ package android.webkit; import android.net.ParseException; import android.net.WebAddress; +import android.net.http.HttpDateTime; import android.util.Log; -import com.android.common.HttpDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -150,8 +150,13 @@ public final class CookieManager { } boolean exactMatch(Cookie in) { + // An exact match means that domain, path, and name are equal. If + // both values are null, the cookies match. If both values are + // non-null, the cookies match. If one value is null and the other + // is non-null, the cookies do not match (i.e. "foo=;" and "foo;") + boolean valuesMatch = !((value == null) ^ (in.value == null)); return domain.equals(in.domain) && path.equals(in.path) && - name.equals(in.name); + name.equals(in.name) && valuesMatch; } boolean domainMatch(String urlHost) { @@ -206,17 +211,29 @@ public final class CookieManager { // As Set is not modified if the two objects are same, we do want to // assign different value for each cookie. int diff = cookie2.path.length() - cookie1.path.length(); - if (diff == 0) { - diff = cookie2.domain.length() - cookie1.domain.length(); - if (diff == 0) { - diff = cookie2.name.hashCode() - cookie1.name.hashCode(); - if (diff == 0) { - Log.w(LOGTAG, "Found two cookies with the same value." + - "cookie1=" + cookie1 + " , cookie2=" + cookie2); - } - } + if (diff != 0) return diff; + + diff = cookie2.domain.length() - cookie1.domain.length(); + if (diff != 0) return diff; + + diff = cookie2.name.hashCode() - cookie1.name.hashCode(); + if (diff != 0) return diff; + + // If cookie2 has a null value, it should come later in + // the list. + if (cookie2.value == null) { + return -1; + } else if (cookie1.value == null) { + // Now we know that cookie2 does not have a null value, if + // cookie1 has a null value, place it later in the list. + return 1; } - return diff; + + // cookie1 and cookie2 both have non-null values so we emit a + // warning and treat them as the same. + Log.w(LOGTAG, "Found two cookies with the same value." + + "cookie1=" + cookie1 + " , cookie2=" + cookie2); + return 0; } } @@ -459,8 +476,10 @@ public final class CookieManager { } ret.append(cookie.name); - ret.append(EQUAL); - ret.append(cookie.value); + if (cookie.value != null) { + ret.append(EQUAL); + ret.append(cookie.value); + } } if (ret.length() > 0) { @@ -634,7 +653,10 @@ public final class CookieManager { byteCount += cookie.domain.length() + cookie.path.length() + cookie.name.length() - + cookie.value.length() + 14; + + (cookie.value != null + ? cookie.value.length() + : 0) + + 14; count++; } } else { @@ -779,38 +801,45 @@ public final class CookieManager { */ int semicolonIndex = cookieString.indexOf(SEMICOLON, index); int equalIndex = cookieString.indexOf(EQUAL, index); - if (equalIndex == -1) { - // bad format, force return - break; - } - if (semicolonIndex > -1 && semicolonIndex < equalIndex) { - // empty cookie, like "; path=/", return - break; - } cookie = new Cookie(host, path); - cookie.name = cookieString.substring(index, equalIndex); - if (cookieString.charAt(equalIndex + 1) == QUOTATION) { - index = cookieString.indexOf(QUOTATION, equalIndex + 2); - if (index == -1) { - // bad format, force return - break; + + // Cookies like "testcookie; path=/;" are valid and used + // (lovefilm.se). Check for equal as in the string "testcookie" + // Check for equalIndex == -1 as in the string "testcookie;" + if (semicolonIndex <= equalIndex || equalIndex == -1) { + // Fix up the index in case we have a string like "testcookie" + if (semicolonIndex == -1) { + semicolonIndex = length; } - } - semicolonIndex = cookieString.indexOf(SEMICOLON, index); - if (semicolonIndex == -1) { - semicolonIndex = length; - } - if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) { - // cookie is too big, trim it - cookie.value = cookieString.substring(equalIndex + 1, - equalIndex + MAX_COOKIE_LENGTH); - } else if (equalIndex + 1 == semicolonIndex - || semicolonIndex < equalIndex) { - // these are unusual case like foo=; and foo; path=/ - cookie.value = ""; + cookie.name = cookieString.substring(index, semicolonIndex); + cookie.value = null; } else { - cookie.value = cookieString.substring(equalIndex + 1, - semicolonIndex); + cookie.name = cookieString.substring(index, equalIndex); + if (cookieString.charAt(equalIndex + 1) == QUOTATION) { + index = cookieString.indexOf(QUOTATION, equalIndex + 2); + if (index == -1) { + // bad format, force return + break; + } + } + // Get the semicolon index again in case it was contained within + // the quotations. + semicolonIndex = cookieString.indexOf(SEMICOLON, index); + if (semicolonIndex == -1) { + semicolonIndex = length; + } + if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) { + // cookie is too big, trim it + cookie.value = cookieString.substring(equalIndex + 1, + equalIndex + 1 + MAX_COOKIE_LENGTH); + } else if (equalIndex + 1 == semicolonIndex + || semicolonIndex < equalIndex) { + // this is an unusual case like foo=; + cookie.value = ""; + } else { + cookie.value = cookieString.substring(equalIndex + 1, + semicolonIndex); + } } // get attributes index = semicolonIndex; diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java index cdcb662..df7d0c4 100644 --- a/core/java/android/webkit/PluginManager.java +++ b/core/java/android/webkit/PluginManager.java @@ -165,6 +165,7 @@ public class PluginManager { continue; } +/* temporarily disable signatures checking // check to ensure the plugin is properly signed Signature signatures[] = pkgInfo.signatures; if (signatures == null) { @@ -184,7 +185,7 @@ public class PluginManager { continue; } } - +*/ // determine the type of plugin from the manifest if (serviceInfo.metaData == null) { Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined"); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index adae0cb..d29d6f3 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -213,7 +213,6 @@ public class WebView extends AbsoluteLayout static private final boolean AUTO_REDRAW_HACK = false; // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK private boolean mAutoRedraw; - private int mRootLayer; // C++ pointer to the root layer static final String LOGTAG = "webview"; @@ -375,6 +374,7 @@ public class WebView extends AbsoluteLayout private static final int PREVENT_DRAG_NO = 0; private static final int PREVENT_DRAG_MAYBE_YES = 1; private static final int PREVENT_DRAG_YES = 2; + private static final int PREVENT_DRAG_CANCEL = 3; private int mPreventDrag = PREVENT_DRAG_NO; // by default mPreventLongPress is false. If it is true, long press event @@ -617,6 +617,12 @@ public class WebView extends AbsoluteLayout private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK private boolean mSnapPositive; + // keep these in sync with their counterparts in WebView.cpp + private static final int DRAW_EXTRAS_NONE = 0; + private static final int DRAW_EXTRAS_FIND = 1; + private static final int DRAW_EXTRAS_SELECTION = 2; + private static final int DRAW_EXTRAS_CURSOR_RING = 3; + // Used to match key downs and key ups private boolean mGotKeyDown; @@ -1305,6 +1311,7 @@ public class WebView extends AbsoluteLayout // onSizeChanged() is called, the rest will be set // correctly mActualScale = scale; + mInvActualScale = 1 / scale; mTextWrapScale = b.getFloat("textwrapScale", scale); mInZoomOverview = b.getBoolean("overview"); invalidate(); @@ -3115,7 +3122,7 @@ public class WebView extends AbsoluteLayout int mScrollY; int mWidth; int mHeight; - float mScale; + float mInvScale; } private Metrics getViewMetrics() { @@ -3124,23 +3131,22 @@ public class WebView extends AbsoluteLayout metrics.mScrollY = computeVerticalScrollOffset(); metrics.mWidth = getWidth(); metrics.mHeight = getHeight() - getVisibleTitleHeight(); - metrics.mScale = mActualScale; + metrics.mInvScale = mInvActualScale; return metrics; } - private void drawLayers(Canvas canvas) { - if (mRootLayer != 0) { - // Currently for each draw we compute the animation values; - // We may in the future decide to do that independently. - if (nativeEvaluateLayersAnimations(mRootLayer)) { - // If we have unfinished (or unstarted) animations, - // we ask for a repaint. - invalidate(); - } - - // We can now draw the layers. - nativeDrawLayers(mRootLayer, canvas); + private void drawExtras(Canvas canvas, int extras) { + // If mNativeClass is 0, we should not reach here, so we do not + // need to check it again. + // Currently for each draw we compute the animation values; + // We may in the future decide to do that independently. + if (nativeEvaluateLayersAnimations()) { + // If we have unfinished (or unstarted) animations, + // we ask for a repaint. + invalidate(); } + + nativeDrawExtras(canvas, extras); } private void drawCoreAndCursorRing(Canvas canvas, int color, @@ -3148,7 +3154,6 @@ public class WebView extends AbsoluteLayout if (mDrawHistory) { canvas.scale(mActualScale, mActualScale); canvas.drawPicture(mHistoryPicture); - drawLayers(canvas); return; } @@ -3227,27 +3232,29 @@ public class WebView extends AbsoluteLayout mWebViewCore.drawContentPicture(canvas, color, (animateZoom || mPreviewZoomOnly), animateScroll); - boolean cursorIsInLayer = nativeCursorIsInLayer(); - if (drawCursorRing && !cursorIsInLayer) { - nativeDrawCursorRing(canvas); - } - // When the FindDialog is up, only draw the matches if we are not in - // the process of scrolling them into view. - if (mFindIsUp && !animateScroll) { - nativeDrawMatches(canvas); - } - drawLayers(canvas); - if (mNativeClass == 0) return; - if (mShiftIsPressed && !(animateZoom || mPreviewZoomOnly)) { - if (mTouchSelection || mExtendSelection) { - nativeDrawSelectionRegion(canvas); - } - if (!mTouchSelection) { - nativeDrawSelectionPointer(canvas, mInvActualScale, mSelectX, - mSelectY - getTitleHeight(), mExtendSelection); + // decide which adornments to draw + int extras = DRAW_EXTRAS_NONE; + if (mFindIsUp) { + // When the FindDialog is up, only draw the matches if we are not in + // the process of scrolling them into view. + if (!animateScroll) { + extras = DRAW_EXTRAS_FIND; + } + } else if (mShiftIsPressed) { + if (!animateZoom && !mPreviewZoomOnly) { + extras = DRAW_EXTRAS_SELECTION; + nativeSetSelectionRegion(mTouchSelection || mExtendSelection); + nativeSetSelectionPointer(!mTouchSelection, mInvActualScale, + mSelectX, mSelectY - getTitleHeight(), + mExtendSelection); } } else if (drawCursorRing) { + extras = DRAW_EXTRAS_CURSOR_RING; + } + drawExtras(canvas, extras); + + if (extras == DRAW_EXTRAS_CURSOR_RING) { if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) { mTouchMode = TOUCH_SHORTPRESS_MODE; HitTestResult hitTest = getHitTestResult(); @@ -3258,7 +3265,6 @@ public class WebView extends AbsoluteLayout LONG_PRESS_TIMEOUT); } } - if (cursorIsInLayer) nativeDrawCursorRing(canvas); } if (mFocusSizeChanged) { mFocusSizeChanged = false; @@ -3351,23 +3357,32 @@ public class WebView extends AbsoluteLayout InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + // bring it back to the default scale so that user can enter text + boolean zoom = mActualScale < mDefaultScale; + if (zoom) { + mInZoomOverview = false; + mZoomCenterX = mLastTouchX; + mZoomCenterY = mLastTouchY; + // do not change text wrap scale so that there is no reflow + setNewZoomScale(mDefaultScale, false, false); + } if (isTextView) { rebuildWebTextView(); - if (!inEditingMode()) return; - imm.showSoftInput(mWebTextView, 0); - // bring it back to the default scale so that user can enter text - if (mActualScale < mDefaultScale) { - mInZoomOverview = false; - mZoomCenterX = mLastTouchX; - mZoomCenterY = mLastTouchY; - // do not change text wrap scale so that there is no reflow - setNewZoomScale(mDefaultScale, false, false); - didUpdateTextViewBounds(true); + if (inEditingMode()) { + mWebTextView.setDefaultSelection(); + imm.showSoftInput(mWebTextView, 0); + if (zoom) { + didUpdateTextViewBounds(true); + } + return; } } - else { // used by plugins - imm.showSoftInput(this, 0); - } + // Used by plugins. + // Also used if the navigation cache is out of date, and + // does not recognize that a textfield is in focus. In that + // case, use WebView as the targeted view. + // see http://b/issue?id=2457459 + imm.showSoftInput(this, 0); } // Called by WebKit to instruct the UI to hide the keyboard @@ -3672,6 +3687,7 @@ public class WebView extends AbsoluteLayout // might be. Check it, and if so, hand over to the WebTextView. rebuildWebTextView(); if (inEditingMode()) { + mWebTextView.setDefaultSelection(); return mWebTextView.dispatchKeyEvent(event); } } @@ -4429,8 +4445,11 @@ public class WebView extends AbsoluteLayout } // pass the touch events from UI thread to WebCore thread - if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE - || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) { + if (mForwardTouchEvents + && (action != MotionEvent.ACTION_MOVE || eventTime + - mLastSentTouchTime > mCurrentTouchInterval) + && (action == MotionEvent.ACTION_DOWN + || mPreventDrag != PREVENT_DRAG_CANCEL)) { WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); ted.mAction = action; ted.mX = viewToContentX((int) x + mScrollX); @@ -5711,12 +5730,17 @@ public class WebView extends AbsoluteLayout break; } case SWITCH_TO_SHORTPRESS: { - // if mPreventDrag is not confirmed, treat it as no so that - // it won't block panning the page. + // if mPreventDrag is not confirmed, cancel it so that it + // won't block panning the page. if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { - mPreventDrag = PREVENT_DRAG_NO; + mPreventDrag = PREVENT_DRAG_CANCEL; mPreventLongPress = false; mPreventDoubleTap = false; + // remove the pending TOUCH_EVENT and send a cancel + mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); + WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); + ted.mAction = MotionEvent.ACTION_CANCEL; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } if (mTouchMode == TOUCH_INIT_MODE) { mTouchMode = mFullScreenHolder == null @@ -5743,7 +5767,7 @@ public class WebView extends AbsoluteLayout // don't set it. ted.mMetaState = 0; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - } else if (mPreventDrag == PREVENT_DRAG_NO) { + } else if (mPreventDrag != PREVENT_DRAG_YES) { mTouchMode = TOUCH_DONE_MODE; if (mFullScreenHolder == null) { performLongClick(); @@ -5754,13 +5778,18 @@ public class WebView extends AbsoluteLayout } case RELEASE_SINGLE_TAP: { if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { - // if mPreventDrag is not confirmed, treat it as - // no so that it won't block tap. - mPreventDrag = PREVENT_DRAG_NO; + // if mPreventDrag is not confirmed, cancel it so that + // it won't block panning the page. + mPreventDrag = PREVENT_DRAG_CANCEL; mPreventLongPress = false; mPreventDoubleTap = false; + // remove the pending TOUCH_EVENT and send a cancel + mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); + WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); + ted.mAction = MotionEvent.ACTION_CANCEL; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } - if (mPreventDrag == PREVENT_DRAG_NO) { + if (mPreventDrag != PREVENT_DRAG_YES) { mTouchMode = TOUCH_DONE_MODE; doShortPress(); } @@ -5979,12 +6008,7 @@ public class WebView extends AbsoluteLayout break; } case SET_ROOT_LAYER_MSG_ID: { - int oldLayer = mRootLayer; - mRootLayer = msg.arg1; - nativeSetRootLayer(mRootLayer); - if (oldLayer > 0) { - nativeDestroyLayer(oldLayer); - } + nativeSetRootLayer(msg.arg1); invalidate(); break; } @@ -6744,7 +6768,6 @@ public class WebView extends AbsoluteLayout /* package */ native boolean nativeCursorMatchesFocus(); private native boolean nativeCursorIntersects(Rect visibleRect); private native boolean nativeCursorIsAnchor(); - private native boolean nativeCursorIsInLayer(); private native boolean nativeCursorIsTextInput(); private native Point nativeCursorPosition(); private native String nativeCursorText(); @@ -6755,14 +6778,8 @@ public class WebView extends AbsoluteLayout private native boolean nativeCursorWantsKeyEvents(); private native void nativeDebugDump(); private native void nativeDestroy(); - private native void nativeDrawCursorRing(Canvas content); - private native void nativeDestroyLayer(int layer); - private native boolean nativeEvaluateLayersAnimations(int layer); - private native void nativeDrawLayers(int layer, Canvas canvas); - private native void nativeDrawMatches(Canvas canvas); - private native void nativeDrawSelectionPointer(Canvas content, - float scale, int x, int y, boolean extendSelection); - private native void nativeDrawSelectionRegion(Canvas content); + private native boolean nativeEvaluateLayersAnimations(); + private native void nativeDrawExtras(Canvas canvas, int extra); private native void nativeDumpDisplayTree(String urlOrNull); private native int nativeFindAll(String findLower, String findUpper); private native void nativeFindNext(boolean forward); @@ -6809,6 +6826,9 @@ public class WebView extends AbsoluteLayout private native void nativeSetFollowedLink(boolean followed); private native void nativeSetHeightCanMeasure(boolean measure); private native void nativeSetRootLayer(int layer); + private native void nativeSetSelectionPointer(boolean set, + float scale, int x, int y, boolean extendSelection); + private native void nativeSetSelectionRegion(boolean set); private native int nativeTextGeneration(); // Never call this version except by updateCachedTextfield(String) - // we always want to pass in our generation number. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index a79bbee..9ddfeff 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1314,7 +1314,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te position, -1); } } else { - isScrap[0] = true; + isScrap[0] = true; + child.dispatchFinishTemporaryDetach(); } } else { child = mAdapter.getView(position, null, this); @@ -1960,7 +1961,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (getHeight() > 0 && getChildCount() > 0) { // We do not lose focus initiating a touch (since AbsListView is focusable in // touch mode). Force an initial layout to get rid of the selection. - mLayoutMode = LAYOUT_NORMAL; layoutChildren(); } } else { @@ -3118,7 +3118,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te void hideSelector() { if (mSelectedPosition != INVALID_POSITION) { - mResurrectToPosition = mSelectedPosition; + if (mLayoutMode != LAYOUT_SPECIFIC) { + mResurrectToPosition = mSelectedPosition; + } if (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) { mResurrectToPosition = mNextSelectedPosition; } @@ -4144,8 +4146,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (mViewTypeCount == 1) { + scrap.dispatchStartTemporaryDetach(); mCurrentScrap.add(scrap); } else { + scrap.dispatchStartTemporaryDetach(); mScrapViews[viewType].add(scrap); } @@ -4164,7 +4168,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te ArrayList<View> scrapViews = mCurrentScrap; final int count = activeViews.length; - for (int i = 0; i < count; ++i) { + for (int i = count - 1; i >= 0; i--) { final View victim = activeViews[i]; if (victim != null) { int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType; @@ -4180,6 +4184,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (multipleScraps) { scrapViews = mScrapViews[whichScrap]; } + victim.dispatchStartTemporaryDetach(); scrapViews.add(victim); if (hasListener) { diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java index c939e3f..2b3b98d 100644 --- a/core/java/android/widget/AbsSpinner.java +++ b/core/java/android/widget/AbsSpinner.java @@ -28,8 +28,6 @@ import android.util.AttributeSet; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; -import android.view.animation.Interpolator; - /** * An abstract base class for spinner widgets. SDK users will probably not @@ -38,24 +36,21 @@ import android.view.animation.Interpolator; * @attr ref android.R.styleable#AbsSpinner_entries */ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { - SpinnerAdapter mAdapter; int mHeightMeasureSpec; int mWidthMeasureSpec; boolean mBlockLayoutRequests; + int mSelectionLeftPadding = 0; int mSelectionTopPadding = 0; int mSelectionRightPadding = 0; int mSelectionBottomPadding = 0; - Rect mSpinnerPadding = new Rect(); - View mSelectedView = null; - Interpolator mInterpolator; + final Rect mSpinnerPadding = new Rect(); - RecycleBin mRecycler = new RecycleBin(); + final RecycleBin mRecycler = new RecycleBin(); private DataSetObserver mDataSetObserver; - /** Temporary frame to hold a child View's frame rectangle */ private Rect mTouchFrame; @@ -95,7 +90,6 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { setWillNotDraw(false); } - /** * The Adapter is used to provide the data which backs this Spinner. * It also provides methods to transform spinner items based on their position @@ -190,7 +184,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { boolean needsMeasuring = true; int selectedPosition = getSelectedItemPosition(); - if (selectedPosition >= 0 && mAdapter != null) { + if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) { // Try looking in the recycler. (Maybe we were measured once already) View view = mRecycler.get(selectedPosition); if (view == null) { @@ -237,7 +231,6 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { mWidthMeasureSpec = widthMeasureSpec; } - int getChildHeight(View child) { return child.getMeasuredHeight(); } @@ -254,26 +247,17 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { } void recycleAllViews() { - int childCount = getChildCount(); + final int childCount = getChildCount(); final AbsSpinner.RecycleBin recycleBin = mRecycler; + final int position = mFirstPosition; // All views go in recycler - for (int i=0; i<childCount; i++) { + for (int i = 0; i < childCount; i++) { View v = getChildAt(i); - int index = mFirstPosition + i; + int index = position + i; recycleBin.put(index, v); } } - - @Override - void handleDataChanged() { - // FIXME -- this is called from both measure and layout. - // This is harmless right now, but we don't want to do redundant work if - // this gets more complicated - super.handleDataChanged(); - } - - /** * Jump directly to a specific item in the adapter data. @@ -284,7 +268,6 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { position <= mFirstPosition + getChildCount() - 1; setSelectionInt(position, shouldAnimate); } - @Override public void setSelection(int position) { @@ -335,8 +318,6 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { } } - - @Override public SpinnerAdapter getAdapter() { return mAdapter; @@ -452,7 +433,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { } class RecycleBin { - private SparseArray<View> mScrapHeap = new SparseArray<View>(); + private final SparseArray<View> mScrapHeap = new SparseArray<View>(); public void put(int position, View v) { mScrapHeap.put(position, v); @@ -469,12 +450,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { } return result; } - - View peek(int position) { - // System.out.print("Looking for " + position); - return mScrapHeap.get(position); - } - + void clear() { final SparseArray<View> scrapHeap = mScrapHeap; final int count = scrapHeap.size(); diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index aa9062b..bf63607 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -24,6 +24,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; @@ -73,7 +74,8 @@ public class CheckedTextView extends TextView implements Checkable { public void toggle() { setChecked(!mChecked); } - + + @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 98b0976..bf02ad3 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -26,6 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; /** @@ -98,6 +99,7 @@ public abstract class CompoundButton extends Button implements Checkable { return super.performClick(); } + @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java index ccce7c1..9c43e9b 100644 --- a/core/java/android/widget/ExpandableListConnector.java +++ b/core/java/android/widget/ExpandableListConnector.java @@ -25,7 +25,6 @@ import android.view.ViewGroup; import java.util.ArrayList; import java.util.Collections; -import java.util.List; /* * Implementation notes: @@ -637,7 +636,7 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { // Check to see if it's already expanded if (posMetadata.groupMetadata != null) return false; - /* Restrict number of exp groups to mMaxExpGroupCount */ + /* Restrict number of expanded groups to mMaxExpGroupCount */ if (mExpGroupMetadataList.size() >= mMaxExpGroupCount) { /* Collapse a group */ // TODO: Collapse something not on the screen instead of the first one? diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 9fcb829..bd07e1f 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -632,6 +632,8 @@ public class LinearLayout extends ViewGroup { final boolean baselineAligned = mBaselineAligned; final boolean useLargestChild = mUseLargestChild; + + final boolean isExactly = widthMode == MeasureSpec.EXACTLY; int largestChildWidth = Integer.MIN_VALUE; @@ -658,14 +660,19 @@ public class LinearLayout extends ViewGroup { // Optimization: don't bother measuring children who are going to use // leftover space. These views will get measured again down below if // there is any leftover space. - final int totalLength = mTotalLength; - mTotalLength = Math.max(totalLength, totalLength + lp.leftMargin + lp.rightMargin); + if (isExactly) { + mTotalLength += lp.leftMargin + lp.rightMargin; + } else { + final int totalLength = mTotalLength; + mTotalLength = Math.max(totalLength, totalLength + + lp.leftMargin + lp.rightMargin); + } // Baseline alignment requires to measure widgets to obtain the - // baseline offset (in particular for TextViews). - // The following defeats the optimization mentioned above. - // Allow the child to use as much space as it wants because we - // can shrink things later (and re-measure). + // baseline offset (in particular for TextViews). The following + // defeats the optimization mentioned above. Allow the child to + // use as much space as it wants because we can shrink things + // later (and re-measure). if (baselineAligned) { final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); child.measure(freeSpec, freeSpec); @@ -695,9 +702,14 @@ public class LinearLayout extends ViewGroup { } final int childWidth = child.getMeasuredWidth(); - final int totalLength = mTotalLength; - mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin + - lp.rightMargin + getNextLocationOffset(child)); + if (isExactly) { + mTotalLength += childWidth + lp.leftMargin + lp.rightMargin + + getNextLocationOffset(child); + } else { + final int totalLength = mTotalLength; + mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin + + lp.rightMargin + getNextLocationOffset(child)); + } if (useLargestChild) { largestChildWidth = Math.max(childWidth, largestChildWidth); @@ -782,9 +794,14 @@ public class LinearLayout extends ViewGroup { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); - final int totalLength = mTotalLength; - mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + - lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); + if (isExactly) { + mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin + + getNextLocationOffset(child); + } else { + final int totalLength = mTotalLength; + mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + + lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); + } } } @@ -854,9 +871,14 @@ public class LinearLayout extends ViewGroup { } } - final int totalLength = mTotalLength; - mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + - lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); + if (isExactly) { + mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + + getNextLocationOffset(child); + } else { + final int totalLength = mTotalLength; + mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + + lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); + } boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT; diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 2feed03..8d688a5 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1485,7 +1485,6 @@ public class ListView extends AbsListView { } // Clear out old views - //removeAllViewsInLayout(); detachAllViewsFromParent(); switch (mLayoutMode) { diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 1dcb203..202e658 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -36,6 +36,7 @@ import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; +import android.view.ViewDebug; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -335,6 +336,7 @@ public class ProgressBar extends View { * * @return true if the progress bar is in indeterminate mode */ + @ViewDebug.ExportedProperty public synchronized boolean isIndeterminate() { return mIndeterminate; } @@ -607,6 +609,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ + @ViewDebug.ExportedProperty public synchronized int getProgress() { return mIndeterminate ? 0 : mProgress; } @@ -623,6 +626,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ + @ViewDebug.ExportedProperty public synchronized int getSecondaryProgress() { return mIndeterminate ? 0 : mSecondaryProgress; } @@ -636,6 +640,7 @@ public class ProgressBar extends View { * @see #getProgress() * @see #getSecondaryProgress() */ + @ViewDebug.ExportedProperty public synchronized int getMax() { return mMax; } @@ -691,8 +696,7 @@ public class ProgressBar extends View { * <p>Start the indeterminate progress animation.</p> */ void startAnimation() { - int visibility = getVisibility(); - if (visibility != VISIBLE) { + if (getVisibility() != VISIBLE) { return; } @@ -766,7 +770,7 @@ public class ProgressBar extends View { // let's be nice with the UI thread if (v == GONE || v == INVISIBLE) { stopAnimation(); - } else if (v == VISIBLE) { + } else { startAnimation(); } } @@ -774,6 +778,20 @@ public class ProgressBar extends View { } @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + + if (mIndeterminate) { + // let's be nice with the UI thread + if (visibility == GONE || visibility == INVISIBLE) { + stopAnimation(); + } else { + startAnimation(); + } + } + } + + @Override public void invalidateDrawable(Drawable dr) { if (!mInDrawing) { if (verifyDrawable(dr)) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 52ed11d..fd24058 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; import android.view.FocusFinder; import android.view.KeyEvent; import android.view.MotionEvent; @@ -51,6 +52,8 @@ import java.util.List; * <p>ScrollView only supports vertical scrolling. */ public class ScrollView extends FrameLayout { + private static final String TAG = "ScrollView"; + static final int ANIMATED_SCROLL_GAP = 250; static final float MAX_SCROLL_FACTOR = 0.5f; @@ -112,6 +115,18 @@ public class ScrollView extends FrameLayout { private int mTouchSlop; private int mMinimumVelocity; private int mMaximumVelocity; + + /** + * ID of the active pointer. This is used to retain consistency during + * drags/flings if multiple pointers are used. + */ + private int mActivePointerId = INVALID_POINTER; + + /** + * Sentinel value for no current active pointer. + * Used by {@link #mActivePointerId}. + */ + private static final int INVALID_POINTER = -1; public ScrollView(Context context) { this(context, null); @@ -360,6 +375,17 @@ public class ScrollView extends FrameLayout { return handled; } + private boolean inChild(int x, int y) { + if (getChildCount() > 0) { + final View child = getChildAt(0); + return !(y < child.getTop() + || y >= child.getBottom() + || x < child.getLeft() + || x >= child.getRight()); + } + return false; + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* @@ -378,10 +404,8 @@ public class ScrollView extends FrameLayout { return true; } - final float y = ev.getY(); - - switch (action) { - case MotionEvent.ACTION_MOVE: + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. @@ -391,16 +415,29 @@ public class ScrollView extends FrameLayout { * Locally do absolute value. mLastMotionY is set to the y value * of the down event. */ + final int pointerIndex = ev.findPointerIndex(mActivePointerId); + final float y = ev.getY(pointerIndex); final int yDiff = (int) Math.abs(y - mLastMotionY); if (yDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionY = y; } break; + } - case MotionEvent.ACTION_DOWN: - /* Remember location of down touch */ + case MotionEvent.ACTION_DOWN: { + final float y = ev.getY(); + if (!inChild((int)ev.getX(), (int)y)) { + mIsBeingDragged = false; + break; + } + + /* + * Remember location of down touch. + * ACTION_DOWN always refers to pointer index 0. + */ mLastMotionY = y; + mActivePointerId = ev.getPointerId(0); /* * If being flinged and user touches the screen, initiate drag; @@ -409,11 +446,16 @@ public class ScrollView extends FrameLayout { */ mIsBeingDragged = !mScroller.isFinished(); break; + } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: /* Release the drag */ mIsBeingDragged = false; + mActivePointerId = INVALID_POINTER; + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); break; } @@ -439,10 +481,9 @@ public class ScrollView extends FrameLayout { mVelocityTracker.addMovement(ev); final int action = ev.getAction(); - final float y = ev.getY(); - switch (action) { - case MotionEvent.ACTION_DOWN: + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: { /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. @@ -451,41 +492,78 @@ public class ScrollView extends FrameLayout { mScroller.abortAnimation(); } + final float y = ev.getY(); + if (!(mIsBeingDragged = inChild((int)ev.getX(), (int)y))) { + return false; + } + // Remember where the motion event started mLastMotionY = y; + mActivePointerId = ev.getPointerId(0); break; + } case MotionEvent.ACTION_MOVE: - // Scroll to follow the motion event - final int deltaY = (int) (mLastMotionY - y); - mLastMotionY = y; + if (mIsBeingDragged) { + // Scroll to follow the motion event + final int activePointerIndex = ev.findPointerIndex(mActivePointerId); + final float y = ev.getY(activePointerIndex); + final int deltaY = (int) (mLastMotionY - y); + mLastMotionY = y; - overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(), - 0, getOverscrollMax()); + overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(), + 0, getOverscrollMax()); + } break; - case MotionEvent.ACTION_UP: - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int initialVelocity = (int) velocityTracker.getYVelocity(); - - if (getChildCount() > 0) { - if ((Math.abs(initialVelocity) > mMinimumVelocity)) { - fling(-initialVelocity); - } else { - final int bottom = getScrollRange(); - if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, bottom)) { - invalidate(); + case MotionEvent.ACTION_UP: + if (mIsBeingDragged) { + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); + + if (getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity)) { + fling(-initialVelocity); + } else { + final int bottom = getScrollRange(); + if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, bottom)) { + invalidate(); + } } } - } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; + mActivePointerId = INVALID_POINTER; + mIsBeingDragged = false; + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } } + break; + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; } return true; } + private void onSecondaryPointerUp(MotionEvent ev) { + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> + MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // This was our active pointer going up. Choose a new + // active pointer and adjust accordingly. + // TODO: Make this decision more intelligent. + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastMotionY = ev.getY(newPointerIndex); + mActivePointerId = ev.getPointerId(newPointerIndex); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } + } + } + @Override protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7ba0fa1..951563a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -198,6 +198,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mFreezesText; private boolean mFrozenWithFocus; private boolean mTemporaryDetach; + private boolean mDispatchTemporaryDetach; private boolean mEatTouchRelease = false; private boolean mScrolled = false; @@ -5731,6 +5732,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionStart}. */ + @ViewDebug.ExportedProperty public int getSelectionStart() { return Selection.getSelectionStart(getText()); } @@ -5738,6 +5740,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionEnd}. */ + @ViewDebug.ExportedProperty public int getSelectionEnd() { return Selection.getSelectionEnd(getText()); } @@ -6369,14 +6372,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBlink.postAtTime(mBlink, mShowCursor + BLINK); } + /** + * @hide + */ + @Override + public void dispatchFinishTemporaryDetach() { + mDispatchTemporaryDetach = true; + super.dispatchFinishTemporaryDetach(); + mDispatchTemporaryDetach = false; + } + @Override public void onStartTemporaryDetach() { - mTemporaryDetach = true; + super.onStartTemporaryDetach(); + // Only track when onStartTemporaryDetach() is called directly, + // usually because this instance is an editable field in a list + if (!mDispatchTemporaryDetach) mTemporaryDetach = true; } @Override public void onFinishTemporaryDetach() { - mTemporaryDetach = false; + super.onFinishTemporaryDetach(); + // Only track when onStartTemporaryDetach() is called directly, + // usually because this instance is an editable field in a list + if (!mDispatchTemporaryDetach) mTemporaryDetach = false; } @Override diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 906bca1..c2517a8 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -62,6 +62,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { private static final int STATE_PLAYING = 3; private static final int STATE_PAUSED = 4; private static final int STATE_PLAYBACK_COMPLETED = 5; + private static final int STATE_SUSPEND = 6; + private static final int STATE_RESUME = 7; // mCurrentState is a VideoView object's current state. // mTargetState is the state that a method caller intends to reach. @@ -87,6 +89,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { private boolean mCanPause; private boolean mCanSeekBack; private boolean mCanSeekForward; + private int mStateWhenSuspended; //state before calling suspend() public VideoView(Context context) { super(context); @@ -466,7 +469,14 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; - openVideo(); + //resume() was called before surfaceCreated() + if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND + && mTargetState == STATE_RESUME) { + mMediaPlayer.setDisplay(mSurfaceHolder); + resume(); + } else { + openVideo(); + } } public void surfaceDestroyed(SurfaceHolder holder) @@ -474,7 +484,6 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { // after we return from this we can't use the surface any more mSurfaceHolder = null; if (mMediaController != null) mMediaController.hide(); - release(true); } }; @@ -567,7 +576,36 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mTargetState = STATE_PAUSED; } - // cache duration as mDuration for faster access + public void suspend() { + if (isInPlaybackState()) { + if (mMediaPlayer.suspend()) { + mStateWhenSuspended = mCurrentState; + mCurrentState = STATE_SUSPEND; + mTargetState = STATE_SUSPEND; + } else { + Log.w(TAG, "Unable to suspend video"); + mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + public void resume() { + if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND){ + mTargetState = STATE_RESUME; + return; + } + if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND) { + if (mMediaPlayer.resume()) { + mCurrentState = mStateWhenSuspended; + mTargetState = mStateWhenSuspended; + } else { + Log.w(TAG, "Unable to resume video"); + mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + // cache duration as mDuration for faster access public int getDuration() { if (isInPlaybackState()) { if (mDuration > 0) { diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index f56b15c..107b145 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -48,7 +49,6 @@ import android.widget.ScrollView; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; -import android.util.AttributeSet; import com.android.internal.R; @@ -322,24 +322,24 @@ public class AlertController { public Button getButton(int whichButton) { switch (whichButton) { case DialogInterface.BUTTON_POSITIVE: - return mButtonPositiveMessage != null ? mButtonPositive : null; + return mButtonPositive; case DialogInterface.BUTTON_NEGATIVE: - return mButtonNegativeMessage != null ? mButtonNegative : null; + return mButtonNegative; case DialogInterface.BUTTON_NEUTRAL: - return mButtonNeutralMessage != null ? mButtonNeutral : null; + return mButtonNeutral; default: return null; } } + @SuppressWarnings({"UnusedDeclaration"}) public boolean onKeyDown(int keyCode, KeyEvent event) { - if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true; - return false; + return mScrollView != null && mScrollView.executeKeyEvent(event); } + @SuppressWarnings({"UnusedDeclaration"}) public boolean onKeyUp(int keyCode, KeyEvent event) { - if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true; - return false; + return mScrollView != null && mScrollView.executeKeyEvent(event); } private void setupView() { @@ -469,7 +469,6 @@ public class AlertController { } private boolean setupButtons() { - View defaultButton = null; int BIT_BUTTON_POSITIVE = 1; int BIT_BUTTON_NEGATIVE = 2; int BIT_BUTTON_NEUTRAL = 4; @@ -482,7 +481,6 @@ public class AlertController { } else { mButtonPositive.setText(mButtonPositiveText); mButtonPositive.setVisibility(View.VISIBLE); - defaultButton = mButtonPositive; whichButtons = whichButtons | BIT_BUTTON_POSITIVE; } @@ -495,9 +493,6 @@ public class AlertController { mButtonNegative.setText(mButtonNegativeText); mButtonNegative.setVisibility(View.VISIBLE); - if (defaultButton == null) { - defaultButton = mButtonNegative; - } whichButtons = whichButtons | BIT_BUTTON_NEGATIVE; } @@ -510,9 +505,6 @@ public class AlertController { mButtonNeutral.setText(mButtonNeutralText); mButtonNeutral.setVisibility(View.VISIBLE); - if (defaultButton == null) { - defaultButton = mButtonNeutral; - } whichButtons = whichButtons | BIT_BUTTON_NEUTRAL; } @@ -565,8 +557,6 @@ public class AlertController { R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright); int bottomMedium = a.getResourceId( R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium); - int centerMedium = a.getResourceId( - R.styleable.AlertDialog_centerMedium, R.drawable.popup_center_medium); /* * We now set the background of all of the sections of the alert. @@ -596,7 +586,7 @@ public class AlertController { */ views[pos] = (contentPanel.getVisibility() == View.GONE) ? null : contentPanel; - light[pos] = mListView == null ? false : true; + light[pos] = mListView != null; pos++; if (customPanel != null) { views[pos] = customPanel; diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java index a48ccf9..5d71231 100644 --- a/core/java/com/android/internal/app/TetherActivity.java +++ b/core/java/com/android/internal/app/TetherActivity.java @@ -141,7 +141,7 @@ public class TetherActivity extends AlertActivity implements } } else { for (String t : tethered) { - if (!cm.untether("ppp0")) { + if (!cm.untether(t)) { error = true; } } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index bc7dbf4..c5db83f 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -36,7 +36,7 @@ public class PackageHelper { public static final int RECOMMEND_INSTALL_EXTERNAL = 2; public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1; public static final int RECOMMEND_FAILED_INVALID_APK = -2; - private static final boolean DEBUG_SD_INSTALL = true; + private static final boolean localLOGV = true; private static final String TAG = "PackageHelper"; public static IMountService getMountService() { @@ -58,7 +58,7 @@ public class PackageHelper { if ((len - (mbLen * 1024 * 1024)) > 0) { mbLen++; } - if (DEBUG_SD_INSTALL) Log.i(TAG, "Size of resource " + mbLen); + if (localLOGV) Log.i(TAG, "Size of resource " + mbLen); try { int rc = mountService.createSecureContainer( @@ -68,7 +68,7 @@ public class PackageHelper { return null; } String cachePath = mountService.getSecureContainerPath(cid); - if (DEBUG_SD_INSTALL) Log.i(TAG, "Created secure container " + cid + + if (localLOGV) Log.i(TAG, "Created secure container " + cid + " at " + cachePath); return cachePath; } catch (RemoteException e) { @@ -93,7 +93,7 @@ public class PackageHelper { public static boolean unMountSdDir(String cid) { try { - int rc = getMountService().unmountSecureContainer(cid, false); + int rc = getMountService().unmountSecureContainer(cid, true); if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc); return false; @@ -148,7 +148,8 @@ public class PackageHelper { public static boolean destroySdDir(String cid) { try { - int rc = getMountService().destroySecureContainer(cid, false); + if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid); + int rc = getMountService().destroySecureContainer(cid, true); if (rc != StorageResultCode.OperationSucceeded) { Log.i(TAG, "Failed to destroy container " + cid); return false; diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java new file mode 100644 index 0000000..343041f --- /dev/null +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -0,0 +1,287 @@ +package com.android.internal.content; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; + +import java.util.HashSet; + +/** + * Helper class for monitoring the state of packages: adding, removing, + * updating, and disappearing and reappearing on the SD card. + */ +public abstract class PackageMonitor extends android.content.BroadcastReceiver { + static final IntentFilter sPackageFilt = new IntentFilter(); + static final IntentFilter sNonDataFilt = new IntentFilter(); + static final IntentFilter sExternalFilt = new IntentFilter(); + + static { + sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); + sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); + sPackageFilt.addAction(Intent.ACTION_UID_REMOVED); + sPackageFilt.addDataScheme("package"); + sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED); + sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + } + + final HashSet<String> mUpdatingPackages = new HashSet<String>(); + + Context mRegisteredContext; + String[] mDisappearingPackages; + String[] mAppearingPackages; + String[] mModifiedPackages; + int mChangeType; + boolean mSomePackagesChanged; + + String[] mTempArray = new String[1]; + + public void register(Context context, boolean externalStorage) { + if (mRegisteredContext != null) { + throw new IllegalStateException("Already registered"); + } + mRegisteredContext = context; + context.registerReceiver(this, sPackageFilt); + context.registerReceiver(this, sNonDataFilt); + if (externalStorage) { + context.registerReceiver(this, sExternalFilt); + } + } + + public void unregister() { + if (mRegisteredContext == null) { + throw new IllegalStateException("Not registered"); + } + mRegisteredContext.unregisterReceiver(this); + mRegisteredContext = null; + } + + //not yet implemented + boolean isPackageUpdating(String packageName) { + synchronized (mUpdatingPackages) { + return mUpdatingPackages.contains(packageName); + } + } + + public void onBeginPackageChanges() { + } + + public void onPackageAdded(String packageName, int uid) { + } + + public void onPackageRemoved(String packageName, int uid) { + } + + public void onPackageUpdateStarted(String packageName, int uid) { + } + + public void onPackageUpdateFinished(String packageName, int uid) { + } + + public void onPackageChanged(String packageName, int uid, String[] components) { + } + + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + return false; + } + + public void onUidRemoved(int uid) { + } + + public void onPackagesAvailable(String[] packages) { + } + + public void onPackagesUnavailable(String[] packages) { + } + + public static final int PACKAGE_UNCHANGED = 0; + public static final int PACKAGE_UPDATING = 1; + public static final int PACKAGE_TEMPORARY_CHANGE = 2; + public static final int PACKAGE_PERMANENT_CHANGE = 3; + + public void onPackageDisappeared(String packageName, int reason) { + } + + public void onPackageAppeared(String packageName, int reason) { + } + + public void onPackageModified(String packageName) { + } + + public boolean didSomePackagesChange() { + return mSomePackagesChanged; + } + + public int isPackageAppearing(String packageName) { + if (mAppearingPackages != null) { + for (int i=mAppearingPackages.length-1; i>=0; i--) { + if (packageName.equals(mAppearingPackages[i])) { + return mChangeType; + } + } + } + return PACKAGE_UNCHANGED; + } + + public boolean anyPackagesAppearing() { + return mAppearingPackages != null; + } + + public int isPackageDisappearing(String packageName) { + if (mDisappearingPackages != null) { + for (int i=mDisappearingPackages.length-1; i>=0; i--) { + if (packageName.equals(mDisappearingPackages[i])) { + return mChangeType; + } + } + } + return PACKAGE_UNCHANGED; + } + + public boolean anyPackagesDisappearing() { + return mDisappearingPackages != null; + } + + public boolean isPackageModified(String packageName) { + if (mModifiedPackages != null) { + for (int i=mModifiedPackages.length-1; i>=0; i--) { + if (packageName.equals(mModifiedPackages[i])) { + return true; + } + } + } + return false; + } + + public void onSomePackagesChanged() { + } + + public void onFinishPackageChanges() { + } + + String getPackageName(Intent intent) { + Uri uri = intent.getData(); + String pkg = uri != null ? uri.getSchemeSpecificPart() : null; + return pkg; + } + + @Override + public void onReceive(Context context, Intent intent) { + onBeginPackageChanges(); + + mDisappearingPackages = mAppearingPackages = null; + mSomePackagesChanged = false; + + String action = intent.getAction(); + if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + // We consider something to have changed regardless of whether + // this is just an update, because the update is now finished + // and the contents of the package may have changed. + mSomePackagesChanged = true; + if (pkg != null) { + mAppearingPackages = mTempArray; + mTempArray[0] = pkg; + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mModifiedPackages = mTempArray; + mChangeType = PACKAGE_UPDATING; + onPackageUpdateFinished(pkg, uid); + onPackageModified(pkg); + } else { + mChangeType = PACKAGE_PERMANENT_CHANGE; + onPackageAdded(pkg, uid); + } + onPackageAppeared(pkg, mChangeType); + if (mChangeType == PACKAGE_UPDATING) { + synchronized (mUpdatingPackages) { + mUpdatingPackages.remove(pkg); + } + } + } + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + if (pkg != null) { + mDisappearingPackages = mTempArray; + mTempArray[0] = pkg; + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mChangeType = PACKAGE_UPDATING; + synchronized (mUpdatingPackages) { + //not used for now + //mUpdatingPackages.add(pkg); + } + onPackageUpdateStarted(pkg, uid); + } else { + mChangeType = PACKAGE_PERMANENT_CHANGE; + // We only consider something to have changed if this is + // not a replace; for a replace, we just need to consider + // it when it is re-added. + mSomePackagesChanged = true; + onPackageRemoved(pkg, uid); + } + onPackageDisappeared(pkg, mChangeType); + } + } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + String[] components = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); + if (pkg != null) { + mModifiedPackages = mTempArray; + mTempArray[0] = pkg; + onPackageChanged(pkg, uid, components); + // XXX Don't want this to always cause mSomePackagesChanged, + // since it can happen a fair amount. + onPackageModified(pkg); + } + } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { + mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + mChangeType = PACKAGE_TEMPORARY_CHANGE; + boolean canRestart = onHandleForceStop(intent, + mDisappearingPackages, + intent.getIntExtra(Intent.EXTRA_UID, 0), false); + if (canRestart) setResultCode(Activity.RESULT_OK); + } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { + mDisappearingPackages = new String[] {getPackageName(intent)}; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + onHandleForceStop(intent, mDisappearingPackages, + intent.getIntExtra(Intent.EXTRA_UID, 0), true); + } else if (Intent.ACTION_UID_REMOVED.equals(action)) { + onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0)); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + mAppearingPackages = pkgList; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + mSomePackagesChanged = true; + if (pkgList != null) { + onPackagesAvailable(pkgList); + for (int i=0; i<pkgList.length; i++) { + onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + } + } + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + mDisappearingPackages = pkgList; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + mSomePackagesChanged = true; + if (pkgList != null) { + onPackagesUnavailable(pkgList); + for (int i=0; i<pkgList.length; i++) { + onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + } + } + } + + if (mSomePackagesChanged) { + onSomePackagesChanged(); + } + + onFinishPackageChanges(); + } +} diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java index cd6a9a1..274082c 100644 --- a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java +++ b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java @@ -50,6 +50,11 @@ public class SyncStateContentProviderHelper { public static final String PATH = "syncstate"; + private static final String QUERY_COUNT_SYNC_STATE_ROWS = + "SELECT count(*)" + + " FROM " + SYNC_STATE_TABLE + + " WHERE " + SyncStateContract.Columns._ID + "=?"; + public void createDatabase(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE); db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " (" @@ -96,11 +101,17 @@ public class SyncStateContentProviderHelper { return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs); } - public void update(SQLiteDatabase db, long rowId, Object data) { + public int update(SQLiteDatabase db, long rowId, Object data) { + if (DatabaseUtils.longForQuery(db, QUERY_COUNT_SYNC_STATE_ROWS, + new String[]{Long.toString(rowId)}) < 1) { + return 0; + } db.execSQL("UPDATE " + SYNC_STATE_TABLE + " SET " + SyncStateContract.Columns.DATA + "=?" + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId, new Object[]{data}); + // assume a row was modified since we know it exists + return 1; } public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) { diff --git a/common/java/com/android/common/DNParser.java b/core/java/com/android/internal/net/DNParser.java index 32d57c0..5254207 100644 --- a/common/java/com/android/common/DNParser.java +++ b/core/java/com/android/internal/net/DNParser.java @@ -15,7 +15,8 @@ * limitations under the License. */ -package com.android.common; +package com.android.internal.net; + import android.util.Log; @@ -34,6 +35,8 @@ import javax.security.auth.x500.X500Principal; * * <p>This class is used by {@link DomainNameValidator} only. However, in order to make this * class visible from unit tests, it's made public. + * + * @hide */ public final class DNParser { private static final String TAG = "DNParser"; diff --git a/common/java/com/android/common/DomainNameValidator.java b/core/java/com/android/internal/net/DomainNameValidator.java index 25dc007..dbd5019 100644 --- a/common/java/com/android/common/DomainNameValidator.java +++ b/core/java/com/android/internal/net/DomainNameValidator.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.common; +package com.android.internal.net; + import android.util.Config; import android.util.Log; @@ -30,6 +31,7 @@ import java.util.regex.PatternSyntaxException; import javax.security.auth.x500.X500Principal; +/** @hide */ public class DomainNameValidator { private final static String TAG = "DomainNameValidator"; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index dfcc8f7..71ccb3b 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -59,6 +59,13 @@ public final class BatteryStatsImpl extends BatteryStats { // Current on-disk Parcel version private static final int VERSION = 42; + // The maximum number of names wakelocks we will keep track of + // per uid; once the limit is reached, we batch the remaining wakelocks + // in to one common name. + private static final int MAX_WAKELOCKS_PER_UID = 20; + + private static final String BATCHED_WAKELOCK_NAME = "*overflow*"; + private static int sNumSpeedSteps; private final File mFile; @@ -1757,7 +1764,12 @@ public final class BatteryStatsImpl extends BatteryStats { String wakelockName = in.readString(); Uid.Wakelock wakelock = new Wakelock(); wakelock.readFromParcelLocked(unpluggables, in); - mWakelockStats.put(wakelockName, wakelock); + if (mWakelockStats.size() < MAX_WAKELOCKS_PER_UID) { + // We will just drop some random set of wakelocks if + // the previous run of the system was an older version + // that didn't impose a limit. + mWakelockStats.put(wakelockName, wakelock); + } } int numSensors = in.readInt(); @@ -2583,8 +2595,14 @@ public final class BatteryStatsImpl extends BatteryStats { public StopwatchTimer getWakeTimerLocked(String name, int type) { Wakelock wl = mWakelockStats.get(name); if (wl == null) { - wl = new Wakelock(); - mWakelockStats.put(name, wl); + if (mWakelockStats.size() > MAX_WAKELOCKS_PER_UID) { + name = BATCHED_WAKELOCK_NAME; + wl = mWakelockStats.get(name); + } + if (wl == null) { + wl = new Wakelock(); + mWakelockStats.put(name, wl); + } } StopwatchTimer t = null; switch (type) { diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 9e5bdff..2369d25 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -20,7 +20,7 @@ package com.android.internal.os; import android.content.Context; import android.content.res.XmlResourceParser; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/common/java/com/android/common/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java index 0d33941..592a8fa 100644 --- a/common/java/com/android/common/FastXmlSerializer.java +++ b/core/java/com/android/internal/util/FastXmlSerializer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.common; +package com.android.internal.util; import org.xmlpull.v1.XmlSerializer; diff --git a/common/java/com/android/common/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index dd57e49..8d8df16 100644 --- a/common/java/com/android/common/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -14,7 +14,8 @@ * limitations under the License. */ -package com.android.common; +package com.android.internal.util; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f074b80..9713c27 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -104,14 +104,24 @@ public class LockPatternUtils { private static String sLockPatternFilename; private static String sLockPasswordFilename; + DevicePolicyManager getDevicePolicyManager() { + if (mDevicePolicyManager == null) { + mDevicePolicyManager = + (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (mDevicePolicyManager == null) { + Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?", + new IllegalStateException("Stack trace:")); + } + } + return mDevicePolicyManager; + } /** * @param contentResolver Used to look up and save settings. */ public LockPatternUtils(Context context) { mContext = context; mContentResolver = context.getContentResolver(); - mDevicePolicyManager = - (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE); + mDevicePolicyManager = getDevicePolicyManager(); // Initialize the location of gesture lock file if (sLockPatternFilename == null) { sLockPatternFilename = android.os.Environment.getDataDirectory() @@ -123,7 +133,7 @@ public class LockPatternUtils { } public int getRequestedMinimumPasswordLength() { - return mDevicePolicyManager.getPasswordMinimumLength(null); + return getDevicePolicyManager().getPasswordMinimumLength(null); } /** @@ -133,7 +143,7 @@ public class LockPatternUtils { * @return */ public int getRequestedPasswordMode() { - int policyMode = mDevicePolicyManager.getPasswordQuality(null); + int policyMode = getDevicePolicyManager().getPasswordQuality(null); switch (policyMode) { case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: return MODE_PASSWORD; @@ -151,11 +161,11 @@ public class LockPatternUtils { * @return */ public void reportFailedPasswordAttempt() { - mDevicePolicyManager.reportFailedPasswordAttempt(); + getDevicePolicyManager().reportFailedPasswordAttempt(); } public void reportSuccessfulPasswordAttempt() { - mDevicePolicyManager.reportSuccessfulPasswordAttempt(); + getDevicePolicyManager().reportSuccessfulPasswordAttempt(); } public void setActivePasswordState(int mode, int length) { @@ -171,7 +181,7 @@ public class LockPatternUtils { policyMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; break; } - mDevicePolicyManager.setActivePasswordState(policyMode, length); + getDevicePolicyManager().setActivePasswordState(policyMode, length); } /** @@ -279,7 +289,7 @@ public class LockPatternUtils { saveLockPattern(null); setLong(PASSWORD_TYPE_KEY, MODE_PATTERN); } - + /** * Save a lock pattern. * @param pattern The new pattern to save. @@ -334,7 +344,7 @@ public class LockPatternUtils { hasNonDigit = true; } } - + // First check if it is sufficient. switch (reqMode) { case MODE_PASSWORD: { @@ -342,19 +352,19 @@ public class LockPatternUtils { return MODE_UNSPECIFIED; } } break; - + case MODE_PIN: case MODE_PATTERN: { // Whatever we have is acceptable; we may need to promote the // mode later. } break; - + default: // If it isn't a mode we specifically know, then fail fast. Log.w(TAG, "adjustPasswordMode: unknown mode " + reqMode); return MODE_UNSPECIFIED; } - + // Do we need to promote? if (hasNonDigit) { if (reqMode < MODE_PASSWORD) { @@ -366,10 +376,10 @@ public class LockPatternUtils { reqMode = MODE_PIN; } } - + return reqMode; } - + /** * Save a lock password. Does not ensure that the pattern is as good * as the requested mode, but will adjust the mode to be as good as the diff --git a/core/jni/Android.mk b/core/jni/Android.mk index e8749ed..85d1a6f 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -45,6 +45,7 @@ LOCAL_SRC_FILES:= \ android_view_Surface.cpp \ android_view_ViewRoot.cpp \ android_text_AndroidCharacter.cpp \ + android_text_AndroidBidi.cpp \ android_text_KeyCharacterMap.cpp \ android_os_Debug.cpp \ android_os_FileUtils.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 7c8df03..8586aca 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -142,6 +142,7 @@ extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_net_wifi_WifiManager(JNIEnv* env); extern int register_android_security_Md5MessageDigest(JNIEnv *env); extern int register_android_text_AndroidCharacter(JNIEnv *env); +extern int register_android_text_AndroidBidi(JNIEnv *env); extern int register_android_text_KeyCharacterMap(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_bluetooth_HeadsetBase(JNIEnv* env); @@ -1184,6 +1185,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_emoji_EmojiFactory), REG_JNI(register_android_security_Md5MessageDigest), REG_JNI(register_android_text_AndroidCharacter), + REG_JNI(register_android_text_AndroidBidi), REG_JNI(register_android_text_KeyCharacterMap), REG_JNI(register_android_os_Process), REG_JNI(register_android_os_Binder), diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp new file mode 100644 index 0000000..7696bb3 --- /dev/null +++ b/core/jni/android_text_AndroidBidi.cpp @@ -0,0 +1,81 @@ +/* //device/libs/android_runtime/android_text_AndroidBidi.cpp +** +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "AndroidUnicode" + +#include <jni.h> +#include <android_runtime/AndroidRuntime.h> +#include "utils/misc.h" +#include "utils/Log.h" +#include "unicode/ubidi.h" + +namespace android { + +static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL) +{ + jclass excClazz = env->FindClass(exc); + LOG_ASSERT(excClazz, "Unable to find class %s", exc); + + env->ThrowNew(excClazz, msg); +} + +static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, + jbyteArray infoArray, int n, jboolean haveInfo) +{ + // Parameters are checked on java side + // Failures from GetXXXArrayElements indicate a serious out-of-memory condition + // that we don't bother to report, we're probably dead anyway. + jint result = 0; + jchar* chs = env->GetCharArrayElements(chsArray, NULL); + if (chs != NULL) { + jbyte* info = env->GetByteArrayElements(infoArray, NULL); + if (info != NULL) { + UErrorCode status = U_ZERO_ERROR; + UBiDi* bidi = ubidi_openSized(n, 0, &status); + ubidi_setPara(bidi, chs, n, dir, NULL, &status); + if (U_SUCCESS(status)) { + for (int i = 0; i < n; ++i) { + info[i] = ubidi_getLevelAt(bidi, i); + } + result = ubidi_getParaLevel(bidi); + } else { + jniThrowException(env, "java/lang/RuntimeException", NULL); + } + ubidi_close(bidi); + + env->ReleaseByteArrayElements(infoArray, info, 0); + } + env->ReleaseCharArrayElements(chsArray, chs, JNI_ABORT); + } + return result; +} + +static JNINativeMethod gMethods[] = { + { "runBidi", "(I[C[BIZ)I", + (void*) runBidi } +}; + +int register_android_text_AndroidBidi(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/text/AndroidBidi"); + LOG_ASSERT(clazz, "Cannot find android/text/AndroidBidi"); + + return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidBidi", + gMethods, NELEM(gMethods)); +} + +} diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index a82a21e..5afa034 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1466,19 +1466,25 @@ static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { value = bag->map.value; jstring str = NULL; - + // Take care of resolving the found resource to its final value. ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); if (value.dataType == Res_value::TYPE_STRING) { - const char16_t* str16 = res.getTableStringBlock(block)->stringAt(value.data, &strLen); - str = env->NewString(str16, strLen); - if (str == NULL) { - doThrow(env, "java/lang/OutOfMemoryError"); - res.unlockBag(startOfBag); - return NULL; + const ResStringPool* pool = res.getTableStringBlock(block); + const char* str8 = pool->string8At(value.data, &strLen); + if (str8 != NULL) { + str = env->NewStringUTF(str8); + } else { + const char16_t* str16 = pool->stringAt(value.data, &strLen); + str = env->NewString(str16, strLen); + if (str == NULL) { + doThrow(env, "java/lang/OutOfMemoryError"); + res.unlockBag(startOfBag); + return NULL; + } } } - + env->SetObjectArrayElement(array, i, str); } res.unlockBag(startOfBag); diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp index ffb271c..641fbce 100644 --- a/core/jni/android_util_StringBlock.cpp +++ b/core/jni/android_util_StringBlock.cpp @@ -89,6 +89,11 @@ static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject } size_t len; + const char* str8 = osb->string8At(idx, &len); + if (str8 != NULL) { + return env->NewStringUTF(str8); + } + const char16_t* str = osb->stringAt(idx, &len); if (str == NULL) { doThrow(env, "java/lang/IndexOutOfBoundsException"); diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png Binary files differnew file mode 100644 index 0000000..c6503c7 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png Binary files differnew file mode 100644 index 0000000..152de8b --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png Binary files differindex 7acd0df..72faccf 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png Binary files differindex a8b843a..369be10 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png Binary files differindex cfbc092..7e996ec 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png Binary files differindex 3d0d16e..44668b3 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png Binary files differindex 2ccd3da..3a4571e 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png Binary files differindex 966ea44..60dc632 100644 --- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png +++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png Binary files differdeleted file mode 100644 index af80855..0000000 --- a/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png Binary files differdeleted file mode 100644 index dc47275..0000000 --- a/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png Binary files differdeleted file mode 100644 index 007f279..0000000 --- a/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png Binary files differdeleted file mode 100644 index 24592a3..0000000 --- a/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png Binary files differnew file mode 100644 index 0000000..f7464c7 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png Binary files differnew file mode 100644 index 0000000..ffe219f --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png Binary files differindex febf222..437fbc7 100644 --- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png +++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png Binary files differindex 70a200b..a679426 100644 --- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png +++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png Binary files differindex 6f2989f..eb95f22 100644 --- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png +++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png Binary files differdeleted file mode 100644 index d8e268d..0000000 --- a/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png Binary files differdeleted file mode 100644 index 087e650..0000000 --- a/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png Binary files differdeleted file mode 100644 index f1f2ff5..0000000 --- a/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png Binary files differdeleted file mode 100644 index f537b3b..0000000 --- a/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png +++ /dev/null diff --git a/core/res/res/drawable/btn_circle.xml b/core/res/res/drawable/btn_circle.xml index 9208010..243f506 100644 --- a/core/res/res/drawable/btn_circle.xml +++ b/core/res/res/drawable/btn_circle.xml @@ -19,6 +19,8 @@ android:drawable="@drawable/btn_circle_normal" /> <item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/btn_circle_disable" /> + <item android:state_pressed="true" android:state_enabled="false" + android:drawable="@drawable/btn_circle_disable" /> <item android:state_pressed="true" android:drawable="@drawable/btn_circle_pressed" /> <item android:state_focused="true" android:state_enabled="true" diff --git a/core/res/res/drawable/btn_dropdown.xml b/core/res/res/drawable/btn_dropdown.xml index 8ec8ece..34a0504 100644 --- a/core/res/res/drawable/btn_dropdown.xml +++ b/core/res/res/drawable/btn_dropdown.xml @@ -15,10 +15,24 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_window_focused="false" android:drawable="@drawable/btn_dropdown_normal" /> - <item android:state_pressed="true" android:drawable="@drawable/btn_dropdown_pressed" /> - <item android:state_focused="true" android:state_pressed="false" + <item + android:state_window_focused="false" android:state_enabled="true" + android:drawable="@drawable/btn_dropdown_normal" /> + <item + android:state_window_focused="false" android:state_enabled="false" + android:drawable="@drawable/btn_dropdown_disabled" /> + <item + android:state_pressed="true" + android:drawable="@drawable/btn_dropdown_pressed" /> + <item + android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_dropdown_selected" /> - <item android:drawable="@drawable/btn_dropdown_normal" /> + <item + android:state_enabled="true" + android:drawable="@drawable/btn_dropdown_normal" /> + <item + android:state_focused="true" + android:drawable="@drawable/btn_dropdown_disabled_focused" /> + <item + android:drawable="@drawable/btn_dropdown_disabled" /> </selector> - diff --git a/core/res/res/drawable/spinnerbox_arrows.xml b/core/res/res/drawable/spinnerbox_arrows.xml deleted file mode 100644 index 276a0f0..0000000 --- a/core/res/res/drawable/spinnerbox_arrows.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/drawable/spinnerbox_arrows.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_single="true" android:drawable="@drawable/spinnerbox_arrow_single" /> - <item android:state_first="true" android:drawable="@drawable/spinnerbox_arrow_first" /> - <item android:state_last="true" android:drawable="@drawable/spinnerbox_arrow_last" /> - <item android:state_middle="true" android:drawable="@drawable/spinnerbox_arrow_middle" /> - <item android:state_pressed="true" android:drawable="@drawable/spinnerbox_arrow_single" /> -</selector> diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml index 7ae68f9..25a41f8 100644 --- a/core/res/res/layout/alert_dialog.xml +++ b/core/res/res/layout/alert_dialog.xml @@ -80,7 +80,8 @@ android:paddingTop="2dip" android:paddingBottom="12dip" android:paddingLeft="14dip" - android:paddingRight="10dip"> + android:paddingRight="10dip" + android:overscrollMode="ifContentScrolls"> <TextView android:id="@+id/message" style="?android:attr/textAppearanceMedium" android:layout_width="match_parent" diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index cf246ba..7935e2a 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -34,7 +34,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="12dip" - android:paddingRight="12dip" + android:paddingRight="6dip" android:paddingTop="7dip" android:paddingBottom="16dip" android:background="@drawable/search_plate_global" > @@ -95,7 +95,10 @@ android:id="@+id/search_voice_btn" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginLeft="8dip" + android:layout_marginLeft="0dip" + android:layout_marginTop="-6.5dip" + android:layout_marginBottom="-7dip" + android:layout_marginRight="-5dip" android:background="@drawable/btn_search_dialog_voice" android:src="@android:drawable/ic_btn_speak_now" /> diff --git a/core/res/res/layout/status_bar_expanded.xml b/core/res/res/layout/status_bar_expanded.xml index a0cd11d..30138a7 100644 --- a/core/res/res/layout/status_bar_expanded.xml +++ b/core/res/res/layout/status_bar_expanded.xml @@ -20,9 +20,9 @@ <com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" - android:background="@drawable/status_bar_background" android:focusable="true" - android:descendantFocusability="afterDescendants"> + android:descendantFocusability="afterDescendants" + > <LinearLayout android:layout_width="match_parent" @@ -79,19 +79,18 @@ <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="1" > <ScrollView android:id="@+id/scroll" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="1" + android:layout_height="match_parent" android:fadingEdge="none" > <com.android.server.status.NotificationLinearLayout android:id="@+id/notificationLinearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" android:orientation="vertical" > @@ -136,7 +135,7 @@ <ImageView android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:src="@drawable/title_bar_shadow" android:scaleType="fitXY" /> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index cdc15c2..0c065ef 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -127,15 +127,4 @@ <item><xliff:g id="id">ime</xliff:g></item> </string-array> - <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH - handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers, - the first component from this list which is found to be installed is set as the - preferred activity. --> - <string-array name="default_web_search_providers"> - <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item> - <item>com.android.quicksearchbox/.google.GoogleSearch</item> - <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item> - <item>com.android.googlesearch/.GoogleSearch</item> - <item>com.android.websearch/.Search.1</item> - </string-array> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 33b509b..d66f513 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -302,7 +302,7 @@ <!-- Specify whether an activity should be finished when a "close system windows" request has been made. This happens, for example, when the home key is pressed, when the device is locked, when a system - dialog like recent apps is displayed, etc. --> + dialog showing recent applications is displayed, etc. --> <attr name="finishOnCloseSystemDialogs" format="boolean" /> <!-- Specify whether an activity's task should be cleared when it @@ -487,7 +487,7 @@ the display will rotate based on how the user moves the device. --> <enum name="sensor" value="4" /> <!-- Always ignore orientation determined by orientation sensor: - tthe display will not rotate when the user moves the device. --> + the display will not rotate when the user moves the device. --> <enum name="nosensor" value="5" /> </attr> @@ -523,7 +523,7 @@ <flag name="keyboard" value="0x0010" /> <!-- The keyboard or navigation accessibility has changed, for example the user has slid the keyboard out to expose it. Note that - inspite of its name, this applied to any accessibility: keyboard + despite its name, this applied to any accessibility: keyboard or navigation. --> <flag name="keyboardHidden" value="0x0020" /> <!-- The navigation type has changed. Should never normally happen. --> @@ -592,11 +592,16 @@ backup and restore of the application's settings to external storage. --> <attr name="backupAgent" format="string" /> - <!-- This is not the attribute you are looking for. --> + <!-- Whether to allow the application to participate in backup + infrastructure. If this attribute is set to <code>false</code>, no backup + of the application will ever be performed, even by a full-system backup that + would otherwise cause all application data to be saved via adb. The + default value of this attribute is <code>true</code>. --> <attr name="allowBackup" format="boolean" /> <!-- Whether the application in question should be terminated after its - settings have been restored. The default is to do so. --> + settings have been restored. The default is <code>true</code>, + which means to do so. --> <attr name="killAfterRestore" format="boolean" /> <!-- Whether the application needs to have its own Application subclass @@ -604,15 +609,25 @@ Application class to avoid interference with application logic. --> <attr name="restoreNeedsApplication" format="boolean" /> + <!-- Indicate that the application is prepared to attempt a restore of any + backed-up dataset, even if the backup is apparently from a newer version + of the application than is currently installed on the device. Setting + this attribute to <code>true</code> will permit the Backup Manager to + attempt restore even when a version mismatch suggests that the data are + incompatible. <em>Use with caution!</em> + + <p>The default value of this attribute is <code>false</code>. --> + <attr name="restoreAnyVersion" format="boolean" /> + <!-- The default install location defined by an application. --> <attr name="installLocation"> <!-- Let the system decide ideal install location --> <enum name="auto" value="0" /> - <!-- Explicitly request to be installed on internal phone storate + <!-- Explicitly request to be installed on internal phone storage only. --> <enum name="internalOnly" value="1" /> - <!-- Prefer to be installed on sdcard. There is no guarantee that - the system will honour this request. The application might end + <!-- Prefer to be installed on SD card. There is no guarantee that + the system will honor this request. The application might end up being installed on internal storage if external media is unavailable or too full. --> <enum name="preferExternal" value="2" /> @@ -698,6 +713,7 @@ <attr name="allowBackup" /> <attr name="killAfterRestore" /> <attr name="restoreNeedsApplication" /> + <attr name="restoreAnyVersion" /> <attr name="neverEncrypt" /> </declare-styleable> @@ -813,7 +829,7 @@ <!-- The <code>uses-feature</code> tag specifies a specific feature used by the application. For example an application might specify that it requires - specific version of open gl. Multiple such attribute + specific version of OpenGL. Multiple such attribute values can be specified by the application. <p>This appears as a child tag of the root @@ -1359,7 +1375,7 @@ {@link android.content.Intent#setData Intent.setData()}. <p><em>Note: scheme and host name matching in the Android framework is case-sensitive, unlike the formal RFC. As a result, - Uris here should always be normalized to use lower case letters + URIs here should always be normalized to use lower case letters for these elements (as well as other proper Uri normalization).</em></p> --> <attr name="data" format="string" /> <!-- The MIME type name to assign to the Intent, as per diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 5d561b8..1e11a99 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -138,6 +138,9 @@ closed. The default is 0. --> <integer name="config_lidNavigationAccessibility">0</integer> + <!-- Indicate whether the SD card is accessible without removing the battery. --> + <bool name="config_batterySdCardAccessibility">false</bool> + <!-- Vibrator pattern for feedback about a long screen/key press --> <integer-array name="config_longPressVibePattern"> <item>0</item> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b334337..5da8e85 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1231,6 +1231,7 @@ <public type="attr" name="safeMode" id="0x010102b8" /> <public type="attr" name="webTextViewStyle" id="0x010102b9" /> <public type="attr" name="overscrollMode" id="0x010102ba" /> + <public type="attr" name="restoreAnyVersion" id="0x010102bb" /> <public type="anim" name="cycle_interpolator" id="0x010a000c" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f1501ae..5d2d272 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -779,18 +779,18 @@ applications can use this to read phone owner data.</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_readCalendar">read calendar data</string> + <string name="permlab_readCalendar">read calendar events</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_readCalendar">Allows an application to read all of the calendar events stored on your phone. Malicious applications can use this to send your calendar events to other people.</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_writeCalendar">write calendar data</string> + <string name="permlab_writeCalendar">add or modify calendar events and send email to guests</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_writeCalendar">Allows an application to modify the - calendar events stored on your phone. Malicious - applications can use this to erase or modify your calendar data.</string> + <string name="permdesc_writeCalendar">Allows an application to add or change the + events on your calendar, which may send email to guests. Malicious applications can use this + to erase or modify your calendar events or to send email to guests.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessMockLocation">mock location sources for testing</string> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 1fb9852..b0e2843 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -9,6 +9,8 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ src/android/os/IAidlTest.aidl +LOCAL_STATIC_JAVA_LIBRARIES += android-common + LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java index 9b4520e..6cb31d4 100644 --- a/core/tests/coretests/src/android/app/SearchablesTest.java +++ b/core/tests/coretests/src/android/app/SearchablesTest.java @@ -64,39 +64,7 @@ public class SearchablesTest extends AndroidTestCase { * findActionKey works * getIcon works */ - - /** - * The goal of this test is to confirm proper operation of the - * SearchableInfo helper class. - * - * TODO: The metadata source needs to be mocked out because adding - * searchability metadata via this test is causing it to leak into the - * real system. So for now I'm just going to test for existence of the - * GlobalSearch app (which is searchable). - */ - public void testSearchableGlobalSearch() { - // test basic array & hashmap - Searchables searchables = new Searchables(mContext); - searchables.buildSearchableList(); - // test linkage from another activity - // TODO inject this via mocking into the package manager. - // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test) - ComponentName thisActivity = new ComponentName( - "com.android.globalsearch", - "com.android.globalsearch.GlobalSearch"); - - SearchableInfo si = searchables.getSearchableInfo(thisActivity); - assertNotNull(si); - assertEquals(thisActivity, si.getSearchActivity()); - - Context appContext = si.getActivityContext(mContext); - assertNotNull(appContext); - MoreAsserts.assertNotEqual(appContext, mContext); - assertEquals("Quick Search Box", appContext.getString(si.getHintId())); - assertEquals("Quick Search Box", appContext.getString(si.getLabelId())); - } - /** * Test that non-searchable activities return no searchable info (this would typically * trigger the use of the default searchable e.g. contacts) @@ -113,18 +81,7 @@ public class SearchablesTest extends AndroidTestCase { SearchableInfo si = searchables.getSearchableInfo(nonActivity); assertNull(si); } - - /** - * Test that there is a default searchable (aka global search provider). - */ - public void testDefaultSearchable() { - Searchables searchables = new Searchables(mContext); - searchables.buildSearchableList(); - SearchableInfo si = searchables.getDefaultSearchable(); - checkSearchable(si); - assertTrue(searchables.isDefaultSearchable(si)); - } - + /** * This is an attempt to run the searchable info list with a mocked context. Here are some * things I'd like to test. @@ -371,7 +328,8 @@ public class SearchablesTest extends AndroidTestCase { public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { assertNotNull(intent); assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) - || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)); + || intent.getAction().equals(Intent.ACTION_WEB_SEARCH) + || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.queryIntentActivities(intent, flags); @@ -472,6 +430,20 @@ public class SearchablesTest extends AndroidTestCase { throw new UnsupportedOperationException(); } } + + @Override + public int checkPermission(String permName, String pkgName) { + assertNotNull(permName); + assertNotNull(pkgName); + switch (mSearchablesMode) { + case SEARCHABLES_PASSTHROUGH: + return mRealPackageManager.checkPermission(permName, pkgName); + case SEARCHABLES_MOCK_ZERO: + return PackageManager.PERMISSION_DENIED; + default: + throw new UnsupportedOperationException(); + } + } } } diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java new file mode 100644 index 0000000..da6036a --- /dev/null +++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java @@ -0,0 +1,137 @@ +/* + * 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.text; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Tests StaticLayout bidi implementation. + */ +public class StaticLayoutBidiTest extends TestCase { + + public static final int REQ_DL = 2; // Layout.DIR_REQUEST_DEFAULT_LTR; + public static final int REQ_DR = -2; // Layout.DIR_REQUEST_DEFAULT_RTL; + public static final int REQ_L = 1; // Layout.DIR_REQUEST_LTR; + public static final int REQ_R = -1; // Layout.DIR_REQUEST_RTL; + public static final int L = Layout.DIR_LEFT_TO_RIGHT; + public static final int R = Layout.DIR_RIGHT_TO_LEFT; + + public static final String SP = " "; + public static final String ALEF = "\u05d0"; + public static final String BET = "\u05d1"; + public static final String GIMEL = "\u05d2"; + public static final String DALET = "\u05d3"; + + @SmallTest + public void testAllLtr() { + expectBidi(REQ_DL, "a test", "000000", L); + } + + @SmallTest + public void testLtrRtl() { + expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L); + } + + @SmallTest + public void testAllRtl() { + expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R); + } + + @SmallTest + public void testRtlLtr() { + expectBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111000", R); + } + + @SmallTest + public void testRAllLtr() { + expectBidi(REQ_R, "a test", "000000", R); + } + + @SmallTest + public void testRLtrRtl() { + expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R); + } + + @SmallTest + public void testLAllRtl() { + expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L); + } + + @SmallTest + public void testLRtlLtr() { + expectBidi(REQ_L, ALEF + BET + GIMEL + " abc", "1110000", L); + } + + private void expectBidi(int dir, String text, + String expectedLevels, int expectedDir) { + char[] chs = text.toCharArray(); + int n = chs.length; + byte[] chInfo = new byte[n]; + + int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false); + + { + StringBuilder sb = new StringBuilder("info:"); + for (int i = 0; i < n; ++i) { + sb.append(" ").append(String.valueOf(chInfo[i])); + } + Log.i("BIDI", sb.toString()); + } + + char[] resultLevelChars = new char[n]; + for (int i = 0; i < n; ++i) { + resultLevelChars[i] = (char)('0' + chInfo[i]); + } + String resultLevels = new String(resultLevelChars); + assertEquals("direction", expectedDir, resultDir); + assertEquals("levels", expectedLevels, resultLevels); + } + + @SmallTest + public void testNativeBidi() { + // native bidi returns levels, not simply directions + expectNativeBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111222", R); + } + + private void expectNativeBidi(int dir, String text, + String expectedLevels, int expectedDir) { + char[] chs = text.toCharArray(); + int n = chs.length; + byte[] chInfo = new byte[n]; + + int resultDir = AndroidBidi.bidi(dir, chs, chInfo, n, false); + + { + StringBuilder sb = new StringBuilder("info:"); + for (int i = 0; i < n; ++i) { + sb.append(" ").append(String.valueOf(chInfo[i])); + } + Log.i("BIDI", sb.toString()); + } + + char[] resultLevelChars = new char[n]; + for (int i = 0; i < n; ++i) { + resultLevelChars[i] = (char)('0' + chInfo[i]); + } + String resultLevels = new String(resultLevelChars); + assertEquals("direction", expectedDir, resultDir); + assertEquals("levels", expectedLevels, resultLevels); + } +} diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java index 1e4db3d..7511ec1 100644 --- a/core/tests/coretests/src/android/text/StaticLayoutTest.java +++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java @@ -213,13 +213,13 @@ public class StaticLayoutTest extends TestCase { } public int scale(float height) { - int altVal = (int)(height * sMult + sAdd + 0.5); // existing impl + int altVal = (int)(height * sMult + sAdd + 0.5); int rndVal = Math.round(height * sMult + sAdd); if (altVal != rndVal) { Log.i("Scale", "expected scale: " + rndVal + " != returned scale: " + altVal); } - return altVal; // existing implementation + return rndVal; } } diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java index 23a4cde..22bbabc 100644 --- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java +++ b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java @@ -16,29 +16,28 @@ package android.widget.expandablelistview; -import android.widget.expandablelistview.ExpandableListSimple; +import android.app.Instrumentation; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.MediumTest; import android.util.ExpandableListScenario; import android.util.ListUtil; import android.util.ExpandableListScenario.MyGroup; - -import java.util.List; - -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; +import android.view.View; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; -public class ExpandableListBasicTest extends ActivityInstrumentationTestCase<ExpandableListSimple> { +import java.util.List; + +public class ExpandableListBasicTest extends ActivityInstrumentationTestCase2<ExpandableListSimple> { private ExpandableListScenario mActivity; private ExpandableListView mListView; private ExpandableListAdapter mAdapter; private ListUtil mListUtil; public ExpandableListBasicTest() { - super("com.android.frameworks.coretests", - ExpandableListSimple.class); + super(ExpandableListSimple.class); } @Override @@ -87,7 +86,6 @@ public class ExpandableListBasicTest extends ActivityInstrumentationTestCase<Exp @MediumTest public void testExpandedGroupMovement() { - // Expand the first group mListUtil.arrowScrollToSelectedPosition(0); sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); @@ -125,5 +123,55 @@ public class ExpandableListBasicTest extends ActivityInstrumentationTestCase<Exp assertFalse("The expanded state was given to the inserted group", mListView.isGroupExpanded(0)); } - + + // Static utility method, shared by different ExpandableListView scenario. + static void checkGroupAndChildPositions(ExpandableListView elv, + ActivityInstrumentationTestCase2<? extends ExpandableListScenario> activityInstrumentation) { + // Add a position tester ContextMenu listener to the ExpandableListView + PositionTesterContextMenuListener menuListener = new PositionTesterContextMenuListener(); + elv.setOnCreateContextMenuListener(menuListener); + + ListUtil listUtil = new ListUtil(elv, activityInstrumentation.getInstrumentation()); + ExpandableListAdapter adapter = elv.getExpandableListAdapter(); + Instrumentation instrumentation = activityInstrumentation.getInstrumentation(); + + int index = elv.getHeaderViewsCount(); + int groupCount = adapter.getGroupCount(); + for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) { + + // Expand group + assertFalse("Group is already expanded", elv.isGroupExpanded(groupIndex)); + listUtil.arrowScrollToSelectedPosition(index); + instrumentation.waitForIdleSync(); + activityInstrumentation.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); + activityInstrumentation.getInstrumentation().waitForIdleSync(); + assertTrue("Group did not expand " + groupIndex, elv.isGroupExpanded(groupIndex)); + + // Check group index in context menu + menuListener.expectGroupContextMenu(groupIndex); + // Make sure the group is visible so that getChild finds it + listUtil.arrowScrollToSelectedPosition(index); + View groupChild = elv.getChildAt(index - elv.getFirstVisiblePosition()); + elv.showContextMenuForChild(groupChild); + index++; + + final int childrenCount = adapter.getChildrenCount(groupIndex); + for (int childIndex = 0; childIndex < childrenCount; childIndex++) { + // Check child index in context menu + listUtil.arrowScrollToSelectedPosition(index); + menuListener.expectChildContextMenu(groupIndex, childIndex); + View child = elv.getChildAt(index - elv.getFirstVisiblePosition()); + elv.showContextMenuForChild(child); + index++; + } + } + + // Cleanup: remove the listener we added. + elv.setOnCreateContextMenuListener(null); + } + + @MediumTest + public void testGroupChildPositions() { + checkGroupAndChildPositions(mListView, this); + } } diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java index 7965f9f..2251c1d 100644 --- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java +++ b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java @@ -16,13 +16,10 @@ package android.widget.expandablelistview; -import android.util.ExpandableListScenario; - import android.os.Bundle; +import android.util.ExpandableListScenario; import android.widget.Button; import android.widget.ExpandableListView; -import android.widget.ListAdapter; -import android.widget.ListView; public class ExpandableListWithHeaders extends ExpandableListScenario { private static final int[] sNumChildren = {1, 4, 3, 2, 6}; @@ -46,13 +43,13 @@ public class ExpandableListWithHeaders extends ExpandableListScenario { for (int i = 0; i < sNumOfHeadersAndFooters; i++) { Button header = new Button(this); - header.setText("Header View"); + header.setText("Header View " + i); expandableListView.addHeaderView(header); } for (int i = 0; i < sNumOfHeadersAndFooters; i++) { Button footer = new Button(this); - footer.setText("Footer View"); + footer.setText("Footer View " + i); expandableListView.addFooterView(footer); } diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java index 50d0929..ca07e6b 100644 --- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java +++ b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java @@ -16,22 +16,20 @@ package android.widget.expandablelistview; -import android.test.ActivityInstrumentationTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; +import android.util.ListUtil; import android.view.KeyEvent; import android.widget.ExpandableListView; -import android.widget.expandablelistview.ExpandableListWithHeaders; -import android.util.ListUtil; - -public class ExpandableListWithHeadersTest extends ActivityInstrumentationTestCase<ExpandableListWithHeaders> { +public class ExpandableListWithHeadersTest extends + ActivityInstrumentationTestCase2<ExpandableListWithHeaders> { private ExpandableListView mExpandableListView; private ListUtil mListUtil; public ExpandableListWithHeadersTest() { - super("com.android.frameworks.coretests", - ExpandableListWithHeaders.class); + super(ExpandableListWithHeaders.class); } @Override @@ -63,4 +61,9 @@ public class ExpandableListWithHeadersTest extends ActivityInstrumentationTestCa getInstrumentation().waitForIdleSync(); assertTrue(mExpandableListView.isGroupExpanded(0)); } + + @MediumTest + public void testGroupChildPositions() { + ExpandableListBasicTest.checkGroupAndChildPositions(mExpandableListView, this); + } } diff --git a/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java b/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java index 08b0d31..f4c9d56 100644 --- a/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java +++ b/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.view.widget.expandablelistview; +package android.widget.expandablelistview; import com.android.frameworks.coretests.R; diff --git a/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java b/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java new file mode 100644 index 0000000..b96bca1 --- /dev/null +++ b/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java @@ -0,0 +1,43 @@ +package android.widget.expandablelistview; + +import android.view.ContextMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View.OnCreateContextMenuListener; +import android.widget.ExpandableListView; + +import junit.framework.Assert; + +public class PositionTesterContextMenuListener implements OnCreateContextMenuListener { + + private int groupPosition, childPosition; + + private int testType; // as returned by getPackedPositionType + + public void expectGroupContextMenu(int groupPosition) { + this.groupPosition = groupPosition; + testType = ExpandableListView.PACKED_POSITION_TYPE_GROUP; + } + + public void expectChildContextMenu(int groupPosition, int childPosition) { + this.groupPosition = groupPosition; + this.childPosition = childPosition; + testType = ExpandableListView.PACKED_POSITION_TYPE_CHILD; + } + + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + ExpandableListView.ExpandableListContextMenuInfo elvMenuInfo = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo; + long packedPosition = elvMenuInfo.packedPosition; + + int packedPositionType = ExpandableListView.getPackedPositionType(packedPosition); + Assert.assertEquals("Wrong packed position type", testType, packedPositionType); + + int packedPositionGroup = ExpandableListView.getPackedPositionGroup(packedPosition); + Assert.assertEquals("Wrong group position", groupPosition, packedPositionGroup); + + if (testType == ExpandableListView.PACKED_POSITION_TYPE_CHILD) { + int packedPositionChild = ExpandableListView.getPackedPositionChild(packedPosition); + Assert.assertEquals("Wrong child position", childPosition, packedPositionChild); + } + } +} diff --git a/docs/html/guide/practices/design/performance.jd b/docs/html/guide/practices/design/performance.jd index ab3b3d3..baed020 100644 --- a/docs/html/guide/practices/design/performance.jd +++ b/docs/html/guide/practices/design/performance.jd @@ -356,25 +356,19 @@ for loop syntax will be exactly equivalent to explicit iterator usage.</p> <p>There are several alternatives for iterating through an array:</p> -<pre>public class Foo { - int mSplat; -} -public class ArrayBenchmark { - Foo[] mArray = new Foo[27]; - { - for (int i = 0; i < mArray.length; ++i) { - mArray[i] = new Foo(); - } +<pre> static class Foo { + int mSplat; } + Foo[] mArray = ... - public static void zero() { + public void zero() { int sum = 0; for (int i = 0; i < mArray.length; ++i) { sum += mArray[i].mSplat; } } - public static void one() { + public void one() { int sum = 0; Foo[] localArray = mArray; int len = localArray.length; @@ -384,13 +378,13 @@ public class ArrayBenchmark { } } - public static void two() { + public void two() { int sum = 0; for (Foo a : mArray) { sum += a.mSplat; } } -}</pre> +</pre> <p><strong>zero()</strong> is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index ba6c711..66c34b2 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -30,6 +30,7 @@ class IBinder; class ProcessState; class String8; class TextOutput; +class Flattenable; struct flat_binder_object; // defined in support_p/binder_module.h @@ -81,6 +82,7 @@ public: status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp<IBinder>& val); status_t writeWeakBinder(const wp<IBinder>& val); + status_t write(const Flattenable& val); // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle @@ -119,7 +121,7 @@ public: const char16_t* readString16Inplace(size_t* outLen) const; sp<IBinder> readStrongBinder() const; wp<IBinder> readWeakBinder() const; - + status_t read(Flattenable& val) const; // Retrieve native_handle from the parcel. This returns a copy of the // parcel's native_handle (the caller takes ownership). The caller diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h index 8e5f05f..ea15a5c 100644 --- a/include/media/stagefright/AudioPlayer.h +++ b/include/media/stagefright/AudioPlayer.h @@ -61,7 +61,7 @@ public: status_t seekTo(int64_t time_us); bool isSeeking(); - bool reachedEOS(); + bool reachedEOS(status_t *finalStatus); private: sp<MediaSource> mSource; @@ -81,6 +81,7 @@ private: bool mSeeking; bool mReachedEOS; + status_t mFinalStatus; int64_t mSeekTimeUs; bool mStarted; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index f8bc7ab..24c2f46 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -96,6 +96,7 @@ private: kRequiresFlushBeforeShutdown = 64, kDefersOutputBufferAllocation = 128, kDecoderLiesAboutNumberOfChannels = 256, + kInputBufferSizesAreBogus = 512, }; struct BufferInfo { @@ -131,6 +132,7 @@ private: PortStatus mPortStatus[2]; bool mInitialBufferSubmit; bool mSignalledEOS; + status_t mFinalStatus; bool mNoMoreOutputData; bool mOutputPortSettingsHaveChanged; int64_t mSeekTimeUs; diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index b9c491b..e72b6b3 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -23,6 +23,7 @@ #include <ui/android_native_buffer.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> +#include <utils/Flattenable.h> #include <pixelflinger/pixelflinger.h> struct android_native_buffer_t; @@ -30,7 +31,6 @@ struct android_native_buffer_t; namespace android { class GraphicBufferMapper; -class Parcel; // =========================================================================== // GraphicBuffer @@ -40,7 +40,7 @@ class GraphicBuffer : public EGLNativeBase< android_native_buffer_t, GraphicBuffer, - LightRefBase<GraphicBuffer> > + LightRefBase<GraphicBuffer> >, public Flattenable { public: @@ -97,7 +97,6 @@ public: uint32_t getVerticalStride() const; protected: - GraphicBuffer(const Parcel& reply); virtual ~GraphicBuffer(); enum { @@ -122,8 +121,16 @@ private: status_t initSize(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage); - static status_t writeToParcel(Parcel* reply, - android_native_buffer_t const* buffer); + void free_handle(); + + // Flattenable interface + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void* buffer, size_t size, + int fds[], size_t count) const; + status_t unflatten(void const* buffer, size_t size, + int fds[], size_t count); + GraphicBufferMapper& mBufferMapper; ssize_t mInitCheck; diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h new file mode 100644 index 0000000..852be3b --- /dev/null +++ b/include/utils/Flattenable.h @@ -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. + */ + +#ifndef ANDROID_UTILS_FLATTENABLE_H +#define ANDROID_UTILS_FLATTENABLE_H + + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Errors.h> + +namespace android { + +class Flattenable +{ +public: + // size in bytes of the flattened object + virtual size_t getFlattenedSize() const = 0; + + // number of file descriptors to flatten + virtual size_t getFdCount() const = 0; + + // flattens the object into buffer. + // size should be at least of getFlattenedSize() + // file descriptors are written in the fds[] array but ownership is + // not transfered (ie: they must be dupped by the caller of + // flatten() if needed). + virtual status_t flatten(void* buffer, size_t size, + int fds[], size_t count) const = 0; + + // unflattens the object from buffer. + // size should be equal to the value of getFlattenedSize() when the + // object was flattened. + // unflattened file descriptors are found in the fds[] array and + // don't need to be dupped(). ie: the caller of unflatten doesn't + // keep ownership. If a fd is not retained by unflatten() it must be + // explicitly closed. + virtual status_t unflatten(void const* buffer, size_t size, + int fds[], size_t count) = 0; + +protected: + virtual ~Flattenable() = 0; + +}; + +}; // namespace android + + +#endif /* ANDROID_UTILS_FLATTENABLE_H */ diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 13ea27e..cd657e8 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -447,6 +447,8 @@ public: } const char16_t* stringAt(size_t idx, size_t* outLen) const; + const char* string8At(size_t idx, size_t* outLen) const; + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; const ResStringPool_span* styleAt(size_t idx) const; diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java index 61a4293..abdb0ae 100644 --- a/keystore/java/android/security/SystemKeyStore.java +++ b/keystore/java/android/security/SystemKeyStore.java @@ -17,6 +17,7 @@ package android.security; import android.os.Environment; +import android.os.FileUtils; import android.os.Process; import java.io.File; @@ -92,6 +93,8 @@ public class SystemKeyStore { fos.write(retKey); fos.flush(); fos.close(); + FileUtils.setPermissions(keyFile.getName(), (FileUtils.S_IRUSR | FileUtils.S_IWUSR), + -1, -1); } catch (IOException ioe) { return null; } diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp index 096aa73..7b866c7 100644 --- a/libs/audioflinger/AudioPolicyManagerBase.cpp +++ b/libs/audioflinger/AudioPolicyManagerBase.cpp @@ -82,8 +82,8 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev // keep track of SCO device address mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); #ifdef WITH_A2DP - if ((mA2dpDeviceAddress == mScoDeviceAddress) && - (mPhoneState != AudioSystem::MODE_NORMAL)) { + if (mA2dpOutput != 0 && + mPhoneState != AudioSystem::MODE_NORMAL) { mpClientInterface->suspendOutput(mA2dpOutput); } #endif @@ -116,8 +116,8 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev if (AudioSystem::isBluetoothScoDevice(device)) { mScoDeviceAddress = ""; #ifdef WITH_A2DP - if ((mA2dpDeviceAddress == mScoDeviceAddress) && - (mPhoneState != AudioSystem::MODE_NORMAL)) { + if (mA2dpOutput != 0 && + mPhoneState != AudioSystem::MODE_NORMAL) { mpClientInterface->restoreOutput(mA2dpOutput); } #endif @@ -275,10 +275,8 @@ void AudioPolicyManagerBase::setPhoneState(int state) newDevice = getNewDevice(mHardwareOutput, false); #ifdef WITH_A2DP checkOutputForAllStrategies(newDevice); - // suspend A2DP output if SCO device address is the same as A2DP device address. - // no need to check that a SCO device is actually connected as mScoDeviceAddress == "" - // if none is connected and the test below will fail. - if (mA2dpDeviceAddress == mScoDeviceAddress) { + // suspend A2DP output if a SCO device is present. + if (mA2dpOutput != 0 && mScoDeviceAddress != "") { if (oldState == AudioSystem::MODE_NORMAL) { mpClientInterface->suspendOutput(mA2dpOutput); } else if (state == AudioSystem::MODE_NORMAL) { @@ -295,13 +293,31 @@ void AudioPolicyManagerBase::setPhoneState(int state) if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) { newDevice = hwOutputDesc->device(); } + + // when changing from ring tone to in call mode, mute the ringing tone + // immediately and delay the route change to avoid sending the ring tone + // tail into the earpiece or headset. + int delayMs = 0; + if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) { + // delay the device change command by twice the output latency to have some margin + // and be sure that audio buffers not yet affected by the mute are out when + // we actually apply the route change + delayMs = hwOutputDesc->mLatency*2; + setStreamMute(AudioSystem::RING, true, mHardwareOutput); + } + // change routing is necessary - setOutputDevice(mHardwareOutput, newDevice, force); + setOutputDevice(mHardwareOutput, newDevice, force, delayMs); // if entering in call state, handle special case of active streams // pertaining to sonification strategy see handleIncallSonification() if (state == AudioSystem::MODE_IN_CALL) { LOGV("setPhoneState() in call state management: new state is %d", state); + // unmute the ringing tone after a sufficient delay if it was muted before + // setting output device above + if (oldState == AudioSystem::MODE_RINGTONE) { + setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS); + } for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { handleIncallSonification(stream, true, true); } @@ -1173,7 +1189,7 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices } AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); - if (mA2dpDeviceAddress == mScoDeviceAddress) { + if (mScoDeviceAddress != "") { // It is normal to suspend twice if we are both in call, // and have the hardware audio output routed to BT SCO if (mPhoneState != AudioSystem::MODE_NORMAL) { @@ -1207,10 +1223,10 @@ status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devi return INVALID_OPERATION; } - // mute media during 2 seconds to avoid outputing sound on hardware output while music stream + // mute media strategy to avoid outputting sound on hardware output while music stream // is switched from A2DP output and before music is paused by music application setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput); - setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, 2000); + setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS); if (!a2dpUsedForSonification()) { // unmute music on A2DP output if a notification or ringtone is playing @@ -1538,9 +1554,9 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t usleep(outputDesc->mLatency*2*1000); } #ifdef WITH_A2DP - // suspend A2D output if SCO device is selected + // suspend A2DP output if SCO device is selected if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { - if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) { + if (mA2dpOutput != 0) { mpClientInterface->suspendOutput(mA2dpOutput); } } @@ -1555,7 +1571,7 @@ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t #ifdef WITH_A2DP // if disconnecting SCO device, restore A2DP output if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) { - if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) { + if (mA2dpOutput != 0) { LOGV("restore A2DP output"); mpClientInterface->restoreOutput(mA2dpOutput); } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index e397bce..00d2210 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -28,6 +28,7 @@ #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/misc.h> +#include <utils/Flattenable.h> #include <private/binder/binder_module.h> @@ -675,6 +676,42 @@ status_t Parcel::writeDupFileDescriptor(int fd) return writeObject(obj, true); } +status_t Parcel::write(const Flattenable& val) +{ + status_t err; + + // size if needed + size_t len = val.getFlattenedSize(); + size_t fd_count = val.getFdCount(); + + err = this->writeInt32(len); + if (err) return err; + + err = this->writeInt32(fd_count); + if (err) return err; + + // payload + void* buf = this->writeInplace(PAD_SIZE(len)); + if (buf == NULL) + return BAD_VALUE; + + int* fds = NULL; + if (fd_count) { + fds = new int[fd_count]; + } + + err = val.flatten(buf, len, fds, fd_count); + for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) { + err = this->writeDupFileDescriptor( fds[i] ); + } + + if (fd_count) { + delete [] fds; + } + + return err; +} + status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) { const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; @@ -713,7 +750,6 @@ restart_write: goto restart_write; } - void Parcel::remove(size_t start, size_t amt) { LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); @@ -940,6 +976,38 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } +status_t Parcel::read(Flattenable& val) const +{ + // size + const size_t len = this->readInt32(); + const size_t fd_count = this->readInt32(); + + // payload + void const* buf = this->readInplace(PAD_SIZE(len)); + if (buf == NULL) + return BAD_VALUE; + + int* fds = NULL; + if (fd_count) { + fds = new int[fd_count]; + } + + status_t err = NO_ERROR; + for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) { + fds[i] = dup(this->readFileDescriptor()); + if (fds[i] < 0) err = BAD_VALUE; + } + + if (err == NO_ERROR) { + err = val.unflatten(buf, len, fds, fd_count); + } + + if (fd_count) { + delete [] fds; + } + + return err; +} const flat_binder_object* Parcel::readObject(bool nullMetaData) const { const size_t DPOS = mDataPos; diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index dec993a..cc3a74f 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -546,6 +546,8 @@ void Context::setSurface(uint32_t w, uint32_t h, android_native_window_t *sur) glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits); glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors); + + mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot"); } } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 03e65f1..04bd748 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -163,6 +163,8 @@ public: mutable const ObjectBase * mObjHead; + bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;} + protected: Device *mDev; @@ -196,6 +198,8 @@ protected: int32_t mMaxVertexAttribs; int32_t mMaxVertexUniformVectors; int32_t mMaxVertexTextureUnits; + + bool OES_texture_npot; } mGL; uint32_t mWidth; diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 15f3269..c17b94c 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -109,7 +109,7 @@ void ProgramFragment::setupGL(const Context *rsc, ProgramFragmentState *state) } if (mSamplers[ct].get()) { - mSamplers[ct]->setupGL(rsc); + mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2()); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -159,7 +159,7 @@ void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID()); rsc->checkError("ProgramFragment::setupGL2 tex bind"); if (mSamplers[ct].get()) { - mSamplers[ct]->setupGL(rsc); + mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2()); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp index 7552d54..71f508f 100644 --- a/libs/rs/rsSampler.cpp +++ b/libs/rs/rsSampler.cpp @@ -53,7 +53,7 @@ Sampler::~Sampler() { } -void Sampler::setupGL(const Context *rsc) +void Sampler::setupGL(const Context *rsc, bool npot) { GLenum trans[] = { GL_NEAREST, //RS_SAMPLER_NEAREST, @@ -64,11 +64,21 @@ void Sampler::setupGL(const Context *rsc) }; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + bool forceNonMip = false; + if (!rsc->ext_OES_texture_npot() && npot) { + forceNonMip = true; + } + + if ((mMinFilter == RS_SAMPLER_LINEAR_MIP_LINEAR) && forceNonMip) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[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]); + rsc->checkError("ProgramFragment::setupGL2 tex env"); } diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h index 9e20a2f..0506081 100644 --- a/libs/rs/rsSampler.h +++ b/libs/rs/rsSampler.h @@ -41,7 +41,7 @@ public: virtual ~Sampler(); void bind(Allocation *); - void setupGL(const Context *); + void setupGL(const Context *, bool npot); void bindToContext(SamplerState *, uint32_t slot); void unbindFromContext(SamplerState *); diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp index 9d24c6c..c09e979 100644 --- a/libs/rs/rsType.cpp +++ b/libs/rs/rsType.cpp @@ -283,6 +283,24 @@ void Type::dumpLOGV(const char *prefix) const mElement->dumpLOGV(buf); } +bool Type::getIsNp2() const +{ + uint32_t x = getDimX(); + uint32_t y = getDimY(); + uint32_t z = getDimZ(); + + if (x && (x & (x-1))) { + return true; + } + if (y && (y & (y-1))) { + return true; + } + if (z && (z & (z-1))) { + return true; + } + return false; +} + ////////////////////////////////////////////////// // diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h index 28e6274..c25577c 100644 --- a/libs/rs/rsType.h +++ b/libs/rs/rsType.h @@ -56,6 +56,7 @@ public: uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const; uint32_t getLODCount() const {return mLODCount;} + bool getIsNp2() const; void setElement(const Element *e) {mElement.set(e);} @@ -65,6 +66,7 @@ public: void setDimFaces(bool v) {mFaces = v;} void setDimLOD(bool v) {mDimLOD = v;} + void clear(); void compute(); diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 395a937..86eb78d 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -35,7 +35,6 @@ LOCAL_SHARED_LIBRARIES := \ libpixelflinger \ libhardware \ libutils \ - libskia \ libEGL \ libGLESv1_CM \ libbinder \ diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index d1bbd04..6aacd82 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -22,6 +22,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <GLES/gl.h> #include <utils/RefBase.h> diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 2d6152e..b408779 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -1496,8 +1496,8 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) layer->needsBlending(), layer->needsDithering(), layer->contentDirty, s.alpha, s.flags, - s.transform[0], s.transform[1], - s.transform[2], s.transform[3]); + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); result.append(buffer); buffer[0] = 0; /*** LayerBaseClient ***/ @@ -1833,27 +1833,25 @@ void GraphicPlane::setDisplayHardware(DisplayHardware *hw) status_t GraphicPlane::orientationToTransfrom( int orientation, int w, int h, Transform* tr) -{ - float a, b, c, d, x, y; +{ + uint32_t flags = 0; switch (orientation) { case ISurfaceComposer::eOrientationDefault: - // make sure the default orientation is optimal - tr->reset(); - return NO_ERROR; + flags = Transform::ROT_0; + break; case ISurfaceComposer::eOrientation90: - a=0; b=-1; c=1; d=0; x=w; y=0; + flags = Transform::ROT_90; break; case ISurfaceComposer::eOrientation180: - a=-1; b=0; c=0; d=-1; x=w; y=h; + flags = Transform::ROT_180; break; case ISurfaceComposer::eOrientation270: - a=0; b=1; c=-1; d=0; x=0; y=h; + flags = Transform::ROT_270; break; default: return BAD_VALUE; } - tr->set(a, b, c, d); - tr->set(x, y); + tr->set(flags, w, h); return NO_ERROR; } @@ -1869,24 +1867,13 @@ status_t GraphicPlane::setOrientation(int orientation) mHeight = int(h); Transform orientationTransform; - if (UNLIKELY(orientation == 42)) { - float a, b, c, d, x, y; - const float r = (3.14159265f / 180.0f) * 42.0f; - const float si = sinf(r); - const float co = cosf(r); - a=co; b=-si; c=si; d=co; - x = si*(h*0.5f) + (1-co)*(w*0.5f); - y =-si*(w*0.5f) + (1-co)*(h*0.5f); - orientationTransform.set(a, b, c, d); - orientationTransform.set(x, y); - } else { - GraphicPlane::orientationToTransfrom(orientation, w, h, - &orientationTransform); - if (orientation & ISurfaceComposer::eOrientationSwapMask) { - mWidth = int(h); - mHeight = int(w); - } + GraphicPlane::orientationToTransfrom(orientation, w, h, + &orientationTransform); + if (orientation & ISurfaceComposer::eOrientationSwapMask) { + mWidth = int(h); + mHeight = int(w); } + mOrientation = orientation; mGlobalTransform = mDisplayTransform * orientationTransform; return NO_ERROR; diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp index ab6f7ba..b2d5856 100644 --- a/libs/surfaceflinger/Transform.cpp +++ b/libs/surfaceflinger/Transform.cpp @@ -14,180 +14,257 @@ * limitations under the License. */ -#include <ui/Region.h> +#include <math.h> -#include <private/pixelflinger/ggl_fixed.h> +#include <cutils/compiler.h> +#include <utils/String8.h> +#include <ui/Region.h> #include "Transform.h" // --------------------------------------------------------------------------- -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) +namespace android { // --------------------------------------------------------------------------- -namespace android { +template <typename T> inline T min(T a, T b) { + return a<b ? a : b; +} +template <typename T> inline T min(T a, T b, T c) { + return min(a, min(b, c)); +} +template <typename T> inline T min(T a, T b, T c, T d) { + return min(a, b, min(c, d)); +} + +template <typename T> inline T max(T a, T b) { + return a>b ? a : b; +} +template <typename T> inline T max(T a, T b, T c) { + return max(a, max(b, c)); +} +template <typename T> inline T max(T a, T b, T c, T d) { + return max(a, b, max(c, d)); +} // --------------------------------------------------------------------------- -Transform::Transform() - : mType(0) -{ - mTransform.reset(); +Transform::Transform() { + reset(); } Transform::Transform(const Transform& other) - : mTransform(other.mTransform), mType(other.mType) -{ + : mMatrix(other.mMatrix), mType(other.mType) { } -Transform::Transform(int32_t flags) { - mTransform.reset(); - int sx = (flags & FLIP_H) ? -1 : 1; - int sy = (flags & FLIP_V) ? -1 : 1; - if (flags & ROT_90) { - this->set(0, -sy, sx, 0); - } else { - this->set(sx, 0, 0, sy); - } +Transform::Transform(uint32_t orientation) { + set(orientation, 0, 0); } Transform::~Transform() { } + +bool Transform::absIsOne(float f) { + return fabs(f) == 1.0f; +} + +bool Transform::isZero(float f) { + return fabs(f) == 0.0f; +} + +bool Transform::absEqual(float a, float b) { + return fabs(a) == fabs(b); +} + Transform Transform::operator * (const Transform& rhs) const { - if (LIKELY(mType == 0)) + if (CC_LIKELY(mType == IDENTITY)) return rhs; Transform r(*this); - r.mTransform.preConcat(rhs.mTransform); + if (rhs.mType == IDENTITY) + return r; + + // TODO: we could use mType to optimize the matrix multiply + const mat33& A(mMatrix); + const mat33& B(rhs.mMatrix); + mat33& D(r.mMatrix); + for (int i=0 ; i<3 ; i++) { + const float v0 = A[0][i]; + const float v1 = A[1][i]; + const float v2 = A[2][i]; + D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; + D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; + D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; + } r.mType |= rhs.mType; - return r; -} -float Transform::operator [] (int i) const -{ - float r = 0; - switch(i) { - case 0: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleX] ); break; - case 1: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewX] ); break; - case 2: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewY] ); break; - case 3: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleY] ); break; - } + // TODO: we could recompute this value from r and rhs + r.mType &= 0xFF; + r.mType |= UNKNOWN_TYPE; return r; } -uint8_t Transform::type() const -{ - if (UNLIKELY(mType & 0x80000000)) { - mType = mTransform.getType(); - } - return uint8_t(mType & 0xFF); +float const* Transform::operator [] (int i) const { + return mMatrix[i].v; } bool Transform::transformed() const { - return type() > SkMatrix::kTranslate_Mask; + return type() > TRANSLATE; } int Transform::tx() const { - return SkScalarRound( mTransform[SkMatrix::kMTransX] ); + return floorf(mMatrix[2][0] + 0.5f); } int Transform::ty() const { - return SkScalarRound( mTransform[SkMatrix::kMTransY] ); + return floorf(mMatrix[2][1] + 0.5f); } void Transform::reset() { - mTransform.reset(); - mType = 0; + mType = IDENTITY; + for(int i=0 ; i<3 ; i++) { + vec3& v(mMatrix[i]); + for (int j=0 ; j<3 ; j++) + v[j] = ((i==j) ? 1.0f : 0.0f); + } } -void Transform::set( float xx, float xy, - float yx, float yy) +void Transform::set(float tx, float ty) { - mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(xx)); - mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(xy)); - mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(yx)); - mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(yy)); - mType |= 0x80000000; + mMatrix[2][0] = tx; + mMatrix[2][1] = ty; + mMatrix[2][2] = 1.0f; + + if (isZero(tx) && isZero(ty)) { + mType &= ~TRANSLATE; + } else { + mType |= TRANSLATE; + } } -void Transform::set(float radian, float x, float y) -{ - float r00 = cosf(radian); float r01 = -sinf(radian); - float r10 = sinf(radian); float r11 = cosf(radian); - mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(r00)); - mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(r01)); - mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(r10)); - mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(r11)); - mTransform.set(SkMatrix::kMTransX, SkIntToScalar(x - r00*x - r01*y)); - mTransform.set(SkMatrix::kMTransY, SkIntToScalar(y - r10*x - r11*y)); - mType |= 0x80000000 | SkMatrix::kTranslate_Mask; -} - -void Transform::scale(float s, float x, float y) +void Transform::set(float a, float b, float c, float d) { - mTransform.postScale(s, s, x, y); - mType |= 0x80000000; + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; + M[0][1] = c; M[1][1] = d; + M[0][2] = 0; M[1][2] = 0; + mType = UNKNOWN_TYPE; } -void Transform::set(int tx, int ty) +void Transform::set(uint32_t flags, float w, float h) { - if (tx | ty) { - mTransform.set(SkMatrix::kMTransX, SkIntToScalar(tx)); - mTransform.set(SkMatrix::kMTransY, SkIntToScalar(ty)); - mType |= SkMatrix::kTranslate_Mask; + mType = flags << 8; + float sx = (flags & FLIP_H) ? -1 : 1; + float sy = (flags & FLIP_V) ? -1 : 1; + float a=0, b=0, c=0, d=0, x=0, y=0; + int xmask = 0; + + // computation of x,y + // x y + // 0 0 0 + // w 0 ROT90 + // w h FLIPH|FLIPV + // 0 h FLIPH|FLIPV|ROT90 + + if (flags & ROT_90) { + mType |= ROTATE; + b = -sy; + c = sx; + xmask = 1; } else { - mTransform.set(SkMatrix::kMTransX, 0); - mTransform.set(SkMatrix::kMTransY, 0); - mType &= ~SkMatrix::kTranslate_Mask; + a = sx; + d = sy; } + + if (flags & FLIP_H) { + mType ^= SCALE; + xmask ^= 1; + } + + if (flags & FLIP_V) { + mType ^= SCALE; + y = h; + } + + if ((flags & ROT_180) == ROT_180) { + mType |= ROTATE; + } + + if (xmask) { + x = w; + } + + if (!isZero(x) || !isZero(y)) { + mType |= TRANSLATE; + } + + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; M[2][0] = x; + M[0][1] = c; M[1][1] = d; M[2][1] = y; + M[0][2] = 0; M[1][2] = 0; M[2][2] = 1; +} + +Transform::vec2 Transform::transform(const vec2& v) const { + vec2 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; + return r; } -void Transform::transform(GLfixed* point, int x, int y) const +Transform::vec3 Transform::transform(const vec3& v) const { + vec3 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; + r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; + return r; +} + +void Transform::transform(fixed1616* point, int x, int y) const { - SkPoint s; - mTransform.mapXY(SkIntToScalar(x), SkIntToScalar(y), &s); - point[0] = SkScalarToFixed(s.fX); - point[1] = SkScalarToFixed(s.fY); + const float toFixed = 65536.0f; + const mat33& M(mMatrix); + vec2 v(x, y); + v = transform(v); + point[0] = v[0] * toFixed; + point[1] = v[1] * toFixed; } Rect Transform::makeBounds(int w, int h) const { - Rect r; - SkRect d, s; - s.set(0, 0, SkIntToScalar(w), SkIntToScalar(h)); - mTransform.mapRect(&d, s); - r.left = SkScalarRound( d.fLeft ); - r.top = SkScalarRound( d.fTop ); - r.right = SkScalarRound( d.fRight ); - r.bottom = SkScalarRound( d.fBottom ); - return r; + return transform( Rect(w, h) ); } Rect Transform::transform(const Rect& bounds) const { Rect r; - SkRect d, s; - s.set( SkIntToScalar( bounds.left ), - SkIntToScalar( bounds.top ), - SkIntToScalar( bounds.right ), - SkIntToScalar( bounds.bottom )); - mTransform.mapRect(&d, s); - r.left = SkScalarRound( d.fLeft ); - r.top = SkScalarRound( d.fTop ); - r.right = SkScalarRound( d.fRight ); - r.bottom = SkScalarRound( d.fBottom ); + vec2 lt( bounds.left, bounds.top ); + vec2 rt( bounds.right, bounds.top ); + vec2 lb( bounds.left, bounds.bottom ); + vec2 rb( bounds.right, bounds.bottom ); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + return r; } Region Transform::transform(const Region& reg) const { Region out; - if (UNLIKELY(transformed())) { - if (LIKELY(preserveRects())) { + if (CC_UNLIKELY(transformed())) { + if (CC_LIKELY(preserveRects())) { Region::const_iterator it = reg.begin(); Region::const_iterator const end = reg.end(); while (it != end) { @@ -202,31 +279,108 @@ Region Transform::transform(const Region& reg) const return out; } -int32_t Transform::getOrientation() const +uint32_t Transform::type() const { - uint32_t flags = 0; - if (UNLIKELY(transformed())) { - SkScalar a = mTransform[SkMatrix::kMScaleX]; - SkScalar b = mTransform[SkMatrix::kMSkewX]; - SkScalar c = mTransform[SkMatrix::kMSkewY]; - SkScalar d = mTransform[SkMatrix::kMScaleY]; - if (b==0 && c==0 && a && d) { - if (a<0) flags |= FLIP_H; - if (d<0) flags |= FLIP_V; - } else if (b && c && a==0 && d==0) { - flags |= ROT_90; - if (b>0) flags |= FLIP_H; - if (c<0) flags |= FLIP_V; + if (mType & UNKNOWN_TYPE) { + // recompute what this transform is + + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + bool scale = false; + uint32_t flags = ROT_0; + if (isZero(b) && isZero(c)) { + if (absEqual(a, d)) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + if (!absIsOne(a) || !absIsOne(d)) { + scale = true; + } + } else { + flags = ROT_INVALID; + } + } else if (isZero(a) && isZero(d)) { + if (absEqual(b, c)) { + flags |= ROT_90; + if (b>0) flags |= FLIP_H; + if (c<0) flags |= FLIP_V; + if (!absIsOne(b) || !absIsOne(c)) { + scale = true; + } + } else { + flags = ROT_INVALID; + } + } else { + flags = ROT_INVALID; + } + + mType = flags << 8; + if (flags & ROT_INVALID) { + mType |= UNKNOWN; } else { - flags = 0x80000000; + if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) + mType |= ROTATE; + if (flags & FLIP_H) + mType ^= SCALE; + if (flags & FLIP_V) + mType ^= SCALE; + if (scale) + mType |= SCALE; } + + if (!isZero(x) || !isZero(y)) + mType |= TRANSLATE; } - return flags; + return mType; +} + +uint32_t Transform::getType() const { + return type() & 0xFF; +} + +uint32_t Transform::getOrientation() const +{ + return (type() >> 8) & 0xFF; } bool Transform::preserveRects() const { - return mTransform.rectStaysRect(); + return (type() & ROT_INVALID) ? false : true; +} + +void Transform::dump(const char* name) const +{ + type(); // updates the type + + String8 flags, type; + const mat33& m(mMatrix); + uint32_t orient = mType >> 8; + + if (orient&ROT_INVALID) + flags.append("ROT_INVALID "); + if (orient&ROT_90) + flags.append("ROT_90 "); + if (orient&FLIP_V) + flags.append("FLIP_V "); + if (orient&FLIP_H) + flags.append("FLIP_H "); + + if (mType&SCALE) + type.append("SCALE "); + if (mType&ROTATE) + type.append("ROTATE "); + if (mType&TRANSLATE) + type.append("TRANSLATE "); + + LOGD("%s (%s, %s)", name, flags.string(), type.string()); + LOGD("%.2f %.2f %.2f", m[0][0], m[1][0], m[2][0]); + LOGD("%.2f %.2f %.2f", m[0][1], m[1][1], m[2][1]); + LOGD("%.2f %.2f %.2f", m[0][2], m[1][2], m[2][2]); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h index ddab404..51d3e3f 100644 --- a/libs/surfaceflinger/Transform.h +++ b/libs/surfaceflinger/Transform.h @@ -23,10 +23,6 @@ #include <ui/Point.h> #include <ui/Rect.h> -#include <GLES/gl.h> - -#include <core/SkMatrix.h> - namespace android { class Region; @@ -38,9 +34,12 @@ class Transform public: Transform(); Transform(const Transform& other); - Transform(int32_t flags); + explicit Transform(uint32_t orientation); ~Transform(); + typedef int32_t fixed1616; + + // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h enum orientation_flags { ROT_0 = 0x00000000, FLIP_H = 0x00000001, @@ -48,48 +47,79 @@ public: ROT_90 = 0x00000004, ROT_180 = FLIP_H|FLIP_V, ROT_270 = ROT_180|ROT_90, - ROT_INVALID = 0x80000000 + ROT_INVALID = 0x80 }; enum type_mask { IDENTITY = 0, TRANSLATE = 0x1, - SCALE = 0x2, - AFFINE = 0x4, - PERSPECTIVE = 0x8 + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 }; - bool transformed() const; - int32_t getOrientation() const; - bool preserveRects() const; - + // query the transform + bool transformed() const; + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + float const* operator [] (int i) const; // returns column i int tx() const; int ty() const; - + + // modify the transform void reset(); - void set(float xx, float xy, float yx, float yy); - void set(int tx, int ty); - void set(float radian, float x, float y); - void scale(float s, float x, float y); - + void set(float tx, float ty); + void set(float a, float b, float c, float d); + void set(uint32_t flags, float w, float h); + + // transform data Rect makeBounds(int w, int h) const; - void transform(GLfixed* point, int x, int y) const; + void transform(fixed1616* point, int x, int y) const; Region transform(const Region& reg) const; - Rect transform(const Rect& bounds) const; - Transform operator * (const Transform& rhs) const; - float operator [] (int i) const; - - inline uint32_t getType() const { return type(); } - - inline Transform(bool) : mType(0xFF) { }; - -private: - uint8_t type() const; private: - SkMatrix mTransform; - mutable uint32_t mType; + struct vec3 { + float v[3]; + inline vec3() { } + inline vec3(float a, float b, float c) { + v[0] = a; v[1] = b; v[2] = c; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct vec2 { + float v[2]; + inline vec2() { } + inline vec2(float a, float b) { + v[0] = a; v[1] = b; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (int i) const { return v[i]; } + inline vec3& operator [] (int i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + Rect transform(const Rect& bounds) const; + uint32_t type() const; + static bool absIsOne(float f); + static bool absEqual(float a, float b); + static bool isZero(float f); + + void dump(const char* name) const; + + mat33 mMatrix; + mutable uint32_t mType; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index 9125146..bb86199 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -78,7 +78,8 @@ public: data.writeInt32(bufferIdx); data.writeInt32(usage); remote()->transact(REQUEST_BUFFER, data, &reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(reply); + sp<GraphicBuffer> buffer = new GraphicBuffer(); + reply.read(*buffer); return buffer; } @@ -141,7 +142,9 @@ status_t BnSurface::onTransact( int bufferIdx = data.readInt32(); int usage = data.readInt32(); sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage)); - return GraphicBuffer::writeToParcel(reply, buffer.get()); + if (buffer == NULL) + return BAD_VALUE; + return reply->write(*buffer); } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 6a5c8a9..ba1fd9c 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ +#define LOG_TAG "GraphicBuffer" + #include <stdlib.h> #include <stdint.h> #include <sys/types.h> -#include <binder/Parcel.h> - #include <utils/Errors.h> #include <utils/Log.h> @@ -77,34 +77,21 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, handle = inHandle; } -GraphicBuffer::GraphicBuffer(const Parcel& data) - : BASE(), mOwner(ownHandle), mBufferMapper(GraphicBufferMapper::get()), - mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) +GraphicBuffer::~GraphicBuffer() { - // we own the handle in this case - width = data.readInt32(); - if (width < 0) { - width = height = stride = format = usage = 0; - handle = 0; - } else { - height = data.readInt32(); - stride = data.readInt32(); - format = data.readInt32(); - usage = data.readInt32(); - handle = data.readNativeHandle(); + if (handle) { + free_handle(); } } -GraphicBuffer::~GraphicBuffer() +void GraphicBuffer::free_handle() { - if (handle) { - if (mOwner == ownHandle) { - native_handle_close(handle); - native_handle_delete(const_cast<native_handle*>(handle)); - } else if (mOwner == ownData) { - GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); - allocator.free(handle); - } + if (mOwner == ownHandle) { + native_handle_close(handle); + native_handle_delete(const_cast<native_handle*>(handle)); + } else if (mOwner == ownData) { + GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); + allocator.free(handle); } } @@ -192,29 +179,83 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) return res; } +size_t GraphicBuffer::getFlattenedSize() const { + return (8 + (handle ? handle->numInts : 0))*sizeof(int); +} + +size_t GraphicBuffer::getFdCount() const { + return handle ? handle->numFds : 0; +} -status_t GraphicBuffer::writeToParcel(Parcel* reply, - android_native_buffer_t const* buffer) +status_t GraphicBuffer::flatten(void* buffer, size_t size, + int fds[], size_t count) const { - if (buffer == NULL) - return BAD_VALUE; + size_t sizeNeeded = GraphicBuffer::getFlattenedSize(); + if (size < sizeNeeded) return NO_MEMORY; + + size_t fdCountNeeded = GraphicBuffer::getFdCount(); + if (count < fdCountNeeded) return NO_MEMORY; + + int* buf = static_cast<int*>(buffer); + buf[0] = 'GBFR'; + buf[1] = width; + buf[2] = height; + buf[3] = stride; + buf[4] = format; + buf[5] = usage; + buf[6] = 0; + buf[7] = 0; - if (buffer->width < 0 || buffer->height < 0) - return BAD_VALUE; + if (handle) { + buf[6] = handle->numFds; + buf[7] = handle->numInts; + native_handle_t const* const h = handle; + memcpy(fds, h->data, h->numFds*sizeof(int)); + memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int)); + } + + return NO_ERROR; +} + +status_t GraphicBuffer::unflatten(void const* buffer, size_t size, + int fds[], size_t count) +{ + if (size < 8*sizeof(int)) return NO_MEMORY; + + int const* buf = static_cast<int const*>(buffer); + if (buf[0] != 'GBFR') return BAD_TYPE; + + const size_t numFds = buf[6]; + const size_t numInts = buf[7]; - status_t err = NO_ERROR; - if (buffer->handle == NULL) { - // this buffer doesn't have a handle - reply->writeInt32(NO_MEMORY); + const size_t sizeNeeded = (8 + numInts) * sizeof(int); + if (size < sizeNeeded) return NO_MEMORY; + + size_t fdCountNeeded = 0; + if (count < fdCountNeeded) return NO_MEMORY; + + if (handle) { + // free previous handle if any + free_handle(); + } + + if (numFds || numInts) { + width = buf[1]; + height = buf[2]; + stride = buf[3]; + format = buf[4]; + usage = buf[5]; + native_handle* h = native_handle_create(numFds, numInts); + memcpy(h->data, fds, numFds*sizeof(int)); + memcpy(h->data + numFds, &buf[8], numInts*sizeof(int)); + handle = h; } else { - reply->writeInt32(buffer->width); - reply->writeInt32(buffer->height); - reply->writeInt32(buffer->stride); - reply->writeInt32(buffer->format); - reply->writeInt32(buffer->usage); - err = reply->writeNativeHandle(buffer->handle); + width = height = stride = format = usage = 0; + handle = NULL; } - return err; + + mOwner = ownHandle; + return NO_ERROR; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d2cfd3b..d0eedb4 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -25,6 +25,7 @@ commonSources:= \ CallStack.cpp \ Debug.cpp \ FileMap.cpp \ + Flattenable.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/Flattenable.cpp b/libs/utils/Flattenable.cpp new file mode 100644 index 0000000..1f2ffaa --- /dev/null +++ b/libs/utils/Flattenable.cpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#include <utils/Flattenable.h> + +namespace android { + +Flattenable::~Flattenable() { +} + +}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index e8bd5cf..38600b9 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -497,6 +497,34 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const return NULL; } +const char* ResStringPool::string8At(size_t idx, size_t* outLen) const +{ + if (mError == NO_ERROR && idx < mHeader->stringCount) { + const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; + const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); + if (off < (mStringPoolSize-1)) { + if (isUTF8) { + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; + DECODE_LENGTH(str, sizeof(uint8_t), *outLen) + size_t encLen; + DECODE_LENGTH(str, sizeof(uint8_t), encLen) + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + return (const char*)str; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + } + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const { return styleAt(ref.index); @@ -4018,14 +4046,19 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const printf("(attribute) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_STRING) { size_t len; - const char16_t* str = pkg->header->values.stringAt( + const char* str8 = pkg->header->values.string8At( value.data, &len); - if (str == NULL) { - printf("(string) null\n"); + if (str8 != NULL) { + printf("(string8) \"%s\"\n", str8); } else { - printf("(string%d) \"%s\"\n", - pkg->header->values.isUTF8()?8:16, - String8(str, len).string()); + const char16_t* str16 = pkg->header->values.stringAt( + value.data, &len); + if (str16 != NULL) { + printf("(string16) \"%s\"\n", + String8(str16, len).string()); + } else { + printf("(string) null\n"); + } } } else if (value.dataType == Res_value::TYPE_FLOAT) { printf("(float) %g\n", *(const float*)&value.data); diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 8e84106..90b50cc 100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -321,7 +321,9 @@ public class GpsLocationProvider implements LocationProviderInterface { public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; mLocationManager = locationManager; - mNIHandler= new GpsNetInitiatedHandler(context, this); + mNIHandler = new GpsNetInitiatedHandler(context, this); + + mLocation.setExtras(mLocationExtras); // Create a wake lock PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -759,7 +761,7 @@ public class GpsLocationProvider implements LocationProviderInterface { positionMode = GPS_POSITION_MODE_STANDALONE; } - if (!native_start(positionMode, false, mFixInterval)) { + if (!native_start(positionMode, false, 1)) { mStarted = false; Log.e(TAG, "native_start failed in startNavigating()"); return; @@ -870,7 +872,12 @@ public class GpsLocationProvider implements LocationProviderInterface { } if (mStarted && mStatus != LocationProvider.AVAILABLE) { - mAlarmManager.cancel(mTimeoutIntent); + // we still want to time out if we do not receive MIN_FIX_COUNT + // within the time out and we are requesting infrequent fixes + if (mFixInterval < NO_FIX_TIMEOUT) { + mAlarmManager.cancel(mTimeoutIntent); + } + // send an intent to notify that the GPS is receiving fixes. Intent intent = new Intent(GPS_FIX_CHANGE_ACTION); intent.putExtra(EXTRA_ENABLED, true); diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java index 02a9f48..978bda2 100644 --- a/location/java/com/android/internal/location/GpsXtraDownloader.java +++ b/location/java/com/android/internal/location/GpsXtraDownloader.java @@ -32,10 +32,10 @@ import java.util.Random; import android.content.Context; import android.net.Proxy; +import android.net.http.AndroidHttpClient; import android.util.Config; import android.util.Log; -import com.android.common.AndroidHttpClient; /** diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 2294069..9d1d420 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -17,13 +17,16 @@ package android.media; import android.content.ContentValues; -import android.os.SystemProperties; import android.provider.MediaStore.Audio; import android.provider.MediaStore.Images; import android.provider.MediaStore.Video; +import android.media.DecoderCapabilities; +import android.media.DecoderCapabilities.VideoDecoder; +import android.media.DecoderCapabilities.AudioDecoder; import java.util.HashMap; import java.util.Iterator; +import java.util.List; /** * MediaScanner helper class. @@ -98,13 +101,34 @@ public class MediaFile { sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType)); sMimeTypeMap.put(mimeType, Integer.valueOf(fileType)); } + + private static boolean isWMAEnabled() { + List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders(); + for (AudioDecoder decoder: decoders) { + if (decoder == AudioDecoder.AUDIO_DECODER_WMA) { + return true; + } + } + return false; + } + + private static boolean isWMVEnabled() { + List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders(); + for (VideoDecoder decoder: decoders) { + if (decoder == VideoDecoder.VIDEO_DECODER_WMV) { + return true; + } + } + return false; + } + static { addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg"); addFileType("M4A", FILE_TYPE_M4A, "audio/mp4"); addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav"); addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); - if (SystemProperties.getInt("ro.media.dec.aud.wma.enabled", 0) != 0) { + if (isWMAEnabled()) { addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma"); } addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); @@ -127,7 +151,7 @@ public class MediaFile { addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp"); addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2"); addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2"); - if (SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", 0) != 0) { + if (isWMVEnabled()) { addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv"); addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf"); } diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp index f248557..d965d9a 100644 --- a/media/jni/android_media_ResampleInputStream.cpp +++ b/media/jni/android_media_ResampleInputStream.cpp @@ -92,7 +92,7 @@ static void android_media_ResampleInputStream_fir21(JNIEnv *env, jclass clazz, jint jNpoints) { // safety first! - if (nFir21 + jNpoints > BUF_SIZE) { + if (nFir21 + jNpoints * 2 > BUF_SIZE) { throwException(env, "java/lang/IllegalArgumentException", "FIR+data too long %d", nFir21 + jNpoints); return; diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 7997cd6..57f58be 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -38,6 +38,7 @@ AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink) mPositionTimeRealUs(-1), mSeeking(false), mReachedEOS(false), + mFinalStatus(OK), mStarted(false), mAudioSink(audioSink) { } @@ -168,6 +169,7 @@ void AudioPlayer::stop() { mPositionTimeRealUs = -1; mSeeking = false; mReachedEOS = false; + mFinalStatus = OK; mStarted = false; } @@ -181,8 +183,11 @@ bool AudioPlayer::isSeeking() { return mSeeking; } -bool AudioPlayer::reachedEOS() { +bool AudioPlayer::reachedEOS(status_t *finalStatus) { + *finalStatus = OK; + Mutex::Autolock autoLock(mLock); + *finalStatus = mFinalStatus; return mReachedEOS; } @@ -245,6 +250,7 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { if (err != OK) { mReachedEOS = true; + mFinalStatus = err; break; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b3a73b0..ab65b44 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -434,14 +434,22 @@ void AwesomePlayer::onStreamDone() { } mStreamDoneEventPending = false; - if (mFlags & LOOPING) { + if (mStreamDoneStatus == ERROR_END_OF_STREAM && (mFlags & LOOPING)) { seekTo_l(0); if (mVideoSource != NULL) { postVideoEvent_l(); } } else { - notifyListener_l(MEDIA_PLAYBACK_COMPLETE); + if (mStreamDoneStatus == ERROR_END_OF_STREAM) { + LOGV("MEDIA_PLAYBACK_COMPLETE"); + notifyListener_l(MEDIA_PLAYBACK_COMPLETE); + } else { + LOGV("MEDIA_ERROR %d", mStreamDoneStatus); + + notifyListener_l( + MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus); + } pause_l(); @@ -802,7 +810,7 @@ void AwesomePlayer::onVideoEvent() { continue; } - postStreamDoneEvent_l(); + postStreamDoneEvent_l(err); return; } @@ -904,11 +912,13 @@ void AwesomePlayer::postVideoEvent_l(int64_t delayUs) { mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs); } -void AwesomePlayer::postStreamDoneEvent_l() { +void AwesomePlayer::postStreamDoneEvent_l(status_t status) { if (mStreamDoneEventPending) { return; } mStreamDoneEventPending = true; + + mStreamDoneStatus = status; mQueue.postEvent(mStreamDoneEvent); } @@ -947,9 +957,10 @@ void AwesomePlayer::onCheckAudioStatus() { notifyListener_l(MEDIA_SEEK_COMPLETE); } - if (mWatchForAudioEOS && mAudioPlayer->reachedEOS()) { + status_t finalStatus; + if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) { mWatchForAudioEOS = false; - postStreamDoneEvent_l(); + postStreamDoneEvent_l(finalStatus); } postCheckAudioStatusEvent_l(); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 16635d3..165ac09 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -463,7 +463,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } return OK; } @@ -496,6 +499,23 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } } + if (chunk_type == FOURCC('t', 'r', 'a', 'k')) { + Track *track = new Track; + track->next = NULL; + if (mLastTrack) { + mLastTrack->next = track; + } else { + mFirstTrack = track; + } + mLastTrack = track; + + track->meta = new MetaData; + track->includes_expensive_metadata = false; + track->timescale = 0; + track->sampleTable = new SampleTable(mDataSource); + track->meta->setCString(kKeyMIMEType, "application/octet-stream"); + } + off_t stop_offset = *offset + chunk_size; *offset = data_offset; while (*offset < stop_offset) { @@ -504,9 +524,18 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); - if (chunk_type == FOURCC('m', 'o', 'o', 'v')) { + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + + if (chunk_type == FOURCC('t', 'r', 'a', 'k')) { + status_t err = verifyTrack(mLastTrack); + + if (err != OK) { + return err; + } + } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) { mHaveMetadata = true; return UNKNOWN_ERROR; // Return a dummy error. @@ -516,7 +545,9 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { case FOURCC('t', 'k', 'h', 'd'): { - CHECK(chunk_data_size >= 4); + if (chunk_data_size < 4) { + return ERROR_MALFORMED; + } uint8_t version; if (mDataSource->readAt(data_offset, &version, 1) < 1) { @@ -562,21 +593,6 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { height = U32_AT(&buffer[80]); } - Track *track = new Track; - track->next = NULL; - if (mLastTrack) { - mLastTrack->next = track; - } else { - mFirstTrack = track; - } - mLastTrack = track; - - track->meta = new MetaData; - track->includes_expensive_metadata = false; - track->timescale = 0; - track->sampleTable = new SampleTable(mDataSource); - track->meta->setCString(kKeyMIMEType, "application/octet-stream"); - *offset += chunk_size; break; } @@ -670,7 +686,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } uint8_t buffer[8]; - CHECK(chunk_data_size >= (off_t)sizeof(buffer)); + if (chunk_data_size < (off_t)sizeof(buffer)) { + return ERROR_MALFORMED; + } + if (mDataSource->readAt( data_offset, buffer, 8) < 8) { return ERROR_IO; @@ -696,7 +715,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } break; } @@ -748,7 +770,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } break; } @@ -792,7 +817,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } break; } @@ -942,7 +970,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { case FOURCC('m', 'e', 't', 'a'): { uint8_t buffer[4]; - CHECK(chunk_data_size >= (off_t)sizeof(buffer)); + if (chunk_data_size < (off_t)sizeof(buffer)) { + return ERROR_MALFORMED; + } + if (mDataSource->readAt( data_offset, buffer, 4) < 4) { return ERROR_IO; @@ -961,7 +992,10 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { return err; } } - CHECK_EQ(*offset, stop_offset); + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } break; } @@ -995,8 +1029,9 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { int64_t creationTime; if (header[0] == 1) { creationTime = U64_AT(&header[4]); + } else if (header[0] != 0) { + return ERROR_MALFORMED; } else { - CHECK_EQ(header[0], 0); creationTime = U32_AT(&header[4]); } @@ -1174,6 +1209,30 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) { track->meta, mDataSource, track->timescale, track->sampleTable); } +// static +status_t MPEG4Extractor::verifyTrack(Track *track) { + const char *mime; + CHECK(track->meta->findCString(kKeyMIMEType, &mime)); + + uint32_t type; + const void *data; + size_t size; + if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + if (!track->meta->findData(kKeyAVCC, &type, &data, &size) + || type != kTypeAVCC) { + return ERROR_MALFORMED; + } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) + || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + if (!track->meta->findData(kKeyESDS, &type, &data, &size) + || type != kTypeESDS) { + return ERROR_MALFORMED; + } + } + + return OK; +} + status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( const void *esds_data, size_t esds_size) { ESDS esds(esds_data, esds_size); @@ -1391,6 +1450,14 @@ status_t MPEG4Source::read( &sampleIndex, SampleTable::kSyncSample_Flag); if (err != OK) { + if (err == ERROR_OUT_OF_RANGE) { + // An attempt to seek past the end of the stream would + // normally cause this ERROR_OUT_OF_RANGE error. Propagating + // this all the way to the MediaPlayer would cause abnormal + // termination. Legacy behaviour appears to be to behave as if + // we had seeked to the end of stream, ending normally. + err = ERROR_END_OF_STREAM; + } return err; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 6cf7cff..974413d 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -298,6 +298,10 @@ uint32_t OMXCodec::getComponentQuirks(const char *componentName) { quirks |= kRequiresAllocateBufferOnOutputPorts; } + if (!strcmp(componentName, "OMX.TI.Video.Decoder")) { + quirks |= kInputBufferSizesAreBogus; + } + return quirks; } @@ -561,7 +565,8 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) { mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); CHECK_EQ(err, OK); - if (def.nBufferSize < size) { + if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus)) + || (def.nBufferSize < size)) { def.nBufferSize = size; } @@ -574,7 +579,12 @@ void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) { CHECK_EQ(err, OK); // Make sure the setting actually stuck. - CHECK(def.nBufferSize >= size); + if (portIndex == kPortIndexInput + && (mQuirks & kInputBufferSizesAreBogus)) { + CHECK_EQ(def.nBufferSize, size); + } else { + CHECK(def.nBufferSize >= size); + } } status_t OMXCodec::setVideoPortFormatType( @@ -1923,6 +1933,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) { CODEC_LOGV("signalling end of input stream."); flags |= OMX_BUFFERFLAG_EOS; + mFinalStatus = err; mSignalledEOS = true; } else { mNoMoreOutputData = false; @@ -2401,7 +2412,7 @@ status_t OMXCodec::read( } if (mFilledBuffers.empty()) { - return ERROR_END_OF_STREAM; + return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM; } if (mOutputPortSettingsHaveChanged) { diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp index 363e121..9c73f4a 100644 --- a/media/libstagefright/Prefetcher.cpp +++ b/media/libstagefright/Prefetcher.cpp @@ -55,6 +55,7 @@ private: size_t mIndex; bool mStarted; bool mReachedEOS; + status_t mFinalStatus; int64_t mSeekTimeUs; int64_t mCacheDurationUs; bool mPrefetcherStopped; @@ -306,7 +307,7 @@ status_t PrefetchedSource::read( } if (mCachedBuffers.empty()) { - return ERROR_END_OF_STREAM; + return mReachedEOS ? mFinalStatus : ERROR_END_OF_STREAM; } *out = *mCachedBuffers.begin(); @@ -353,6 +354,7 @@ void PrefetchedSource::cacheMore() { if (err != OK) { mReachedEOS = true; + mFinalStatus = err; mCondition.signal(); return; diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp index f1f7194..6d6e408 100644 --- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp +++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp @@ -160,14 +160,25 @@ status_t MP3Decoder::read( mConfig->outputFrameSize = buffer->size() / sizeof(int16_t); mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data()); - if (pvmp3_framedecoder(mConfig, mDecoderBuf) != NO_DECODING_ERROR) { - buffer->release(); - buffer = NULL; + ERROR_CODE decoderErr; + if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) + != NO_DECODING_ERROR) { + LOGV("mp3 decoder returned error %d", decoderErr); - mInputBuffer->release(); - mInputBuffer = NULL; + if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) { + buffer->release(); + buffer = NULL; + + mInputBuffer->release(); + mInputBuffer = NULL; + + return UNKNOWN_ERROR; + } - return UNKNOWN_ERROR; + // This is recoverable, just ignore the current frame and + // play silence instead. + memset(buffer->data(), 0, mConfig->outputFrameSize); + mConfig->inputBufferUsedLength = mInputBuffer->range_length(); } buffer->set_range( diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index ce8eeae..3590987 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -145,10 +145,11 @@ private: Condition mPreparedCondition; bool mIsAsyncPrepare; status_t mPrepareResult; + status_t mStreamDoneStatus; void postVideoEvent_l(int64_t delayUs = -1); void postBufferingEvent_l(); - void postStreamDoneEvent_l(); + void postStreamDoneEvent_l(status_t status); void postCheckAudioStatusEvent_l(); status_t getPosition_l(int64_t *positionUs); status_t play_l(); diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index 3a63e88..9d35e0c 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -68,6 +68,8 @@ private: status_t updateAudioTrackInfoFromESDS_MPEG4Audio( const void *esds_data, size_t esds_size); + static status_t verifyTrack(Track *track); + MPEG4Extractor(const MPEG4Extractor &); MPEG4Extractor &operator=(const MPEG4Extractor &); }; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 9ca060d..ff8757d 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -401,6 +401,33 @@ void OMX::invalidateNodeID_l(node_id node) { //////////////////////////////////////////////////////////////////////////////// +struct SharedVideoRenderer : public VideoRenderer { + SharedVideoRenderer(void *libHandle, VideoRenderer *obj) + : mLibHandle(libHandle), + mObj(obj) { + } + + virtual ~SharedVideoRenderer() { + delete mObj; + mObj = NULL; + + dlclose(mLibHandle); + mLibHandle = NULL; + } + + virtual void render( + const void *data, size_t size, void *platformPrivate) { + return mObj->render(data, size, platformPrivate); + } + +private: + void *mLibHandle; + VideoRenderer *mObj; + + SharedVideoRenderer(const SharedVideoRenderer &); + SharedVideoRenderer &operator=(const SharedVideoRenderer &); +}; + sp<IOMXRenderer> OMX::createRenderer( const sp<ISurface> &surface, const char *componentName, @@ -411,11 +438,7 @@ sp<IOMXRenderer> OMX::createRenderer( VideoRenderer *impl = NULL; - static void *libHandle = NULL; - - if (!libHandle) { - libHandle = dlopen("libstagefrighthw.so", RTLD_NOW); - } + void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW); if (libHandle) { typedef VideoRenderer *(*CreateRendererFunc)( @@ -434,6 +457,16 @@ sp<IOMXRenderer> OMX::createRenderer( if (func) { impl = (*func)(surface, componentName, colorFormat, displayWidth, displayHeight, encodedWidth, encodedHeight); + + if (impl) { + impl = new SharedVideoRenderer(libHandle, impl); + libHandle = NULL; + } + } + + if (libHandle) { + dlclose(libHandle); + libHandle = NULL; } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java index 717f7ba..d7cf069 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java @@ -1,28 +1,35 @@ package com.android.mediaframeworktest; import android.media.MediaRecorder; +import android.media.EncoderCapabilities; +import android.media.EncoderCapabilities.VideoEncoderCap; +import android.media.EncoderCapabilities.AudioEncoderCap; +import android.media.DecoderCapabilities; +import android.media.DecoderCapabilities.VideoDecoder; +import android.media.DecoderCapabilities.AudioDecoder; + import android.os.SystemProperties; +import java.util.List; import java.util.HashMap; -public class MediaProfileReader { +public class MediaProfileReader +{ + private static final List<VideoDecoder> videoDecoders = DecoderCapabilities.getVideoDecoders(); + private static final List<AudioDecoder> audioDecoders = DecoderCapabilities.getAudioDecoders(); + private static final List<VideoEncoderCap> videoEncoders = EncoderCapabilities.getVideoEncoders(); + private static final List<AudioEncoderCap> audioEncoders = EncoderCapabilities.getAudioEncoders(); + private static final HashMap<Integer, String> encoderMap = new HashMap<Integer, String>(); - public static final HashMap<String, Integer> - OUTPUT_FORMAT_TABLE = new HashMap<String, Integer>(); - public static String MEDIA_ENC_VID = "ro.media.enc.vid."; - public static String MEDIA_AUD_VID = "ro.media.enc.aud."; - public static String[] VIDEO_ENCODER_PROPERTY = {".width", ".height", ".bps", ".fps",}; - public static String[] AUDIO_ENCODER_PROPERTY = {".bps", ".hz", ".ch",}; + static { + initEncoderMap(); + }; - public static String getVideoCodecProperty() { - String s; - s = SystemProperties.get("ro.media.enc.vid.codec"); - return s; + public static List<VideoEncoderCap> getVideoEncoders() { + return videoEncoders; } - public static String getAudioCodecProperty() { - String s; - s = SystemProperties.get("ro.media.enc.aud.codec"); - return s; + public static List<AudioEncoderCap> getAudioEncoders() { + return audioEncoders; } public static String getDeviceType() { @@ -33,78 +40,56 @@ public class MediaProfileReader { } public static boolean getWMAEnable() { - // push all the property into one big table - int wmaEnable = 1; - wmaEnable = SystemProperties.getInt("ro.media.dec.aud.wma.enabled", - wmaEnable); - if (wmaEnable == 1) { - return true; - } else { - return false; + for (AudioDecoder decoder: audioDecoders) { + if (decoder == AudioDecoder.AUDIO_DECODER_WMA) { + return true; + } } + return false; } public static boolean getWMVEnable(){ - int wmvEnable = 1; - wmvEnable = SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", - wmvEnable); - if (wmvEnable == 1) { - return true; - } else { - return false; + for (VideoDecoder decoder: videoDecoders) { + if (decoder == VideoDecoder.VIDEO_DECODER_WMV) { + return true; + } } + return false; } - public static void createVideoProfileTable() { - // push all the property into one big table - String encoderType = getVideoCodecProperty(); - if (encoderType.length() != 0) { - String encoder[] = encoderType.split(","); - for (int i = 0; i < encoder.length; i++) { - for (int j = 0; j < VIDEO_ENCODER_PROPERTY.length; j++) { - String propertyName = MEDIA_ENC_VID + encoder[i] + VIDEO_ENCODER_PROPERTY[j]; - String prop = SystemProperties.get(propertyName); - // push to the table - String propRange[] = prop.split(","); - OUTPUT_FORMAT_TABLE.put((encoder[i] + VIDEO_ENCODER_PROPERTY[j] + "_low"), - Integer.parseInt(propRange[0])); - OUTPUT_FORMAT_TABLE.put((encoder[i] + VIDEO_ENCODER_PROPERTY[j] + "_high"), - Integer.parseInt(propRange[1])); - } - - } + public static String getVideoCodecName(int videoEncoder) { + if (videoEncoder != MediaRecorder.VideoEncoder.H263 && + videoEncoder != MediaRecorder.VideoEncoder.H264 && + videoEncoder != MediaRecorder.VideoEncoder.MPEG_4_SP) { + throw new IllegalArgumentException("Unsupported video encoder " + videoEncoder); } + return encoderMap.get(videoEncoder); } - public static void createAudioProfileTable() { - // push all the property into one big table - String audioType = getAudioCodecProperty(); - String encoder[] = audioType.split(","); - if (audioType.length() != 0) { - for (int i = 0; i < encoder.length; i++) { - for (int j = 0; j < AUDIO_ENCODER_PROPERTY.length; j++) { - String propertyName = MEDIA_AUD_VID + encoder[i] + AUDIO_ENCODER_PROPERTY[j]; - String prop = SystemProperties.get(propertyName); - // push to the table - String propRange[] = prop.split(","); - OUTPUT_FORMAT_TABLE.put((encoder[i] + AUDIO_ENCODER_PROPERTY[j] + "_low"), - Integer.parseInt(propRange[0])); - OUTPUT_FORMAT_TABLE.put((encoder[i] + AUDIO_ENCODER_PROPERTY[j] + "_high"), - Integer.parseInt(propRange[1])); - } - } + public static String getAudioCodecName(int audioEncoder) { + if (audioEncoder != MediaRecorder.AudioEncoder.AMR_NB && + audioEncoder != MediaRecorder.AudioEncoder.AMR_WB && + audioEncoder != MediaRecorder.AudioEncoder.AAC && + audioEncoder != MediaRecorder.AudioEncoder.AAC_PLUS && + audioEncoder != MediaRecorder.AudioEncoder.EAAC_PLUS) { + throw new IllegalArgumentException("Unsupported audio encodeer " + audioEncoder); } + return encoderMap.get(audioEncoder); } - public static void createEncoderTable(){ - OUTPUT_FORMAT_TABLE.put("h263", MediaRecorder.VideoEncoder.H263); - OUTPUT_FORMAT_TABLE.put("h264", MediaRecorder.VideoEncoder.H264); - OUTPUT_FORMAT_TABLE.put("m4v", MediaRecorder.VideoEncoder.MPEG_4_SP); - OUTPUT_FORMAT_TABLE.put("amrnb", MediaRecorder.AudioEncoder.AMR_NB); - OUTPUT_FORMAT_TABLE.put("amrwb", MediaRecorder.AudioEncoder.AMR_WB); - OUTPUT_FORMAT_TABLE.put("aac", MediaRecorder.AudioEncoder.AAC); - OUTPUT_FORMAT_TABLE.put("aacplus", MediaRecorder.AudioEncoder.AAC_PLUS); - OUTPUT_FORMAT_TABLE.put("eaacplus", - MediaRecorder.AudioEncoder.EAAC_PLUS); + private MediaProfileReader() {} // Don't call me + + private static void initEncoderMap() { + // video encoders + encoderMap.put(MediaRecorder.VideoEncoder.H263, "h263"); + encoderMap.put(MediaRecorder.VideoEncoder.H264, "h264"); + encoderMap.put(MediaRecorder.VideoEncoder.MPEG_4_SP, "m4v"); + + // audio encoders + encoderMap.put(MediaRecorder.AudioEncoder.AMR_NB, "amrnb"); + encoderMap.put(MediaRecorder.AudioEncoder.AMR_WB, "amrwb"); + encoderMap.put(MediaRecorder.AudioEncoder.AAC, "aac"); + encoderMap.put(MediaRecorder.AudioEncoder.AAC_PLUS, "aacplus"); + encoderMap.put(MediaRecorder.AudioEncoder.EAAC_PLUS, "eaacplus"); } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java index fdc5970..39caccd 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java @@ -25,6 +25,9 @@ import android.content.Context; import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; +import android.media.EncoderCapabilities; +import android.media.EncoderCapabilities.VideoEncoderCap; +import android.media.EncoderCapabilities.AudioEncoderCap; import android.test.ActivityInstrumentationTestCase; import android.util.Log; import android.view.SurfaceHolder; @@ -33,6 +36,7 @@ import com.android.mediaframeworktest.MediaProfileReader; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.Suppress; +import java.util.List; /** @@ -99,31 +103,29 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram } } - private boolean recordVideoWithPara(String encoder, String audio, String quality){ + private boolean recordVideoWithPara(VideoEncoderCap videoCap, AudioEncoderCap audioCap, boolean highQuality){ boolean recordSuccess = false; - int videoEncoder = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder); - int audioEncoder = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio); - int videoWidth = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".width_" + quality); - int videoHeight = - MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".height_" + quality); - int videoFps = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".fps_" + quality); - int videoBitrate = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".bps_" + quality); - int audioBitrate = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".bps_" + quality); - int audioChannels = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".ch_" + quality); - int audioSamplingRate = - MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".hz_" + quality); + int videoEncoder = videoCap.mCodec; + int audioEncoder = audioCap.mCodec; + int videoWidth = highQuality? videoCap.mMaxFrameWidth: videoCap.mMinFrameWidth; + int videoHeight = highQuality? videoCap.mMaxFrameHeight: videoCap.mMinFrameHeight; + int videoFps = highQuality? videoCap.mMaxFrameRate: videoCap.mMinFrameRate; + int videoBitrate = highQuality? videoCap.mMaxBitRate: videoCap.mMinBitRate; + int audioBitrate = highQuality? audioCap.mMaxBitRate: audioCap.mMinBitRate; + int audioChannels = highQuality? audioCap.mMaxChannels: audioCap.mMinChannels ; + int audioSamplingRate = highQuality? audioCap.mMaxSampleRate: audioCap.mMinSampleRate; if (videoFps < MIN_VIDEO_FPS) { videoFps = MIN_VIDEO_FPS; } mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); - String filename = ("/sdcard/" + encoder + "_" + audio + "_" + quality + ".3gp"); + String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp"); try { Log.v(TAG, "video encoder :" + videoEncoder); Log.v(TAG, "audio encoder :" + audioEncoder); - Log.v(TAG, "quality : " + quality); - Log.v(TAG, "encoder : " + encoder); - Log.v(TAG, "audio : " + audio); + Log.v(TAG, "quality : " + (highQuality?"high": "low")); + Log.v(TAG, "encoder : " + MediaProfileReader.getVideoCodecName(videoEncoder)); + Log.v(TAG, "audio : " + MediaProfileReader.getAudioCodecName(audioEncoder)); Log.v(TAG, "videoWidth : " + videoWidth); Log.v(TAG, "videoHeight : " + videoHeight); Log.v(TAG, "videoFPS : " + videoFps); @@ -434,35 +436,26 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram boolean recordSuccess = false; String deviceType = MediaProfileReader.getDeviceType(); Log.v(TAG, "deviceType = " + deviceType); - // Test cases are device specified - MediaProfileReader.createVideoProfileTable(); - MediaProfileReader.createAudioProfileTable(); - MediaProfileReader.createEncoderTable(); - String encoderType = MediaProfileReader.getVideoCodecProperty(); - String audioType = MediaProfileReader.getAudioCodecProperty(); - if ((encoderType.length() != 0) || (audioType.length() != 0)) { - String audio[] = audioType.split(","); - String encoder[] = encoderType.split(","); - for (int k = 0; k < 2; k++) { - for (int i = 0; i < encoder.length; i++) { - for (int j = 0; j < audio.length; j++) { - if (k == 0) { - recordSuccess = recordVideoWithPara(encoder[i], audio[j], "high"); - } else { - recordSuccess = recordVideoWithPara(encoder[i], audio[j], "low"); - } - if (!recordSuccess) { - Log.v(TAG, "testDeviceSpecificCodec failed"); - Log.v(TAG, "Encoder = " + encoder[i] + "Audio Encoder = " + audio[j]); - noOfFailure++; - } - // assertTrue((encoder[i] + audio[j]), recordSuccess); + List<VideoEncoderCap> videoEncoders = MediaProfileReader.getVideoEncoders(); + List<AudioEncoderCap> audioEncoders = MediaProfileReader.getAudioEncoders(); + for (int k = 0; k < 2; k++) { + for (VideoEncoderCap videoEncoder: videoEncoders) { + for (AudioEncoderCap audioEncoder: audioEncoders) { + if (k == 0) { + recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, true); + } else { + recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, false); + } + if (!recordSuccess) { + Log.v(TAG, "testDeviceSpecificCodec failed"); + Log.v(TAG, "Encoder = " + videoEncoder.mCodec + "Audio Encoder = " + audioEncoder.mCodec); + noOfFailure++; } } } - if (noOfFailure != 0) { - assertTrue("testDeviceSpecificCodec", false); - } + } + if (noOfFailure != 0) { + assertTrue("testDeviceSpecificCodec", false); } } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java index 8750098..4c259f1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java @@ -39,9 +39,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { public static void testAlbumArt() throws Exception { Log.v(TAG, "testAlbumArt starts."); MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - MediaProfileReader reader = new MediaProfileReader(); - boolean supportWMA = reader.getWMAEnable(); - boolean supportWMV = reader.getWMVEnable(); + boolean supportWMA = MediaProfileReader.getWMAEnable(); + boolean supportWMV = MediaProfileReader.getWMVEnable(); retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY); for (int i = 0, n = MediaNames.ALBUMART_TEST_FILES.length; i < n; ++i) { try { @@ -74,9 +73,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { @LargeTest public static void testThumbnailCapture() throws Exception { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - MediaProfileReader reader = new MediaProfileReader(); - boolean supportWMA = reader.getWMAEnable(); - boolean supportWMV = reader.getWMVEnable(); + boolean supportWMA = MediaProfileReader.getWMAEnable(); + boolean supportWMV = MediaProfileReader.getWMVEnable(); Log.v(TAG, "Thumbnail processing starts"); long startedAt = System.currentTimeMillis(); for(int i = 0, n = MediaNames.THUMBNAIL_CAPTURE_TEST_FILES.length; i < n; ++i) { @@ -110,9 +108,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { @LargeTest public static void testMetadataRetrieval() throws Exception { - MediaProfileReader reader = new MediaProfileReader(); - boolean supportWMA = reader.getWMAEnable(); - boolean supportWMV = reader.getWMVEnable(); + boolean supportWMA = MediaProfileReader.getWMAEnable(); + boolean supportWMV = MediaProfileReader.getWMVEnable(); MediaMetadataRetriever retriever = new MediaMetadataRetriever(); retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY); for(int i = 0, n = MediaNames.METADATA_RETRIEVAL_TEST_FILES.length; i < n; ++i) { diff --git a/mms-common/Android.mk b/mms-common/Android.mk index de994c0..57f1ccc 100644 --- a/mms-common/Android.mk +++ b/mms-common/Android.mk @@ -20,6 +20,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := mms-common LOCAL_SRC_FILES := $(call all-java-files-under, java) +LOCAL_STATIC_JAVA_LIBRARIES += android-common include $(BUILD_STATIC_JAVA_LIBRARY) # Include this library in the build server's output directory diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java index 0237bc2..87e4758 100644 --- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java +++ b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java @@ -32,8 +32,8 @@ import android.telephony.SmsMessage; import android.text.TextUtils; import android.util.Config; import android.util.Log; +import android.util.Patterns; -import com.android.common.Patterns; import android.database.sqlite.SqliteWrapper; /** diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 95ab684..a79f0cd 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -39,7 +39,7 @@ import android.provider.Settings; */ public class DefaultContainerService extends IntentService { private static final String TAG = "DefContainer"; - private static final boolean localLOGV = false; + private static final boolean localLOGV = true; private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { /* @@ -211,6 +211,8 @@ public class DefaultContainerService extends IntentService { if (PackageHelper.isContainerMounted(newCid)) { if (localLOGV) Log.i(TAG, "Unmounting " + newCid + " at path " + newCachePath + " after " + errMsg); + // Force a gc to avoid being killed. + Runtime.getRuntime().gc(); PackageHelper.unMountSdDir(newCid); } else { if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted"); @@ -328,8 +330,8 @@ public class DefaultContainerService extends IntentService { boolean auto = true; // To make final copy long reqInstallSize = pkgLen; - // For dex files - long reqInternalSize = 1 * pkgLen; + // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now. + long reqInternalSize = 0; boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize); boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk && diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 654ca32..1e9c312 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -60,14 +60,14 @@ <!-- user interface sound effects --> <integer name="def_power_sounds_enabled">1</integer> - <string name="def_low_battery_sound">/system/media/ui/LowBattery.ogg</string> - <integer name="def_dock_sounds_enabled">1</integer> - <string name="def_desk_dock_sound">/system/media/audio/ui/dock.ogg</string> - <string name="def_desk_undock_sound">/system/media/audio/ui/undock.ogg</string> - <string name="def_car_dock_sound">/system/media/audio/ui/Dock.ogg</string> - <string name="def_car_undock_sound">/system/media/audio/ui/Undock.ogg</string> - <integer name="def_lockscreen_sounds_enabled">1</integer> - <string name="def_lock_sound">/system/media/audio/ui/Lock.ogg</string> - <string name="def_unlock_sound">/system/media/audio/ui/Unlock.ogg</string> + <string name="def_low_battery_sound" translatable="false">/system/media/ui/LowBattery.ogg</string> + <integer name="def_dock_sounds_enabled">0</integer> + <string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string> + <string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string> + <string name="def_car_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string> + <string name="def_car_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string> + <integer name="def_lockscreen_sounds_enabled">0</integer> + <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string> + <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index cf34d4e..18e247e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -44,8 +44,8 @@ import android.text.TextUtils; import android.util.Config; import android.util.Log; import android.util.Xml; -import com.android.common.XmlUtils; import com.android.internal.telephony.RILConstants; +import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; @@ -76,7 +76,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 50; + private static final int DATABASE_VERSION = 51; private Context mContext; @@ -618,18 +618,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { if (upgradeVersion == 48) { /* - * Adding a new setting for which voice recognition service to use. + * Default recognition service no longer initialized here, + * moved to RecognitionManagerService. */ - db.beginTransaction(); - try { - SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" - + " VALUES(?,?);"); - loadVoiceRecognitionServiceSetting(stmt); - stmt.close(); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } upgradeVersion = 49; } @@ -651,6 +642,25 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 50; } + if (upgradeVersion == 50) { + /* + * New settings for set install location UI. + */ + db.beginTransaction(); + try { + SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)" + + " VALUES(?,?);"); + loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION, + R.bool.set_install_location); + stmt.close(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + upgradeVersion = 51; + } + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion + ", must wipe the settings provider"); @@ -1028,8 +1038,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, R.bool.def_mount_ums_notify_enabled); - loadVoiceRecognitionServiceSetting(stmt); - stmt.close(); } @@ -1041,32 +1049,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { R.string.def_backup_transport); } - /** - * Introduced in database version 49. - */ - private void loadVoiceRecognitionServiceSetting(SQLiteStatement stmt) { - String selectedService = null; - List<ResolveInfo> availableRecognitionServices = - mContext.getPackageManager().queryIntentServices( - new Intent(RecognitionService.SERVICE_INTERFACE), 0); - int numAvailable = availableRecognitionServices.size(); - - if (numAvailable == 0) { - Log.w(TAG, "no available voice recognition services found"); - } else { - if (numAvailable > 1) { - Log.w(TAG, "more than one voice recognition service found, picking first"); - } - - ServiceInfo serviceInfo = availableRecognitionServices.get(0).serviceInfo; - selectedService = - new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToString(); - } - - loadSetting(stmt, Settings.Secure.VOICE_RECOGNITION_SERVICE, - selectedService == null ? "" : selectedService); - } - private void loadSetting(SQLiteStatement stmt, String key, Object value) { stmt.bindString(1, key); stmt.bindString(2, value.toString()); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 4080a6a..db802d3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -20,7 +20,6 @@ import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Random; import android.backup.BackupManager; import android.content.ContentProvider; @@ -30,14 +29,11 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; -import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.ParcelFileDescriptor; -import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.DrmStore; import android.provider.MediaStore; diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 35b5675..2f5cfa3 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -45,6 +45,9 @@ #define USAGEMODE_PLAY_IMMEDIATELY 0 #define USAGEMODE_WRITE_TO_FILE 1 +#define SYNTHPLAYSTATE_IS_STOPPED 0 +#define SYNTHPLAYSTATE_IS_PLAYING 1 + using namespace android; // ---------------------------------------------------------------------------- @@ -154,6 +157,8 @@ class SynthProxyJniStorage { TtsEngine* mNativeSynthInterface; void* mEngineLibHandle; AudioTrack* mAudioOut; + int8_t mPlayState; + Mutex mPlayLock; AudioSystem::stream_type mStreamType; uint32_t mSampleRate; uint32_t mAudFormat; @@ -166,6 +171,7 @@ class SynthProxyJniStorage { mNativeSynthInterface = NULL; mEngineLibHandle = NULL; mAudioOut = NULL; + mPlayState = SYNTHPLAYSTATE_IS_STOPPED; mStreamType = DEFAULT_TTS_STREAM_TYPE; mSampleRate = DEFAULT_TTS_RATE; mAudFormat = DEFAULT_TTS_FORMAT; @@ -223,6 +229,7 @@ class SynthProxyJniStorage { if (minBufCount < 2) minBufCount = 2; int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate; + mPlayLock.lock(); mAudioOut = new AudioTrack(mStreamType, rate, format, (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO, minFrameCount > 4096 ? minFrameCount : 4096, @@ -237,6 +244,7 @@ class SynthProxyJniStorage { mAudioOut->setVolume(1.0f, 1.0f); LOGV("AudioTrack ready"); } + mPlayLock.unlock(); } }; @@ -288,6 +296,12 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, if (bufferSize > 0) { prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel); if (pJniData->mAudioOut) { + pJniData->mPlayLock.lock(); + if(pJniData->mAudioOut->stopped() + && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) { + pJniData->mAudioOut->start(); + } + pJniData->mPlayLock.unlock(); if (bUseFilter) { applyFilter((int16_t*)wav, bufferSize/2); } @@ -711,9 +725,9 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; - if (pSynthData->mAudioOut) { - pSynthData->mAudioOut->start(); - } + pSynthData->mPlayLock.lock(); + pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING; + pSynthData->mPlayLock.unlock(); afterSynthData_t* pForAfter = new (afterSynthData_t); pForAfter->jniStorage = jniData; @@ -744,9 +758,13 @@ android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData) SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + pSynthData->mPlayLock.lock(); + pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED; if (pSynthData->mAudioOut) { pSynthData->mAudioOut->stop(); } + pSynthData->mPlayLock.unlock(); + if (pSynthData->mNativeSynthInterface) { result = pSynthData->mNativeSynthInterface->stop(); } diff --git a/preloaded-classes b/preloaded-classes index 092b539..9fc000f 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -1,850 +1,1039 @@ # Classes which are preloaded by com.android.internal.os.ZygoteInit. +# Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java. +# MIN_LOAD_TIME_MICROS=1250 android.R$styleable +android.accounts.AccountManager +android.accounts.AccountManager$4 +android.accounts.AccountManager$6 +android.accounts.AccountManager$AmsTask +android.accounts.AccountManager$BaseFutureTask +android.accounts.AccountManager$Future2Task +android.accounts.AuthenticatorDescription +android.accounts.IAccountAuthenticatorResponse$Stub +android.accounts.IAccountManager$Stub +android.accounts.IAccountManagerResponse$Stub android.app.Activity android.app.ActivityGroup -android.app.ActivityManager$MemoryInfo$1 +android.app.ActivityManager$RunningAppProcessInfo +android.app.ActivityManager$RunningServiceInfo android.app.ActivityManagerNative android.app.ActivityManagerProxy android.app.ActivityThread -android.app.ActivityThread$ActivityRecord -android.app.ActivityThread$AppBindData android.app.ActivityThread$ApplicationThread -android.app.ActivityThread$ContextCleanupInfo -android.app.ActivityThread$GcIdler android.app.ActivityThread$H -android.app.ActivityThread$Idler -android.app.ActivityThread$PackageInfo -android.app.ActivityThread$PackageInfo$ReceiverDispatcher -android.app.ActivityThread$PackageInfo$ReceiverDispatcher$InnerReceiver -android.app.ActivityThread$PackageInfo$ServiceDispatcher -android.app.ActivityThread$PackageInfo$ServiceDispatcher$InnerConnection -android.app.ActivityThread$ProviderRecord -android.app.ActivityThread$ProviderRefCount android.app.AlertDialog -android.app.Application -android.app.ApplicationLoaders android.app.ApplicationThreadNative android.app.ContextImpl -android.app.ContextImpl$ApplicationContentResolver android.app.ContextImpl$ApplicationPackageManager -android.app.ContextImpl$ApplicationPackageManager$PackageRemovedReceiver -android.app.ContextImpl$ApplicationPackageManager$ResourceName -android.app.ContextImpl$SharedPreferencesImpl +android.app.DatePickerDialog android.app.Dialog android.app.ExpandableListActivity android.app.IActivityManager -android.app.IActivityManager$ContentProviderHolder$1 +android.app.IActivityManager$ContentProviderHolder android.app.IAlarmManager$Stub -android.app.IAlarmManager$Stub$Proxy -android.app.IApplicationThread -android.app.INotificationManager$Stub -android.app.INotificationManager$Stub$Proxy -android.app.ISearchManager -android.app.ISearchManager$Stub -android.app.ISearchManager$Stub$Proxy +android.app.IDevicePolicyManager$Stub +android.app.IStatusBar$Stub +android.app.ITransientNotification$Stub android.app.Instrumentation -android.app.IntentReceiverLeaked +android.app.IntentService android.app.ListActivity -android.app.ListActivity$1 -android.app.ListActivity$2 android.app.LocalActivityManager android.app.Notification -android.app.NotificationManager android.app.PendingIntent -android.app.PendingIntent$1 android.app.ProgressDialog -android.app.ReceiverRestrictedContext android.app.ResultInfo -android.app.ResultInfo$1 android.app.SearchDialog android.app.SearchDialog$SearchAutoComplete +android.app.SearchDialog$SearchBar +android.app.SearchableInfo android.app.Service -android.app.ServiceConnectionLeaked +android.app.SuggestionsAdapter +android.app.SuperNotCalledException android.app.TabActivity -android.content.BroadcastReceiver -android.content.ComponentCallbacks +android.app.TimePickerDialog +android.appwidget.AppWidgetHost +android.appwidget.AppWidgetHostView +android.appwidget.AppWidgetHostView$ParcelableSparseArray +android.appwidget.AppWidgetManager +android.appwidget.AppWidgetProvider +android.appwidget.AppWidgetProviderInfo +android.backup.BackupDataInput +android.backup.BackupDataInput$EntityHeader +android.backup.BackupDataOutput +android.backup.BackupHelperAgent +android.backup.BackupHelperDispatcher +android.backup.BackupHelperDispatcher$Header +android.backup.FileBackupHelperBase +android.backup.IBackupManager$Stub +android.backup.RestoreSet +android.bluetooth.BluetoothAdapter +android.bluetooth.BluetoothAudioGateway +android.bluetooth.BluetoothSocket +android.bluetooth.BluetoothUuid +android.bluetooth.HeadsetBase +android.bluetooth.IBluetooth +android.bluetooth.IBluetooth$Stub +android.bluetooth.IBluetoothA2dp +android.bluetooth.IBluetoothA2dp$Stub +android.bluetooth.IBluetoothHeadset$Stub +android.bluetooth.ScoSocket android.content.ComponentName -android.content.ComponentName$1 android.content.ContentProvider$Transport -android.content.ContentProviderProxy -android.content.ContentQueryMap -android.content.ContentQueryMap$1 +android.content.ContentProviderOperation +android.content.ContentProviderResult android.content.ContentResolver -android.content.ContentResolver$CursorWrapperInner android.content.ContentValues android.content.Context android.content.ContextWrapper -android.content.DialogInterface -android.content.DialogInterface$OnCancelListener -android.content.DialogInterface$OnDismissListener -android.content.IContentProvider -android.content.IContentService android.content.IContentService$Stub +android.content.ISyncContext$Stub android.content.Intent -android.content.Intent$1 android.content.IntentFilter +android.content.IntentSender android.content.SearchRecentSuggestionsProvider android.content.SyncResult android.content.SyncStats android.content.UriMatcher android.content.pm.ActivityInfo -android.content.pm.ActivityInfo$1 android.content.pm.ApplicationInfo -android.content.pm.ApplicationInfo$1 -android.content.pm.ComponentInfo -android.content.pm.IPackageManager +android.content.pm.ConfigurationInfo +android.content.pm.IPackageDataObserver$Stub android.content.pm.IPackageManager$Stub android.content.pm.IPackageManager$Stub$Proxy +android.content.pm.IPackageStatsObserver$Stub android.content.pm.InstrumentationInfo -android.content.pm.InstrumentationInfo$1 -android.content.pm.PackageItemInfo +android.content.pm.PackageInfo android.content.pm.PackageManager android.content.pm.PackageManager$NameNotFoundException +android.content.pm.PackageStats android.content.pm.PermissionInfo android.content.pm.ProviderInfo -android.content.pm.ProviderInfo$1 -android.content.pm.ResolveInfo$1 -android.content.pm.ServiceInfo$1 +android.content.pm.ResolveInfo +android.content.pm.ResolveInfo$DisplayNameComparator +android.content.pm.Signature +android.content.res.AssetFileDescriptor +android.content.res.AssetFileDescriptor$1 android.content.res.AssetManager android.content.res.AssetManager$AssetInputStream android.content.res.ColorStateList android.content.res.ColorStateList$1 +android.content.res.CompatibilityInfo +android.content.res.CompatibilityInfo$1 android.content.res.Configuration +android.content.res.Configuration$1 android.content.res.Resources -android.content.res.Resources$Theme +android.content.res.Resources$1 android.content.res.StringBlock android.content.res.TypedArray android.content.res.XmlBlock android.content.res.XmlBlock$Parser +android.content.res.XmlResourceParser android.database.AbstractCursor -android.database.AbstractCursor$SelfContentObserver android.database.AbstractWindowedCursor -android.database.BulkCursorNative -android.database.BulkCursorProxy android.database.BulkCursorToCursorAdaptor -android.database.ContentObservable -android.database.ContentObserver$Transport -android.database.Cursor +android.database.CharArrayBuffer android.database.CursorToBulkCursorAdaptor -android.database.CursorToBulkCursorAdaptor$ContentObserverProxy android.database.CursorWindow +android.database.CursorWindow$1 android.database.CursorWrapper -android.database.DataSetObservable -android.database.IContentObserver$Stub$Proxy -android.database.MergeCursor +android.database.MatrixCursor +android.database.sqlite.SQLiteClosable +android.database.sqlite.SQLiteCompiledSql +android.database.sqlite.SQLiteContentHelper android.database.sqlite.SQLiteCursor android.database.sqlite.SQLiteDatabase -android.database.sqlite.SQLiteDatabase$CursorFactory -android.database.sqlite.SQLiteDirectCursorDriver +android.database.sqlite.SQLiteDebug +android.database.sqlite.SQLiteDebug$PagerStats +android.database.sqlite.SQLiteProgram android.database.sqlite.SQLiteQuery +android.database.sqlite.SQLiteQueryBuilder android.database.sqlite.SQLiteStatement -android.ddm.DdmHandleAppName +android.database.sqlite.SqliteWrapper android.ddm.DdmHandleExit android.ddm.DdmHandleHeap android.ddm.DdmHandleHello android.ddm.DdmHandleNativeHeap +android.ddm.DdmHandleProfiling android.ddm.DdmHandleThread android.ddm.DdmRegister +android.debug.JNITest +android.emoji.EmojiFactory +android.graphics.AvoidXfermode android.graphics.Bitmap +android.graphics.Bitmap$1 +android.graphics.Bitmap$CompressFormat +android.graphics.Bitmap$Config +android.graphics.BitmapFactory +android.graphics.BitmapFactory$Options android.graphics.BitmapShader +android.graphics.BlurMaskFilter +android.graphics.Camera android.graphics.Canvas -android.graphics.Canvas$EdgeType +android.graphics.Canvas$VertexMode android.graphics.Color +android.graphics.ColorFilter +android.graphics.ColorMatrixColorFilter +android.graphics.ComposePathEffect +android.graphics.ComposeShader +android.graphics.CornerPathEffect +android.graphics.DashPathEffect +android.graphics.DiscretePathEffect +android.graphics.DrawFilter +android.graphics.EmbossMaskFilter android.graphics.Interpolator +android.graphics.LayerRasterizer +android.graphics.LightingColorFilter android.graphics.LinearGradient +android.graphics.MaskFilter android.graphics.Matrix -android.graphics.Matrix$ScaleToFit +android.graphics.Movie android.graphics.NinePatch android.graphics.Paint +android.graphics.Paint$Align +android.graphics.Paint$Cap +android.graphics.Paint$FontMetrics +android.graphics.Paint$FontMetricsInt +android.graphics.Paint$Join +android.graphics.Paint$Style android.graphics.PaintFlagsDrawFilter android.graphics.Path -android.graphics.Path$Direction +android.graphics.Path$FillType +android.graphics.PathDashPathEffect +android.graphics.PathEffect +android.graphics.PathMeasure android.graphics.Picture -android.graphics.PorterDuff +android.graphics.PixelFormat +android.graphics.PixelXorXfermode +android.graphics.Point +android.graphics.PointF android.graphics.PorterDuff$Mode +android.graphics.PorterDuffColorFilter android.graphics.PorterDuffXfermode +android.graphics.RadialGradient +android.graphics.Rasterizer android.graphics.Rect +android.graphics.Rect$1 android.graphics.RectF +android.graphics.RectF$1 android.graphics.Region +android.graphics.Region$1 android.graphics.Region$Op +android.graphics.RegionIterator android.graphics.Shader android.graphics.Shader$TileMode +android.graphics.SumPathEffect +android.graphics.SweepGradient +android.graphics.TableMaskFilter android.graphics.Typeface android.graphics.Xfermode +android.graphics.YuvImage +android.graphics.drawable.Animatable +android.graphics.drawable.AnimatedRotateDrawable +android.graphics.drawable.AnimatedRotateDrawable$AnimatedRotateState android.graphics.drawable.AnimationDrawable +android.graphics.drawable.AnimationDrawable$AnimationState android.graphics.drawable.BitmapDrawable android.graphics.drawable.BitmapDrawable$BitmapState +android.graphics.drawable.ClipDrawable +android.graphics.drawable.ClipDrawable$ClipState android.graphics.drawable.ColorDrawable android.graphics.drawable.ColorDrawable$ColorState android.graphics.drawable.Drawable +android.graphics.drawable.Drawable$Callback +android.graphics.drawable.Drawable$ConstantState android.graphics.drawable.DrawableContainer +android.graphics.drawable.DrawableContainer$DrawableContainerState android.graphics.drawable.GradientDrawable +android.graphics.drawable.GradientDrawable$GradientState +android.graphics.drawable.GradientDrawable$Orientation android.graphics.drawable.LayerDrawable android.graphics.drawable.LayerDrawable$ChildDrawable android.graphics.drawable.LayerDrawable$LayerState android.graphics.drawable.NinePatchDrawable android.graphics.drawable.NinePatchDrawable$NinePatchState -android.graphics.drawable.PaintDrawable -android.graphics.drawable.RotateDrawable -android.graphics.drawable.RotateDrawable$RotateState -android.graphics.drawable.ScaleDrawable -android.graphics.drawable.ScaleDrawable$ScaleState android.graphics.drawable.ShapeDrawable -android.graphics.drawable.ShapeDrawable$ShapeState android.graphics.drawable.StateListDrawable android.graphics.drawable.StateListDrawable$StateListState android.graphics.drawable.TransitionDrawable android.graphics.drawable.TransitionDrawable$TransitionState -android.graphics.drawable.shapes.RoundRectShape +android.graphics.utils.BoundaryPatch +android.hardware.Camera +android.hardware.Camera$Parameters +android.hardware.GeomagneticField android.hardware.SensorManager -android.inputmethodservice.KeyboardView +android.location.Address +android.location.Criteria +android.location.GeocoderParams +android.location.IGpsStatusListener$Stub android.location.ILocationManager$Stub +android.location.ILocationManager$Stub$Proxy android.location.Location +android.location.LocationManager +android.location.LocationProviderInterface +android.media.AudioFormat android.media.AudioManager +android.media.AudioRecord +android.media.AudioSystem +android.media.AudioTrack +android.media.ExifInterface android.media.IAudioService$Stub -android.media.IAudioService$Stub$Proxy +android.media.JetPlayer +android.media.MediaFile +android.media.MediaMetadataRetriever +android.media.MediaPlayer +android.media.MediaScanner +android.media.Metadata +android.media.MiniThumbFile +android.media.ThumbnailUtils +android.media.ToneGenerator +android.net.ConnectivityManager +android.net.Credentials +android.net.DhcpInfo +android.net.DhcpInfo$1 +android.net.Downloads +android.net.Downloads$ByUri +android.net.IConnectivityManager$Stub +android.net.LocalServerSocket android.net.LocalSocket -android.net.LocalSocketAddress -android.net.LocalSocketAddress$Namespace android.net.LocalSocketImpl android.net.LocalSocketImpl$SocketInputStream android.net.LocalSocketImpl$SocketOutputStream android.net.NetworkInfo android.net.NetworkInfo$DetailedState +android.net.NetworkUtils android.net.SSLCertificateSocketFactory android.net.TrafficStats android.net.Uri -android.net.Uri$1 -android.net.Uri$AbstractHierarchicalUri -android.net.Uri$AbstractPart android.net.Uri$HierarchicalUri android.net.Uri$OpaqueUri android.net.Uri$Part -android.net.Uri$Part$EmptyPart -android.net.Uri$PathPart -android.net.Uri$PathSegments -android.net.Uri$StringUri android.net.WebAddress -android.net.http.CertificateChainValidator +android.net.http.AndroidHttpClient +android.net.http.AndroidHttpClientConnection android.net.http.EventHandler +android.net.http.Headers android.net.http.HttpsConnection +android.net.http.HttpDateTime +android.net.http.Request android.net.http.RequestQueue +android.net.http.SslCertificate android.net.http.SslError android.net.wifi.IWifiManager$Stub +android.net.wifi.ScanResult android.net.wifi.SupplicantState android.net.wifi.WifiConfiguration android.net.wifi.WifiInfo -android.opengl.Material +android.net.wifi.WifiManager +android.net.wifi.WifiNative +android.opengl.ETC1 +android.opengl.GLES10 +android.opengl.GLES10Ext +android.opengl.GLES11 +android.opengl.GLES11Ext +android.opengl.GLES20 +android.opengl.GLSurfaceView +android.opengl.GLSurfaceView$ComponentSizeChooser +android.opengl.GLUtils +android.opengl.Matrix +android.opengl.Visibility android.os.Binder android.os.BinderProxy android.os.Build +android.os.Build$VERSION android.os.Bundle -android.os.Bundle$1 +android.os.Debug +android.os.Debug$MemoryInfo +android.os.Debug$MemoryInfo$1 +android.os.DropBoxManager$Entry android.os.Environment +android.os.FileObserver$ObserverThread android.os.FileUtils +android.os.FileUtils$FileStatus android.os.Handler -android.os.HandlerThread android.os.IBinder +android.os.IInterface android.os.IPowerManager$Stub -android.os.IPowerManager$Stub$Proxy -android.os.IServiceManager -android.os.IVibratorService$Stub -android.os.IVibratorService$Stub$Proxy android.os.Looper +android.os.MemoryFile android.os.Message -android.os.Message$1 -android.os.MessageQueue -android.os.MessageQueue$IdleHandler android.os.Parcel -android.os.PatternMatcher -android.os.PatternMatcher$1 -android.os.PowerManager -android.os.PowerManager$WakeLock -android.os.PowerManager$WakeLock$1 +android.os.Parcel$1 +android.os.ParcelFileDescriptor +android.os.ParcelFileDescriptor$1 +android.os.ParcelUuid +android.os.Parcelable +android.os.Parcelable$Creator +android.os.Power android.os.Process -android.os.ServiceManager -android.os.ServiceManagerNative -android.os.ServiceManagerProxy -android.os.Vibrator -android.preference.CheckBoxPreference +android.os.RecoverySystem +android.os.ResultReceiver +android.os.StatFs +android.os.SystemClock +android.os.SystemProperties +android.os.UEventObserver +android.os.storage.IMountService$Stub +android.os.storage.IMountService$Stub$Proxy +android.pim.EventRecurrence +android.pim.RecurrenceSet +android.preference.CheckBoxPreference$SavedState android.preference.DialogPreference -android.preference.EditTextPreference android.preference.ListPreference android.preference.Preference android.preference.PreferenceActivity android.preference.PreferenceGroup android.preference.PreferenceGroupAdapter +android.preference.PreferenceInflater android.preference.PreferenceManager android.preference.PreferenceScreen android.preference.RingtonePreference -android.sax.RootElement +android.preference.VolumePreference +android.preference.VolumePreference$SeekBarVolumizer +android.provider.Browser +android.provider.Calendar +android.provider.Calendar$Attendees +android.provider.Calendar$CalendarAlerts +android.provider.Calendar$Calendars +android.provider.Calendar$EventDays +android.provider.Calendar$Events +android.provider.Calendar$Reminders +android.provider.Contacts +android.provider.Contacts$ContactMethods +android.provider.ContactsContract +android.provider.ContactsContract$CommonDataKinds$Email +android.provider.ContactsContract$CommonDataKinds$Phone +android.provider.ContactsContract$CommonDataKinds$StructuredPostal +android.provider.ContactsContract$Contacts +android.provider.ContactsContract$Data +android.provider.ContactsContract$DataColumnsWithJoins +android.provider.ContactsContract$PhoneLookup +android.provider.ContactsContract$RawContacts +android.provider.ContactsContract$RawContacts$EntityIteratorImpl +android.provider.ContactsContract$RawContactsEntity +android.provider.Downloads +android.provider.Downloads$Impl +android.provider.MediaStore +android.provider.MediaStore$Audio$Artists +android.provider.MediaStore$Audio$Media +android.provider.MediaStore$Images$Media +android.provider.MediaStore$Images$Thumbnails +android.provider.MediaStore$Video$Media +android.provider.SearchRecentSuggestions +android.provider.Settings$Secure +android.provider.Settings$System +android.provider.UserDictionary$Words +android.security.KeyStore +android.security.Md5MessageDigest +android.security.MessageDigest +android.security.Sha1MessageDigest +android.server.BluetoothA2dpService +android.server.BluetoothEventLoop +android.server.BluetoothService +android.speech.tts.ITts$Stub +android.speech.tts.ITts$Stub$Proxy +android.speech.tts.ITtsCallback$Stub +android.speech.tts.TextToSpeech android.telephony.PhoneNumberUtils -android.telephony.PhoneStateListener android.telephony.ServiceState -android.telephony.TelephonyManager -android.telephony.SmsManager +android.telephony.SignalStrength android.telephony.SmsMessage -android.text.AutoText +android.telephony.SmsMessage$MessageClass +android.telephony.TelephonyManager +android.text.AndroidCharacter android.text.BoringLayout -android.text.BoringLayout$Metrics android.text.DynamicLayout -android.text.DynamicLayout$ChangeWatcher -android.text.Editable -android.text.Editable$Factory -android.text.GetChars -android.text.GraphicsOperations android.text.Html$HtmlParser -android.text.InputFilter +android.text.HtmlToSpannedConverter android.text.Layout -android.text.Layout$Alignment -android.text.Layout$Directions -android.text.Layout$Ellipsizer -android.text.NoCopySpan -android.text.NoCopySpan$Concrete -android.text.PackedIntVector -android.text.PackedObjectVector -android.text.ParcelableSpan android.text.Selection -android.text.Selection$END -android.text.Selection$START -android.text.SpanWatcher -android.text.Spannable -android.text.Spannable$Factory -android.text.SpannableString android.text.SpannableStringBuilder -android.text.SpannableStringInternal -android.text.Spanned android.text.SpannedString -android.text.StaticLayout -android.text.Styled -android.text.TextPaint android.text.TextUtils -android.text.TextUtils$1 -android.text.TextUtils$EllipsizeCallback -android.text.TextUtils$SimpleStringSplitter -android.text.TextUtils$TruncateAt -android.text.TextWatcher android.text.format.DateUtils +android.text.format.Formatter android.text.format.Time android.text.method.ArrowKeyMovementMethod android.text.method.BaseKeyListener -android.text.method.KeyListener +android.text.method.DigitsKeyListener +android.text.method.LinkMovementMethod android.text.method.MetaKeyKeyListener -android.text.method.MovementMethod android.text.method.QwertyKeyListener -android.text.method.ReplacementTransformationMethod android.text.method.ReplacementTransformationMethod$SpannedReplacementCharSequence android.text.method.SingleLineTransformationMethod android.text.method.TextKeyListener android.text.method.TextKeyListener$Capitalize -android.text.method.TextKeyListener$SettingsObserver -android.text.method.TransformationMethod -android.text.style.AlignmentSpan -android.text.style.CharacterStyle -android.text.style.ForegroundColorSpan -android.text.style.LeadingMarginSpan -android.text.style.LineBackgroundSpan -android.text.style.LineHeightSpan -android.text.style.MetricAffectingSpan -android.text.style.ParagraphStyle -android.text.style.ReplacementSpan +android.text.style.ImageSpan +android.text.style.RelativeSizeSpan +android.text.style.ScaleXSpan android.text.style.StyleSpan -android.text.style.URLSpan -android.text.style.UpdateAppearance -android.text.style.UpdateLayout -android.text.style.WrapTogetherSpan +android.text.style.TextAppearanceSpan android.text.util.Linkify android.util.AttributeSet android.util.DisplayMetrics +android.util.EventLog +android.util.EventLog$Event android.util.FloatMath +android.util.Log +android.util.LongSparseArray +android.util.MonthDisplayHelper +android.util.Patterns android.util.SparseArray +android.util.StateSet android.util.TypedValue -android.util.Xml$XmlSerializerFactory +android.util.Xml +android.util.Xml$Encoding +android.util.base64.Base64$Encoder android.view.AbsSavedState -android.view.ContextMenu -android.view.ContextMenu$ContextMenuInfo android.view.ContextThemeWrapper android.view.Display android.view.FocusFinder -android.view.FocusFinder$1 -android.view.GestureDetector$SimpleOnGestureListener -android.view.Gravity -android.view.IWindow +android.view.GestureDetector android.view.IWindow$Stub -android.view.IWindowManager android.view.IWindowManager$Stub android.view.IWindowManager$Stub$Proxy -android.view.IWindowSession android.view.IWindowSession$Stub -android.view.IWindowSession$Stub$Proxy android.view.KeyCharacterMap +android.view.KeyCharacterMap$KeyData android.view.KeyEvent -android.view.KeyEvent$1 -android.view.KeyEvent$Callback -android.view.LayoutInflater -android.view.LayoutInflater$Factory -android.view.Menu -android.view.MenuInflater -android.view.MenuItem android.view.MotionEvent -android.view.MotionEvent$1 +android.view.ScaleGestureDetector android.view.Surface -android.view.SurfaceHolder +android.view.Surface$1 +android.view.SurfaceSession android.view.SurfaceView -android.view.TouchDelegate +android.view.SurfaceView$MyWindow android.view.VelocityTracker android.view.View -android.view.View$AttachInfo android.view.View$AttachInfo$Callbacks +android.view.View$AttachInfo$InvalidateInfo android.view.View$BaseSavedState -android.view.View$BaseSavedState$1 -android.view.View$MeasureSpec -android.view.View$OnCreateContextMenuListener -android.view.View$ScrollabilityCache android.view.ViewConfiguration android.view.ViewGroup -android.view.ViewGroup$LayoutParams -android.view.ViewGroup$MarginLayoutParams -android.view.ViewManager +android.view.ViewParent android.view.ViewRoot -android.view.ViewRoot$1 -android.view.ViewRoot$InputMethodCallback -android.view.ViewRoot$RunQueue -android.view.ViewRoot$TrackballAxis android.view.ViewRoot$W android.view.ViewStub -android.view.ViewTreeObserver -android.view.ViewTreeObserver$InternalInsetsInfo -android.view.ViewTreeObserver$OnPreDrawListener android.view.Window -android.view.Window$Callback -android.view.Window$LocalWindowManager -android.view.WindowLeaked -android.view.WindowManager android.view.WindowManager$LayoutParams -android.view.WindowManager$LayoutParams$1 android.view.WindowManagerImpl -android.view.animation.AccelerateDecelerateInterpolator -android.view.animation.AlphaAnimation +android.view.accessibility.AccessibilityEvent android.view.animation.Animation android.view.animation.AnimationSet -android.view.animation.LinearInterpolator -android.view.animation.Transformation android.view.inputmethod.BaseInputConnection android.view.inputmethod.CompletionInfo -android.view.inputmethod.CompletionInfo$1 - android.view.inputmethod.EditorInfo -android.view.inputmethod.EditorInfo$1 - android.view.inputmethod.ExtractedText -android.view.inputmethod.ExtractedText$1 - -android.view.inputmethod.ExtractedTextRequest -android.view.inputmethod.ExtractedTextRequest$1 - -android.view.inputmethod.InputBinding -android.view.inputmethod.InputBinding$1 -android.view.inputmethod.InputConnection -android.view.inputmethod.InputMethod -android.view.inputmethod.InputMethod$SessionCallback - -android.view.inputmethod.InputMethodInfo -android.view.inputmethod.InputMethodInfo$1 android.view.inputmethod.InputMethodManager -android.view.inputmethod.InputMethodManager$1 -android.view.inputmethod.InputMethodManager$2 -android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper -android.view.inputmethod.InputMethodManager$H - -android.view.inputmethod.InputMethodSession -android.view.inputmethod.InputMethodSession$EventCallback android.webkit.BrowserFrame android.webkit.CacheManager android.webkit.CallbackProxy +android.webkit.ConsoleMessage$MessageLevel android.webkit.CookieManager android.webkit.CookieSyncManager +android.webkit.DownloadListener +android.webkit.FileLoader +android.webkit.GeolocationPermissions +android.webkit.GeolocationService +android.webkit.HTML5VideoViewProxy android.webkit.JWebCoreJavaBridge android.webkit.LoadListener -android.webkit.MimeTypeMap +android.webkit.PluginManager android.webkit.URLUtil -android.webkit.WebBackForwardList -android.webkit.WebHistoryItem -android.webkit.WebIconDatabase -android.webkit.WebIconDatabase$EventHandler -android.webkit.WebIconDatabase$EventHandler$1 android.webkit.WebIconDatabase$EventHandler$IconResult +android.webkit.WebIconDatabase$IconListener android.webkit.WebSettings -android.webkit.WebSettings$EventHandler -android.webkit.WebSettings$EventHandler$1 -android.webkit.WebSettings$LayoutAlgorithm -android.webkit.WebSettings$RenderPriority android.webkit.WebSettings$TextSize -android.webkit.WebSyncManager -android.webkit.WebSyncManager$SyncHandler +android.webkit.WebStorage android.webkit.WebTextView android.webkit.WebView -android.webkit.WebView$ExtendedZoomControls -android.webkit.WebView$PrivateHandler +android.webkit.WebView$DragTrackerHandler +android.webkit.WebView$ScaleDetectorListener android.webkit.WebViewCore -android.webkit.WebViewCore$CursorData -android.webkit.WebViewCore$EventHub -android.webkit.WebViewCore$EventHub$1 -android.webkit.WebViewCore$WebCoreThread -android.webkit.WebViewCore$WebCoreThread$1 +android.webkit.WebViewCore$4 +android.webkit.WebViewCore$TextSelectionData +android.webkit.WebViewCore$TouchEventData +android.webkit.WebViewCore$TouchUpData android.webkit.WebViewDatabase android.widget.AbsListView -android.widget.AbsListView$CheckForLongPress -android.widget.AbsListView$CheckForTap -android.widget.AbsListView$LayoutParams +android.widget.AbsListView$3 +android.widget.AbsListView$CheckForKeyLongPress android.widget.AbsListView$PerformClick -android.widget.AbsListView$RecycleBin android.widget.AbsListView$SavedState -android.widget.AbsListView$SavedState$1 android.widget.AbsSeekBar android.widget.AbsSpinner +android.widget.AbsSpinner$SavedState android.widget.AbsoluteLayout -android.widget.AbsoluteLayout$LayoutParams android.widget.AdapterView -android.widget.AdapterView$AdapterDataSetObserver android.widget.ArrayAdapter android.widget.AutoCompleteTextView android.widget.AutoCompleteTextView$DropDownItemClickListener android.widget.AutoCompleteTextView$DropDownListView android.widget.BaseAdapter -android.widget.Button +android.widget.BaseExpandableListAdapter android.widget.CheckBox -android.widget.Checkable -android.widget.CheckedTextView android.widget.CompoundButton +android.widget.CompoundButton$SavedState android.widget.CursorAdapter -android.widget.CursorAdapter$ChangeObserver -android.widget.CursorAdapter$MyDataSetObserver android.widget.CursorTreeAdapter +android.widget.DatePicker android.widget.EditText +android.widget.ExpandableListConnector android.widget.ExpandableListView android.widget.FrameLayout -android.widget.FrameLayout$LayoutParams -android.widget.Gallery +android.widget.GridView android.widget.HeaderViewListAdapter android.widget.ImageView android.widget.ImageView$ScaleType android.widget.LinearLayout -android.widget.LinearLayout$LayoutParams android.widget.ListView -android.widget.ListView$ArrowScrollFocusResult android.widget.ListView$SavedState -android.widget.ListView$SavedState$1 +android.widget.MediaController +android.widget.MediaController$4 +android.widget.MultiAutoCompleteTextView +android.widget.NumberPicker android.widget.PopupWindow +android.widget.PopupWindow$PopupViewContainer android.widget.ProgressBar -android.widget.RadioGroup +android.widget.ProgressBar$SavedState +android.widget.QuickContactBadge android.widget.RatingBar android.widget.RelativeLayout -android.widget.RelativeLayout$LayoutParams +android.widget.RelativeLayout$DependencyGraph$Node android.widget.RemoteViews +android.widget.ResourceCursorAdapter android.widget.ScrollBarDrawable android.widget.ScrollView -android.widget.Scroller android.widget.SeekBar android.widget.SimpleCursorAdapter android.widget.SlidingDrawer android.widget.Spinner -android.widget.Spinner$DropDownAdapter android.widget.TabHost android.widget.TabWidget android.widget.TableLayout android.widget.TableRow android.widget.TextView -android.widget.TextView$1 -android.widget.TextView$Blink -android.widget.TextView$BufferType -android.widget.TextView$ChangeWatcher -android.widget.TextView$CharWrapper -android.widget.TextView$Drawables -android.widget.TextView$InputContentType -android.widget.TextView$InputMethodState +android.widget.TextView$CommitSelectionReceiver android.widget.TextView$Marquee -android.widget.TextView$MenuHandler -android.widget.TextView$SavedState -android.widget.TextView$SavedState$1 -android.widget.ToggleButton +android.widget.TimePicker android.widget.TwoLineListItem +android.widget.VideoView android.widget.ViewAnimator android.widget.ViewSwitcher android.widget.ZoomButton +android.widget.ZoomButtonsController android.widget.ZoomControls -com.android.common.ArrayListCursor -com.android.common.FastXmlSerializer -com.android.common.NetworkConnectivityListener -com.android.common.NetworkConnectivityListener$State -com.android.common.XmlUtils -com.android.internal.database.SortCursor +com.android.internal.R$styleable +com.android.internal.app.AlertActivity +com.android.internal.app.AlertController +com.android.internal.app.AlertController$AlertParams +com.android.internal.app.AlertController$RecycleListView +com.android.internal.app.ChooserActivity +com.android.internal.app.ResolverActivity +com.android.internal.app.ResolverActivity$ResolveListAdapter com.android.internal.appwidget.IAppWidgetService$Stub -com.android.internal.http.multipart.FilePart -com.android.internal.http.multipart.MultipartEntity -com.android.internal.http.multipart.Part -com.android.internal.http.multipart.PartSource -com.android.internal.http.multipart.StringPart -com.android.internal.logging.AndroidConfig +com.android.internal.content.SyncStateContentProviderHelper +com.android.internal.graphics.NativeUtils +com.android.internal.location.DummyLocationProvider +com.android.internal.location.GpsLocationProvider com.android.internal.logging.AndroidHandler com.android.internal.os.AndroidPrintStream +com.android.internal.os.BinderInternal com.android.internal.os.BinderInternal$GcWatcher +com.android.internal.os.IResultReceiver$Stub com.android.internal.os.LoggingPrintStream com.android.internal.os.LoggingPrintStream$1 com.android.internal.os.RuntimeInit com.android.internal.os.RuntimeInit$1 com.android.internal.os.RuntimeInit$UncaughtHandler -com.android.internal.os.ZygoteInit$MethodAndArgsCaller -com.android.internal.policy.IPolicy +com.android.internal.os.SamplingProfilerIntegration +com.android.internal.os.ZygoteConnection +com.android.internal.os.ZygoteConnection$Arguments +com.android.internal.os.ZygoteInit +com.android.internal.net.DomainNameValidator com.android.internal.policy.PolicyManager com.android.internal.policy.impl.PhoneLayoutInflater com.android.internal.policy.impl.PhoneWindow -com.android.internal.policy.impl.PhoneWindow$1 -com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback com.android.internal.policy.impl.PhoneWindow$DecorView -com.android.internal.policy.impl.PhoneWindow$PanelFeatureState com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState -com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState$1 +com.android.internal.policy.impl.PhoneWindowManager com.android.internal.policy.impl.Policy -com.android.internal.telephony.Connection$DisconnectCause -com.android.internal.telephony.Connection$PostDialState -com.android.internal.telephony.IPhoneStateListener$Stub +com.android.internal.telephony.GsmAlphabet com.android.internal.telephony.ITelephony$Stub -com.android.internal.telephony.Phone -com.android.internal.telephony.Phone$DataActivityState -com.android.internal.telephony.Phone$DataState -com.android.internal.telephony.Phone$State -com.android.internal.telephony.Phone$SuppService -com.android.internal.telephony.PhoneBase -com.android.internal.telephony.PhoneStateIntentReceiver +com.android.internal.telephony.ITelephony$Stub$Proxy +com.android.internal.telephony.ITelephonyRegistry$Stub com.android.internal.telephony.IccCard$State -com.android.internal.telephony.BaseCommands -com.android.internal.telephony.CallForwardInfo -com.android.internal.telephony.CommandsInterface -com.android.internal.telephony.DriverCall -com.android.internal.telephony.DriverCall$State -com.android.internal.telephony.gsm.GsmConnection -com.android.internal.telephony.gsm.GSMPhone -com.android.internal.telephony.GsmAlphabet -com.android.internal.telephony.gsm.GsmMmiCode -com.android.internal.telephony.gsm.SimCard -com.android.internal.telephony.ISms$Stub -com.android.internal.telephony.RIL -com.android.internal.telephony.ServiceStateTracker - -com.android.internal.telephony.gsm.stk.ComprehensionTlvTag -com.android.internal.telephony.gsm.stk.ResultCode +com.android.internal.telephony.Phone$State +com.android.internal.telephony.SmsAddress +com.android.internal.telephony.SmsMessageBase +com.android.internal.telephony.gsm.GsmSmsAddress +com.android.internal.telephony.gsm.SmsMessage +com.android.internal.telephony.gsm.SmsMessage$PduParser +com.android.internal.util.ArrayUtils +com.android.internal.util.FastMath +com.android.internal.util.FastXmlSerializer +com.android.internal.util.HanziToPinyin +com.android.internal.util.XmlUtils com.android.internal.view.IInputConnectionWrapper -com.android.internal.view.IInputConnectionWrapper$MyHandler -com.android.internal.view.IInputConnectionWrapper$SomeArgs - -com.android.internal.view.IInputContext com.android.internal.view.IInputContext$Stub -com.android.internal.view.IInputContext$Stub$Proxy - -com.android.internal.view.IInputContextCallback -com.android.internal.view.IInputContextCallback$Stub -com.android.internal.view.IInputContextCallback$Stub$Proxy - -com.android.internal.view.IInputMethod -com.android.internal.view.IInputMethod$Stub -com.android.internal.view.IInputMethod$Stub$Proxy - -com.android.internal.view.IInputMethodCallback -com.android.internal.view.IInputMethodCallback$Stub -com.android.internal.view.IInputMethodCallback$Stub$Proxy - -com.android.internal.view.IInputMethodClient -com.android.internal.view.IInputMethodClient$Stub -com.android.internal.view.IInputMethodClient$Stub$Proxy - -com.android.internal.view.IInputMethodManager com.android.internal.view.IInputMethodManager$Stub -com.android.internal.view.IInputMethodManager$Stub$Proxy - -com.android.internal.view.IInputMethodSession -com.android.internal.view.IInputMethodSession$Stub -com.android.internal.view.IInputMethodSession$Stub$Proxy - -com.android.internal.view.InputBindResult -com.android.internal.view.InputBindResult$1 - -com.android.internal.view.InputConnectionWrapper -com.android.internal.view.InputConnectionWrapper$InputContextCallback -com.android.internal.view.menu.ExpandedMenuView +com.android.internal.view.menu.ContextMenuBuilder com.android.internal.view.menu.IconMenuItemView com.android.internal.view.menu.IconMenuView +com.android.internal.view.menu.IconMenuView$SavedState com.android.internal.view.menu.ListMenuItemView com.android.internal.view.menu.MenuBuilder -com.android.internal.view.menu.MenuBuilder$Callback -com.android.internal.view.menu.MenuDialogHelper com.android.internal.view.menu.MenuItemImpl com.android.internal.view.menu.SubMenuBuilder -com.android.internal.widget.RotarySelector -com.android.internal.widget.Smileys -com.google.android.gles_jni.EGLDisplayImpl +com.android.internal.widget.ContactHeaderWidget +com.android.internal.widget.DialogTitle +com.android.internal.widget.EditableInputConnection +com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient +com.android.internal.widget.LockPatternUtils +com.android.internal.widget.LockPatternView +com.android.internal.widget.LockPatternView$Cell com.google.android.gles_jni.EGLImpl com.google.android.gles_jni.GLImpl com.ibm.icu4jni.charset.CharsetDecoderICU com.ibm.icu4jni.charset.CharsetEncoderICU com.ibm.icu4jni.charset.CharsetICU -com.ibm.icu4jni.text.CollationAttribute +com.ibm.icu4jni.charset.CharsetProviderICU +com.ibm.icu4jni.charset.NativeConverter +com.ibm.icu4jni.common.ErrorCode +com.ibm.icu4jni.lang.UCharacter +com.ibm.icu4jni.regex.NativeRegEx +com.ibm.icu4jni.text.Collator +com.ibm.icu4jni.text.NativeBreakIterator +com.ibm.icu4jni.text.NativeCollation com.ibm.icu4jni.text.NativeDecimalFormat com.ibm.icu4jni.text.RuleBasedCollator +com.ibm.icu4jni.text.RuleBasedNumberFormat +com.ibm.icu4jni.util.Resources com.ibm.icu4jni.util.Resources$DefaultTimeZones -dalvik.system.DexFile +dalvik.system.DalvikLogHandler +dalvik.system.DalvikLogging +dalvik.system.NativeStart dalvik.system.PathClassLoader +dalvik.system.SamplingProfiler +dalvik.system.TouchDex +dalvik.system.VMDebug +dalvik.system.VMRuntime +dalvik.system.VMStack +dalvik.system.Zygote java.beans.PropertyChangeEvent java.beans.PropertyChangeListener java.beans.PropertyChangeSupport java.io.BufferedInputStream +java.io.BufferedReader java.io.ByteArrayInputStream -java.io.ByteArrayOutputStream +java.io.Closeable +java.io.DataInput +java.io.DataOutput +java.io.DataOutputStream java.io.File java.io.FileDescriptor java.io.FileInputStream java.io.FileInputStream$RepositioningLock java.io.FileNotFoundException +java.io.FileOutputStream java.io.FilterInputStream +java.io.FilterOutputStream +java.io.Flushable java.io.IOException +java.io.InputStream +java.io.InputStreamReader +java.io.InterruptedIOException +java.io.ObjectInput +java.io.ObjectInputStream +java.io.ObjectOutput +java.io.ObjectOutputStream java.io.ObjectStreamClass +java.io.ObjectStreamClass$OSCThreadLocalCache +java.io.ObjectStreamConstants +java.io.ObjectStreamException +java.io.ObjectStreamField +java.io.OutputStream +java.io.OutputStreamWriter +java.io.PrintStream java.io.PrintWriter +java.io.PushbackReader java.io.RandomAccessFile java.io.RandomAccessFile$RepositionLock -java.io.StringWriter -java.io.Writer +java.io.Reader +java.io.Serializable +java.io.StreamCorruptedException +java.lang.AbstractStringBuilder +java.lang.Appendable +java.lang.ArrayIndexOutOfBoundsException +java.lang.Boolean +java.lang.BootClassLoader +java.lang.Byte +java.lang.CharSequence +java.lang.Character +java.lang.Character$UnicodeBlock java.lang.Class java.lang.ClassCache -java.lang.ClassNotFoundException +java.lang.ClassCache$EnumComparator +java.lang.ClassLoader +java.lang.ClassLoader$SystemClassLoader +java.lang.Cloneable +java.lang.Comparable +java.lang.Double +java.lang.Enum +java.lang.Error +java.lang.Exception +java.lang.Float java.lang.IllegalArgumentException -java.lang.IllegalStateException +java.lang.IndexOutOfBoundsException java.lang.Integer +java.lang.InternalError +java.lang.InterruptedException +java.lang.Iterable +java.lang.LangAccessImpl java.lang.LinkageError java.lang.Long +java.lang.Math java.lang.NoClassDefFoundError +java.lang.NoSuchMethodError +java.lang.Number java.lang.NumberFormatException java.lang.Object +java.lang.OutOfMemoryError +java.lang.Readable +java.lang.Runnable java.lang.Runtime java.lang.RuntimeException +java.lang.RuntimePermission +java.lang.SecurityException +java.lang.Short +java.lang.StackOverflowError +java.lang.StackTraceElement +java.lang.StrictMath java.lang.String +java.lang.String$CaseInsensitiveComparator java.lang.StringBuffer java.lang.StringBuilder +java.lang.System +java.lang.SystemProperties java.lang.Thread +java.lang.Thread$State +java.lang.Thread$UncaughtExceptionHandler +java.lang.ThreadGroup +java.lang.ThreadGroup$ChildrenGroupsLock +java.lang.ThreadGroup$ChildrenThreadsLock java.lang.ThreadLocal java.lang.ThreadLocal$Values java.lang.Throwable +java.lang.UnsatisfiedLinkError +java.lang.UnsupportedOperationException +java.lang.VMClassLoader java.lang.VMThread +java.lang.VirtualMachineError +java.lang.Void +java.lang.annotation.Annotation +java.lang.ref.PhantomReference +java.lang.ref.Reference java.lang.ref.ReferenceQueue java.lang.ref.SoftReference java.lang.ref.WeakReference +java.lang.reflect.AccessibleObject +java.lang.reflect.AnnotatedElement +java.lang.reflect.Array java.lang.reflect.Constructor +java.lang.reflect.Field +java.lang.reflect.GenericDeclaration +java.lang.reflect.InvocationHandler +java.lang.reflect.Member java.lang.reflect.Method java.lang.reflect.Modifier +java.lang.reflect.Proxy +java.lang.reflect.ReflectionAccessImpl +java.lang.reflect.Type java.math.BigDecimal java.math.BigInt java.math.BigInteger java.math.Multiplication +java.net.AddressCache +java.net.AddressCache$1 +java.net.ConnectException java.net.ContentHandler +java.net.DatagramPacket +java.net.Inet4Address java.net.InetAddress +java.net.InetAddress$1 +java.net.InetAddress$2 java.net.InetAddress$WaitReachable +java.net.InetSocketAddress java.net.JarURLConnection java.net.NetPermission -java.net.ProxySelectorImpl -java.net.Socket$ConnectLock +java.net.NetworkInterface +java.net.ServerSocket +java.net.Socket +java.net.SocketException +java.net.SocketImpl +java.net.SocketOptions java.net.URI java.net.URL java.net.URLConnection java.net.URLConnection$DefaultContentHandler java.net.URLStreamHandler +java.nio.BaseByteBuffer +java.nio.Buffer +java.nio.BufferFactory +java.nio.ByteBuffer java.nio.ByteOrder +java.nio.CharArrayBuffer +java.nio.CharBuffer java.nio.CharSequenceAdapter +java.nio.CharToByteBufferAdapter java.nio.DirectByteBuffer +java.nio.FloatToByteBufferAdapter +java.nio.HeapByteBuffer +java.nio.IntToByteBufferAdapter +java.nio.LongBuffer +java.nio.LongToByteBufferAdapter +java.nio.NIOAccess +java.nio.ReadWriteCharArrayBuffer java.nio.ReadWriteDirectByteBuffer -java.nio.ReadWriteIntArrayBuffer -java.nio.ReadWriteShortArrayBuffer -java.nio.ShortBuffer +java.nio.ReadWriteHeapByteBuffer java.nio.ShortToByteBufferAdapter +java.nio.channels.ByteChannel +java.nio.channels.Channel +java.nio.channels.FileChannel +java.nio.channels.GatheringByteChannel +java.nio.channels.InterruptibleChannel +java.nio.channels.ReadableByteChannel +java.nio.channels.ScatteringByteChannel +java.nio.channels.WritableByteChannel +java.nio.channels.spi.AbstractInterruptibleChannel +java.nio.channels.spi.AbstractInterruptibleChannel$1 +java.nio.channels.spi.AbstractInterruptibleChannel$2 +java.nio.charset.Charset +java.nio.charset.Charset$1 +java.nio.charset.CharsetDecoder java.nio.charset.CharsetEncoder +java.nio.charset.CoderResult +java.nio.charset.CodingErrorAction +java.nio.charset.spi.CharsetProvider java.security.AccessControlContext -java.security.GeneralSecurityException +java.security.AccessController +java.security.BasicPermission +java.security.Guard java.security.KeyStore java.security.MessageDigest +java.security.Permission +java.security.PrivilegedAction +java.security.PrivilegedExceptionAction java.security.ProtectionDomain java.security.Provider -java.security.SecureRandom java.security.Security -java.security.cert.CertPathValidator -java.security.cert.CertificateFactory -java.security.cert.PKIXParameters -java.security.cert.TrustAnchor -java.security.cert.X509CertSelector java.security.cert.X509Certificate +java.text.AttributedCharacterIterator$Attribute java.text.Collator +java.text.Collator$1 java.text.DateFormat java.text.DateFormat$Field java.text.DecimalFormat java.text.DecimalFormatSymbols -java.text.MessageFormat +java.text.Format java.text.NumberFormat -java.text.RuleBasedCollator java.text.SimpleDateFormat -java.util.AbstractList$FullListIterator -java.util.AbstractList$SimpleListIterator +java.util.AbstractCollection +java.util.AbstractList +java.util.AbstractMap +java.util.AbstractSet java.util.ArrayList +java.util.ArrayList$ArrayListIterator java.util.Arrays java.util.Arrays$ArrayList +java.util.BitSet java.util.Calendar -java.util.Collections$SynchronizedCollection -java.util.Collections$UnmodifiableList -java.util.Collections$UnmodifiableMap -java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$1 +java.util.Collection +java.util.Collections +java.util.Collections$EmptyList +java.util.Collections$EmptyMap +java.util.Collections$EmptySet +java.util.Collections$SingletonSet +java.util.Collections$UnmodifiableCollection +java.util.Collections$UnmodifiableCollection$1 +java.util.Collections$UnmodifiableRandomAccessList +java.util.Collections$UnmodifiableSet +java.util.Comparator java.util.Date +java.util.Dictionary java.util.EnumMap +java.util.EnumSet +java.util.Enumeration java.util.EventListener java.util.EventObject java.util.Formatter java.util.GregorianCalendar java.util.HashMap +java.util.HashMap$HashIterator +java.util.HashMap$HashMapEntry +java.util.HashMap$KeyIterator +java.util.HashMap$KeySet +java.util.HashMap$Values java.util.HashSet java.util.Hashtable +java.util.Hashtable$HashIterator +java.util.Hashtable$HashtableEntry +java.util.Hashtable$KeyEnumeration +java.util.Hashtable$ValueIterator +java.util.Hashtable$Values java.util.IdentityHashMap +java.util.Iterator java.util.LinkedHashMap +java.util.LinkedHashMap$LinkedEntry +java.util.LinkedHashMap$LinkedHashIterator +java.util.LinkedHashMap$ValueIterator java.util.LinkedList -java.util.LinkedList$Link java.util.List +java.util.ListIterator java.util.Locale +java.util.Map +java.util.Map$Entry +java.util.MiniEnumSet java.util.Properties -java.util.Random +java.util.PropertyPermission +java.util.RandomAccess java.util.ResourceBundle +java.util.Set java.util.SimpleTimeZone +java.util.SortedMap +java.util.SortedSet +java.util.SpecialAccess +java.util.Stack +java.util.StringTokenizer java.util.TimeZone java.util.TreeMap -java.util.TreeMap$MapEntry java.util.TreeSet +java.util.UUID java.util.Vector java.util.WeakHashMap java.util.WeakHashMap$Entry java.util.concurrent.ConcurrentHashMap java.util.concurrent.ConcurrentLinkedQueue -java.util.concurrent.DelayQueue +java.util.concurrent.ConcurrentLinkedQueue$Node +java.util.concurrent.CopyOnWriteArrayList +java.util.concurrent.CopyOnWriteArrayList$COWIterator +java.util.concurrent.Executors$DelegatedExecutorService +java.util.concurrent.FutureTask java.util.concurrent.LinkedBlockingQueue -java.util.concurrent.ScheduledThreadPoolExecutor -java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue +java.util.concurrent.Semaphore +java.util.concurrent.ThreadPoolExecutor java.util.concurrent.TimeUnit java.util.concurrent.atomic.AtomicBoolean java.util.concurrent.atomic.AtomicInteger +java.util.concurrent.atomic.AtomicLong +java.util.concurrent.atomic.AtomicReference java.util.concurrent.atomic.UnsafeAccess +java.util.concurrent.locks.AbstractOwnableSynchronizer java.util.concurrent.locks.AbstractQueuedSynchronizer java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject java.util.concurrent.locks.AbstractQueuedSynchronizer$Node java.util.concurrent.locks.Lock -java.util.concurrent.locks.LockSupport java.util.concurrent.locks.ReentrantLock -java.util.concurrent.locks.ReentrantLock$FairSync java.util.concurrent.locks.ReentrantLock$NonfairSync java.util.concurrent.locks.ReentrantLock$Sync -java.util.concurrent.locks.ReentrantReadWriteLock -java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync java.util.concurrent.locks.UnsafeAccess java.util.jar.Attributes java.util.jar.Attributes$Name @@ -852,25 +1041,25 @@ java.util.jar.InitManifest java.util.jar.JarEntry java.util.jar.JarFile java.util.jar.JarFile$1JarFileEnumerator -java.util.jar.JarFile$JarFileInputStream java.util.jar.JarVerifier java.util.jar.Manifest -java.util.logging.ErrorManager -java.util.logging.Formatter java.util.logging.Handler java.util.logging.Level java.util.logging.LogManager java.util.logging.LogManager$1 java.util.logging.LogManager$2 java.util.logging.LogManager$2$1 -java.util.logging.LogManager$3 -java.util.logging.LogRecord +java.util.logging.LogManager$4 java.util.logging.Logger +java.util.logging.Logger$1 java.util.logging.LoggingPermission -java.util.logging.SimpleFormatter +java.util.regex.MatchResult java.util.regex.Matcher java.util.regex.Pattern -java.util.zip.DeflaterOutputStream +java.util.zip.Adler32 +java.util.zip.CRC32 +java.util.zip.Checksum +java.util.zip.Deflater java.util.zip.Inflater java.util.zip.InflaterInputStream java.util.zip.ZipConstants @@ -879,63 +1068,90 @@ java.util.zip.ZipEntry$LittleEndianReader java.util.zip.ZipFile java.util.zip.ZipFile$2 java.util.zip.ZipFile$RAFStream -javax.microedition.khronos.egl.EGLContext +java.util.zip.ZipFile$ZipInflaterInputStream +javax.crypto.Cipher +javax.crypto.Mac +javax.crypto.spec.IvParameterSpec +javax.microedition.khronos.egl.EGL +javax.microedition.khronos.egl.EGL10 +javax.microedition.khronos.opengles.GL +javax.microedition.khronos.opengles.GL10 +javax.microedition.khronos.opengles.GL10Ext +javax.microedition.khronos.opengles.GL11 +javax.microedition.khronos.opengles.GL11Ext +javax.microedition.khronos.opengles.GL11ExtensionPack +javax.net.ssl.DefaultHostnameVerifier javax.net.ssl.HttpsURLConnection -javax.net.ssl.SSLHandshakeException +javax.net.ssl.SSLServerSocket +javax.net.ssl.SSLSession +javax.net.ssl.SSLSocket +javax.net.ssl.SSLSocketFactory javax.security.auth.x500.X500Principal javax.security.cert.X509Certificate -javax.security.cert.X509Certificate$2 junit.framework.Assert -org.apache.commons.codec.binary.Base64 -org.apache.commons.codec.binary.Hex org.apache.commons.logging.LogFactory -org.apache.commons.logging.impl.Jdk14Logger org.apache.harmony.archive.util.Util -org.apache.harmony.dalvik.ddmc.Chunk +org.apache.harmony.dalvik.NativeTestTarget org.apache.harmony.dalvik.ddmc.ChunkHandler org.apache.harmony.dalvik.ddmc.DdmServer -org.apache.harmony.dalvik.ddmc.DdmVmInternal +org.apache.harmony.kernel.vm.LangAccess +org.apache.harmony.kernel.vm.ReflectionAccess +org.apache.harmony.lang.annotation.AnnotationFactory +org.apache.harmony.lang.annotation.AnnotationMember org.apache.harmony.luni.internal.net.www.protocol.file.FileURLConnection org.apache.harmony.luni.internal.net.www.protocol.file.Handler org.apache.harmony.luni.internal.net.www.protocol.http.Handler -org.apache.harmony.luni.internal.net.www.protocol.https.Handler org.apache.harmony.luni.internal.net.www.protocol.jar.Handler org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl -org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl$1 org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl$JarURLConnectionInputStream org.apache.harmony.luni.internal.util.TimezoneGetter -org.apache.harmony.luni.internal.util.ZoneInfo org.apache.harmony.luni.internal.util.ZoneInfoDB +org.apache.harmony.luni.net.GenericIPMreq org.apache.harmony.luni.net.PlainSocketImpl +org.apache.harmony.luni.platform.Endianness +org.apache.harmony.luni.platform.ICommonDataTypes +org.apache.harmony.luni.platform.IFileSystem +org.apache.harmony.luni.platform.IMemorySystem +org.apache.harmony.luni.platform.INetworkSystem +org.apache.harmony.luni.platform.OSFileSystem +org.apache.harmony.luni.platform.OSMemory +org.apache.harmony.luni.platform.OSNetworkSystem +org.apache.harmony.luni.platform.Platform org.apache.harmony.luni.platform.PlatformAddress -org.apache.harmony.luni.util.TwoKeyHashMap +org.apache.harmony.luni.platform.PlatformAddressFactory +org.apache.harmony.luni.util.FloatingPointParser +org.apache.harmony.luni.util.InputStreamHelper +org.apache.harmony.luni.util.InputStreamHelper$1 +org.apache.harmony.luni.util.InputStreamHelper$ExposedByteArrayInputStream +org.apache.harmony.luni.util.LocaleCache +org.apache.harmony.luni.util.Msg +org.apache.harmony.luni.util.NumberConverter +org.apache.harmony.luni.util.PriviAction +org.apache.harmony.luni.util.ThreadLocalCache +org.apache.harmony.luni.util.ThreadLocalCache$1 +org.apache.harmony.luni.util.ThreadLocalCache$2 +org.apache.harmony.luni.util.ThreadLocalCache$3 +org.apache.harmony.luni.util.ThreadLocalCache$4 +org.apache.harmony.luni.util.ThreadLocalCache$5 +org.apache.harmony.luni.util.Util +org.apache.harmony.nio.FileChannelFactory +org.apache.harmony.nio.internal.DirectBuffer +org.apache.harmony.nio.internal.FileChannelImpl org.apache.harmony.nio.internal.FileChannelImpl$RepositioningLock +org.apache.harmony.nio.internal.FileLockImpl org.apache.harmony.nio.internal.LockManager org.apache.harmony.nio.internal.LockManager$1 -org.apache.harmony.nio.internal.ReadOnlyFileChannel -org.apache.harmony.security.asn1.ASN1BitString -org.apache.harmony.security.asn1.ASN1BitString$ASN1NamedBitList -org.apache.harmony.security.asn1.ASN1Boolean -org.apache.harmony.security.asn1.ASN1Explicit +org.apache.harmony.nio.internal.WriteOnlyFileChannel org.apache.harmony.security.asn1.ASN1GeneralizedTime -org.apache.harmony.security.asn1.ASN1Implicit -org.apache.harmony.security.asn1.ASN1Integer -org.apache.harmony.security.asn1.ASN1OctetString -org.apache.harmony.security.asn1.ASN1SetOf +org.apache.harmony.security.asn1.ASN1Oid org.apache.harmony.security.asn1.ASN1StringType -org.apache.harmony.security.asn1.ASN1StringType$1 -org.apache.harmony.security.asn1.ASN1StringType$2 -org.apache.harmony.security.asn1.ASN1StringType$3 -org.apache.harmony.security.asn1.ASN1StringType$4 -org.apache.harmony.security.asn1.ASN1StringType$5 -org.apache.harmony.security.asn1.ASN1StringType$6 -org.apache.harmony.security.asn1.ASN1StringType$7 -org.apache.harmony.security.asn1.ASN1UTCTime -org.apache.harmony.security.asn1.BitString +org.apache.harmony.security.asn1.DerInputStream +org.apache.harmony.security.asn1.DerOutputStream org.apache.harmony.security.fortress.Engine org.apache.harmony.security.fortress.SecurityUtils org.apache.harmony.security.fortress.Services org.apache.harmony.security.pkcs7.ContentInfo +org.apache.harmony.security.provider.cert.DRLCertFactory org.apache.harmony.security.provider.cert.X509CertFactoryImpl org.apache.harmony.security.provider.cert.X509CertImpl org.apache.harmony.security.provider.cert.X509CertPathImpl @@ -943,183 +1159,111 @@ org.apache.harmony.security.provider.crypto.RandomBitsSupplier org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl org.apache.harmony.security.utils.AlgNameMapper org.apache.harmony.security.x501.AttributeTypeAndValue -org.apache.harmony.security.x501.AttributeValue org.apache.harmony.security.x501.DirectoryString -org.apache.harmony.security.x501.DirectoryString$1 org.apache.harmony.security.x501.Name -org.apache.harmony.security.x501.Name$1 org.apache.harmony.security.x509.AlgorithmIdentifier -org.apache.harmony.security.x509.AlgorithmIdentifier$1 org.apache.harmony.security.x509.BasicConstraints -org.apache.harmony.security.x509.BasicConstraints$1 org.apache.harmony.security.x509.Certificate -org.apache.harmony.security.x509.Certificate$1 +org.apache.harmony.security.x509.EDIPartyName org.apache.harmony.security.x509.Extension -org.apache.harmony.security.x509.Extension$1 -org.apache.harmony.security.x509.Extension$2 org.apache.harmony.security.x509.Extensions -org.apache.harmony.security.x509.Extensions$1 org.apache.harmony.security.x509.GeneralName org.apache.harmony.security.x509.GeneralNames org.apache.harmony.security.x509.KeyUsage org.apache.harmony.security.x509.ORAddress +org.apache.harmony.security.x509.OtherName org.apache.harmony.security.x509.SubjectPublicKeyInfo -org.apache.harmony.security.x509.SubjectPublicKeyInfo$1 org.apache.harmony.security.x509.TBSCertificate -org.apache.harmony.security.x509.TBSCertificate$1 org.apache.harmony.security.x509.Time -org.apache.harmony.security.x509.Time$1 org.apache.harmony.security.x509.Validity -org.apache.harmony.security.x509.Validity$1 +org.apache.harmony.text.BidiWrapper +org.apache.harmony.xml.ExpatAttributes org.apache.harmony.xml.ExpatParser org.apache.harmony.xml.ExpatPullParser -org.apache.harmony.xml.ExpatReader -org.apache.harmony.xnet.provider.jsse.ClientSessionContext +org.apache.harmony.xml.parsers.SAXParserFactoryImpl +org.apache.harmony.xnet.provider.jsse.FileClientSessionCache +org.apache.harmony.xnet.provider.jsse.NativeCrypto +org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$Finalizer -org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream -org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream +org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$LoggerHolder +org.apache.harmony.xnet.provider.jsse.ProtocolVersion org.apache.harmony.xnet.provider.jsse.SSLContextImpl org.apache.harmony.xnet.provider.jsse.SSLParameters -org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl -org.apache.harmony.xnet.provider.jsse.TrustManagerImpl -org.apache.http.HttpHost -org.apache.http.HttpRequestInterceptor +org.apache.harmony.xnet.provider.jsse.ServerSessionContext org.apache.http.HttpVersion -org.apache.http.auth.AuthSchemeRegistry -org.apache.http.client.HttpClient -org.apache.http.client.RequestDirector +org.apache.http.client.methods.HttpEntityEnclosingRequestBase +org.apache.http.client.methods.HttpGet +org.apache.http.client.methods.HttpPost org.apache.http.client.methods.HttpRequestBase -org.apache.http.client.protocol.RequestAddCookies -org.apache.http.client.protocol.RequestDefaultHeaders -org.apache.http.client.protocol.RequestProxyAuthentication -org.apache.http.client.protocol.RequestTargetAuthentication -org.apache.http.client.protocol.ResponseProcessCookies -org.apache.http.conn.params.ConnManagerParams$1 +org.apache.http.conn.BasicManagedEntity +org.apache.http.conn.params.ConnManagerParams org.apache.http.conn.params.ConnRouteParams org.apache.http.conn.routing.HttpRoute -org.apache.http.conn.routing.RouteInfo$LayerType -org.apache.http.conn.routing.RouteInfo$TunnelType -org.apache.http.conn.routing.RouteTracker -org.apache.http.conn.scheme.PlainSocketFactory -org.apache.http.conn.scheme.Scheme -org.apache.http.conn.scheme.SchemeRegistry -org.apache.http.conn.ssl.AllowAllHostnameVerifier -org.apache.http.conn.ssl.BrowserCompatHostnameVerifier +org.apache.http.conn.ssl.AbstractVerifier org.apache.http.conn.ssl.SSLSocketFactory -org.apache.http.conn.ssl.StrictHostnameVerifier org.apache.http.conn.util.InetAddressUtils -org.apache.http.cookie.CookieSpecRegistry -org.apache.http.impl.DefaultConnectionReuseStrategy -org.apache.http.impl.DefaultHttpResponseFactory +org.apache.http.impl.AbstractHttpClientConnection org.apache.http.impl.EnglishReasonPhraseCatalog -org.apache.http.impl.HttpConnectionMetricsImpl org.apache.http.impl.SocketHttpClientConnection -org.apache.http.impl.auth.BasicSchemeFactory -org.apache.http.impl.auth.DigestSchemeFactory org.apache.http.impl.client.AbstractAuthenticationHandler org.apache.http.impl.client.AbstractHttpClient -org.apache.http.impl.client.BasicCredentialsProvider +org.apache.http.impl.client.BasicCookieStore org.apache.http.impl.client.DefaultHttpClient -org.apache.http.impl.client.DefaultHttpRequestRetryHandler -org.apache.http.impl.client.DefaultProxyAuthenticationHandler -org.apache.http.impl.client.DefaultRedirectHandler -org.apache.http.impl.client.DefaultTargetAuthenticationHandler -org.apache.http.impl.client.DefaultUserTokenHandler org.apache.http.impl.client.EntityEnclosingRequestWrapper org.apache.http.impl.conn.AbstractClientConnAdapter +org.apache.http.impl.conn.AbstractPooledConnAdapter org.apache.http.impl.conn.DefaultClientConnection -org.apache.http.impl.conn.DefaultClientConnectionOperator -org.apache.http.impl.conn.DefaultHttpRoutePlanner -org.apache.http.impl.conn.DefaultResponseParser -org.apache.http.impl.conn.IdleConnectionHandler -org.apache.http.impl.conn.tsccm.BasicPoolEntry -org.apache.http.impl.conn.tsccm.BasicPoolEntryRef +org.apache.http.impl.conn.SingleClientConnManager org.apache.http.impl.conn.tsccm.ConnPoolByRoute -org.apache.http.impl.conn.tsccm.RefQueueWorker -org.apache.http.impl.conn.tsccm.RouteSpecificPool org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager org.apache.http.impl.cookie.BasicClientCookie -org.apache.http.impl.cookie.BestMatchSpecFactory -org.apache.http.impl.cookie.BrowserCompatSpecFactory +org.apache.http.impl.cookie.BrowserCompatSpec org.apache.http.impl.cookie.DateUtils -org.apache.http.impl.cookie.NetscapeDraftSpecFactory -org.apache.http.impl.cookie.RFC2109SpecFactory -org.apache.http.impl.cookie.RFC2965SpecFactory -org.apache.http.impl.entity.EntityDeserializer -org.apache.http.impl.entity.EntitySerializer -org.apache.http.impl.entity.LaxContentLengthStrategy -org.apache.http.impl.entity.StrictContentLengthStrategy -org.apache.http.impl.io.HttpRequestWriter -org.apache.http.impl.io.HttpTransportMetricsImpl +org.apache.http.impl.cookie.DateUtils$DateFormatHolder +org.apache.http.impl.cookie.RFC2109Spec org.apache.http.impl.io.SocketInputBuffer -org.apache.http.impl.io.SocketOutputBuffer -org.apache.http.message.BasicHeaderValueParser org.apache.http.message.BasicHttpEntityEnclosingRequest +org.apache.http.message.BasicHttpRequest org.apache.http.message.BasicHttpResponse -org.apache.http.message.BasicLineFormatter org.apache.http.message.BasicLineParser +org.apache.http.message.BasicNameValuePair +org.apache.http.message.BasicTokenIterator org.apache.http.params.BasicHttpParams org.apache.http.protocol.BasicHttpProcessor org.apache.http.protocol.HTTP -org.apache.http.protocol.HttpRequestExecutor -org.apache.http.protocol.HttpRequestInterceptorList -org.apache.http.protocol.HttpResponseInterceptorList -org.apache.http.protocol.RequestConnControl -org.apache.http.protocol.RequestContent -org.apache.http.protocol.RequestExpectContinue -org.apache.http.protocol.RequestTargetHost -org.apache.http.protocol.RequestUserAgent -org.apache.http.util.ByteArrayBuffer -org.apache.http.util.CharArrayBuffer -org.apache.http.util.EntityUtils -org.apache.http.util.VersionInfo -org.bouncycastle.asn1.DERBitString -org.bouncycastle.asn1.DERIA5String -org.bouncycastle.asn1.DERInteger +org.bouncycastle.asn1.DERNull org.bouncycastle.asn1.DERObject org.bouncycastle.asn1.DERObjectIdentifier -org.bouncycastle.asn1.DEROctetString -org.bouncycastle.asn1.DERPrintableString -org.bouncycastle.asn1.DERSequence -org.bouncycastle.asn1.DERSet -org.bouncycastle.asn1.DERTaggedObject -org.bouncycastle.asn1.DERUTCTime -org.bouncycastle.asn1.DERUTF8String -org.bouncycastle.asn1.OrderedTable +org.bouncycastle.asn1.iana.IANAObjectIdentifiers org.bouncycastle.asn1.nist.NISTObjectIdentifiers +org.bouncycastle.asn1.oiw.OIWObjectIdentifiers org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers -org.bouncycastle.asn1.x509.AlgorithmIdentifier -org.bouncycastle.asn1.x509.RSAPublicKeyStructure -org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -org.bouncycastle.asn1.x509.TBSCertificateStructure -org.bouncycastle.asn1.x509.Time -org.bouncycastle.asn1.x509.X509CertificateStructure -org.bouncycastle.asn1.x509.X509Extension org.bouncycastle.asn1.x509.X509Extensions org.bouncycastle.asn1.x509.X509Name -org.bouncycastle.asn1.x509.X509NameElementList -org.bouncycastle.asn1.x9.X9ObjectIdentifiers +org.bouncycastle.crypto.digests.SHA1Digest org.bouncycastle.crypto.engines.AESFastEngine +org.bouncycastle.crypto.macs.HMac +org.bouncycastle.jce.provider.BouncyCastleProvider org.bouncycastle.jce.provider.CertPathValidatorUtilities -org.bouncycastle.jce.provider.JCEBlockCipher$AES -org.bouncycastle.jce.provider.JCERSAPublicKey -org.bouncycastle.jce.provider.JDKKeyFactory$RSA +org.bouncycastle.jce.provider.JCEBlockCipher org.bouncycastle.jce.provider.JDKKeyStore -org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry +org.bouncycastle.jce.provider.JDKX509CertificateFactory org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi -org.bouncycastle.jce.provider.RSAUtil +org.bouncycastle.jce.provider.WrapCipherSpi org.bouncycastle.jce.provider.X509CertificateObject org.ccil.cowan.tagsoup.HTMLScanner +org.ccil.cowan.tagsoup.HTMLSchema org.ccil.cowan.tagsoup.Parser -org.json.JSONArray org.json.JSONObject -org.json.JSONStringer org.kxml2.io.KXmlParser org.kxml2.io.KXmlSerializer +org.openssl.NativeBN +org.xml.sax.Attributes +org.xml.sax.InputSource +org.xml.sax.helpers.AttributesImpl org.xml.sax.helpers.DefaultHandler -org.xml.sax.helpers.NewInstance +org.xmlpull.v1.XmlPullParser org.xmlpull.v1.XmlPullParserFactory -org.xmlpull.v1.sax2.Driver sun.misc.Unsafe diff --git a/sax/tests/saxtests/src/android/sax/SafeSaxTest.java b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java index bee3938..e8cf2f7 100644 --- a/sax/tests/saxtests/src/android/sax/SafeSaxTest.java +++ b/sax/tests/saxtests/src/android/sax/SafeSaxTest.java @@ -29,7 +29,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.text.format.Time; import android.util.Log; import android.util.Xml; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java index 477ea0c..c55dcb3 100644 --- a/services/java/com/android/server/AccessibilityManagerService.java +++ b/services/java/com/android/server/AccessibilityManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import com.android.internal.content.PackageMonitor; import com.android.internal.os.HandlerCaller; import com.android.internal.os.HandlerCaller.SomeArgs; @@ -56,6 +57,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -145,13 +147,58 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void registerPackageChangeAndBootCompletedBroadcastReceiver() { Context context = mContext; - BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + PackageMonitor monitor = new PackageMonitor() { @Override - public void onReceive(Context context, Intent intent) { + public void onSomePackagesChanged() { synchronized (mLock) { populateAccessibilityServiceListLocked(); + manageServicesLocked(); + } + } + + @Override + public boolean onHandleForceStop(Intent intent, String[] packages, + int uid, boolean doit) { + synchronized (mLock) { + boolean changed = false; + Iterator<ComponentName> it = mEnabledServices.iterator(); + while (it.hasNext()) { + ComponentName comp = it.next(); + String compPkg = comp.getPackageName(); + for (String pkg : packages) { + if (compPkg.equals(pkg)) { + if (!doit) { + return true; + } + it.remove(); + changed = true; + } + } + } + if (changed) { + it = mEnabledServices.iterator(); + StringBuilder str = new StringBuilder(); + while (it.hasNext()) { + if (str.length() > 0) { + str.append(':'); + } + str.append(it.next().flattenToShortString()); + } + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + str.toString()); + manageServicesLocked(); + } + return false; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { + synchronized (mLock) { + populateAccessibilityServiceListLocked(); - if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { // get the accessibility enabled setting on boot mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; @@ -160,29 +207,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mIsEnabled) { updateClientsLocked(); } - } - manageServicesLocked(); + manageServicesLocked(); + } + + return; } + + super.onReceive(context, intent); } }; // package changes - IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - packageFilter.addDataScheme("package"); - context.registerReceiver(broadcastReceiver, packageFilter); - // Register for events related to sdcard installation. - IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(broadcastReceiver, sdFilter); + monitor.register(context, true); // boot completed IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - mContext.registerReceiver(broadcastReceiver, bootFiler); + mContext.registerReceiver(monitor, bootFiler); } /** @@ -529,8 +570,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; splitter.setString(servicesValue); while (splitter.hasNext()) { - ComponentName enabledService = ComponentName.unflattenFromString(splitter.next()); - enabledServices.add(enabledService); + String str = splitter.next(); + if (str == null || str.length() <= 0) { + continue; + } + ComponentName enabledService = ComponentName.unflattenFromString(str); + if (enabledService != null) { + enabledServices.add(enabledService); + } } } } diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 44cc0bb..f480209 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.IAlarmManager; @@ -344,6 +345,22 @@ class AlarmManagerService extends IAlarmManager.Stub { } } + public boolean lookForPackageLocked(String packageName) { + return lookForPackageLocked(mRtcWakeupAlarms, packageName) + || lookForPackageLocked(mRtcAlarms, packageName) + || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName) + || lookForPackageLocked(mElapsedRealtimeAlarms, packageName); + } + + private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) { + for (int i=alarmList.size()-1; i>=0; i--) { + if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) { + return true; + } + } + return false; + } + private ArrayList<Alarm> getAlarmList(int type) { switch (type) { case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms; @@ -778,6 +795,7 @@ class AlarmManagerService extends IAlarmManager.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addDataScheme("package"); mContext.registerReceiver(this, filter); // Register for events related to sdcard installation. @@ -791,7 +809,16 @@ class AlarmManagerService extends IAlarmManager.Stub { synchronized (mLock) { String action = intent.getAction(); String pkgList[] = null; - if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + for (String packageName : pkgList) { + if (lookForPackageLocked(packageName)) { + setResultCode(Activity.RESULT_OK); + return; + } + } + return; + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } else { Uri data = intent.getData(); diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 0b466f1..a5b0db9 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -57,7 +57,7 @@ import java.util.HashSet; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.appwidget.IAppWidgetHost; -import com.android.common.FastXmlSerializer; +import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index f79a02a..d4b28e2 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -1647,11 +1647,22 @@ class BackupManagerService extends IBackupManager.Stub { } if (metaInfo.versionCode > packageInfo.versionCode) { - String message = "Version " + metaInfo.versionCode - + " > installed version " + packageInfo.versionCode; - Log.w(TAG, "Package " + packageName + ": " + message); - EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, message); - continue; + // Data is from a "newer" version of the app than we have currently + // installed. If the app has not declared that it is prepared to + // handle this case, we do not attempt the restore. + if ((packageInfo.applicationInfo.flags + & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { + String message = "Version " + metaInfo.versionCode + + " > installed version " + packageInfo.versionCode; + Log.w(TAG, "Package " + packageName + ": " + message); + EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, + packageName, message); + continue; + } else { + if (DEBUG) Log.v(TAG, "Version " + metaInfo.versionCode + + " > installed " + packageInfo.versionCode + + " but restoreAnyVersion"); + } } if (!signaturesMatch(metaInfo.signatures, packageInfo)) { @@ -1695,8 +1706,10 @@ class BackupManagerService extends IBackupManager.Stub { // The agent was probably running with a stub Application object, // which isn't a valid run mode for the main app logic. Shut // down the app so that next time it's launched, it gets the - // usual full initialization. - if ((packageInfo.applicationInfo.flags + // usual full initialization. Note that this is only done for + // full-system restores: when a single app has requested a restore, + // it is explicitly not killed following that operation. + if (mTargetPackage == null && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { if (DEBUG) Log.d(TAG, "Restore complete, killing host process of " + packageInfo.applicationInfo.processName); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 108246d..19f4b8a 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -289,6 +289,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * the number of different network types is not going * to change very often. */ + boolean noMobileData = !getMobileDataEnabled(); for (int netType : mPriorityList) { switch (mNetAttributes[netType].mRadio) { case ConnectivityManager.TYPE_WIFI: @@ -306,6 +307,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler, netType, mNetAttributes[netType].mName); mNetTrackers[netType].startMonitoring(); + if (noMobileData) { + if (DBG) Log.d(TAG, "tearing down Mobile networks due to setting"); + mNetTrackers[netType].teardown(); + } break; default: Log.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " + @@ -530,6 +535,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { // TODO - move this into the MobileDataStateTracker int usedNetworkType = networkType; if(networkType == ConnectivityManager.TYPE_MOBILE) { + if (!getMobileDataEnabled()) { + if (DBG) Log.d(TAG, "requested special network with data disabled - rejected"); + return Phone.APN_TYPE_NOT_AVAILABLE; + } if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { @@ -767,6 +776,46 @@ public class ConnectivityService extends IConnectivityManager.Stub { mContext.sendBroadcast(broadcast); } + /** + * @see ConnectivityManager#getMobileDataEnabled() + */ + public boolean getMobileDataEnabled() { + enforceAccessPermission(); + boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.MOBILE_DATA, 1) == 1; + if (DBG) Log.d(TAG, "getMobileDataEnabled returning " + retVal); + return retVal; + } + + /** + * @see ConnectivityManager#setMobileDataEnabled(boolean) + */ + public synchronized void setMobileDataEnabled(boolean enabled) { + enforceChangePermission(); + if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + ")"); + + if (getMobileDataEnabled() == enabled) return; + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.MOBILE_DATA, enabled ? 1 : 0); + + if (enabled) { + if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) { + if (DBG) Log.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]); + mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect(); + } + } else { + for (NetworkStateTracker nt : mNetTrackers) { + if (nt == null) continue; + int netType = nt.getNetworkInfo().getType(); + if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) { + if (DBG) Log.d(TAG, "tearing down " + nt); + nt.teardown(); + } + } + } + } + private int getNumConnectedNetworks() { int numConnectedNets = 0; @@ -885,9 +934,18 @@ public class ConnectivityService extends IConnectivityManager.Stub { int newType = -1; int newPriority = -1; + boolean noMobileData = !getMobileDataEnabled(); for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) { if (checkType == prevNetType) continue; if (mNetAttributes[checkType] == null) continue; + if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE && + noMobileData) { + if (DBG) { + Log.d(TAG, "not failing over to mobile type " + checkType + + " because Mobile Data Disabled"); + } + continue; + } if (mNetAttributes[checkType].isDefault()) { /* TODO - if we have multiple nets we could use * we may want to put more thought into which we choose diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index a267e0f..9899e99 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -16,7 +16,9 @@ package com.android.server; -import com.android.common.FastXmlSerializer; +import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import org.xmlpull.v1.XmlPullParser; @@ -34,6 +36,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.IBinder; import android.os.IPowerManager; @@ -58,9 +61,10 @@ import java.util.List; * Implementation of the device policy APIs. */ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { - private static final String TAG = "DevicePolicyManagerService"; + static final String TAG = "DevicePolicyManagerService"; - private final Context mContext; + final Context mContext; + final MyPackageMonitor mMonitor; IPowerManager mIPowerManager; @@ -89,6 +93,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void writeToXml(XmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { + out.startTag(null, "policies"); + info.writePoliciesToXml(out); + out.endTag(null, "policies"); if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { out.startTag(null, "password-quality"); out.attribute(null, "value", Integer.toString(passwordQuality)); @@ -121,7 +128,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { continue; } String tag = parser.getName(); - if ("password-quality".equals(tag)) { + if ("policies".equals(tag)) { + info.readPoliciesFromXml(parser); + } else if ("password-quality".equals(tag)) { passwordQuality = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-length".equals(tag)) { @@ -133,6 +142,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("max-failed-password-wipe".equals(tag)) { maximumFailedPasswordsForWipe = Integer.parseInt( parser.getAttributeValue(null, "value")); + } else { + Log.w(TAG, "Unknown admin tag: " + tag); + } + XmlUtils.skipCurrentTag(parser); + } + } + } + + class MyPackageMonitor extends PackageMonitor { + public void onSomePackagesChanged() { + synchronized (DevicePolicyManagerService.this) { + for (int i=mAdminList.size()-1; i>=0; i--) { + ActiveAdmin aa = mAdminList.get(i); + int change = isPackageDisappearing(aa.info.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + Log.w(TAG, "Admin unexpectedly uninstalled: " + + aa.info.getComponent()); + mAdminList.remove(i); + } else if (isPackageModified(aa.info.getPackageName())) { + try { + mContext.getPackageManager().getReceiverInfo( + aa.info.getComponent(), 0); + } catch (NameNotFoundException e) { + Log.w(TAG, "Admin package change removed component: " + + aa.info.getComponent()); + mAdminList.remove(i); + } + } } } } @@ -143,6 +181,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ public DevicePolicyManagerService(Context context) { mContext = context; + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); } private IPowerManager getIPowerManager() { @@ -336,6 +376,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("failed-password-attempts".equals(tag)) { mFailedPasswordAttempts = Integer.parseInt( parser.getAttributeValue(null, "value")); + XmlUtils.skipCurrentTag(parser); + } else { + Log.w(TAG, "Unknown tag: " + tag); + XmlUtils.skipCurrentTag(parser); } } } catch (NullPointerException e) { @@ -420,6 +464,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public boolean packageHasActiveAdmins(String packageName) { + synchronized (this) { + final int N = mAdminList.size(); + for (int i=0; i<N; i++) { + if (mAdminList.get(i).info.getPackageName().equals(packageName)) { + return true; + } + } + return false; + } + } + public void removeActiveAdmin(ComponentName adminReceiver) { synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 4791718..a0c850f 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -41,6 +41,7 @@ import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Binder; +import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; @@ -304,6 +305,14 @@ class DockObserver extends UEventObserver { (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); mKeyguardLock = keyguardManager.newKeyguardLock(TAG); + final boolean enableCarMode = mDockState == Intent.EXTRA_DOCK_STATE_CAR; + if (enableCarMode) { + try { + setCarMode(enableCarMode); + } catch (RemoteException e) { + Log.w(TAG, "Unable to change car mode.", e); + } + } // don't bother broadcasting undocked here if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { update(); @@ -379,7 +388,10 @@ class DockObserver extends UEventObserver { final Uri soundUri = Uri.parse("file://" + soundPath); if (soundUri != null) { final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) sfx.play(); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } } } } @@ -412,7 +424,7 @@ class DockObserver extends UEventObserver { mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, LOCATION_UPDATE_MS, LOCATION_UPDATE_DISTANCE_METER, mLocationListener); retrieveLocation(); - if (mLocation != null) { + if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) { try { DockObserver.this.updateTwilight(); } catch (RemoteException e) { @@ -458,8 +470,8 @@ class DockObserver extends UEventObserver { if (location == null) { Time currentTime = new Time(); currentTime.set(System.currentTimeMillis()); - double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * currentTime.gmtoff - - (currentTime.isDst > 0 ? 3600 : 0); + double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * + (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0)); location = new Location("fake"); location.setLongitude(lngOffset); location.setLatitude(59.95); @@ -583,20 +595,26 @@ class DockObserver extends UEventObserver { } // schedule next update - final int mLastTwilightState = tw.mState; - // add some extra time to be on the save side. - long nextUpdate = DateUtils.MINUTE_IN_MILLIS; - if (currentTime > tw.mSunset) { - // next update should be on the following day - tw.calculateTwilight(currentTime - + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(), - mLocation.getLongitude()); - } - - if (mLastTwilightState == TwilightCalculator.NIGHT) { - nextUpdate += tw.mSunrise; + long nextUpdate = 0; + if (tw.mSunrise == -1 || tw.mSunset == -1) { + // In the case the day or night never ends the update is scheduled 12 hours later. + nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS; } else { - nextUpdate += tw.mSunset; + final int mLastTwilightState = tw.mState; + // add some extra time to be on the save side. + nextUpdate += DateUtils.MINUTE_IN_MILLIS; + if (currentTime > tw.mSunset) { + // next update should be on the following day + tw.calculateTwilight(currentTime + + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(), + mLocation.getLongitude()); + } + + if (mLastTwilightState == TwilightCalculator.NIGHT) { + nextUpdate += tw.mSunrise; + } else { + nextUpdate += tw.mSunset; + } } Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE); @@ -605,8 +623,11 @@ class DockObserver extends UEventObserver { mAlarmManager.cancel(pendingIntent); mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent); - // set current mode - setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4); + // Make sure that we really set the new mode only if we're in car mode and + // automatic switching is enables. + if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) { + setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4); + } } } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 5e96a2f..59d4c9b 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import com.android.internal.content.PackageMonitor; import com.android.internal.os.HandlerCaller; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethod; @@ -48,7 +49,6 @@ import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; -import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -332,51 +332,68 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - class PackageReceiver extends android.content.BroadcastReceiver { + class MyPackageMonitor extends PackageMonitor { + @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - String pkgList[] = null; - if (Intent.ACTION_PACKAGE_ADDED.equals(action) || - Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - Uri uri = intent.getData(); - String pkg = uri != null ? uri.getSchemeSpecificPart() : null; - if (pkg != null) { - pkgList = new String[] { pkg }; + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + synchronized (mMethodMap) { + String curInputMethodId = Settings.Secure.getString(mContext + .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final int N = mMethodList.size(); + if (curInputMethodId != null) { + for (int i=0; i<N; i++) { + InputMethodInfo imi = mMethodList.get(i); + if (imi.getId().equals(curInputMethodId)) { + for (String pkg : packages) { + if (imi.getPackageName().equals(pkg)) { + if (!doit) { + return true; + } + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, ""); + chooseNewDefaultIMELocked(); + return true; + } + } + } + } } - } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) || - Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } - if (pkgList == null || pkgList.length == 0) { - return; - } - synchronized (mMethodMap) { - buildInputMethodListLocked(mMethodList, mMethodMap); + return false; + } + @Override + public void onSomePackagesChanged() { + synchronized (mMethodMap) { InputMethodInfo curIm = null; - String curInputMethodId = Settings.Secure.getString(context + String curInputMethodId = Settings.Secure.getString(mContext .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); final int N = mMethodList.size(); if (curInputMethodId != null) { for (int i=0; i<N; i++) { - if (mMethodList.get(i).getId().equals(curInputMethodId)) { - curIm = mMethodList.get(i); + InputMethodInfo imi = mMethodList.get(i); + if (imi.getId().equals(curInputMethodId)) { + curIm = imi; + } + int change = isPackageDisappearing(imi.getPackageName()); + if (change == PACKAGE_TEMPORARY_CHANGE + || change == PACKAGE_PERMANENT_CHANGE) { + Log.i(TAG, "Input method uninstalled, disabling: " + + imi.getComponent()); + setInputMethodEnabledLocked(imi.getId(), false); } } } + buildInputMethodListLocked(mMethodList, mMethodMap); + boolean changed = false; if (curIm != null) { - boolean foundPkg = false; - for (String pkg : pkgList) { - if (curIm.getPackageName().equals(pkg)) { - foundPkg = true; - break; - } - } - if (foundPkg) { + int change = isPackageDisappearing(curIm.getPackageName()); + if (change == PACKAGE_TEMPORARY_CHANGE + || change == PACKAGE_PERMANENT_CHANGE) { ServiceInfo si = null; try { si = mContext.getPackageManager().getServiceInfo( @@ -387,7 +404,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Uh oh, current input method is no longer around! // Pick another one... Log.i(TAG, "Current input method removed: " + curInputMethodId); - if (!chooseNewDefaultIME()) { + if (!chooseNewDefaultIMELocked()) { changed = true; curIm = null; curInputMethodId = ""; @@ -397,16 +414,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub curInputMethodId); } } - - } else if (curIm == null) { - // We currently don't have a default input method... is - // one now available? - changed = chooseNewDefaultIME(); } + } + + if (curIm == null) { + // We currently don't have a default input method... is + // one now available? + changed = chooseNewDefaultIMELocked(); + } - if (changed) { - updateFromSettingsLocked(); - } + if (changed) { + updateFromSettingsLocked(); } } } @@ -438,19 +456,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } }); - PackageReceiver mBroadcastReceiver = new PackageReceiver(); - IntentFilter packageFilt = new IntentFilter(); - packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); - packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); - packageFilt.addDataScheme("package"); - mContext.registerReceiver(mBroadcastReceiver, packageFilt); - // Register for events related to sdcard installation. - IntentFilter sdFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(mBroadcastReceiver, sdFilter); + (new MyPackageMonitor()).register(mContext, true); IntentFilter screenOnOffFilt = new IntentFilter(); screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); @@ -1385,7 +1391,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub & ApplicationInfo.FLAG_SYSTEM) != 0; } - private boolean chooseNewDefaultIME() { + private boolean chooseNewDefaultIMELocked() { List<InputMethodInfo> enabled = getEnabledInputMethodListLocked(); if (enabled != null && enabled.size() > 0) { Settings.Secure.putString(mContext.getContentResolver(), @@ -1446,7 +1452,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub String defaultIme = Settings.Secure.getString(mContext .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); if (!map.containsKey(defaultIme)) { - if (chooseNewDefaultIME()) { + if (chooseNewDefaultIMELocked()) { updateFromSettingsLocked(); } } @@ -1557,90 +1563,94 @@ public class InputMethodManagerService extends IInputMethodManager.Stub "Requires permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - + long ident = Binder.clearCallingIdentity(); try { - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { - if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); - } - } + return setInputMethodEnabledLocked(id, enabled); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + boolean setInputMethodEnabledLocked(String id, boolean enabled) { + // Make sure this is a valid input method. + InputMethodInfo imm = mMethodMap.get(id); + if (imm == null) { + if (imm == null) { + throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + } + } - StringBuilder builder = new StringBuilder(256); - - boolean removed = false; - String firstId = null; - - // Look through the currently enabled input methods. - String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS); - if (enabledStr != null) { - final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; - splitter.setString(enabledStr); - while (splitter.hasNext()) { - String curId = splitter.next(); - if (curId.equals(id)) { - if (enabled) { - // We are enabling this input method, but it is - // already enabled. Nothing to do. The previous - // state was enabled. - return true; - } - // We are disabling this input method, and it is - // currently enabled. Skip it to remove from the - // new list. - removed = true; - } else if (!enabled) { - // We are building a new list of input methods that - // doesn't contain the given one. - if (firstId == null) firstId = curId; - if (builder.length() > 0) builder.append(':'); - builder.append(curId); - } - } - } + StringBuilder builder = new StringBuilder(256); - if (!enabled) { - if (!removed) { - // We are disabling the input method but it is already - // disabled. Nothing to do. The previous state was - // disabled. - return false; - } - // Update the setting with the new list of input methods. - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); - // We the disabled input method is currently selected, switch - // to another one. - String selId = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); - if (id.equals(selId)) { - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - firstId != null ? firstId : ""); - } - // Previous state was enabled. - return true; - } + boolean removed = false; + String firstId = null; - // Add in the newly enabled input method. - if (enabledStr == null || enabledStr.length() == 0) { - enabledStr = id; - } else { - enabledStr = enabledStr + ':' + id; + // Look through the currently enabled input methods. + String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS); + if (enabledStr != null) { + final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(enabledStr); + while (splitter.hasNext()) { + String curId = splitter.next(); + if (curId.equals(id)) { + if (enabled) { + // We are enabling this input method, but it is + // already enabled. Nothing to do. The previous + // state was enabled. + return true; + } + // We are disabling this input method, and it is + // currently enabled. Skip it to remove from the + // new list. + removed = true; + } else if (!enabled) { + // We are building a new list of input methods that + // doesn't contain the given one. + if (firstId == null) firstId = curId; + if (builder.length() > 0) builder.append(':'); + builder.append(curId); } + } + } - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); - - // Previous state was disabled. + if (!enabled) { + if (!removed) { + // We are disabling the input method but it is already + // disabled. Nothing to do. The previous state was + // disabled. return false; - } finally { - Binder.restoreCallingIdentity(ident); } + // Update the setting with the new list of input methods. + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); + // We the disabled input method is currently selected, switch + // to another one. + String selId = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + if (id.equals(selId)) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + firstId != null ? firstId : ""); + } + // Previous state was enabled. + return true; + } + + // Add in the newly enabled input method. + if (enabledStr == null || enabledStr.length() == 0) { + enabledStr = id; + } else { + enabledStr = enabledStr + ':' + id; } + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); + + // Previous state was disabled. + return false; } // ---------------------------------------------------------------------- diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index 2640cfb..1bb897b 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -32,7 +32,7 @@ import android.view.RawInputEvent; import android.view.Surface; import android.view.WindowManagerPolicy; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 004fcf1..c0dcdf9 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -27,6 +27,7 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; +import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -484,14 +485,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run super(); mContext = context; - Thread thread = new Thread(null, this, "LocationManagerService"); - thread.start(); - if (LOCAL_LOGV) { Log.v(TAG, "Constructed LocationManager Service"); } } + void systemReady() { + // we defer starting up the service until the system is ready + Thread thread = new Thread(null, this, "LocationManagerService"); + thread.start(); + } + private void initialize() { // Create a wake lock, needs to be done before calling loadProviders() below PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -506,6 +510,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Register for Package Manager updates intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); mContext.registerReceiver(mBroadcastReceiver, intentFilter); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mBroadcastReceiver, sdFilter); @@ -1539,8 +1544,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - - if (action.equals(Intent.ACTION_PACKAGE_REMOVED) + boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART); + if (queryRestart + || action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { synchronized (mLock) { @@ -1560,6 +1566,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run for (int j=i.size()-1; j>=0; j--) { UpdateRecord ur = i.get(j); if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) { + if (queryRestart) { + setResultCode(Activity.RESULT_OK); + return; + } if (removedRecs == null) { removedRecs = new ArrayList<Receiver>(); } @@ -1572,6 +1582,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run ArrayList<ProximityAlert> removedAlerts = null; for (ProximityAlert i : mProximityAlerts.values()) { if (i.mUid == uid) { + if (queryRestart) { + setResultCode(Activity.RESULT_OK); + return; + } if (removedAlerts == null) { removedAlerts = new ArrayList<ProximityAlert>(); } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 2a78806..4485c79 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -16,32 +16,29 @@ package com.android.server; +import com.android.server.am.ActivityManagerService; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.res.Resources; import android.net.Uri; import android.os.storage.IMountService; import android.os.storage.IMountServiceListener; import android.os.storage.StorageResultCode; +import android.os.Handler; +import android.os.Message; import android.os.RemoteException; import android.os.IBinder; import android.os.Environment; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UEventObserver; -import android.os.Handler; -import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; import java.util.HashSet; -import java.io.File; -import java.io.FileReader; - /** * MountService implements back-end services for platform storage * management. @@ -115,14 +112,119 @@ class MountService extends IMountService.Stub private PackageManagerService mPms; private boolean mUmsEnabling; private ArrayList<MountServiceBinderListener> mListeners; - private boolean mBooted; - private boolean mReady; + private boolean mBooted = false; + private boolean mReady = false; + private boolean mSendUmsConnectedOnBoot = false; /** * Private hash of currently mounted secure containers. */ private HashSet<String> mAsecMountSet = new HashSet<String>(); + private static final int H_UNMOUNT_PM_UPDATE = 1; + private static final int H_UNMOUNT_PM_DONE = 2; + private static final int H_UNMOUNT_MS = 3; + private static final int RETRY_UNMOUNT_DELAY = 30; // in ms + private static final int MAX_UNMOUNT_RETRIES = 4; + + private IntentFilter mPmFilter = new IntentFilter( + Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + private BroadcastReceiver mPmReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } + } + }; + + class UnmountCallBack { + String path; + int retries; + boolean force; + + UnmountCallBack(String path, boolean force) { + retries = 0; + this.path = path; + this.force = force; + } + } + + final private Handler mHandler = new Handler() { + ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); + + public void handleMessage(Message msg) { + switch (msg.what) { + case H_UNMOUNT_PM_UPDATE: { + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + mForceUnmounts.add(ucb); + mContext.registerReceiver(mPmReceiver, mPmFilter); + boolean hasExtPkgs = mPms.updateExternalMediaStatus(false); + if (!hasExtPkgs) { + // Unregister right away + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } + break; + } + case H_UNMOUNT_PM_DONE: { + // Unregister receiver + mContext.unregisterReceiver(mPmReceiver); + UnmountCallBack ucb = mForceUnmounts.get(0); + if (ucb == null || ucb.path == null) { + // Just ignore + return; + } + String path = ucb.path; + boolean done = false; + if (!ucb.force) { + done = true; + } else { + int pids[] = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } else { + // Kill processes holding references first + ActivityManagerService ams = (ActivityManagerService) + ServiceManager.getService("activity"); + // Eliminate system process here? + boolean ret = ams.killPidsForMemory(pids); + if (ret) { + // Confirm if file references have been freed. + pids = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } + } + } + } + if (done) { + mForceUnmounts.remove(0); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, + ucb)); + } else { + if (ucb.retries >= MAX_UNMOUNT_RETRIES) { + Log.i(TAG, "Cannot unmount inspite of " + + MAX_UNMOUNT_RETRIES + " to unmount media"); + // Send final broadcast indicating failure to unmount. + } else { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(H_UNMOUNT_PM_DONE, + ucb.retries++), + RETRY_UNMOUNT_DELAY); + } + } + break; + } + case H_UNMOUNT_MS : { + UnmountCallBack ucb = (UnmountCallBack) msg.obj; + String path = ucb.path; + doUnmountVolume(path, true); + break; + } + } + } + }; + private void waitForReady() { while (mReady == false) { for (int retries = 5; retries > 0; retries--) { @@ -162,6 +264,14 @@ class MountService extends IMountService.Stub Log.e(TAG, String.format("Boot-time mount failed (%d)", rc)); } } + /* + * If UMS is connected in boot, send the connected event + * now that we're up. + */ + if (mSendUmsConnectedOnBoot) { + sendUmsIntent(true); + mSendUmsConnectedOnBoot = false; + } } catch (Exception ex) { Log.e(TAG, "Boot-time mount exception", ex); } @@ -202,7 +312,7 @@ class MountService extends IMountService.Stub String vs = getVolumeState(path); if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { mUmsEnabling = enable; // Override for isUsbMassStorageEnabled() - int rc = doUnmountVolume(path, false); + int rc = doUnmountVolume(path, true); mUmsEnabling = false; // Clear override if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc)); @@ -545,14 +655,28 @@ class MountService extends IMountService.Stub return rc; } + /* + * If force is not set, we do not unmount if there are + * processes holding references to the volume about to be unmounted. + * If force is set, all the processes holding references need to be + * killed via the ActivityManager before actually unmounting the volume. + * This might even take a while and might be retried after timed delays + * to make sure we dont end up in an instable state and kill some core + * processes. + */ private int doUnmountVolume(String path, boolean force) { if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) { return VoldResponseCode.OpFailedVolNotMounted; } + // We unmounted the volume. No of the asec containers are available now. + synchronized (mAsecMountSet) { + mAsecMountSet.clear(); + } // Notify PackageManager of potential media removal and deal with // return code later on. The caller of this api should be aware or have been // notified that the applications installed on the media will be killed. + // Redundant probably. But no harm in updating state again. mPms.updateExternalMediaStatus(false); try { mConnector.doCommand(String.format( @@ -636,16 +760,17 @@ class MountService extends IMountService.Stub } if (mBooted == true) { - Intent intent; - if (avail) { - intent = new Intent(Intent.ACTION_UMS_CONNECTED); - } else { - intent = new Intent(Intent.ACTION_UMS_DISCONNECTED); - } - mContext.sendBroadcast(intent); + sendUmsIntent(avail); + } else { + mSendUmsConnectedOnBoot = avail; } } + private void sendUmsIntent(boolean c) { + mContext.sendBroadcast( + new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED))); + } + private void validatePermission(String perm) { if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(String.format("Requires %s permission", perm)); @@ -804,11 +929,12 @@ class MountService extends IMountService.Stub return doMountVolume(path); } - public int unmountVolume(String path, boolean force) { + public void unmountVolume(String path, boolean force) { validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); - return doUnmountVolume(path, force); + UnmountCallBack ucb = new UnmountCallBack(path, force); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } public int formatVolume(String path) { @@ -1003,7 +1129,11 @@ class MountService extends IMountService.Stub warnOnNotMounted(); synchronized (mAsecMountSet) { - if (mAsecMountSet.contains(oldId)) { + /* + * Because a mounted container has active internal state which cannot be + * changed while active, we must ensure both ids are not currently mounted. + */ + if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { return StorageResultCode.OperationFailedStorageMounted; } } @@ -1015,6 +1145,13 @@ class MountService extends IMountService.Stub } catch (NativeDaemonConnectorException e) { rc = StorageResultCode.OperationFailedInternalError; } + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mAsecMountSet) { + if (!mAsecMountSet.contains(newId)) { + mAsecMountSet.add(newId); + } + } + } return rc; } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 958d089..7c555e2 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -256,14 +256,14 @@ class NetworkManagementService extends INetworkManagementService.Stub { Log.e(TAG, "Failed to parse netmask", uhe); cfg.netmask = 0; } - cfg.interfaceFlags = st.nextToken("]"); + cfg.interfaceFlags = st.nextToken("]").trim() +"]"; Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); return cfg; } public void setInterfaceConfig( String iface, InterfaceConfiguration cfg) throws IllegalStateException { - String cmd = String.format("interface setcfg %s %s %s", iface, + String cmd = String.format("interface setcfg %s %s %s %s", iface, intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); mConnector.doCommand(cmd); } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 3657133..3c43352 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -307,6 +307,8 @@ class NotificationManagerService extends INotificationManager.Stub public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + boolean queryRestart = false; + if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0); int level = intent.getIntExtra("level", -1); @@ -330,10 +332,13 @@ class NotificationManagerService extends INotificationManager.Stub updateAdbNotification(); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) + || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { String pkgList[] = null; if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + } else if (queryRestart) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); } else { Uri uri = intent.getData(); if (uri == null) { @@ -347,7 +352,7 @@ class NotificationManagerService extends INotificationManager.Stub } if (pkgList != null && (pkgList.length > 0)) { for (String pkgName : pkgList) { - cancelAllNotificationsInt(pkgName, 0, 0); + cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); } } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { @@ -436,11 +441,15 @@ class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_UMS_CONNECTED); filter.addAction(Intent.ACTION_UMS_DISCONNECTED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver(mIntentReceiver, filter); + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + mContext.registerReceiver(mIntentReceiver, pkgFilter); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mIntentReceiver, sdFilter); @@ -920,8 +929,8 @@ class NotificationManagerService extends INotificationManager.Stub * Cancels all notifications from a given package that have all of the * {@code mustHaveFlags}. */ - void cancelAllNotificationsInt(String pkg, int mustHaveFlags, - int mustNotHaveFlags) { + boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, + int mustNotHaveFlags, boolean doit) { EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); synchronized (mNotificationList) { @@ -938,13 +947,17 @@ class NotificationManagerService extends INotificationManager.Stub if (!r.pkg.equals(pkg)) { continue; } + canceledSomething = true; + if (!doit) { + return true; + } mNotificationList.remove(i); cancelNotificationLocked(r); - canceledSomething = true; } if (canceledSomething) { updateLightsLocked(); } + return canceledSomething; } } @@ -966,7 +979,7 @@ class NotificationManagerService extends INotificationManager.Stub // Calling from user space, don't allow the canceling of actively // running foreground services. - cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE); + cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); } void checkIncomingCall(String pkg) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 38d8615..63fdaef 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -18,9 +18,9 @@ package com.android.server; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; -import com.android.common.FastXmlSerializer; -import com.android.common.XmlUtils; import com.android.internal.content.PackageHelper; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; import com.android.server.JournaledFile; import org.xmlpull.v1.XmlPullParser; @@ -28,7 +28,9 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; +import android.app.DevicePolicyManager; import android.app.IActivityManager; +import android.app.IDevicePolicyManager; import android.backup.IBackupManager; import android.content.ComponentName; import android.content.Context; @@ -4856,42 +4858,67 @@ class PackageManagerService extends IPackageManager.Stub { String oldCodePath) { String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME); String newCachePath = null; - final int RENAME_FAILED = 1; - final int MOUNT_FAILED = 2; - final int PASS = 4; - int errCode = RENAME_FAILED; - String errMsg = "RENAME_FAILED"; - boolean mounted = PackageHelper.isContainerMounted(cid); - if (mounted) { - // Unmount the container - if (!PackageHelper.unMountSdDir(cid)) { - Log.i(TAG, "Failed to unmount " + cid + " before renaming"); + boolean enableRename = false; + if (enableRename) { + if (PackageHelper.isContainerMounted(cid)) { + // Unmount the container + if (!PackageHelper.unMountSdDir(cid)) { + Log.i(TAG, "Failed to unmount " + cid + " before renaming"); + return false; + } + } + if (!PackageHelper.renameSdDir(cid, newCacheId)) { + Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId); return false; } - mounted = false; - } - if (PackageHelper.renameSdDir(cid, newCacheId)) { - errCode = MOUNT_FAILED; - errMsg = "MOUNT_FAILED"; - if ((newCachePath = PackageHelper.mountSdDir(newCacheId, - getEncryptKey(), Process.SYSTEM_UID)) != null) { - errCode = PASS; - errMsg = "PASS"; + if (!PackageHelper.isContainerMounted(newCacheId)) { + Log.w(TAG, "Mounting container " + newCacheId); + newCachePath = PackageHelper.mountSdDir(newCacheId, + getEncryptKey(), Process.SYSTEM_UID); + } else { + newCachePath = PackageHelper.getSdDir(newCacheId); + } + if (newCachePath == null) { + Log.w(TAG, "Failed to get cache path for " + newCacheId); + return false; } - } - if (errCode != PASS) { - Log.i(TAG, "Failed to rename " + cid + " to " + newCacheId + - " at path: " + cachePath + " to new path: " + newCachePath + - "err = " + errMsg); // Mount old container? - return false; + Log.i(TAG, "Succesfully renamed " + cid + + " at path: " + cachePath + " to " + newCacheId + + " at new path: " + newCachePath); + cid = newCacheId; + cachePath = newCachePath; + return true; } else { - Log.i(TAG, "Succesfully renamed " + cid + " to " + newCacheId + - " at path: " + cachePath + " to new path: " + newCachePath); + // STOPSHIP work around for rename + Log.i(TAG, "Copying instead of renaming"); + File srcFile = new File(getCodePath()); + // Create new container + newCachePath = PackageHelper.createSdDir(srcFile, newCacheId, + getEncryptKey(), Process.SYSTEM_UID); + Log.i(TAG, "Created rename container " + newCacheId); + File destFile = new File(newCachePath + "/" + RES_FILE_NAME); + if (!FileUtils.copyFile(srcFile, destFile)) { + Log.e(TAG, "Failed to copy " + srcFile + " to " + destFile); + return false; + } + Log.i(TAG, "Successfully copied resource to " + newCachePath); + if (!PackageHelper.finalizeSdDir(newCacheId)) { + Log.e(TAG, "Failed to finalize " + newCacheId); + PackageHelper.destroySdDir(newCacheId); + return false; + } + Log.i(TAG, "Finalized " + newCacheId); + Runtime.getRuntime().gc(); + // Unmount first + PackageHelper.unMountSdDir(cid); + // Delete old container + PackageHelper.destroySdDir(cid); + // Dont have to mount. Already mounted. + cid = newCacheId; + cachePath = newCachePath; + return true; } - cid = newCacheId; - cachePath = newCachePath; - return true; } int doPostInstall(int status) { @@ -5401,6 +5428,7 @@ class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; return; } + if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; @@ -5594,6 +5622,16 @@ class PackageManagerService extends IPackageManager.Stub { PackageRemovedInfo info = new PackageRemovedInfo(); boolean res; + IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); + try { + if (dpm != null && dpm.packageHasActiveAdmins(packageName)) { + Log.w(TAG, "Not removing package " + packageName + ": has active device admin"); + return false; + } + } catch (RemoteException e) { + } + synchronized (mInstallLock) { res = deletePackageLI(packageName, deleteCodeAndResources, flags, info); } @@ -5692,6 +5730,18 @@ class PackageManagerService extends IPackageManager.Stub { // remove permissions associated with package mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids); } + if (deletedPs != null) { + // remove from preferred activities. + ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>(); + for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) { + if (pa.mActivity.getPackageName().equals(deletedPs.name)) { + removed.add(pa); + } + } + for (PreferredActivity pa : removed) { + mSettings.mPreferredActivities.removeFilter(pa); + } + } // Save settings now mSettings.writeLP(); } @@ -7449,9 +7499,9 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Trying to update system app code path from " + p.codePathString + " to " + codePath.toString()); } else { - // Let the app continue with previous uid if code path changes. - reportSettingsProblem(Log.WARN, - "Package " + name + " codePath changed from " + p.codePath + // Just a change in the code path is not an issue, but + // let's log a message about it. + Log.i(TAG, "Package " + name + " codePath changed from " + p.codePath + " to " + codePath + "; Retaining data and using new"); } } @@ -8785,7 +8835,7 @@ class PackageManagerService extends IPackageManager.Stub { } static String getTempContainerId() { - String prefix = "smdl1tmp"; + String prefix = "smdl2tmp"; int tmpIdx = 1; String list[] = PackageHelper.getSecureContainerList(); if (list != null) { @@ -8827,30 +8877,56 @@ class PackageManagerService extends IPackageManager.Stub { return prefix + tmpIdx; } - public void updateExternalMediaStatus(final boolean mediaStatus) { + /* + * Return true if PackageManager does have packages to be updated. + */ + public boolean updateExternalMediaStatus(final boolean mediaStatus) { synchronized (mPackages) { if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus+", mMediaMounted=" + mMediaMounted); if (mediaStatus == mMediaMounted) { - return; + return false; } mMediaMounted = mediaStatus; + boolean ret = false; + synchronized (mPackages) { + Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD); + ret = appList != null && appList.size() > 0; + } + if (!ret) { + // No packages will be effected by the sdcard update. Just return. + return false; + } // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - updateExternalMediaStatusInner(mediaStatus); + // If we are up here that means there are packages to be + // enabled or disabled. + final HashMap<SdInstallArgs, String> processCids = + new HashMap<SdInstallArgs, String>(); + final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids); + if (mediaStatus) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); + loadMediaPackages(processCids, uidArr); + startCleaningPackages(); + } else { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); + unloadMediaPackages(processCids, uidArr); + } } }); + return true; } } - void updateExternalMediaStatusInner(boolean mediaStatus) { + private int[] getExternalMediaPackages(boolean mediaStatus, + Map<SdInstallArgs, String> processCids) { final String list[] = PackageHelper.getSecureContainerList(); if (list == null || list.length == 0) { - return; + return null; } - HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); + int uidList[] = new int[list.length]; int num = 0; synchronized (mPackages) { @@ -8892,14 +8968,7 @@ class PackageManagerService extends IPackageManager.Stub { } } } - if (mediaStatus) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr); - startCleaningPackages(); - } else { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); - unloadMediaPackages(processCids, uidArr); - } + return uidArr; } private void sendResourcesChangedBroadcast(boolean mediaStatus, @@ -8919,7 +8988,7 @@ class PackageManagerService extends IPackageManager.Stub { } } - void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { + private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { ArrayList<String> pkgList = new ArrayList<String>(); Set<SdInstallArgs> keys = processCids.keySet(); for (SdInstallArgs args : keys) { @@ -8961,15 +9030,15 @@ class PackageManagerService extends IPackageManager.Stub { } args.doPostInstall(retCode); } - // Send broadcasts first + // Send a broadcast to let everyone know we are done processing + sendResourcesChangedBroadcast(true, pkgList, uidArr); if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(true, pkgList, uidArr); Runtime.getRuntime().gc(); // If something failed do we clean up here or next install? } } - void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { + private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages"); ArrayList<String> pkgList = new ArrayList<String>(); ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); @@ -8991,9 +9060,9 @@ class PackageManagerService extends IPackageManager.Stub { } } } + sendResourcesChangedBroadcast(false, pkgList, uidArr); // Send broadcasts if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(false, pkgList, uidArr); Runtime.getRuntime().gc(); } // Do clean up. Just unmount diff --git a/services/java/com/android/server/RecognitionManagerService.java b/services/java/com/android/server/RecognitionManagerService.java new file mode 100644 index 0000000..7305b07 --- /dev/null +++ b/services/java/com/android/server/RecognitionManagerService.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import com.android.internal.content.PackageMonitor; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; +import android.provider.Settings; +import android.speech.RecognitionService; +import android.text.TextUtils; +import android.util.Log; + +import java.util.List; + +public class RecognitionManagerService extends Binder { + final static String TAG = "RecognitionManagerService"; + + final Context mContext; + final MyPackageMonitor mMonitor; + + class MyPackageMonitor extends PackageMonitor { + public void onSomePackagesChanged() { + ComponentName comp = getCurRecognizer(); + if (comp == null) { + if (anyPackagesAppearing()) { + comp = findAvailRecognizer(null); + if (comp != null) { + setCurRecognizer(comp); + } + } + return; + } + + int change = isPackageDisappearing(comp.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + setCurRecognizer(findAvailRecognizer(null)); + + } else if (isPackageModified(comp.getPackageName())) { + setCurRecognizer(findAvailRecognizer(comp.getPackageName())); + } + } + } + + RecognitionManagerService(Context context) { + mContext = context; + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); + } + + public void systemReady() { + ComponentName comp = getCurRecognizer(); + if (comp != null) { + // See if the current recognizer is no longer available. + try { + mContext.getPackageManager().getServiceInfo(comp, 0); + } catch (NameNotFoundException e) { + setCurRecognizer(null); + } + } else { + comp = findAvailRecognizer(null); + if (comp != null) { + setCurRecognizer(comp); + } + } + } + + ComponentName findAvailRecognizer(String prefPackage) { + List<ResolveInfo> available = + mContext.getPackageManager().queryIntentServices( + new Intent(RecognitionService.SERVICE_INTERFACE), 0); + int numAvailable = available.size(); + + if (numAvailable == 0) { + Log.w(TAG, "no available voice recognition services found"); + return null; + } else { + if (prefPackage != null) { + for (int i=0; i<numAvailable; i++) { + ServiceInfo serviceInfo = available.get(i).serviceInfo; + if (prefPackage.equals(serviceInfo.packageName)) { + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + } + if (numAvailable > 1) { + Log.w(TAG, "more than one voice recognition service found, picking first"); + } + + ServiceInfo serviceInfo = available.get(0).serviceInfo; + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + + ComponentName getCurRecognizer() { + String curRecognizer = Settings.Secure.getString( + mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE); + if (TextUtils.isEmpty(curRecognizer)) { + return null; + } + return ComponentName.unflattenFromString(curRecognizer); + } + + void setCurRecognizer(ComponentName comp) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, + comp != null ? comp.flattenToShortString() : ""); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a09896a..1f46faf 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -97,6 +97,7 @@ class ServerThread extends Thread { BluetoothA2dpService bluetoothA2dp = null; HeadsetObserver headset = null; DockObserver dock = null; + RecognitionManagerService recognition = null; // Critical services... try { @@ -208,6 +209,7 @@ class ServerThread extends Thread { AppWidgetService appWidget = null; NotificationManagerService notification = null; WallpaperManagerService wallpaper = null; + LocationManagerService location = null; if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { @@ -302,8 +304,8 @@ class ServerThread extends Thread { try { Log.i(TAG, "Location Manager"); - ServiceManager.addService(Context.LOCATION_SERVICE, - new LocationManagerService(context)); + location = new LocationManagerService(context); + ServiceManager.addService(Context.LOCATION_SERVICE, location); } catch (Throwable e) { Log.e(TAG, "Failure starting Location Manager", e); } @@ -377,6 +379,13 @@ class ServerThread extends Thread { } try { + Log.i(TAG, "Recognition Service"); + recognition = new RecognitionManagerService(context); + } catch (Throwable e) { + Log.e(TAG, "Failure starting Recognition Service", e); + } + + try { com.android.server.status.StatusBarPolicy.installIcons(context, statusBar); } catch (Throwable e) { Log.e(TAG, "Failure installing status bar icons", e); @@ -435,6 +444,8 @@ class ServerThread extends Thread { final AppWidgetService appWidgetF = appWidget; final WallpaperManagerService wallpaperF = wallpaper; final InputMethodManagerService immF = imm; + final RecognitionManagerService recognitionF = recognition; + final LocationManagerService locationF = location; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -449,6 +460,7 @@ class ServerThread extends Thread { if (batteryF != null) batteryF.systemReady(); if (connectivityF != null) connectivityF.systemReady(); if (dockF != null) dockF.systemReady(); + if (recognitionF != null) recognitionF.systemReady(); Watchdog.getInstance().start(); // It is now okay to let the various system services start their @@ -457,6 +469,7 @@ class ServerThread extends Thread { if (appWidgetF != null) appWidgetF.systemReady(safeMode); if (wallpaperF != null) wallpaperF.systemReady(); if (immF != null) immF.systemReady(); + if (locationF != null) locationF.systemReady(); } }); diff --git a/services/java/com/android/server/TwilightCalculator.java b/services/java/com/android/server/TwilightCalculator.java index a8f67d8..a5c93b5 100644 --- a/services/java/com/android/server/TwilightCalculator.java +++ b/services/java/com/android/server/TwilightCalculator.java @@ -46,10 +46,16 @@ public class TwilightCalculator { // Java time on Jan 1, 2000 12:00 UTC. private static final long UTC_2000 = 946728000000L; - /** Time of sunset (civil twilight) in milliseconds. */ + /** + * Time of sunset (civil twilight) in milliseconds or -1 in the case the day + * or night never ends. + */ public long mSunset; - /** Time of sunrise (civil twilight) in milliseconds. */ + /** + * Time of sunrise (civil twilight) in milliseconds or -1 in the case the + * day or night never ends. + */ public long mSunrise; /** Current state */ @@ -85,10 +91,24 @@ public class TwilightCalculator { double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY)); final double latRad = latiude * DEGREES_TO_RADIANS; - float hourAngle = (float) (Math - .acos((FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad) - * Math.sin(solarDec)) - / (Math.cos(latRad) * Math.cos(solarDec))) / (2 * Math.PI)); + + double cosHourAngle = (FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad) + * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec)); + // The day or night never ends for the given date and location, if this value is out of + // range. + if (cosHourAngle >= 1) { + mState = NIGHT; + mSunset = -1; + mSunrise = -1; + return; + } else if (cosHourAngle <= -1) { + mState = DAY; + mSunset = -1; + mSunrise = -1; + return; + } + + float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI)); mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 81255ee..aebb0ff 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -65,8 +65,11 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import com.android.internal.content.PackageMonitor; import com.android.internal.service.wallpaper.ImageWallpaper; -import com.android.common.FastXmlSerializer; +import com.android.internal.util.FastXmlSerializer; +import com.android.server.DevicePolicyManagerService.ActiveAdmin; +import com.android.server.DevicePolicyManagerService.MyPackageMonitor; class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperService"; @@ -122,6 +125,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { final Context mContext; final IWindowManager mIWindowManager; + final MyPackageMonitor mMonitor; int mWidth = -1; int mHeight = -1; @@ -150,6 +154,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { WallpaperConnection mWallpaperConnection; long mLastDiedTime; + boolean mWallpaperUpdating; class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { @@ -165,6 +170,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaperConnection == this) { + mLastDiedTime = SystemClock.uptimeMillis(); mService = IWallpaperService.Stub.asInterface(service); attachServiceLocked(this); // XXX should probably do saveSettingsLocked() later @@ -182,8 +188,8 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mEngine = null; if (mWallpaperConnection == this) { Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent); - if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME) - < SystemClock.uptimeMillis()) { + if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME) + > SystemClock.uptimeMillis()) { Log.w(TAG, "Reverting to built-in wallpaper!"); bindWallpaperComponentLocked(null); } @@ -205,11 +211,92 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } + class MyPackageMonitor extends PackageMonitor { + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + synchronized (mLock) { + if (mWallpaperComponent != null && + mWallpaperComponent.getPackageName().equals(packageName)) { + mWallpaperUpdating = false; + ComponentName comp = mWallpaperComponent; + clearWallpaperComponentLocked(); + bindWallpaperComponentLocked(comp); + } + } + } + + @Override + public void onPackageUpdateStarted(String packageName, int uid) { + synchronized (mLock) { + if (mWallpaperComponent != null && + mWallpaperComponent.getPackageName().equals(packageName)) { + mWallpaperUpdating = true; + } + } + } + + @Override + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + return doPackagesChanged(doit); + } + + @Override + public void onSomePackagesChanged() { + doPackagesChanged(true); + } + + boolean doPackagesChanged(boolean doit) { + boolean changed = false; + synchronized (mLock) { + if (mWallpaperComponent != null) { + int change = isPackageDisappearing(mWallpaperComponent.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + changed = true; + if (doit) { + Log.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent); + clearWallpaperLocked(); + } + } + } + if (mNextWallpaperComponent != null) { + int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + mNextWallpaperComponent = null; + } + } + if (mWallpaperComponent != null + && isPackageModified(mWallpaperComponent.getPackageName())) { + try { + mContext.getPackageManager().getServiceInfo( + mWallpaperComponent, 0); + } catch (NameNotFoundException e) { + Log.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent); + clearWallpaperLocked(); + } + } + if (mNextWallpaperComponent != null + && isPackageModified(mNextWallpaperComponent.getPackageName())) { + try { + mContext.getPackageManager().getServiceInfo( + mNextWallpaperComponent, 0); + } catch (NameNotFoundException e) { + mNextWallpaperComponent = null; + } + } + } + return changed; + } + } + public WallpaperManagerService(Context context) { if (DEBUG) Log.v(TAG, "WallpaperService startup"); mContext = context; mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); + mMonitor = new MyPackageMonitor(); + mMonitor.register(context, true); WALLPAPER_DIR.mkdirs(); loadSettingsLocked(); mWallpaperObserver.startWatching(); @@ -241,16 +328,20 @@ class WallpaperManagerService extends IWallpaperManager.Stub { public void clearWallpaper() { if (DEBUG) Log.v(TAG, "clearWallpaper"); synchronized (mLock) { - File f = WALLPAPER_FILE; - if (f.exists()) { - f.delete(); - } - final long ident = Binder.clearCallingIdentity(); - try { - bindWallpaperComponentLocked(null); - } finally { - Binder.restoreCallingIdentity(ident); - } + clearWallpaperLocked(); + } + } + + public void clearWallpaperLocked() { + File f = WALLPAPER_FILE; + if (f.exists()) { + f.delete(); + } + final long ident = Binder.clearCallingIdentity(); + try { + bindWallpaperComponentLocked(null); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -499,7 +590,9 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mWidth, mHeight); } catch (RemoteException e) { Log.w(TAG, "Failed attaching wallpaper; clearing", e); - bindWallpaperComponentLocked(null); + if (!mWallpaperUpdating) { + bindWallpaperComponentLocked(null); + } } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 45c3f00..7b64704 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -47,6 +47,7 @@ import android.app.ResultInfo; import android.app.Service; import android.backup.IBackupManager; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -93,7 +94,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; -import android.text.TextUtils; import android.util.Config; import android.util.EventLog; import android.util.Log; @@ -107,7 +107,6 @@ import android.view.WindowManagerPolicy; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; @@ -136,6 +135,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_PROVIDER = localLOGV || false; + static final boolean DEBUG_URI_PERMISSION = localLOGV || false; static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean DEBUG_RESULTS = localLOGV || false; static final boolean DEBUG_BACKUP = localLOGV || false; @@ -296,12 +296,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Memory pages are 4K. static final int PAGE_SIZE = 4*1024; - // System property defining error report receiver for system apps - static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; - - // System property defining default error report receiver - static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; - // Corresponding memory levels for above adjustments. static final int EMPTY_APP_MEM; static final int HIDDEN_APP_MEM; @@ -975,6 +969,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int PROC_START_TIMEOUT_MSG = 20; static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21; static final int KILL_APPLICATION_MSG = 22; + static final int FINALIZE_PENDING_INTENT_MSG = 23; AlertDialog mUidAlert; @@ -1193,9 +1188,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int uid = msg.arg1; boolean restart = (msg.arg2 == 1); String pkg = (String) msg.obj; - forceStopPackageLocked(pkg, uid, restart, false); + forceStopPackageLocked(pkg, uid, restart, false, true); } } break; + case FINALIZE_PENDING_INTENT_MSG: { + ((PendingIntentRecord)msg.obj).completeFinalize(); + } break; } } }; @@ -1396,7 +1394,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); - mConfiguration.makeDefault(); + mConfiguration.setToDefaults(); + mConfiguration.locale = Locale.getDefault(); mProcessStats.init(); // Add ourself to the Watchdog monitors. @@ -3605,10 +3604,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { + int callingPid; + int callingUid; + if (caller == null) { + callingPid = Binder.getCallingPid(); + callingUid = Binder.getCallingUid(); + } else { + callingPid = callingUid = -1; + } final long origId = Binder.clearCallingIdentity(); int res = startActivityLocked(caller, intent, resolvedType, grantedUriPermissions, grantedMode, aInfo, - resultTo, resultWho, requestCode, -1, -1, + resultTo, resultWho, requestCode, callingPid, callingUid, onlyIfNeeded, componentSpecified); Binder.restoreCallingIdentity(origId); return res; @@ -4846,7 +4853,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } killPackageProcessesLocked(packageName, pkgUid, - SECONDARY_SERVER_ADJ, false); + SECONDARY_SERVER_ADJ, false, true); } } finally { Binder.restoreCallingIdentity(callingId); @@ -4988,7 +4995,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private void forceStopPackageLocked(final String packageName, int uid) { - forceStopPackageLocked(packageName, uid, false, false); + forceStopPackageLocked(packageName, uid, false, false, true); Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, Uri.fromParts("package", packageName, null)); intent.putExtra(Intent.EXTRA_UID, uid); @@ -4997,8 +5004,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen false, false, MY_PID, Process.SYSTEM_UID); } - private final void killPackageProcessesLocked(String packageName, int uid, - int minOomAdj, boolean callerWillRestart) { + private final boolean killPackageProcessesLocked(String packageName, int uid, + int minOomAdj, boolean callerWillRestart, boolean doit) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the @@ -5010,11 +5017,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int ia=0; ia<NA; ia++) { ProcessRecord app = apps.valueAt(ia); if (app.removed) { - procs.add(app); + if (doit) { + procs.add(app); + } } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid) || app.processName.equals(packageName) || app.processName.startsWith(procNamePrefix)) { if (app.setAdj >= minOomAdj) { + if (!doit) { + return true; + } app.removed = true; procs.add(app); } @@ -5026,10 +5038,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart); } + return N > 0; } - private final void forceStopPackageLocked(String name, int uid, - boolean callerWillRestart, boolean purgeCache) { + private final boolean forceStopPackageLocked(String name, int uid, + boolean callerWillRestart, boolean purgeCache, boolean doit) { int i, N; if (uid < 0) { @@ -5039,21 +5052,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - Log.i(TAG, "Force stopping package " + name + " uid=" + uid); + if (doit) { + Log.i(TAG, "Force stopping package " + name + " uid=" + uid); - Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); - while (badApps.hasNext()) { - SparseArray<Long> ba = badApps.next(); - if (ba.get(uid) != null) { - badApps.remove(); + Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator(); + while (badApps.hasNext()) { + SparseArray<Long> ba = badApps.next(); + if (ba.get(uid) != null) { + badApps.remove(); + } } } - - killPackageProcessesLocked(name, uid, -100, callerWillRestart); + + boolean didSomething = killPackageProcessesLocked(name, uid, -100, + callerWillRestart, doit); for (i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); if (r.packageName.equals(name)) { + if (!doit) { + return true; + } + didSomething = true; Log.i(TAG, " Force finishing activity " + r); if (r.app != null) { r.app.removed = true; @@ -5066,6 +5086,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); for (ServiceRecord service : mServices.values()) { if (service.packageName.equals(name)) { + if (!doit) { + return true; + } + didSomething = true; Log.i(TAG, " Force stopping service " + service); if (service.app != null) { service.app.removed = true; @@ -5080,13 +5104,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen bringDownServiceLocked(services.get(i), true); } - resumeTopActivityLocked(null); - if (purgeCache) { - AttributeCache ac = AttributeCache.instance(); - if (ac != null) { - ac.removePackage(name); + if (doit) { + if (purgeCache) { + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.removePackage(name); + } } + resumeTopActivityLocked(null); } + + return didSomething; } private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) { @@ -5583,19 +5611,38 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final void finishBooting() { - // Ensure that any processes we had put on hold are now started - // up. - final int NP = mProcessesOnHold.size(); - if (NP > 0) { - ArrayList<ProcessRecord> procs = - new ArrayList<ProcessRecord>(mProcessesOnHold); - for (int ip=0; ip<NP; ip++) { - this.startProcessLocked(procs.get(ip), "on-hold", null); + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + if (pkgs != null) { + for (String pkg : pkgs) { + if (forceStopPackageLocked(pkg, -1, false, false, false)) { + setResultCode(Activity.RESULT_OK); + return; + } + } + } } - } - if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - // Tell anyone interested that we are done booting! - synchronized (this) { + }, pkgFilter); + + synchronized (this) { + // Ensure that any processes we had put on hold are now started + // up. + final int NP = mProcessesOnHold.size(); + if (NP > 0) { + ArrayList<ProcessRecord> procs = + new ArrayList<ProcessRecord>(mProcessesOnHold); + for (int ip=0; ip<NP; ip++) { + this.startProcessLocked(procs.get(ip), "on-hold", null); + } + } + + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + // Tell anyone interested that we are done booting! broadcastIntentLocked(null, null, new Intent(Intent.ACTION_BOOT_COMPLETED, null), null, null, 0, null, null, @@ -6132,10 +6179,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Requested grant " + targetPkg + " permission to " + uri); + final IPackageManager pm = ActivityThread.getPackageManager(); // If this is not a content: uri, we can't do anything with it. if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Can't grant URI permission for non-content URI: " + uri); return; } @@ -6161,6 +6213,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { targetUid = pm.getPackageUid(targetPkg); if (targetUid < 0) { + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Can't grant URI permission no uid for: " + targetPkg); return; } } catch (RemoteException ex) { @@ -6170,17 +6224,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // First... does the target actually need this permission? if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) { // No need to grant the target this permission. + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Target " + targetPkg + " already has full permission to " + uri); return; } - // Second... maybe someone else has already granted the - // permission? - if (checkUriPermissionLocked(uri, targetUid, modeFlags)) { - // No need to grant the target this permission. - return; - } - - // Third... is the provider allowing granting of URI permissions? + // Second... is the provider allowing granting of URI permissions? if (!pi.grantUriPermissions) { throw new SecurityException("Provider " + pi.packageName + "/" + pi.name @@ -6205,7 +6254,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - // Fourth... does the caller itself have permission to access + // Third... does the caller itself have permission to access // this uri? if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) { if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { @@ -6218,6 +6267,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // to the uri, and the target doesn't. Let's now give this to // the target. + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Granting " + targetPkg + " permission to " + uri); + HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid); if (targetUris == null) { @@ -6291,6 +6343,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(perm.uid); if (perms != null) { + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Removing " + perm.uid + " permission to " + perm.uri); perms.remove(perm.uri); if (perms.size() == 0) { mGrantedUriPermissions.remove(perm.uid); @@ -6330,6 +6384,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Revoking all granted permissions to " + uri); + final IPackageManager pm = ActivityThread.getPackageManager(); final String authority = uri.getAuthority(); @@ -6388,6 +6445,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen continue toploop; } } + if (DEBUG_URI_PERMISSION) Log.v(TAG, + "Revoking " + perm.uid + " permission to " + perm.uri); perm.clearModes(modeFlags); if (perm.modeFlags == 0) { it.remove(); @@ -7556,6 +7615,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ? cpi.readPermission : cpi.writePermission); } + if (!mSystemReady && !mDidUpdate && !mWaitingUpdate + && !cpi.processName.equals("system")) { + // If this content provider does not run in the system + // process, and the system is not yet ready to run other + // processes, then fail fast instead of hanging. + throw new IllegalArgumentException( + "Attempt to launch content provider before system ready"); + } + cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name); final boolean firstClass = cpr == null; if (firstClass) { @@ -7803,6 +7871,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public static final void installSystemProviders() { ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID); List providers = mSelf.generateApplicationProvidersLocked(app); + if (providers != null) { + for (int i=providers.size()-1; i>=0; i--) { + ProviderInfo pi = (ProviderInfo)providers.get(i); + if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + Log.w(TAG, "Not installing system proc provider " + pi.name + + ": not system .apk"); + providers.remove(i); + } + } + } mSystemThread.installSystemProviders(providers); } @@ -8050,7 +8128,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mDebugTransient = !persistent; if (packageName != null) { final long origId = Binder.clearCallingIdentity(); - forceStopPackageLocked(packageName, -1, false, false); + forceStopPackageLocked(packageName, -1, false, false, true); Binder.restoreCallingIdentity(origId); } } @@ -8339,7 +8417,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mAlwaysFinishActivities = alwaysFinishActivities; // This happens before any activities are started, so we can // change mConfiguration in-place. - mConfiguration.locale = Locale.getDefault(); mConfiguration.updateFrom(configuration); mConfigurationSeq = mConfiguration.seq = 1; if (DEBUG_CONFIGURATION) Log.v(TAG, "Initial config: " + mConfiguration); @@ -8537,73 +8614,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return handleAppCrashLocked(app); } - private ComponentName getErrorReportReceiver(ProcessRecord app) { - // check if error reporting is enabled in secure settings - int enabled = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SEND_ACTION_APP_ERROR, 0); - if (enabled == 0) { - return null; - } - - IPackageManager pm = ActivityThread.getPackageManager(); - - try { - // look for receiver in the installer package - String candidate = pm.getInstallerPackageName(app.info.packageName); - ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate); - if (result != null) { - return result; - } - - // if the error app is on the system image, look for system apps - // error receiver - if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); - result = getErrorReportReceiver(pm, app.info.packageName, candidate); - if (result != null) { - return result; - } - } - - // if there is a default receiver, try that - candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); - return getErrorReportReceiver(pm, app.info.packageName, candidate); - } catch (RemoteException e) { - // should not happen - Log.e(TAG, "error talking to PackageManager", e); - return null; - } - } - - /** - * Return activity in receiverPackage that handles ACTION_APP_ERROR. - * - * @param pm PackageManager isntance - * @param errorPackage package which caused the error - * @param receiverPackage candidate package to receive the error - * @return activity component within receiverPackage which handles - * ACTION_APP_ERROR, or null if not found - */ - private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage, - String receiverPackage) throws RemoteException { - if (receiverPackage == null || receiverPackage.length() == 0) { - return null; - } - - // break the loop if it's the error report receiver package that crashed - if (receiverPackage.equals(errorPackage)) { - return null; - } - - Intent intent = new Intent(Intent.ACTION_APP_ERROR); - intent.setPackage(receiverPackage); - ResolveInfo info = pm.resolveIntent(intent, null, 0); - if (info == null || info.activityInfo == null) { - return null; - } - return new ComponentName(receiverPackage, info.activityInfo.name); - } - private void makeAppNotRespondingLocked(ProcessRecord app, String activity, String shortMsg, String longMsg) { app.notResponding = true; @@ -8717,7 +8727,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void startAppProblemLocked(ProcessRecord app) { - app.errorReportReceiver = getErrorReportReceiver(app); + app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( + mContext, app.info.packageName, app.info.flags); skipCurrentReceiverLocked(app); } @@ -11951,7 +11962,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); if (list != null && (list.length > 0)) { for (String pkg : list) { - forceStopPackageLocked(pkg, -1, false, true); + forceStopPackageLocked(pkg, -1, false, true, true); } } } else { @@ -11960,7 +11971,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) { forceStopPackageLocked(ssp, - intent.getIntExtra(Intent.EXTRA_UID, -1), false, true); + intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true); } } } @@ -12931,7 +12942,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final long origId = Binder.clearCallingIdentity(); - forceStopPackageLocked(ii.targetPackage, -1, true, false); + forceStopPackageLocked(ii.targetPackage, -1, true, false, true); ProcessRecord app = addAppLocked(ai); app.instrumentationClass = className; app.instrumentationInfo = ai; @@ -12986,7 +12997,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.instrumentationProfileFile = null; app.instrumentationArguments = null; - forceStopPackageLocked(app.processName, -1, false, false); + forceStopPackageLocked(app.processName, -1, false, false, true); } public void finishInstrumentation(IApplicationThread target, diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index b3086d5..fac47d7 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -145,8 +145,9 @@ class PendingIntentRecord extends IIntentSender.Stub { public String toString() { return "Key{" + typeName() + " pkg=" + packageName - + " intent=" + requestIntent.toShortString(true, false) + " flags=0x" - + Integer.toHexString(flags) + "}"; + + " intent=" + + (requestIntent != null ? requestIntent.toShortString(true, false) : "<null>") + + " flags=0x" + Integer.toHexString(flags) + "}"; } String typeName() { @@ -262,17 +263,26 @@ class PendingIntentRecord extends IIntentSender.Stub { } protected void finalize() throws Throwable { - if (!canceled) { - synchronized(owner) { - WeakReference<PendingIntentRecord> current = - owner.mIntentSenderRecords.get(key); - if (current == ref) { - owner.mIntentSenderRecords.remove(key); - } + try { + if (!canceled) { + owner.mHandler.sendMessage(owner.mHandler.obtainMessage( + ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this)); } + } finally { + super.finalize(); } } + public void completeFinalize() { + synchronized(owner) { + WeakReference<PendingIntentRecord> current = + owner.mIntentSenderRecords.get(key); + if (current == ref) { + owner.mIntentSenderRecords.remove(key); + } + } + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(" packageName="); pw.print(key.packageName); @@ -286,8 +296,10 @@ class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("requestCode="); pw.print(key.requestCode); pw.print(" requestResolvedType="); pw.println(key.requestResolvedType); } - pw.print(prefix); pw.print("requestIntent="); - pw.println(key.requestIntent.toShortString(true, true)); + if (key.requestIntent != null) { + pw.print(prefix); pw.print("requestIntent="); + pw.println(key.requestIntent.toShortString(true, true)); + } if (sent || canceled) { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 2f2cc32..5a02c40 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -269,8 +269,12 @@ class ServiceRecord extends Binder { inm.enqueueNotification(localPackageName, localForegroundId, localForegroundNoti, outId); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, "Error showing notification for service", - e); + Log.w(ActivityManagerService.TAG, + "Error showing notification for service", e); + // If it gave us a garbage notification, it doesn't + // get to be foreground. + ams.setServiceForeground(name, ServiceRecord.this, + localForegroundId, null, true); } catch (RemoteException e) { } } @@ -293,8 +297,8 @@ class ServiceRecord extends Binder { try { inm.cancelNotification(localPackageName, localForegroundId); } catch (RuntimeException e) { - Log.w(ActivityManagerService.TAG, "Error canceling notification for" - + " service", e); + Log.w(ActivityManagerService.TAG, + "Error canceling notification for service", e); } catch (RemoteException e) { } } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 1d20074..16638bc 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -27,6 +27,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.NetworkInfo; @@ -56,6 +57,7 @@ import java.util.Set; * * TODO - look for parent classes and code sharing */ + public class Tethering extends INetworkManagementEventObserver.Stub { private Notification mTetheringNotification; @@ -74,12 +76,23 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private BroadcastReceiver mStateReceiver; + private static final String USB_NEAR_IFACE_ADDR = "169.254.2.1"; + private String[] mDhcpRange; + private static final String DHCP_DEFAULT_RANGE_START = "169.254.2.10"; + private static final String DHCP_DEFAULT_RANGE_STOP = "169.254.2.64"; private String[] mDnsServers; + private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8"; + private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2"; private String mUpstreamIfaceName; + // turning on/off RNDIS resets the interface generating and extra discon/conn cycle + // count how many to ignore.. Self correcting if you plug/unplug a bunch of times. + // TODO - brittle - maybe don't need? + private int mUsbResetExpected = 0; + HierarchicalStateMachine mTetherMasterSM; public Tethering(Context context) { @@ -113,8 +126,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { com.android.internal.R.array.config_tether_dhcp_range); if (mDhcpRange.length == 0) { mDhcpRange = new String[2]; - mDhcpRange[0] = new String("169.254.2.1"); - mDhcpRange[1] = new String("169.254.2.64"); + mDhcpRange[0] = DHCP_DEFAULT_RANGE_START; + mDhcpRange[1] = DHCP_DEFAULT_RANGE_STOP; } else if(mDhcpRange.length == 1) { String[] tmp = new String[2]; tmp[0] = mDhcpRange[0]; @@ -127,26 +140,17 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mTetherableWifiRegexs = context.getResources().getStringArray( com.android.internal.R.array.config_tether_wifi_regexs); - String[] ifaces = new String[0]; - try { - ifaces = service.listInterfaces(); - } catch (Exception e) { - Log.e(TAG, "Error listing Interfaces :" + e); - } - for (String iface : ifaces) { - interfaceAdded(iface); - } - // TODO - remove and rely on real notifications of the current iface mDnsServers = new String[2]; - mDnsServers[0] = "8.8.8.8"; - mDnsServers[1] = "4.2.2.2"; + mDnsServers[0] = DNS_DEFAULT_SERVER1; + mDnsServers[1] = DNS_DEFAULT_SERVER2; mUpstreamIfaceName = "rmnet0"; } public void interfaceLinkStatusChanged(String iface, boolean link) { Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link); boolean found = false; + boolean usb = false; for (String regex : mTetherableWifiRegexs) { if (iface.matches(regex)) { found = true; @@ -156,6 +160,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { for (String regex: mTetherableUsbRegexs) { if (iface.matches(regex)) { found = true; + usb = true; break; } } @@ -165,7 +170,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { TetherInterfaceSM sm = mIfaces.get(iface); if (link) { if (sm == null) { - sm = new TetherInterfaceSM(iface); + sm = new TetherInterfaceSM(iface, usb); mIfaces.put(iface, sm); sm.start(); } @@ -179,7 +184,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } public void interfaceAdded(String iface) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); boolean found = false; + boolean usb = false; for (String regex : mTetherableWifiRegexs) { if (iface.matches(regex)) { found = true; @@ -189,6 +197,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { for (String regex : mTetherableUsbRegexs) { if (iface.matches(regex)) { found = true; + usb = true; break; } } @@ -196,13 +205,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub { Log.d(TAG, iface + " is not a tetherable iface, ignoring"); return; } + synchronized (mIfaces) { TetherInterfaceSM sm = mIfaces.get(iface); if (sm != null) { Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring"); return; } - sm = new TetherInterfaceSM(iface); + sm = new TetherInterfaceSM(iface, usb); mIfaces.put(iface, sm); sm.start(); } @@ -297,7 +307,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER, erroredList); mContext.sendStickyBroadcast(broadcast); - + Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " + + activeList.size() + ", " + erroredList.size()); // check if we need to send a USB notification // Check if the user wants to be bothered boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(), @@ -314,8 +325,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (tellUser) { for (Object o : availableList) { String s = (String)o; - for (Object matchObject : mTetherableUsbRegexs) { - if (s.matches((String)matchObject)) { + for (String match : mTetherableUsbRegexs) { + if (s.matches(match)) { showTetherAvailableNotification(); return; } @@ -414,20 +425,32 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - - - private class StateReceiver extends BroadcastReceiver { public void onReceive(Context content, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_UMS_CONNECTED)) { - Tethering.this.handleTtyConnect(); + Log.w(TAG, "got UMS connected"); + synchronized (Tethering.this) { + if(mUsbResetExpected != 0) { + Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored"); + mUsbResetExpected--; + return; + } + } + Tethering.this.enableUsbIfaces(true); // add them } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) { - Tethering.this.handleTtyDisconnect(); + Log.w(TAG, "got UMS disconneded broadcast"); + synchronized (Tethering.this) { + if(mUsbResetExpected != 0) { + Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored"); + mUsbResetExpected--; + return; + } + } + Tethering.this.enableUsbIfaces(false); // remove them } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = - IConnectivityManager.Stub.asInterface(b); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); try { NetworkInfo info = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN); int msg; @@ -442,6 +465,103 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } + // used on cable insert/remove + private void enableUsbIfaces(boolean enable) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + String[] ifaces = new String[0]; + try { + ifaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return; + } + for (String iface : ifaces) { + for (String regex : mTetherableUsbRegexs) { + if (iface.matches(regex)) { + if (enable) { + interfaceAdded(iface); + } else { + interfaceRemoved(iface); + } + } + } + } + } + + // toggled when we enter/leave the fully teathered state + private boolean enableUsbRndis(boolean enabled) { + Log.d(TAG, "enableUsbRndis(" + enabled + ")"); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + try { + if (enabled) { + // turning this on will reset USB and generate two bogus events - ignore them + synchronized (this) { + if (!service.isUsbRNDISStarted()) { + mUsbResetExpected += 2; + service.startUsbRNDIS(); + } + } + } else { + if (service.isUsbRNDISStarted()) { + service.stopUsbRNDIS(); + } + } + } catch (Exception e) { + Log.e(TAG, "Error toggling usb RNDIS :" + e); + return false; + } + return true; + } + + // configured when we start tethering and unconfig'd on error or conclusion + private boolean configureUsbIface(boolean enabled) { + Log.d(TAG, "configureUsbIface(" + enabled + ")"); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + // bring toggle the interfaces + String[] ifaces = new String[0]; + try { + ifaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return false; + } + for (String iface : ifaces) { + for (String regex : mTetherableUsbRegexs) { + if (iface.matches(regex)) { + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1; + ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; + if (enabled) { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + } else { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); + // TODO - clean this up - maybe a better regex? + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running ",""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running",""); + } + service.setInterfaceConfig(iface, ifcg); + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return false; + } + } + } + } + + return true; + } + private void handleTtyConnect() { Log.d(TAG, "handleTtyConnect"); // for each of the available Tty not already supported by a ppp session, @@ -609,6 +729,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private HierarchicalState mUntetherInterfaceErrorState; private HierarchicalState mEnableNatErrorState; private HierarchicalState mDisableNatErrorState; + private HierarchicalState mUsbConfigurationErrorState; private HierarchicalState mUnavailableState; @@ -617,10 +738,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private boolean mTethered; String mIfaceName; + boolean mUsb; - TetherInterfaceSM(String name) { + TetherInterfaceSM(String name, boolean usb) { super(name); mIfaceName = name; + mUsb = usb; mInitialState = new InitialState(); addState(mInitialState); @@ -638,6 +761,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { addState(mEnableNatErrorState); mDisableNatErrorState = new DisableNatErrorState(); addState(mDisableNatErrorState); + mUsbConfigurationErrorState = new UsbConfigurationErrorState(); + addState(mUsbConfigurationErrorState); mUnavailableState = new UnavailableState(); addState(mUnavailableState); @@ -656,6 +781,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState"; if (current == mEnableNatErrorState) res += "EnableNatErrorState"; if (current == mDisableNatErrorState) res += "DisableNatErrorState"; + if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState"; if (current == mUnavailableState) res += "UnavailableState"; if (mAvailable) res += " - Available"; if (mTethered) res += " - Tethered"; @@ -686,8 +812,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return mErrored; } - private synchronized void setErrored(boolean errored) { - mErrored = errored; + private void setErrored(boolean errored) { + synchronized (this) { + mErrored = errored; + } + if (errored && mUsb) { + // note everything's been unwound by this point so nothing to do on + // further error.. + Tethering.this.configureUsbIface(false); + } } class InitialState extends HierarchicalState { @@ -726,6 +859,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub { @Override public void enter() { setAvailable(false); + if (mUsb) { + if (!Tethering.this.configureUsbIface(true)) { + Message m = mTetherMasterSM.obtainMessage( + TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED); + m.obj = TetherInterfaceSM.this; + mTetherMasterSM.sendMessage(m); + + m = obtainMessage(CMD_TRANSITION_TO_ERROR); + m.obj = mUsbConfigurationErrorState; + sendMessageAtFrontOfQueue(m); + return; + } + } sendTetherStateChangedBroadcast(); } @Override @@ -739,6 +885,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub { TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED); m.obj = TetherInterfaceSM.this; mTetherMasterSM.sendMessage(m); + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + transitionTo(mUsbConfigurationErrorState); + break; + } + } transitionTo(mInitialState); break; case CMD_TETHER_MODE_ALIVE: @@ -759,6 +911,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mTetherMasterSM.sendMessage(m); transitionTo(mUnavailableState); break; + case CMD_TRANSITION_TO_ERROR: + HierarchicalState s = (HierarchicalState)(message.obj); + transitionTo(s); + break; default: retValue = false; } @@ -788,12 +944,17 @@ public class Tethering extends INetworkManagementEventObserver.Stub { sendMessageAtFrontOfQueue(m); return; } + if (mUsb) Tethering.this.enableUsbRndis(true); Log.d(TAG, "Tethered " + mIfaceName); setAvailable(false); setTethered(true); sendTetherStateChangedBroadcast(); } @Override + public void exit() { + if (mUsb) Tethering.this.enableUsbRndis(false); + } + @Override public boolean processMessage(Message message) { Log.d(TAG, "TetheredState.processMessage what=" + message.what); boolean retValue = true; @@ -821,12 +982,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub { m.obj = TetherInterfaceSM.this; mTetherMasterSM.sendMessage(m); if (message.what == CMD_TETHER_UNREQUESTED) { - transitionTo(mInitialState); + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + transitionTo(mUsbConfigurationErrorState); + } else { + transitionTo(mInitialState); + } + } else { + transitionTo(mInitialState); + } } else if (message.what == CMD_INTERFACE_DOWN) { transitionTo(mUnavailableState); } Log.d(TAG, "Untethered " + mIfaceName); - sendTetherStateChangedBroadcast(); break; case CMD_CELL_DUN_ERROR: case CMD_IP_FORWARDING_ENABLE_ERROR: @@ -857,6 +1025,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } Log.d(TAG, "Tether lost upstream connection " + mIfaceName); sendTetherStateChangedBroadcast(); + if (mUsb) { + if (!Tethering.this.configureUsbIface(false)) { + transitionTo(mUsbConfigurationErrorState); + break; + } + } transitionTo(mInitialState); break; case CMD_TRANSITION_TO_ERROR: @@ -974,6 +1148,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub { sendTetherStateChangedBroadcast(); } } + + class UsbConfigurationErrorState extends ErrorState { + @Override + public void enter() { + Log.e(TAG, "Error trying to configure USB " + mIfaceName); + setAvailable(false); + setErrored(true); + } + } } class TetherMasterSM extends HierarchicalStateMachine { @@ -1000,7 +1183,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private HierarchicalState mCellDunRequestedState; private HierarchicalState mCellDunAliveState; private HierarchicalState mTetherModeAliveState; - private HierarchicalState mCellDunUnRequestedState; private HierarchicalState mCellDunErrorState; private HierarchicalState mSetIpForwardingEnabledErrorState; @@ -1028,8 +1210,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { addState(mCellDunAliveState); mTetherModeAliveState = new TetherModeAliveState(); addState(mTetherModeAliveState); - mCellDunUnRequestedState = new CellDunUnRequestedState(); - addState(mCellDunUnRequestedState); mCellDunErrorState = new CellDunErrorState(); addState(mCellDunErrorState); @@ -1049,8 +1229,81 @@ public class Tethering extends INetworkManagementEventObserver.Stub { setInitialState(mInitialState); } + class TetherMasterUtilState extends HierarchicalState { + @Override + public boolean processMessage(Message m) { + return false; + } + public int turnOnMobileDun() { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + int retValue = Phone.APN_REQUEST_FAILED; + try { + retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, + Phone.FEATURE_ENABLE_DUN, new Binder()); + } catch (Exception e) { + } + return retValue; + } + public boolean turnOffMobileDun() { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = + IConnectivityManager.Stub.asInterface(b); + try { + service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, + Phone.FEATURE_ENABLE_DUN); + } catch (Exception e) { + return false; + } + return true; + } + public boolean turnOnMasterTetherSettings() { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.setIpForwardingEnabled(true); + } catch (Exception e) { + transitionTo(mSetIpForwardingEnabledErrorState); + return false; + } + try { + service.startTethering(mDhcpRange[0], mDhcpRange[1]); + } catch (Exception e) { + transitionTo(mStartTetheringErrorState); + return false; + } + try { + service.setDnsForwarders(mDnsServers); + } catch (Exception e) { + transitionTo(mSetDnsForwardersErrorState); + return false; + } + transitionTo(mTetherModeAliveState); + return true; + } + public boolean turnOffMasterTetherSettings() { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = + INetworkManagementService.Stub.asInterface(b); + try { + service.stopTethering(); + } catch (Exception e) { + transitionTo(mStopTetheringErrorState); + return false; + } + try { + service.setIpForwardingEnabled(false); + } catch (Exception e) { + transitionTo(mSetIpForwardingDisabledErrorState); + return false; + } + transitionTo(mInitialState); + return true; + } + } - class InitialState extends HierarchicalState { + class InitialState extends TetherMasterUtilState { @Override public boolean processMessage(Message message) { Log.d(TAG, "MasterInitialState.processMessage what=" + message.what); @@ -1080,20 +1333,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return retValue; } } - class CellDunRequestedState extends HierarchicalState { + class CellDunRequestedState extends TetherMasterUtilState { @Override public void enter() { ++mSequenceNumber; - IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = - IConnectivityManager.Stub.asInterface(b); - int result; - try { - result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, - Phone.FEATURE_ENABLE_DUN, new Binder()); - } catch (Exception e) { - result = Phone.APN_REQUEST_FAILED; - } + int result = turnOnMobileDun(); switch (result) { case Phone.APN_ALREADY_ACTIVE: Log.d(TAG, "Dun already active"); @@ -1132,34 +1376,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (index != -1) { mNotifyList.remove(index); if (mNotifyList.isEmpty()) { - transitionTo(mCellDunUnRequestedState); + turnOffMobileDun(); + transitionTo(mInitialState); } } break; case CMD_CELL_DUN_ENABLED: - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = - INetworkManagementService.Stub.asInterface(b); - - try { - service.setIpForwardingEnabled(true); - } catch (Exception e) { - transitionTo(mSetIpForwardingEnabledErrorState); - break; - } - try { - service.startTethering(mDhcpRange[0], mDhcpRange[1]); - } catch (Exception e) { - transitionTo(mStartTetheringErrorState); - break; - } - try { - service.setDnsForwarders(mDnsServers); - } catch (Exception e) { - transitionTo(mSetDnsForwardersErrorState); - break; - } - transitionTo(mTetherModeAliveState); + turnOnMasterTetherSettings(); break; case CMD_CELL_DUN_DISABLED: break; @@ -1176,9 +1399,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - class CellDunAliveState extends HierarchicalState { + class CellDunAliveState extends TetherMasterUtilState { @Override public void enter() { + Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms"); sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS); } @@ -1190,29 +1414,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { case CMD_TETHER_MODE_REQUESTED: TetherInterfaceSM who = (TetherInterfaceSM)message.obj; mNotifyList.add(who); - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = - INetworkManagementService.Stub.asInterface(b); - - try { - service.setIpForwardingEnabled(true); - } catch (Exception e) { - transitionTo(mSetIpForwardingEnabledErrorState); - break; - } - try { - service.startTethering(mDhcpRange[0], mDhcpRange[1]); - } catch (Exception e) { - transitionTo(mStartTetheringErrorState); - break; - } - try { - service.setDnsForwarders(mDnsServers); - } catch (Exception e) { - transitionTo(mSetDnsForwardersErrorState); - break; - } - transitionTo(mTetherModeAliveState); + turnOnMasterTetherSettings(); break; case CMD_TETHER_MODE_UNREQUESTED: who = (TetherInterfaceSM)message.obj; @@ -1220,7 +1422,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (index != -1) { mNotifyList.remove(index); if (mNotifyList.isEmpty()) { - transitionTo(mCellDunUnRequestedState); + turnOffMobileDun(); + transitionTo(mInitialState); } } break; @@ -1228,13 +1431,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { transitionTo(mInitialState); break; case CMD_CELL_DUN_RENEW: - b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b); - try { - cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, - Phone.FEATURE_ENABLE_DUN, new Binder()); - } catch (Exception e) { - } + Log.d(TAG, "renewing dun connection - requeuing for another " + + CELL_DUN_RENEW_MS + "ms"); + turnOnMobileDun(); sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS); break; default: @@ -1245,9 +1444,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - class TetherModeAliveState extends HierarchicalState { + class TetherModeAliveState extends TetherMasterUtilState { @Override public void enter() { + Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms"); + sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS); for (Object o : mNotifyList) { TetherInterfaceSM sm = (TetherInterfaceSM)o; sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE)); @@ -1269,7 +1470,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (index != -1) { mNotifyList.remove(index); if (mNotifyList.isEmpty()) { - transitionTo(mCellDunUnRequestedState); + turnOffMobileDun(); + turnOffMasterTetherSettings(); // transitions appropriately } } break; @@ -1281,22 +1483,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub { sm.sendMessage(sm.obtainMessage( TetherInterfaceSM.CMD_TETHER_MODE_DEAD)); } - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = - INetworkManagementService.Stub.asInterface(b); - try { - service.stopTethering(); - } catch (Exception e) { - transitionTo(mStopTetheringErrorState); - break; - } - try { - service.setIpForwardingEnabled(false); - } catch (Exception e) { - transitionTo(mSetIpForwardingDisabledErrorState); - break; - } - transitionTo(mInitialState); + turnOffMasterTetherSettings(); // transitions appropriately + break; + case CMD_CELL_DUN_RENEW: + Log.d(TAG, "renewing dun connection - requeuing for another " + + CELL_DUN_RENEW_MS + "ms"); + turnOnMobileDun(); + sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS); break; default: retValue = false; @@ -1306,58 +1499,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - class CellDunUnRequestedState extends HierarchicalState { - @Override - public void enter() { - IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = - IConnectivityManager.Stub.asInterface(b); - NetworkInfo dunInfo = null; - try { - dunInfo = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN); - } catch (Exception e) {} - if (dunInfo != null && !dunInfo.isConnectedOrConnecting()) { - sendMessage(obtainMessage(CMD_CELL_DUN_DISABLED)); - return; - } - try { - service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, - Phone.FEATURE_ENABLE_DUN); - } catch (Exception e) {} - Message m = obtainMessage(CMD_CELL_DUN_TIMEOUT); - m.arg1 = ++mSequenceNumber; - // use a short timeout - this will often be a no-op and - // we just want this request to get into the queue before we - // try again. - sendMessageDelayed(m, CELL_DISABLE_DUN_TIMEOUT_MS); - } - @Override - public boolean processMessage(Message message) { - Log.d(TAG, "CellDunUnRequestedState.processMessage what=" + message.what); - boolean retValue = true; - switch (message.what) { - case CMD_TETHER_MODE_REQUESTED: - case CMD_TETHER_MODE_UNREQUESTED: - deferMessage(message); - break; - case CMD_CELL_DUN_DISABLED: - transitionTo(mInitialState); - break; - case CMD_CELL_DUN_TIMEOUT: - // if we aren't using a sep apn, we won't get a disconnect broadcast.. - // just go back to initial after our short pause - if (message.arg1 == mSequenceNumber) { - transitionTo(mInitialState); - } - break; - default: - retValue = false; - break; - } - return retValue; - } - } - class ErrorState extends HierarchicalState { int mErrorNotification; @Override diff --git a/services/java/com/android/server/status/ExpandedView.java b/services/java/com/android/server/status/ExpandedView.java index d0f14cb..33ac8c1 100644 --- a/services/java/com/android/server/status/ExpandedView.java +++ b/services/java/com/android/server/status/ExpandedView.java @@ -11,17 +11,11 @@ import android.util.Log; public class ExpandedView extends LinearLayout { - final Display mDisplay; StatusBarService mService; - boolean mTracking; - int mStartX, mStartY; - int mMaxHeight = 0; int mPrevHeight = -1; public ExpandedView(Context context, AttributeSet attrs) { super(context, attrs); - mDisplay = ((WindowManager)context.getSystemService( - Context.WINDOW_SERVICE)).getDefaultDisplay(); } @Override @@ -36,12 +30,6 @@ public class ExpandedView extends LinearLayout { } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, - MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST)); - } - - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); int height = bottom - top; @@ -51,11 +39,4 @@ public class ExpandedView extends LinearLayout { mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE); } } - - void setMaxHeight(int h) { - if (h != mMaxHeight) { - mMaxHeight = h; - requestLayout(); - } - } } diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index d13f9d3..f1ccb9b 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -826,7 +826,10 @@ public class StatusBarPolicy { final Uri soundUri = Uri.parse("file://" + soundPath); if (soundUri != null) { final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) sfx.play(); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } } } } diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 493bd93..3f77291 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -187,8 +187,9 @@ public class StatusBarService extends IStatusBar.Stub TextView mSpnLabel; TextView mPlmnLabel; TextView mClearButton; + View mExpandedContents; CloseDragHandle mCloseView; - int[] mCloseLocation = new int[2]; + int[] mPositionTmp = new int[2]; boolean mExpanded; boolean mExpandedVisible; @@ -198,7 +199,7 @@ public class StatusBarService extends IStatusBar.Stub // the tracker view TrackingView mTrackingView; WindowManager.LayoutParams mTrackingParams; - int mTrackingPosition; + int mTrackingPosition; // the position of the top of the tracking view. // ticker private Ticker mTicker; @@ -274,6 +275,7 @@ public class StatusBarService extends IStatusBar.Stub mExpandedDialog = new ExpandedDialog(context); mExpandedView = expanded; + mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems); mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); @@ -1530,20 +1532,11 @@ public class StatusBarService extends IStatusBar.Stub /// ---------- Expanded View -------------- pixelFormat = PixelFormat.TRANSLUCENT; - bg = mExpandedView.getBackground(); - if (bg != null) { - pixelFormat = bg.getOpacity(); - if (pixelFormat != PixelFormat.TRANSLUCENT) { - // we want good-looking gradients, so we force a 8-bits per - // pixel format. - pixelFormat = PixelFormat.RGBX_8888; - } - } final int disph = mDisplay.getHeight(); lp = mExpandedDialog.getWindow().getAttributes(); lp.width = ViewGroup.LayoutParams.MATCH_PARENT; - lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; + lp.height = getExpandedHeight(); lp.x = 0; mTrackingPosition = lp.y = -disph; // sufficiently large negative lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; @@ -1562,10 +1555,10 @@ public class StatusBarService extends IStatusBar.Stub mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); mExpandedDialog.setContentView(mExpandedView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.MATCH_PARENT)); + mExpandedDialog.getWindow().setBackgroundDrawable(null); mExpandedDialog.show(); FrameLayout hack = (FrameLayout)mExpandedView.getParent(); - hack.setForeground(null); } void setDateViewVisibility(boolean visible, int anim) { @@ -1630,11 +1623,15 @@ public class StatusBarService extends IStatusBar.Stub mTrackingParams.height = disph-h; WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); - mCloseView.getLocationInWindow(mCloseLocation); - if (mExpandedParams != null) { + mCloseView.getLocationInWindow(mPositionTmp); + final int closePos = mPositionTmp[1]; + + mExpandedContents.getLocationInWindow(mPositionTmp); + final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); + mExpandedParams.y = pos + mTrackingView.getHeight() - - (mTrackingParams.height-mCloseLocation[1]) - mExpandedView.getHeight(); + - (mTrackingParams.height-closePos) - contentsBottom; int max = h; if (mExpandedParams.y > max) { mExpandedParams.y = max; @@ -1644,13 +1641,13 @@ public class StatusBarService extends IStatusBar.Stub mExpandedParams.y = min; } - /* - Log.d(TAG, "mTrackingPosition=" + mTrackingPosition - + " mTrackingView.height=" + mTrackingView.getHeight() - + " diff=" + (mTrackingPosition + mTrackingView.getHeight()) - + " h=" + h); - */ - panelSlightlyVisible((mTrackingPosition + mTrackingView.getHeight()) > h); + boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; + if (!visible) { + // if the contents aren't visible, move the expanded view way off screen + // because the window itself extends below the content view. + mExpandedParams.y = -disph; + } + panelSlightlyVisible(visible); mExpandedDialog.getWindow().setAttributes(mExpandedParams); } @@ -1658,16 +1655,19 @@ public class StatusBarService extends IStatusBar.Stub Log.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition + " mTrackingParams.y=" + mTrackingParams.y + " mTrackingPosition=" + mTrackingPosition - + " mExpandedParams.y=" + mExpandedParams.y); + + " mExpandedParams.y=" + mExpandedParams.y + + " mExpandedParams.height=" + mExpandedParams.height); } } - void updateAvailableHeight() { + int getExpandedHeight() { + return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); + } + + void updateExpandedHeight() { if (mExpandedView != null) { - int disph = mDisplay.getHeight(); - int h = mStatusBarView.getHeight(); - int max = disph - (mCloseView.getHeight() + h); - mExpandedView.setMaxHeight(max); + mExpandedParams.height = getExpandedHeight(); + mExpandedDialog.getWindow().setAttributes(mExpandedParams); } } diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java index 3b79049..e0b288d 100644 --- a/services/java/com/android/server/status/StorageNotification.java +++ b/services/java/com/android/server/status/StorageNotification.java @@ -36,6 +36,7 @@ import android.os.ServiceManager; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; +import android.provider.Settings; import android.util.Log; import android.view.View; import android.widget.Button; @@ -46,6 +47,8 @@ import android.widget.Toast; public class StorageNotification extends StorageEventListener { private static final String TAG = "StorageNotification"; + private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true; + /** * Binder context for this service */ @@ -239,12 +242,28 @@ public class StorageNotification extends StorageEventListener { Intent intent = new Intent(); intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + final boolean adbOn = 1 == Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ADB_ENABLED, + 0); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); setUsbStorageNotification( com.android.internal.R.string.usb_storage_notification_title, com.android.internal.R.string.usb_storage_notification_message, com.android.internal.R.drawable.stat_sys_data_usb, false, true, pi); + + if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { + // We assume that developers don't want to enable UMS every + // time they attach a device to a USB host. The average user, + // however, is looking to charge the phone (in which case this + // is harmless) or transfer files (in which case this coaches + // the user about how to complete that task and saves several + // steps). + mContext.startActivity(intent); + } } else { setUsbStorageNotification(0, 0, 0, false, false, null); } diff --git a/services/java/com/android/server/status/TrackingView.java b/services/java/com/android/server/status/TrackingView.java index 722d10c..886d66d 100644 --- a/services/java/com/android/server/status/TrackingView.java +++ b/services/java/com/android/server/status/TrackingView.java @@ -23,7 +23,7 @@ public class TrackingView extends LinearLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mService.updateAvailableHeight(); + mService.updateExpandedHeight(); } @Override diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index 52c8b1f..cab7b81 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -440,10 +440,11 @@ public abstract class DataConnectionTracker extends Handler { return Phone.APN_REQUEST_FAILED; } - if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " + if (DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " + isApnTypeActive(type) + " and state = " + state); if (!isApnTypeAvailable(type)) { + if (DBG) Log.d(LOG_TAG, "type not available"); return Phone.APN_TYPE_NOT_AVAILABLE; } diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java index 31cf6a7..9f8e57f 100644 --- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java @@ -22,10 +22,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ServiceManager; -import android.telephony.PhoneNumberUtils; -import android.util.Log; -import java.util.ArrayList; import java.util.List; /** @@ -37,7 +34,7 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { protected PhoneBase phone; protected AdnRecordCache adnCache; - protected Object mLock = new Object(); + protected final Object mLock = new Object(); protected int recordSize[]; protected boolean success; protected List<AdnRecord> records; @@ -80,8 +77,7 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { ar = (AsyncResult)msg.obj; synchronized (mLock) { if (ar.exception == null) { - records = (List<AdnRecord>) - ((ArrayList<AdnRecord>) ar.result); + records = (List<AdnRecord>) ar.result; } else { if(DBG) logd("Cannot load ADN records"); if (records != null) { diff --git a/telephony/java/com/android/internal/telephony/IccProvider.java b/telephony/java/com/android/internal/telephony/IccProvider.java index 4232887..fa91457 100644 --- a/telephony/java/com/android/internal/telephony/IccProvider.java +++ b/telephony/java/com/android/internal/telephony/IccProvider.java @@ -19,8 +19,9 @@ package com.android.internal.telephony; import android.content.ContentProvider; import android.content.UriMatcher; import android.content.ContentValues; -import com.android.common.ArrayListCursor; +import android.database.AbstractCursor; import android.database.Cursor; +import android.database.CursorWindow; import android.net.Uri; import android.os.SystemProperties; import android.os.RemoteException; @@ -35,6 +36,149 @@ import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.AdnRecord; import com.android.internal.telephony.IIccPhoneBook; +/** + * XXX old code -- should be replaced with MatrixCursor. + * @deprecated This is has been replaced by MatrixCursor. +*/ +class ArrayListCursor extends AbstractCursor { + private String[] mColumnNames; + private ArrayList<Object>[] mRows; + + @SuppressWarnings({"unchecked"}) + public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) { + int colCount = columnNames.length; + boolean foundID = false; + // Add an _id column if not in columnNames + for (int i = 0; i < colCount; ++i) { + if (columnNames[i].compareToIgnoreCase("_id") == 0) { + mColumnNames = columnNames; + foundID = true; + break; + } + } + + if (!foundID) { + mColumnNames = new String[colCount + 1]; + System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length); + mColumnNames[colCount] = "_id"; + } + + int rowCount = rows.size(); + mRows = new ArrayList[rowCount]; + + for (int i = 0; i < rowCount; ++i) { + mRows[i] = rows.get(i); + if (!foundID) { + mRows[i].add(i); + } + } + } + + @Override + public void fillWindow(int position, CursorWindow window) { + if (position < 0 || position > getCount()) { + return; + } + + window.acquireReference(); + try { + int oldpos = mPos; + mPos = position - 1; + window.clear(); + window.setStartPosition(position); + int columnNum = getColumnCount(); + window.setNumColumns(columnNum); + while (moveToNext() && window.allocRow()) { + for (int i = 0; i < columnNum; i++) { + final Object data = mRows[mPos].get(i); + if (data != null) { + if (data instanceof byte[]) { + byte[] field = (byte[]) data; + if (!window.putBlob(field, mPos, i)) { + window.freeLastRow(); + break; + } + } else { + String field = data.toString(); + if (!window.putString(field, mPos, i)) { + window.freeLastRow(); + break; + } + } + } else { + if (!window.putNull(mPos, i)) { + window.freeLastRow(); + break; + } + } + } + } + + mPos = oldpos; + } catch (IllegalStateException e){ + // simply ignore it + } finally { + window.releaseReference(); + } + } + + @Override + public int getCount() { + return mRows.length; + } + + @Override + public String[] getColumnNames() { + return mColumnNames; + } + + @Override + public byte[] getBlob(int columnIndex) { + return (byte[]) mRows[mPos].get(columnIndex); + } + + @Override + public String getString(int columnIndex) { + Object cell = mRows[mPos].get(columnIndex); + return (cell == null) ? null : cell.toString(); + } + + @Override + public short getShort(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.shortValue(); + } + + @Override + public int getInt(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.intValue(); + } + + @Override + public long getLong(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.longValue(); + } + + @Override + public float getFloat(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.floatValue(); + } + + @Override + public double getDouble(int columnIndex) { + Number num = (Number) mRows[mPos].get(columnIndex); + return num.doubleValue(); + } + + @Override + public boolean isNull(int columnIndex) { + return mRows[mPos].get(columnIndex) == null; + } +} + /** * {@hide} diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 19900c8..1ac2da3 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -39,6 +39,11 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { } protected void finalize() { + try { + super.finalize(); + } catch (Throwable throwable) { + Log.e(LOG_TAG, "Error while finalizing:", throwable); + } Log.d(LOG_TAG, "PhoneSubInfo finalized"); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index fbb3c4c..d5f18e0 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -23,6 +23,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.IConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.AsyncResult; @@ -188,8 +190,16 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // and 2) whether the RIL will setup the baseband to auto-PS attach. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); + boolean dataEnabledSetting = true; + try { + dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager. + getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled(); + } catch (Exception e) { + // nothing to do - use the old behavior and leave data on + } dataEnabled[APN_DEFAULT_ID] = - !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false); + !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false) && + dataEnabledSetting; if (dataEnabled[APN_DEFAULT_ID]) { enabledCount++; } diff --git a/telephony/java/com/android/internal/telephony/cdma/EriManager.java b/telephony/java/com/android/internal/telephony/cdma/EriManager.java index 0e186d0..37c1d55 100644 --- a/telephony/java/com/android/internal/telephony/cdma/EriManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/EriManager.java @@ -23,8 +23,8 @@ import android.util.Xml; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; +import com.android.internal.util.XmlUtils; -import com.android.common.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java index 78e89d5..6e12f24 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java @@ -16,22 +16,10 @@ package com.android.internal.telephony.cdma; -import android.content.pm.PackageManager; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Looper; import android.os.Message; -import android.os.ServiceManager; -import android.telephony.PhoneNumberUtils; import android.util.Log; -import com.android.internal.telephony.AdnRecord; -import com.android.internal.telephony.AdnRecordCache; import com.android.internal.telephony.IccPhoneBookInterfaceManager; -import com.android.internal.telephony.PhoneProxy; - -import java.util.ArrayList; -import java.util.List; /** * RuimPhoneBookInterfaceManager to provide an inter-process communication to @@ -42,20 +30,6 @@ import java.util.List; public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager { static final String LOG_TAG = "CDMA"; - - Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - AsyncResult ar; - - switch(msg.what) { - default: - mBaseHandler.handleMessage(msg); - break; - } - } - }; - public RuimPhoneBookInterfaceManager(CDMAPhone phone) { super(phone); adnCache = phone.mRuimRecords.getAdnCache(); @@ -67,6 +41,11 @@ public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager } protected void finalize() { + try { + super.finalize(); + } catch (Throwable throwable) { + Log.e(LOG_TAG, "Error while finalizing:", throwable); + } if(DBG) Log.d(LOG_TAG, "RuimPhoneBookInterfaceManager finalized"); } diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java index 9439359..cfcfd98 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java @@ -30,6 +30,7 @@ import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; @@ -89,6 +90,11 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { } protected void finalize() { + try { + super.finalize(); + } catch (Throwable throwable) { + Log.e(LOG_TAG, "Error while finalizing:", throwable); + } if(DBG) Log.d(LOG_TAG, "RuimSmsInterfaceManager finalized"); } @@ -143,7 +149,7 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) { //NOTE smsc not used in RUIM if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + - "pdu=("+ pdu + ")"); + "pdu=("+ Arrays.toString(pdu) + ")"); enforceReceiveAndSend("Copying message to RUIM"); synchronized(mLock) { mSuccess = false; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java index 905d5e3..d893ec4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java @@ -18,8 +18,8 @@ package com.android.internal.telephony.gsm; import android.os.Message; import android.util.Log; +import android.util.Patterns; -import com.android.common.Patterns; import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RILConstants; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 1fd6be8..30beaaa 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -27,6 +27,8 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.ContentObserver; import android.database.Cursor; +import android.net.ConnectivityManager; +import android.net.IConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.wifi.WifiManager; @@ -243,7 +245,15 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // This preference tells us 1) initial condition for "dataEnabled", // and 2) whether the RIL will setup the baseband to auto-PS attach. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); - dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); + boolean dataEnabledSetting = true; + try { + dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager. + getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled(); + } catch (Exception e) { + // nothing to do - use the old behavior and leave data on + } + dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false) && + dataEnabledSetting; if (dataEnabled[APN_DEFAULT_ID]) { enabledCount++; } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java index 076da6b..feb508a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java @@ -16,22 +16,10 @@ package com.android.internal.telephony.gsm; -import android.content.pm.PackageManager; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Looper; import android.os.Message; -import android.os.ServiceManager; -import android.telephony.PhoneNumberUtils; import android.util.Log; -import com.android.internal.telephony.AdnRecord; -import com.android.internal.telephony.AdnRecordCache; import com.android.internal.telephony.IccPhoneBookInterfaceManager; -import com.android.internal.telephony.PhoneProxy; - -import java.util.ArrayList; -import java.util.List; /** * SimPhoneBookInterfaceManager to provide an inter-process communication to @@ -42,20 +30,6 @@ import java.util.List; public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager { static final String LOG_TAG = "GSM"; - - Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - AsyncResult ar; - - switch(msg.what) { - default: - mBaseHandler.handleMessage(msg); - break; - } - } - }; - public SimPhoneBookInterfaceManager(GSMPhone phone) { super(phone); adnCache = phone.mSIMRecords.getAdnCache(); @@ -67,6 +41,11 @@ public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager { } protected void finalize() { + try { + super.finalize(); + } catch (Throwable throwable) { + Log.e(LOG_TAG, "Error while finalizing:", throwable); + } if(DBG) Log.d(LOG_TAG, "SimPhoneBookInterfaceManager finalized"); } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java index 875d8d0..2028ca4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java @@ -25,10 +25,10 @@ import android.util.Log; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccSmsInterfaceManager; import com.android.internal.telephony.IccUtils; -import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; @@ -65,8 +65,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { ar = (AsyncResult)msg.obj; synchronized (mLock) { if (ar.exception == null) { - mSms = (List<SmsRawData>) - buildValidRawData((ArrayList<byte[]>) ar.result); + mSms = buildValidRawData((ArrayList<byte[]>) ar.result); } else { if(DBG) log("Cannot load Sms records"); if (mSms != null) @@ -88,6 +87,11 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { } protected void finalize() { + try { + super.finalize(); + } catch (Throwable throwable) { + Log.e(LOG_TAG, "Error while finalizing:", throwable); + } if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized"); } @@ -106,7 +110,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { updateMessageOnIccEf(int index, int status, byte[] pdu) { if (DBG) log("updateMessageOnIccEf: index=" + index + " status=" + status + " ==> " + - "("+ pdu + ")"); + "("+ Arrays.toString(pdu) + ")"); enforceReceiveAndSend("Updating message on SIM"); synchronized(mLock) { mSuccess = false; @@ -118,7 +122,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { mPhone.mCM.deleteSmsOnSim(index, response); } else { byte[] record = makeSmsRecordData(status, pdu); - ((SIMFileHandler)mPhone.getIccFileHandler()).updateEFLinearFixed( + mPhone.getIccFileHandler().updateEFLinearFixed( IccConstants.EF_SMS, index, record, null, response); } @@ -142,7 +146,8 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { */ public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) { if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + - "pdu=("+ pdu + "), smsm=(" + smsc +")"); + "pdu=("+ Arrays.toString(pdu) + + "), smsm=(" + Arrays.toString(smsc) +")"); enforceReceiveAndSend("Copying message to SIM"); synchronized(mLock) { mSuccess = false; @@ -175,8 +180,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { "Reading messages from SIM"); synchronized(mLock) { Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); - ((SIMFileHandler)mPhone.getIccFileHandler()).loadEFLinearFixedAll(IccConstants.EF_SMS, - response); + mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response); try { mLock.wait(); diff --git a/telephony/java/com/android/internal/telephony/gsm/SpnOverride.java b/telephony/java/com/android/internal/telephony/gsm/SpnOverride.java index abb0230..9ea30101 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SpnOverride.java +++ b/telephony/java/com/android/internal/telephony/gsm/SpnOverride.java @@ -13,7 +13,7 @@ import android.os.Environment; import android.util.Log; import android.util.Xml; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; public class SpnOverride { private HashMap<String, String> CarrierSpnMap; diff --git a/telephony/java/com/android/internal/telephony/gsm/VoiceMailConstants.java b/telephony/java/com/android/internal/telephony/gsm/VoiceMailConstants.java index 0bedd53..0e49e35 100644 --- a/telephony/java/com/android/internal/telephony/gsm/VoiceMailConstants.java +++ b/telephony/java/com/android/internal/telephony/gsm/VoiceMailConstants.java @@ -28,7 +28,8 @@ import java.io.IOException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import com.android.common.XmlUtils; + +import com.android.internal.util.XmlUtils; /** * {@hide} diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java index 4ae98e6..ee6b89c 100644 --- a/test-runner/src/android/test/InstrumentationTestRunner.java +++ b/test-runner/src/android/test/InstrumentationTestRunner.java @@ -19,6 +19,7 @@ package android.test; import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE; import com.android.internal.util.Predicate; +import com.android.internal.util.Predicates; import android.app.Activity; import android.app.Instrumentation; @@ -31,11 +32,14 @@ import android.os.PerformanceCollector.PerformanceResultsWriter; import android.test.suitebuilder.TestMethod; import android.test.suitebuilder.TestPredicates; import android.test.suitebuilder.TestSuiteBuilder; +import android.test.suitebuilder.annotation.HasAnnotation; +import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -93,6 +97,18 @@ import junit.textui.ResultPrinter; * -e size large * com.android.foo/android.test.InstrumentationTestRunner * <p/> + * <b>Filter test run to tests with given annotation:</b> adb shell am instrument -w + * -e annotation com.android.foo.MyAnnotation + * com.android.foo/android.test.InstrumentationTestRunner + * <p/> + * If used with other options, the resulting test run will contain the union of the two options. + * e.g. "-e size large -e annotation com.android.foo.MyAnnotation" will run only tests with both + * the {@link LargeTest} and "com.android.foo.MyAnnotation" annotations. + * <p/> + * <b>Filter test run to tests <i>without</i> given annotation:</b> adb shell am instrument -w + * -e notAnnotation com.android.foo.MyAnnotation + * com.android.foo/android.test.InstrumentationTestRunner + * <p/> * <b>Running a single testcase:</b> adb shell am instrument -w * -e class com.android.foo.FooTest * com.android.foo/android.test.InstrumentationTestRunner @@ -161,6 +177,10 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private static final String LARGE_SUITE = "large"; private static final String ARGUMENT_LOG_ONLY = "log"; + /** @hide */ + static final String ARGUMENT_ANNOTATION = "annotation"; + /** @hide */ + static final String ARGUMENT_NOT_ANNOTATION = "notAnnotation"; /** * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" @@ -274,6 +294,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu ClassPathPackageInfoSource.setApkPaths(apkPaths); Predicate<TestMethod> testSizePredicate = null; + Predicate<TestMethod> testAnnotationPredicate = null; + Predicate<TestMethod> testNotAnnotationPredicate = null; boolean includePerformance = false; String testClassesArg = null; boolean logOnly = false; @@ -287,6 +309,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE); testSizePredicate = getSizePredicateFromArg( arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE)); + testAnnotationPredicate = getAnnotationPredicate( + arguments.getString(ARGUMENT_ANNOTATION)); + testNotAnnotationPredicate = getNotAnnotationPredicate( + arguments.getString(ARGUMENT_NOT_ANNOTATION)); + includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF); logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY); mCoverage = getBooleanArgument(arguments, "coverage"); @@ -306,6 +333,12 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu if (testSizePredicate != null) { testSuiteBuilder.addRequirements(testSizePredicate); } + if (testAnnotationPredicate != null) { + testSuiteBuilder.addRequirements(testAnnotationPredicate); + } + if (testNotAnnotationPredicate != null) { + testSuiteBuilder.addRequirements(testNotAnnotationPredicate); + } if (!includePerformance) { testSuiteBuilder.addRequirements(REJECT_PERFORMANCE); } @@ -406,6 +439,59 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } } + /** + * Returns the test predicate object, corresponding to the annotation class value provided via + * the {@link ARGUMENT_ANNOTATION} argument. + * + * @return the predicate or <code>null</code> + */ + private Predicate<TestMethod> getAnnotationPredicate(String annotationClassName) { + Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName); + if (annotationClass != null) { + return new HasAnnotation(annotationClass); + } + return null; + } + + /** + * Returns the negative test predicate object, corresponding to the annotation class value + * provided via the {@link ARGUMENT_NOT_ANNOTATION} argument. + * + * @return the predicate or <code>null</code> + */ + private Predicate<TestMethod> getNotAnnotationPredicate(String annotationClassName) { + Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName); + if (annotationClass != null) { + return Predicates.not(new HasAnnotation(annotationClass)); + } + return null; + } + + /** + * Helper method to return the annotation class with specified name + * + * @param annotationClassName the fully qualified name of the class + * @return the annotation class or <code>null</code> + */ + private Class<? extends Annotation> getAnnotationClass(String annotationClassName) { + if (annotationClassName == null) { + return null; + } + try { + Class<?> annotationClass = Class.forName(annotationClassName); + if (annotationClass.isAnnotation()) { + return (Class<? extends Annotation>)annotationClass; + } else { + Log.e(LOG_TAG, String.format("Provided annotation value %s is not an Annotation", + annotationClassName)); + } + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, String.format("Could not find class for specified annotation %s", + annotationClassName)); + } + return null; + } + @Override public void onStart() { Looper.prepare(); @@ -471,7 +557,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu String coverageFilePath = getCoverageFilePath(); java.io.File coverageFile = new java.io.File(coverageFilePath); try { - Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); + Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", coverageFile.getClass(), boolean.class, boolean.class); diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java index d9afd54..6db72ad 100644 --- a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java +++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java @@ -109,6 +109,33 @@ public class InstrumentationTestRunnerTest extends TestCase { assertTrue(mStubAndroidTestRunner.isRun()); } + /** + * Test that the -e {@link InstrumentationTestRunner.ARGUMENT_ANNOTATION} parameter properly + * selects tests. + */ + public void testAnnotationParameter() throws Exception { + String expectedTestClassName = AnnotationTest.class.getName(); + Bundle args = new Bundle(); + args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName); + args.putString(InstrumentationTestRunner.ARGUMENT_ANNOTATION, FlakyTest.class.getName()); + mInstrumentationTestRunner.onCreate(args); + assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testAnnotated"); + } + + /** + * Test that the -e {@link InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION} parameter + * properly excludes tests. + */ + public void testNotAnnotationParameter() throws Exception { + String expectedTestClassName = AnnotationTest.class.getName(); + Bundle args = new Bundle(); + args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName); + args.putString(InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION, + FlakyTest.class.getName()); + mInstrumentationTestRunner.onCreate(args); + assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testNotAnnotated"); + } + private void assertContentsInOrder(List<TestDescriptor> actual, TestDescriptor... source) { TestDescriptor[] clonedSource = source.clone(); assertEquals("Unexpected number of items.", clonedSource.length, actual.size()); @@ -269,4 +296,17 @@ public class InstrumentationTestRunnerTest extends TestCase { } } + + /** + * Annotated test used for validation. + */ + public static class AnnotationTest extends TestCase { + + public void testNotAnnotated() throws Exception { + } + + @FlakyTest + public void testAnnotated() throws Exception { + } + } } diff --git a/common/tests/res/raw/alt_ip_only.crt b/tests/AndroidTests/res/raw/alt_ip_only.crt index 3ac9f5a..3ac9f5a 100644 --- a/common/tests/res/raw/alt_ip_only.crt +++ b/tests/AndroidTests/res/raw/alt_ip_only.crt diff --git a/common/tests/res/raw/subject_alt_only.crt b/tests/AndroidTests/res/raw/subject_alt_only.crt index d5808fb..d5808fb 100644 --- a/common/tests/res/raw/subject_alt_only.crt +++ b/tests/AndroidTests/res/raw/subject_alt_only.crt diff --git a/common/tests/res/raw/subject_only.crt b/tests/AndroidTests/res/raw/subject_only.crt index 11b34e7..11b34e7 100644 --- a/common/tests/res/raw/subject_only.crt +++ b/tests/AndroidTests/res/raw/subject_only.crt diff --git a/common/tests/res/raw/subject_with_alt_names.crt b/tests/AndroidTests/res/raw/subject_with_alt_names.crt index 6963c7e..6963c7e 100644 --- a/common/tests/res/raw/subject_with_alt_names.crt +++ b/tests/AndroidTests/res/raw/subject_with_alt_names.crt diff --git a/common/tests/res/raw/subject_with_wild_alt_name.crt b/tests/AndroidTests/res/raw/subject_with_wild_alt_name.crt index 19b1174..19b1174 100644 --- a/common/tests/res/raw/subject_with_wild_alt_name.crt +++ b/tests/AndroidTests/res/raw/subject_with_wild_alt_name.crt diff --git a/common/tests/res/raw/wild_alt_name_only.crt b/tests/AndroidTests/res/raw/wild_alt_name_only.crt index fafdebf..fafdebf 100644 --- a/common/tests/res/raw/wild_alt_name_only.crt +++ b/tests/AndroidTests/res/raw/wild_alt_name_only.crt diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java index 3bbb447..5aaf13b 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java @@ -55,6 +55,7 @@ public class AsecTests extends AndroidTestCase { void failStr(String errMsg) { Log.w(TAG, "errMsg="+errMsg); } + void failStr(Exception e) { Log.w(TAG, "e.getMessage="+e.getMessage()); Log.w(TAG, "e="+e); @@ -67,6 +68,13 @@ public class AsecTests extends AndroidTestCase { cleanupContainers(); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); + cleanupContainers(); + } + private void cleanupContainers() throws RemoteException { IMountService ms = getMs(); String[] containers = ms.getSecureContainerList(); @@ -132,6 +140,14 @@ public class AsecTests extends AndroidTestCase { return ms.destroySecureContainer(fullId, force); } + private boolean isContainerMounted(String localId) throws RemoteException { + Assert.assertTrue(isMediaMounted()); + String fullId = "com.android.unittests.AsecTests." + localId; + + IMountService ms = getMs(); + return ms.isSecureContainerMounted(fullId); + } + private IMountService getMs() { IBinder service = ServiceManager.getService("mount"); if (service != null) { @@ -300,7 +316,7 @@ public class AsecTests extends AndroidTestCase { } } - public void testRenameMountedContainer() { + public void testRenameSrcMountedContainer() { try { Assert.assertEquals(StorageResultCode.OperationSucceeded, createContainer("testRenameContainer.1", 4, "none")); @@ -312,12 +328,15 @@ public class AsecTests extends AndroidTestCase { } } - public void testRenameToExistingContainer() { + public void testRenameDstMountedContainer() { try { Assert.assertEquals(StorageResultCode.OperationSucceeded, createContainer("testRenameContainer.1", 4, "none")); Assert.assertEquals(StorageResultCode.OperationSucceeded, + unmountContainer("testRenameContainer.1", false)); + + Assert.assertEquals(StorageResultCode.OperationSucceeded, createContainer("testRenameContainer.2", 4, "none")); Assert.assertEquals(StorageResultCode.OperationFailedStorageMounted, @@ -326,4 +345,25 @@ public class AsecTests extends AndroidTestCase { failStr(e); } } + + public void testIsContainerMountedAfterRename() { + try { + Assert.assertEquals(StorageResultCode.OperationSucceeded, + createContainer("testRenameContainer.1", 4, "none")); + + Assert.assertEquals(StorageResultCode.OperationSucceeded, + unmountContainer("testRenameContainer.1", false)); + + Assert.assertEquals(StorageResultCode.OperationSucceeded, + renameContainer("testRenameContainer.1", "testRenameContainer.2")); + + Assert.assertEquals(false, containerExists("testRenameContainer.1")); + Assert.assertEquals(true, containerExists("testRenameContainer.2")); + // Check if isContainerMounted returns valid value + Assert.assertEquals(true, isContainerMounted("testRenameContainer.2")); + } catch (Exception e) { + failStr(e); + } + } + } diff --git a/common/tests/src/com/android/common/DNParserTest.java b/tests/AndroidTests/src/com/android/unit_tests/DNParserTest.java index 34b140a..61d0b42 100644 --- a/common/tests/src/com/android/common/DNParserTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/DNParserTest.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.common; +package com.android.unit_tests; + +import com.android.internal.net.DNParser; import javax.security.auth.x500.X500Principal; diff --git a/common/tests/src/com/android/common/DomainNameValidatorTest.java b/tests/AndroidTests/src/com/android/unit_tests/DomainNameValidatorTest.java index b825be4..1754dbe 100644 --- a/common/tests/src/com/android/common/DomainNameValidatorTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/DomainNameValidatorTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.common; +package com.android.unit_tests; -import com.android.common.tests.R; +import com.android.internal.net.DomainNameValidator; import android.test.AndroidTestCase; diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index 9c5c44d..5e3895a 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -57,17 +57,25 @@ import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; public final long MAX_WAIT_TIME=120*1000; public final long WAIT_TIME_INCR=20*1000; + private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; + private static final int APP_INSTALL_AUTO = 0; + private static final int APP_INSTALL_DEVICE = 1; + private static final int APP_INSTALL_SDCARD = 2; void failStr(String errMsg) { Log.w(TAG, "errMsg="+errMsg); @@ -244,9 +252,46 @@ public class PackageManagerTests extends AndroidTestCase { packageParser = null; return pkg; } - - private void assertInstall(String pkgName, int flags) { + private boolean getInstallLoc(int flags, int expInstallLocation) { + // Flags explicitly over ride everything else. + if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) { + return false; + } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0 ) { + return true; + } + // Manifest option takes precedence next + if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + return true; + } + // TODO Out of memory checks here. + boolean checkSd = false; + int setLoc = 0; + try { + setLoc = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION); + } catch (SettingNotFoundException e) { + failStr(e); + } + if (setLoc == 1) { + int userPref = APP_INSTALL_AUTO; + try { + userPref = Settings.System.getInt(mContext.getContentResolver(), Settings.System.DEFAULT_INSTALL_LOCATION); + } catch (SettingNotFoundException e) { + failStr(e); + } + if (userPref == APP_INSTALL_DEVICE) { + checkSd = false; + } else if (userPref == APP_INSTALL_SDCARD) { + checkSd = true; + } else if (userPref == APP_INSTALL_AUTO) { + // Might be determined dynamically. TODO fix this + checkSd = false; + } + } + return checkSd; + } + private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) { try { + String pkgName = pkg.packageName; ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0); assertNotNull(info); assertEquals(pkgName, info.packageName); @@ -264,15 +309,14 @@ public class PackageManagerTests extends AndroidTestCase { assertEquals(publicSrcPath, appInstallPath); } else { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); - if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { - assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); - // Hardcoded for now - assertTrue(srcPath.startsWith("/asec")); - assertTrue(publicSrcPath.startsWith("/asec")); - } else { + if (!getInstallLoc(flags, expInstallLocation)) { assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + } else { + assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); } } } catch (NameNotFoundException e) { @@ -300,7 +344,7 @@ public class PackageManagerTests extends AndroidTestCase { private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) { return installFromRawResource("install.apk", R.raw.install, flags, cleanUp, - false, -1); + false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void clearSecureContainersForPkg(String pkgName) { @@ -327,7 +371,8 @@ public class PackageManagerTests extends AndroidTestCase { * PackageManager api to install it. */ private InstallParams installFromRawResource(String outFileName, - int rawResId, int flags, boolean cleanUp, boolean fail, int result) { + int rawResId, int flags, boolean cleanUp, boolean fail, int result, + int expInstallLocation) { File filesDir = mContext.getFilesDir(); File outFile = new File(filesDir, outFileName); Uri packageURI = getInstallablePackage(rawResId, outFile); @@ -337,9 +382,7 @@ public class PackageManagerTests extends AndroidTestCase { // Make sure the package doesn't exist getPm().deletePackage(pkg.packageName, null, 0); // Clean up the containers as well - if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { - clearSecureContainersForPkg(pkg.packageName); - } + clearSecureContainersForPkg(pkg.packageName); try { try { if (fail) { @@ -351,7 +394,7 @@ public class PackageManagerTests extends AndroidTestCase { assertTrue(invokeInstallPackage(packageURI, flags, pkg.packageName, receiver)); // Verify installed information - assertInstall(pkg.packageName, flags); + assertInstall(pkg, flags, expInstallLocation); ip = new InstallParams(pkg, outFileName, packageURI); } } catch (Exception e) { @@ -360,7 +403,7 @@ public class PackageManagerTests extends AndroidTestCase { return ip; } finally { if (cleanUp) { - cleanUpInstall(ip); + //cleanUpInstall(ip); } } } @@ -443,6 +486,7 @@ public class PackageManagerTests extends AndroidTestCase { public void replaceFromRawResource(int flags) { InstallParams ip = sampleInstallFromRawResource(flags, false); boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0); + Log.i(TAG, "replace=" + replace); GenericReceiver receiver; if (replace) { receiver = new ReplaceReceiver(ip.pkg.packageName); @@ -455,7 +499,7 @@ public class PackageManagerTests extends AndroidTestCase { assertEquals(invokeInstallPackage(ip.packageURI, flags, ip.pkg.packageName, receiver), replace); if (replace) { - assertInstall(ip.pkg.packageName, flags); + assertInstall(ip.pkg, flags, ip.pkg.installLocation); } } catch (Exception e) { failStr("Failed with exception : " + e); @@ -738,51 +782,73 @@ public class PackageManagerTests extends AndroidTestCase { } } + class StorageListener extends StorageEventListener { + String oldState; + String newState; + String path; + private boolean doneFlag = false; + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState); + synchronized (this) { + this.oldState = oldState; + this.newState = newState; + this.path = path; + doneFlag = true; + notifyAll(); + } + } + + public boolean isDone() { + return doneFlag; + } + } + private boolean unmountMedia() { if (!getMediaState()) { return true; } + String path = Environment.getExternalStorageDirectory().toString(); + StorageListener observer = new StorageListener(); + StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + sm.registerListener(observer); try { - String mPath = Environment.getExternalStorageDirectory().toString(); - int ret = getMs().unmountVolume(mPath, false); - return ret == StorageResultCode.OperationSucceeded; - } catch (RemoteException e) { - return true; + // Wait on observer + synchronized(observer) { + getMs().unmountVolume(path, false); + long waitTime = 0; + while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!observer.isDone()) { + throw new Exception("Timed out waiting for packageInstalled callback"); + } + return true; + } + } catch (Exception e) { + return false; + } finally { + sm.unregisterListener(observer); } } - /* - * Install package on sdcard. Unmount and then mount the media. - * (Use PackageManagerService private api for now) - * Make sure the installed package is available. - * STOPSHIP will uncomment when MountService api's to mount/unmount - * are made asynchronous. - */ - public void xxxtestMountSdNormalInternal() { - assertTrue(mountFromRawResource()); - } - private boolean mountFromRawResource() { // Install pkg on sdcard - InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL | - PackageManager.INSTALL_REPLACE_EXISTING, false); + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, false); if (localLOGV) Log.i(TAG, "Installed pkg on sdcard"); boolean origState = getMediaState(); + boolean registeredReceiver = false; SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName}); try { if (localLOGV) Log.i(TAG, "Unmounting media"); // Unmount media assertTrue(unmountMedia()); if (localLOGV) Log.i(TAG, "Unmounted media"); - try { - if (localLOGV) Log.i(TAG, "Sleeping for 10 second"); - Thread.sleep(10*1000); - } catch (InterruptedException e) { - failStr(e); - } // Register receiver here PackageManager pm = getPm(); mContext.registerReceiver(receiver, receiver.filter); + registeredReceiver = true; // Wait on receiver synchronized (receiver) { @@ -807,7 +873,7 @@ public class PackageManagerTests extends AndroidTestCase { failStr(e); return false; } finally { - mContext.unregisterReceiver(receiver); + if (registeredReceiver) mContext.unregisterReceiver(receiver); // Restore original media state if (origState) { mountMedia(); @@ -819,6 +885,17 @@ public class PackageManagerTests extends AndroidTestCase { } } + /* + * Install package on sdcard. Unmount and then mount the media. + * (Use PackageManagerService private api for now) + * Make sure the installed package is available. + * STOPSHIP will uncomment when MountService api's to mount/unmount + * are made asynchronous. + */ + public void xxxtestMountSdNormalInternal() { + assertTrue(mountFromRawResource()); + } + void cleanUpInstall(InstallParams ip) { if (ip == null) { return; @@ -834,28 +911,29 @@ public class PackageManagerTests extends AndroidTestCase { public void testManifestInstallLocationInternal() { installFromRawResource("install.apk", R.raw.install_loc_internal, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } public void testManifestInstallLocationSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } public void testManifestInstallLocationAuto() { installFromRawResource("install.apk", R.raw.install_loc_auto, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void testManifestInstallLocationUnspecified() { installFromRawResource("install.apk", R.raw.install_loc_unspecified, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void testManifestInstallLocationFwdLockedSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_FORWARD_LOCK, true, true, - PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION); + PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION, + PackageInfo.INSTALL_LOCATION_AUTO); } public void xxxtestClearAllSecureContainers() { diff --git a/common/tests/src/com/android/common/PatternsTest.java b/tests/AndroidTests/src/com/android/unit_tests/PatternsTest.java index 9e2ad58..0edcd6d 100644 --- a/common/tests/src/com/android/common/PatternsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PatternsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * 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. @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.common; +package com.android.unit_tests; import android.test.suitebuilder.annotation.SmallTest; -import junit.framework.TestCase; +import android.util.Patterns; import java.util.regex.Matcher; -import java.util.regex.Pattern; + +import junit.framework.TestCase; public class PatternsTest extends TestCase { diff --git a/tests/CoreTests/android/core/MiscRegressionTest.java b/tests/CoreTests/android/core/MiscRegressionTest.java index 8fe064c..8281db0 100644 --- a/tests/CoreTests/android/core/MiscRegressionTest.java +++ b/tests/CoreTests/android/core/MiscRegressionTest.java @@ -110,6 +110,16 @@ public class MiscRegressionTest extends TestCase { Logger.global.finest("This has logging Level.FINEST, should become VERBOSE"); } + // Regression test for Issue 5697: + // getContextClassLoader returns a non-application classloader + // http://code.google.com/p/android/issues/detail?id=5697 + // + @MediumTest + public void testJavaContextClassLoader() throws Exception { + Assert.assertNotNull("Must hava a Java context ClassLoader", + Thread.currentThread().getContextClassLoader()); + } + // Regression test for #1045939: Different output for Method.toString() @SmallTest public void testMethodToString() { diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index ac98054..6ceb0f9 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -81,6 +81,30 @@ public class FileFilter { }; static void fillIgnoreResultList() { + // This first block of tests are for HTML5 features, for which Android + // should pass all tests. They are skipped only temporarily. + // TODO: Fix these failing tests and remove them from this list. + ignoreResultList.add("fast/dom/Geolocation/callback-exception.html"); // exception output incorrect with V8 + ignoreResultList.add("http/tests/appcache/auth.html"); // file not found + ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found + ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found + ignoreResultList.add("http/tests/appcache/destroyed-frame.html"); // file not found + ignoreResultList.add("http/tests/appcache/detached-iframe.html"); // file not found + ignoreResultList.add("http/tests/appcache/different-scheme.html"); // file not found + ignoreResultList.add("http/tests/appcache/disabled.html"); // not found + ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky + ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states + ignoreResultList.add("http/tests/appcache/local-content.html"); // text diff + ignoreResultList.add("http/tests/appcache/max-size.html"); // no layoutTestController.setAppCacheMaximumSize + ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky + ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found + ignoreResultList.add("storage/database-lock-after-reload.html"); // failure + ignoreResultList.add("storage/domstorage/localstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8 + ignoreResultList.add("storage/domstorage/sessionstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8 + ignoreResultList.add("storage/statement-error-callback.html"); // expected line number diff in V8 only + ignoreResultList.add("storage/transaction-error-callback.html"); // expected line number diff in V8 only + ignoreResultList.add("storage/transaction-callback-exception-crash.html"); // expected line number diff in V8 only + ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707 ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us ignoreResultList.add("fast/dom/Window/Plug-ins.html"); // need test plugin diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index 8983612..634d683 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -339,14 +339,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh this.mTestList = new Vector<String>(); // Read settings - try { - this.mTestPathPrefix = - (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath(); - } catch (IOException e) { - Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage()); - return; - } - + this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath(); this.mRebaselineResults = runner.mRebaseline; int timeout = runner.mTimeoutInMillis; @@ -395,11 +388,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh if (runner.mTestPath != null) { test_path += runner.mTestPath; } - try { - test_path = new File(test_path).getCanonicalPath(); - } catch (IOException e) { - Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage()); - } + test_path = new File(test_path).getAbsolutePath(); Log.v("LayoutTestsAutoTest", " Test path : " + test_path); return test_path; diff --git a/tests/LocationTracker/Android.mk b/tests/LocationTracker/Android.mk new file mode 100644 index 0000000..b142d22 --- /dev/null +++ b/tests/LocationTracker/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := LocationTracker + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/LocationTracker/AndroidManifest.xml b/tests/LocationTracker/AndroidManifest.xml new file mode 100644 index 0000000..dc7ea99 --- /dev/null +++ b/tests/LocationTracker/AndroidManifest.xml @@ -0,0 +1,30 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.locationtracker"> + + <!-- Permissions for the Location Service --> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + + <!-- Permission for wifi --> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> + + <!-- give the location tracker ability to induce device insomnia --> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <!-- Permission for SD card --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <application android:label="@string/app_label"> + <activity android:name="TrackerActivity" android:label="Location Tracker"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <service android:name=".TrackerService" /> + <activity android:label="@string/settings_menu" android:name="SettingsActivity" /> + <provider android:name=".data.TrackerProvider" + android:authorities="com.android.locationtracker" /> + </application> + +</manifest> diff --git a/tests/LocationTracker/res/layout/entrylist_item.xml b/tests/LocationTracker/res/layout/entrylist_item.xml new file mode 100644 index 0000000..d2a8033 --- /dev/null +++ b/tests/LocationTracker/res/layout/entrylist_item.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2006-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/entrylist_item" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" +/> diff --git a/tests/LocationTracker/res/menu/menu.xml b/tests/LocationTracker/res/menu/menu.xml new file mode 100644 index 0000000..05d13fd --- /dev/null +++ b/tests/LocationTracker/res/menu/menu.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** +* Copyright (c) 2008 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/start_service_menu" + android:title="@string/start_service_menu" /> + <item android:id="@+id/stop_service_menu" + android:title="@string/stop_service_menu" /> + <item android:id="@+id/settings_menu" + android:title="@string/settings_menu" /> + <item android:id="@+id/export_sub_menu" + android:title="@string/export_sub_menu"> + <menu> + <item android:id="@+id/export_kml" + android:title="@string/export_kml" /> + <item android:id="@+id/export_csv" + android:title="@string/export_csv" /> + </menu> + </item> + <item android:title="@string/clear_data" + android:id="@+id/clear_data_menu"/> +</menu> diff --git a/tests/LocationTracker/res/values/strings.xml b/tests/LocationTracker/res/values/strings.xml new file mode 100644 index 0000000..ea6bf2d --- /dev/null +++ b/tests/LocationTracker/res/values/strings.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +* Copyright (C) 2008 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +--> + +<resources> + <string name="start_service_menu">Start Service</string> + <string name="stop_service_menu">Stop Service</string> + <string name="settings_menu">Settings</string> + <string name="update_preference">Update frequency</string> + <string name="title_mintime_preference">Minimum update time</string> + <string name="summary_mintime_preference">The suggested minimum time interval for location updates, in seconds</string> + <string name="dialog_title_mintime_preference">Minimum update time</string> + <string name="title_mindistance_preference">Minimum distance</string> + <string name="summary_mindistance_preference">Minimum distance interval for location updates, in meters</string> + <string name="dialog_title_mindistance_preference">Minimum distance</string> + <string name="provider_preferences">Location providers</string> + <string name="title_network_preference">Network location</string> + <string name="summary_network_preference">Listen for updates to network location (Wi-Fi/cellid)</string> + <string name="title_gps_preference">GPS location</string> + <string name="summary_gps_preference">Listen for updates to GPS location</string> + <string name="title_signal_preference">Signal strength</string> + <string name="summary_signal_preference">Listen for updates to signal strength</string> + <string name="advanced_preferences">Advanced</string> + <string name="title_advanced_log_preference">Location debug logging</string> + <string name="summary_advanced_preference">Logs detailed location data, only relevant for location/test engineers</string> + <string name="app_label">Location Tracker</string> + <string name="export_sub_menu">Export\u2026</string> + <string name="export_kml">Export As KML</string> + <string name="export_csv">Export As CSV</string> + <string name="clear_data">Clear data</string> + <string name="delete_confirm">All current tracking data will be deleted.</string> + <string name="confirm_title">Clear data</string> +</resources> diff --git a/tests/LocationTracker/res/xml/preferences.xml b/tests/LocationTracker/res/xml/preferences.xml new file mode 100755 index 0000000..61d4817 --- /dev/null +++ b/tests/LocationTracker/res/xml/preferences.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- The Location preferences UI --> +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android"> + + <PreferenceCategory android:title="@string/update_preference"> + <EditTextPreference android:key="mintime_preference" + android:defaultValue="0" + android:title="@string/title_mintime_preference" + android:summary="@string/summary_mintime_preference" + android:dialogTitle="@string/dialog_title_mintime_preference" /> + + <EditTextPreference android:key="mindistance_preference" + android:defaultValue="0" + android:title="@string/title_mindistance_preference" + android:summary="@string/summary_mindistance_preference" + android:dialogTitle="@string/dialog_title_mindistance_preference" /> + + </PreferenceCategory> + + <PreferenceCategory android:title="@string/provider_preferences"> + + <CheckBoxPreference android:key="network_preference" + android:defaultValue="true" + android:title="@string/title_network_preference" + android:summary="@string/summary_network_preference" /> + + <CheckBoxPreference android:key="gps_preference" + android:defaultValue="true" + android:title="@string/title_gps_preference" + android:summary="@string/summary_gps_preference" /> + + <CheckBoxPreference android:key="signal_preference" + android:defaultValue="false" + android:title="@string/title_signal_preference" + android:summary="@string/summary_signal_preference" /> + + </PreferenceCategory> + + <PreferenceCategory android:title="@string/advanced_preferences"> + + <CheckBoxPreference android:key="advanced_log_preference" + android:defaultValue="false" + android:title="@string/title_advanced_log_preference" + android:summary="@string/summary_advanced_preference" /> + </PreferenceCategory> + +</PreferenceScreen> diff --git a/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java new file mode 100755 index 0000000..cb77118 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java @@ -0,0 +1,35 @@ +/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Settings preference screen for location tracker
+ */
+public class SettingsActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java new file mode 100644 index 0000000..98d0a50 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.locationtracker; + +import com.android.locationtracker.data.DateUtils; +import com.android.locationtracker.data.TrackerDataHelper; +import com.android.locationtracker.data.TrackerListHelper; + +import android.app.AlertDialog; +import android.app.ListActivity; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.database.Cursor; +import android.location.LocationManager; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.Toast; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +/** + * Activity for location tracker service + * + * Contains facilities for starting and + * stopping location tracker service, as well as displaying the current location + * data + * + * Future enhancements: + * - export data as dB + * - enable/disable "start service" and "stop service" menu items as + * appropriate + */ +public class TrackerActivity extends ListActivity { + + static final String LOG_TAG = "LocationTracker"; + + private TrackerListHelper mDataHelper; + + /** + * Retrieves and displays the currently logged location data from file + * + * @param icicle + */ + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mDataHelper = new TrackerListHelper(this); + mDataHelper.bindListUI(R.layout.entrylist_item); + } + + /** + * Builds the menu + * + * @param menu - menu to add items to + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.menu, menu); + return true; + } + + /** + * Handles menu item selection + * + * @param item - the selected menu item + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.start_service_menu: { + startService(new Intent(TrackerActivity.this, + TrackerService.class)); + break; + } + case R.id.stop_service_menu: { + stopService(new Intent(TrackerActivity.this, + TrackerService.class)); + break; + } + case R.id.settings_menu: { + launchSettings(); + break; + } + case R.id.export_kml: { + exportKML(); + break; + } + case R.id.export_csv: { + exportCSV(); + break; + } + case R.id.clear_data_menu: { + clearData(); + break; + } + } + return super.onOptionsItemSelected(item); + } + + private void clearData() { + Runnable clearAction = new Runnable() { + public void run() { + TrackerDataHelper helper = + new TrackerDataHelper(TrackerActivity.this); + helper.deleteAll(); + } + }; + showConfirm(R.string.delete_confirm, clearAction); + } + + private void showConfirm(int textId, final Runnable onConfirmAction) { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + dialogBuilder.setTitle(R.string.confirm_title); + dialogBuilder.setMessage(textId); + dialogBuilder.setPositiveButton(android.R.string.ok, + new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + onConfirmAction.run(); + } + }); + dialogBuilder.setNegativeButton(android.R.string.cancel, + new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // do nothing + } + }); + dialogBuilder.show(); + } + + private void exportCSV() { + String exportFileName = getUniqueFileName("csv"); + exportFile(null, exportFileName, new TrackerDataHelper(this, + TrackerDataHelper.CSV_FORMATTER)); + } + + private void exportKML() { + String exportFileName = getUniqueFileName( + LocationManager.NETWORK_PROVIDER + ".kml"); + exportFile(LocationManager.NETWORK_PROVIDER, exportFileName, + new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER)); + exportFileName = getUniqueFileName( + LocationManager.GPS_PROVIDER + ".kml"); + exportFile(LocationManager.GPS_PROVIDER, exportFileName, + new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER)); + } + + private void exportFile(String tagFilter, + String exportFileName, + TrackerDataHelper trackerData) { + BufferedWriter exportWriter = null; + Cursor cursor = trackerData.query(tagFilter); + try { + exportWriter = new BufferedWriter(new FileWriter(exportFileName)); + exportWriter.write(trackerData.getOutputHeader()); + + String line = null; + + while ((line = trackerData.getNextOutput(cursor)) != null) { + exportWriter.write(line); + } + exportWriter.write(trackerData.getOutputFooter()); + Toast.makeText(this, "Successfully exported data to " + + exportFileName, Toast.LENGTH_SHORT).show(); + + } catch (IOException e) { + Toast.makeText(this, "Error exporting file: " + + e.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); + + Log.e(LOG_TAG, "Error exporting file", e); + } finally { + closeWriter(exportWriter); + if (cursor != null) { + cursor.close(); + } + } + } + + private void closeWriter(Writer exportWriter) { + if (exportWriter != null) { + try { + exportWriter.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "error closing file", e); + } + } + } + + private String getUniqueFileName(String ext) { + File dir = new File("/sdcard/locationtracker"); + if (!dir.exists()) { + dir.mkdir(); + } + return "/sdcard/locationtracker/tracking-" + + DateUtils.getCurrentTimestamp() + "." + ext; + } + + private void launchSettings() { + Intent settingsIntent = new Intent(); + settingsIntent.setClass(this, SettingsActivity.class); + startActivity(settingsIntent); + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java new file mode 100644 index 0000000..5b75653 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.locationtracker; + +import com.android.locationtracker.data.TrackerDataHelper; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.net.ConnectivityManager; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.IBinder; +import android.preference.PreferenceManager; +import android.telephony.CellLocation; +import android.telephony.PhoneStateListener; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.telephony.cdma.CdmaCellLocation; +import android.telephony.gsm.GsmCellLocation; +import android.util.Log; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Location Tracking service + * + * Records location updates for all registered location providers, and cell + * location updates + */ +public class TrackerService extends Service { + + private List<LocationTrackingListener> mListeners; + + private static final String LOG_TAG = TrackerActivity.LOG_TAG; + + // controls which location providers to track + private Set<String> mTrackedProviders; + + private TrackerDataHelper mTrackerData; + + private TelephonyManager mTelephonyManager; + private Location mNetworkLocation; + + // Handlers and Receivers for phone and network state + private NetworkStateBroadcastReceiver mNetwork; + private static final String CELL_PROVIDER_TAG = "cell"; + // signal strength updates + private static final String SIGNAL_PROVIDER_TAG = "signal"; + private static final String WIFI_PROVIDER_TAG = "wifi"; + // tracking tag for data connectivity issues + private static final String DATA_CONN_PROVIDER_TAG = "data"; + + // preference constants + private static final String MIN_TIME_PREF = "mintime_preference"; + private static final String MIN_DIS_PREF = "mindistance_preference"; + private static final String GPS_PREF = "gps_preference"; + private static final String NETWORK_PREF = "network_preference"; + private static final String SIGNAL_PREF = "signal_preference"; + private static final String DEBUG_PREF = "advanced_log_preference"; + + private PreferenceListener mPrefListener; + + public TrackerService() { + } + + @Override + public IBinder onBind(Intent intent) { + // ignore - nothing to do + return null; + } + + /** + * registers location listeners + * + * @param intent + * @param startId + */ + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + mNetworkLocation = null; + + initLocationListeners(); + Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT); + } + + private synchronized void initLocationListeners() { + mTrackerData = new TrackerDataHelper(this); + LocationManager lm = getLocationManager(); + + mTrackedProviders = getTrackedProviders(); + + List<String> locationProviders = lm.getAllProviders(); + mListeners = new ArrayList<LocationTrackingListener>( + locationProviders.size()); + + long minUpdateTime = getLocationUpdateTime(); + float minDistance = getLocationMinDistance(); + + for (String providerName : locationProviders) { + if (mTrackedProviders.contains(providerName)) { + Log.d(LOG_TAG, "Adding location listener for provider " + + providerName); + if (doDebugLogging()) { + mTrackerData.writeEntry("init", String.format( + "start listening to %s : %d ms; %f meters", + providerName, minUpdateTime, minDistance)); + } + LocationTrackingListener listener = + new LocationTrackingListener(); + lm.requestLocationUpdates(providerName, minUpdateTime, + minDistance, listener); + mListeners.add(listener); + } + } + mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); + + if (doDebugLogging()) { + // register for cell location updates + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION); + + // Register for Network (Wifi or Mobile) updates + mNetwork = new NetworkStateBroadcastReceiver(); + IntentFilter mIntentFilter; + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + Log.d(LOG_TAG, "registering receiver"); + registerReceiver(mNetwork, mIntentFilter); + } + + if (trackSignalStrength()) { + mTelephonyManager.listen(mPhoneStateListener, + PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); + } + + // register for preference changes, so we can restart listeners on + // pref changes + mPrefListener = new PreferenceListener(); + getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener); + } + + private Set<String> getTrackedProviders() { + Set<String> providerSet = new HashSet<String>(); + + if (trackGPS()) { + providerSet.add(LocationManager.GPS_PROVIDER); + } + if (trackNetwork()) { + providerSet.add(LocationManager.NETWORK_PROVIDER); + } + return providerSet; + } + + private SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(this); + } + + private boolean trackNetwork() { + return getPreferences().getBoolean(NETWORK_PREF, true); + } + + private boolean trackGPS() { + return getPreferences().getBoolean(GPS_PREF, true); + } + + private boolean doDebugLogging() { + return getPreferences().getBoolean(DEBUG_PREF, true); + } + + private boolean trackSignalStrength() { + return getPreferences().getBoolean(SIGNAL_PREF, true); + } + + private float getLocationMinDistance() { + try { + String disString = getPreferences().getString(MIN_DIS_PREF, "0"); + return Float.parseFloat(disString); + } + catch (NumberFormatException e) { + Log.e(LOG_TAG, "Invalid preference for location min distance", e); + } + return 0; + } + + private long getLocationUpdateTime() { + try { + String timeString = getPreferences().getString(MIN_TIME_PREF, "0"); + long secondsTime = Long.valueOf(timeString); + return secondsTime * 1000; + } + catch (NumberFormatException e) { + Log.e(LOG_TAG, "Invalid preference for location min time", e); + } + return 0; + } + + /** + * Shuts down this service + */ + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(LOG_TAG, "Removing location listeners"); + stopListeners(); + Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT); + } + + /** + * De-registers all location listeners, closes persistent storage + */ + protected synchronized void stopListeners() { + LocationManager lm = getLocationManager(); + if (mListeners != null) { + for (LocationTrackingListener listener : mListeners) { + lm.removeUpdates(listener); + } + mListeners.clear(); + } + mListeners = null; + + // stop cell state listener + if (mTelephonyManager != null) { + mTelephonyManager.listen(mPhoneStateListener, 0); + } + + // stop network/wifi listener + if (mNetwork != null) { + unregisterReceiver(mNetwork); + } + mNetwork = null; + + mTrackerData = null; + if (mPrefListener != null) { + getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener); + mPrefListener = null; + } + } + + private LocationManager getLocationManager() { + return (LocationManager) getSystemService(Context.LOCATION_SERVICE); + } + + /** + * Determine the current distance from given location to the last + * approximated network location + * + * @param location - new location + * + * @return float distance in meters + */ + private synchronized float getDistanceFromNetwork(Location location) { + float value = 0; + if (mNetworkLocation != null) { + value = location.distanceTo(mNetworkLocation); + } + if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) { + mNetworkLocation = location; + } + return value; + } + + private class LocationTrackingListener implements LocationListener { + + /** + * Writes details of location update to tracking file, including + * recording the distance between this location update and the last + * network location update + * + * @param location - new location + */ + public void onLocationChanged(Location location) { + if (location == null) { + return; + } + float distance = getDistanceFromNetwork(location); + mTrackerData.writeEntry(location, distance); + } + + /** + * Writes update to tracking file + * + * @param provider - name of disabled provider + */ + public void onProviderDisabled(String provider) { + if (doDebugLogging()) { + mTrackerData.writeEntry(provider, "provider disabled"); + } + } + + /** + * Writes update to tracking file + * + * @param provider - name of enabled provider + */ + public void onProviderEnabled(String provider) { + if (doDebugLogging()) { + mTrackerData.writeEntry(provider, "provider enabled"); + } + } + + /** + * Writes update to tracking file + * + * @param provider - name of provider whose status changed + * @param status - new status + * @param extras - optional set of extra status messages + */ + public void onStatusChanged(String provider, int status, Bundle extras) { + if (doDebugLogging()) { + mTrackerData.writeEntry(provider, "status change: " + status); + } + } + } + + PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onCellLocationChanged(CellLocation location) { + try { + if (location instanceof GsmCellLocation) { + GsmCellLocation cellLocation = (GsmCellLocation)location; + String updateMsg = "cid=" + cellLocation.getCid() + + ", lac=" + cellLocation.getLac(); + mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg); + } else if (location instanceof CdmaCellLocation) { + CdmaCellLocation cellLocation = (CdmaCellLocation)location; + String updateMsg = "BID=" + cellLocation.getBaseStationId() + + ", SID=" + cellLocation.getSystemId() + + ", NID=" + cellLocation.getNetworkId() + + ", lat=" + cellLocation.getBaseStationLatitude() + + ", long=" + cellLocation.getBaseStationLongitude() + + ", SID=" + cellLocation.getSystemId() + + ", NID=" + cellLocation.getNetworkId(); + mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg); + } + } catch (Exception e) { + Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e); + } + } + + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { + String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm(); + mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg); + } else if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { + String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength(); + mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg); + } + } + }; + + /** + * Listener + recorder for mobile or wifi updates + */ + private class NetworkStateBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + WifiManager wifiManager = + (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + List<ScanResult> wifiScanResults = wifiManager.getScanResults(); + String updateMsg = "num scan results=" + + (wifiScanResults == null ? "0" : wifiScanResults.size()); + mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg); + + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + String updateMsg; + boolean noConnectivity = + intent.getBooleanExtra( + ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + if (noConnectivity) { + updateMsg = "no connectivity"; + } + else { + updateMsg = "connection available"; + } + mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg); + + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN); + + String stateString = "unknown"; + switch (state) { + case WifiManager.WIFI_STATE_DISABLED: + stateString = "disabled"; + break; + case WifiManager.WIFI_STATE_DISABLING: + stateString = "disabling"; + break; + case WifiManager.WIFI_STATE_ENABLED: + stateString = "enabled"; + break; + case WifiManager.WIFI_STATE_ENABLING: + stateString = "enabling"; + break; + } + mTrackerData.writeEntry(WIFI_PROVIDER_TAG, + "state = " + stateString); + } + } + } + + private class PreferenceListener implements OnSharedPreferenceChangeListener { + + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + Log.d(LOG_TAG, "restarting listeners due to preference change"); + synchronized (TrackerService.this) { + stopListeners(); + initLocationListeners(); + } + } + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java new file mode 100644 index 0000000..672ce28 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.locationtracker.data; + +import com.android.locationtracker.data.TrackerEntry.EntryType; + +/** + * Formats tracker data as CSV output + */ +class CSVFormatter implements IFormatter { + + private static final String DELIMITER = ", "; + + public String getHeader() { + StringBuilder csvBuilder = new StringBuilder(); + for (String col : TrackerEntry.ATTRIBUTES) { + // skip type and id column + if (!TrackerEntry.ENTRY_TYPE.equals(col) && + !TrackerEntry.ID_COL.equals(col)) { + csvBuilder.append(col); + csvBuilder.append(DELIMITER); + } + } + csvBuilder.append("\n"); + return csvBuilder.toString(); + } + + public String getOutput(TrackerEntry entry) { + StringBuilder rowOutput = new StringBuilder(); + // these must match order of columns added in getHeader + rowOutput.append(entry.getTimestamp()); + rowOutput.append(DELIMITER); + rowOutput.append(entry.getTag()); + rowOutput.append(DELIMITER); + //rowOutput.append(entry.getType()); + //rowOutput.append(DELIMITER); + if (entry.getType() == EntryType.LOCATION_TYPE) { + if (entry.getLocation().hasAccuracy()) { + rowOutput.append(entry.getLocation().getAccuracy()); + } + rowOutput.append(DELIMITER); + rowOutput.append(entry.getLocation().getLatitude()); + rowOutput.append(DELIMITER); + rowOutput.append(entry.getLocation().getLongitude()); + rowOutput.append(DELIMITER); + if (entry.getLocation().hasAltitude()) { + rowOutput.append(entry.getLocation().getAltitude()); + } + rowOutput.append(DELIMITER); + if (entry.getLocation().hasSpeed()) { + rowOutput.append(entry.getLocation().getSpeed()); + } + rowOutput.append(DELIMITER); + if (entry.getLocation().hasBearing()) { + rowOutput.append(entry.getLocation().getBearing()); + } + rowOutput.append(DELIMITER); + rowOutput.append(entry.getDistFromNetLocation()); + rowOutput.append(DELIMITER); + rowOutput.append(DateUtils.getKMLTimestamp(entry.getLocation() + .getTime())); + rowOutput.append(DELIMITER); + } + rowOutput.append(entry.getLogMsg()); + rowOutput.append("\n"); + return rowOutput.toString(); + } + + public String getFooter() { + // not needed, return empty string + return ""; + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java new file mode 100644 index 0000000..13226bd --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.locationtracker.data; + +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Provides formatting date as string utilities + */ +public class DateUtils { + + private DateUtils() { + + } + + /** + * Returns timestamp given by param in KML format ie yyyy-mm-ddThh:mm:ssZ, + * where T is the separator between the date and the time and the time zone + * is Z (for UTC) + * + * @return KML timestamp as String + */ + public static String getKMLTimestamp(long when) { + TimeZone tz = TimeZone.getTimeZone("GMT"); + Calendar c = Calendar.getInstance(tz); + c.setTimeInMillis(when); + return String.format("%tY-%tm-%tdT%tH:%tM:%tSZ", c, c, c, c, c, c); + } + + /** + * Helper version of getKMLTimestamp, that returns timestamp for current + * time + */ + public static String getCurrentKMLTimestamp() { + return getKMLTimestamp(System.currentTimeMillis()); + } + + /** + * Returns timestamp in following format: yyyy-mm-dd-hh-mm-ss + */ + public static String getCurrentTimestamp() { + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(System.currentTimeMillis()); + return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c); + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java new file mode 100644 index 0000000..af0b5ed --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.locationtracker.data; + +/** + * interface for formatting tracker data output + */ +interface IFormatter { + + String getHeader(); + String getOutput(TrackerEntry entry); + String getFooter(); +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java new file mode 100644 index 0000000..a5e1816 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.locationtracker.data; + +import com.android.locationtracker.data.TrackerEntry.EntryType; + +import android.location.Location; + +/** + * Formats tracker data as KML output + */ +class KMLFormatter implements IFormatter { + + public String getHeader() { + LineBuilder builder = new LineBuilder(); + builder.addLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + builder.addLine("<kml xmlns=\"http://earth.google.com/kml/2.2\">"); + builder.addLine("<Document>"); + return builder.toString(); + } + + public String getFooter() { + LineBuilder builder = new LineBuilder(); + builder.addLine("</Document>"); + builder.addLine("</kml>"); + return builder.toString(); + } + + public String getOutput(TrackerEntry entry) { + LineBuilder builder = new LineBuilder(); + + if (entry.getType() == EntryType.LOCATION_TYPE) { + + Location loc = entry.getLocation(); + builder.addLine("<Placemark>"); + builder.addLine("<description>"); + builder.addLine("accuracy = " + loc.getAccuracy()); + builder.addLine("distance from last network location = " + + entry.getDistFromNetLocation()); + builder.addLine("</description>"); + builder.addLine("<TimeStamp>"); + builder.addLine("<when>" + entry.getTimestamp() + "</when>"); + builder.addLine("</TimeStamp>"); + builder.addLine("<Point>"); + builder.addLine("<coordinates>"); + builder.addLine(loc.getLongitude() + "," + loc.getLatitude() + "," + + loc.getAltitude()); + builder.addLine("</coordinates>"); + builder.addLine("</Point>"); + builder.addLine("</Placemark>"); + } + return builder.toString(); + } + + private static class LineBuilder { + private StringBuilder mBuilder; + + public LineBuilder() { + mBuilder = new StringBuilder(); + } + + public void addLine(String line) { + mBuilder.append(line); + mBuilder.append("\n"); + } + + @Override + public String toString() { + return mBuilder.toString(); + } + + } + +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java new file mode 100644 index 0000000..a3838df --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.locationtracker.data; + +import android.content.Context; +import android.database.Cursor; +import android.location.Location; + +/** + * Helper class for writing and retrieving data using the TrackerProvider + * content provider + * + */ +public class TrackerDataHelper { + + private Context mContext; + /** formats data output */ + protected IFormatter mFormatter; + + /** formats output as Comma separated value CSV file */ + public static final IFormatter CSV_FORMATTER = new CSVFormatter(); + /** formats output as KML file */ + public static final IFormatter KML_FORMATTER = new KMLFormatter(); + /** provides no formatting */ + public static final IFormatter NO_FORMATTER = new IFormatter() { + public String getFooter() { + return ""; + } + + public String getHeader() { + return ""; + } + + public String getOutput(TrackerEntry entry) { + return ""; + } + }; + + /** + * Creates instance + * + * @param context - content context + * @param formatter - formats the output from the get*Output* methods + */ + public TrackerDataHelper(Context context, IFormatter formatter) { + mContext = context; + mFormatter = formatter; + } + + /** + * Creates a instance with no output formatting capabilities. Useful for + * clients that require write-only access + */ + public TrackerDataHelper(Context context) { + this(context, NO_FORMATTER); + } + + /** + * insert given TrackerEntry into content provider + */ + void writeEntry(TrackerEntry entry) { + mContext.getContentResolver().insert(TrackerProvider.CONTENT_URI, + entry.getAsContentValues()); + } + + /** + * insert given location into tracker data + */ + public void writeEntry(Location loc, float distFromNetLoc) { + writeEntry(TrackerEntry.createEntry(loc, distFromNetLoc)); + } + + /** + * insert given log message into tracker data + */ + public void writeEntry(String tag, String logMsg) { + writeEntry(TrackerEntry.createEntry(tag, logMsg)); + } + + /** + * Deletes all tracker entries + */ + public void deleteAll() { + mContext.getContentResolver().delete(TrackerProvider.CONTENT_URI, null, + null); + } + + /** + * Query tracker data, filtering by given tag + * + * @param tag + * @return Cursor to data + */ + public Cursor query(String tag, int limit) { + String selection = (tag == null ? null : TrackerEntry.TAG + "=?"); + String[] selectionArgs = (tag == null ? null : new String[] {tag}); + Cursor cursor = mContext.getContentResolver().query( + TrackerProvider.CONTENT_URI, TrackerEntry.ATTRIBUTES, + selection, selectionArgs, null); + if (cursor == null) { + return cursor; + } + int pos = (cursor.getCount() < limit ? 0 : cursor.getCount() - limit); + cursor.moveToPosition(pos); + return cursor; + } + + /** + * Retrieves a cursor that starts at the last limit rows + * + * @param limit + * @return a cursor, null if bad things happened + */ + public Cursor query(int limit) { + return query(null, limit); + } + + /** + * Query tracker data, filtering by given tag. mo limit to number of rows + * returned + * + * @param tag + * @return Cursor to data + */ + public Cursor query(String tag) { + return query(tag, Integer.MAX_VALUE); + } + + /** + * Returns the output header particular to the associated formatter + */ + public String getOutputHeader() { + return mFormatter.getHeader(); + } + + /** + * Returns the output footer particular to the associated formatter + */ + public String getOutputFooter() { + return mFormatter.getFooter(); + } + + /** + * Helper method which converts row referenced by given cursor to a string + * output + * + * @param cursor + * @return CharSequence output, null if given cursor is invalid or no more + * data + */ + public String getNextOutput(Cursor cursor) { + if (cursor == null || cursor.isAfterLast()) { + return null; + } + String output = mFormatter.getOutput(TrackerEntry.createEntry(cursor)); + cursor.moveToNext(); + return output; + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java new file mode 100644 index 0000000..b2741f6 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.locationtracker.data; + +import android.content.ContentValues; +import android.database.Cursor; +import android.location.Location; + + +/** + * Class that holds a tracker entry. An entry can be either a valid location, or + * a simple log msg + * + * It provides a concrete data structure to represent data stored in the + * TrackerProvider + */ +class TrackerEntry { + + static final String TIMESTAMP = "Timestamp"; + static final String TAG = "Tag"; + static final String ENTRY_TYPE = "Type"; + + private Location mLocation; + private float mDistFromNetLocation; + private String mLogMsg; + + static final String ID_COL = "_id"; + static final String ACCURACY = "Accuracy"; + static final String LATITUDE = "Latitude"; + static final String LONGITUDE = "Longitude"; + static final String ALTITUDE = "Altitude"; + static final String SPEED = "Speed"; + static final String BEARING = "Bearing"; + static final String DIST_NET_LOCATION = "DistFromNetLocation"; + static final String LOC_TIME = "LocationTime"; + static final String DEBUG_INFO = "DebugInfo"; + + static final String STRING_DATA = "STRING"; + static final String INT_DATA = "INTEGER"; + static final String REAL_DATA = "REAL"; + static final String BLOB_DATA = "BLOB"; + + static final String[] ATTRIBUTES = { + ID_COL, TIMESTAMP, TAG, ENTRY_TYPE, ACCURACY, LATITUDE, LONGITUDE, + ALTITUDE, SPEED, BEARING, DIST_NET_LOCATION, LOC_TIME, DEBUG_INFO}; + static final String[] ATTRIBUTES_DATA_TYPE = { + INT_DATA + " PRIMARY KEY", STRING_DATA, STRING_DATA, STRING_DATA, + REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, + REAL_DATA, INT_DATA, STRING_DATA}; + + // location extra keys used to retrieve debug info + private static final String NETWORK_LOCATION_SOURCE_KEY = + "networkLocationSource"; + private static final String NETWORK_LOCATION_TYPE_KEY = + "networkLocationType"; + private static final String[] LOCATION_DEBUG_KEYS = { + NETWORK_LOCATION_SOURCE_KEY, NETWORK_LOCATION_TYPE_KEY}; + + enum EntryType { + LOCATION_TYPE, LOG_TYPE + } + + private String mTimestamp; + private String mTag; + private EntryType mType; + + private TrackerEntry(String tag, EntryType type) { + mType = type; + mTag = tag; + mLocation = null; + } + + private TrackerEntry(Location loc) { + this(loc.getProvider(), EntryType.LOCATION_TYPE); + mLocation = new Location(loc); + } + + /** + * Creates a TrackerEntry from a Location + */ + static TrackerEntry createEntry(Location loc, float distFromNetLocation) { + TrackerEntry entry = new TrackerEntry(loc); + + String timestampVal = DateUtils.getCurrentKMLTimestamp(); + entry.setTimestamp(timestampVal); + entry.setDistFromNetLocation(distFromNetLocation); + return entry; + } + + /** + * Creates a TrackerEntry from a log msg + */ + static TrackerEntry createEntry(String tag, String msg) { + TrackerEntry entry = new TrackerEntry(tag, EntryType.LOG_TYPE); + String timestampVal = DateUtils.getCurrentKMLTimestamp(); + entry.setTimestamp(timestampVal); + entry.setLogMsg(msg); + return entry; + } + + private void setTimestamp(String timestamp) { + mTimestamp = timestamp; + } + + EntryType getType() { + return mType; + } + + private void setDistFromNetLocation(float distFromNetLocation) { + mDistFromNetLocation = distFromNetLocation; + } + + private void setLogMsg(String msg) { + mLogMsg = msg; + } + + private void setLocation(Location location) { + mLocation = location; + } + + String getTimestamp() { + return mTimestamp; + } + + String getTag() { + return mTag; + } + + Location getLocation() { + return mLocation; + } + + String getLogMsg() { + return mLogMsg; + } + + float getDistFromNetLocation() { + return mDistFromNetLocation; + } + + static void buildCreationString(StringBuilder builder) { + if (ATTRIBUTES.length != ATTRIBUTES_DATA_TYPE.length) { + throw new IllegalArgumentException( + "Attribute length does not match data type length"); + } + for (int i = 0; i < ATTRIBUTES_DATA_TYPE.length; i++) { + if (i != 0) { + builder.append(", "); + } + builder.append(String.format("%s %s", ATTRIBUTES[i], + ATTRIBUTES_DATA_TYPE[i])); + } + } + + ContentValues getAsContentValues() { + ContentValues cValues = new ContentValues(ATTRIBUTES.length); + cValues.put(TIMESTAMP, mTimestamp); + cValues.put(TAG, mTag); + cValues.put(ENTRY_TYPE, mType.toString()); + if (mType == EntryType.LOCATION_TYPE) { + cValues.put(LATITUDE, mLocation.getLatitude()); + cValues.put(LONGITUDE, mLocation.getLongitude()); + if (mLocation.hasAccuracy()) { + cValues.put(ACCURACY, mLocation.getAccuracy()); + } + if (mLocation.hasAltitude()) { + cValues.put(ALTITUDE, mLocation.getAltitude()); + } + if (mLocation.hasSpeed()) { + cValues.put(SPEED, mLocation.getSpeed()); + } + if (mLocation.hasBearing()) { + cValues.put(BEARING, mLocation.getBearing()); + } + cValues.put(DIST_NET_LOCATION, mDistFromNetLocation); + cValues.put(LOC_TIME, mLocation.getTime()); + StringBuilder debugBuilder = new StringBuilder(""); + if (mLocation.getExtras() != null) { + for (String key : LOCATION_DEBUG_KEYS) { + Object val = mLocation.getExtras().get(key); + if (val != null) { + debugBuilder.append(String.format("%s=%s; ", key, val + .toString())); + } + } + } + cValues.put(DEBUG_INFO, debugBuilder.toString()); + } else { + cValues.put(DEBUG_INFO, mLogMsg); + } + return cValues; + } + + static TrackerEntry createEntry(Cursor cursor) { + String timestamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP)); + String tag = cursor.getString(cursor.getColumnIndex(TAG)); + String sType = cursor.getString(cursor.getColumnIndex(ENTRY_TYPE)); + TrackerEntry entry = new TrackerEntry(tag, EntryType.valueOf(sType)); + entry.setTimestamp(timestamp); + if (entry.getType() == EntryType.LOCATION_TYPE) { + Location location = new Location(tag); + location.setLatitude(cursor.getFloat(cursor + .getColumnIndexOrThrow(LATITUDE))); + location.setLongitude(cursor.getFloat(cursor + .getColumnIndexOrThrow(LONGITUDE))); + + Float accuracy = getNullableFloat(cursor, ACCURACY); + if (accuracy != null) { + location.setAccuracy(accuracy); + } + Float altitude = getNullableFloat(cursor, ALTITUDE); + if (altitude != null) { + location.setAltitude(altitude); + } + Float bearing = getNullableFloat(cursor, BEARING); + if (bearing != null) { + location.setBearing(bearing); + } + Float speed = getNullableFloat(cursor, SPEED); + if (speed != null) { + location.setSpeed(speed); + } + location.setTime(cursor.getLong(cursor.getColumnIndex(LOC_TIME))); + entry.setLocation(location); + } + entry.setLogMsg(cursor.getString(cursor.getColumnIndex(DEBUG_INFO))); + + return entry; + } + + private static Float getNullableFloat(Cursor cursor, String colName) { + Float retValue = null; + int colIndex = cursor.getColumnIndexOrThrow(colName); + if (!cursor.isNull(colIndex)) { + retValue = cursor.getFloat(colIndex); + } + return retValue; + } +} diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java new file mode 100644 index 0000000..55d4d1e --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java @@ -0,0 +1,75 @@ +/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.locationtracker.data;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import com.android.locationtracker.R;
+
+/**
+ * Used to bind Tracker data to a list view UI
+ */
+public class TrackerListHelper extends TrackerDataHelper {
+
+ private ListActivity mActivity;
+
+ // sort entries by most recent first
+ private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";
+
+ public TrackerListHelper(ListActivity activity) {
+ super(activity, TrackerDataHelper.CSV_FORMATTER);
+ mActivity = activity;
+ }
+
+ /**
+ * Helper method for binding the list activities UI to the tracker data
+ * Tracker data will be sorted in most-recent first order
+ * Will enable automatic UI changes as tracker data changes
+ *
+ * @param layout - layout to populate data
+ */
+ public void bindListUI(int layout) {
+ Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,
+ TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);
+ // Used to map tracker entries from the database to views
+ TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);
+ mActivity.setListAdapter(adapter);
+ cursor.setNotificationUri(mActivity.getContentResolver(),
+ TrackerProvider.CONTENT_URI);
+
+ }
+
+ private class TrackerAdapter extends ResourceCursorAdapter {
+
+ public TrackerAdapter(Context context, int layout, Cursor c) {
+ super(context, layout, c);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView v = (TextView) view
+ .findViewById(R.id.entrylist_item);
+ String rowText = mFormatter.getOutput(TrackerEntry
+ .createEntry(cursor));
+ v.setText(rowText);
+ }
+ }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java new file mode 100644 index 0000000..88f24c3 --- /dev/null +++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.locationtracker.data; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.util.Log; + +/** + * Content provider for location tracking. + * + * It is recommended to use the TrackerDataHelper class to access this data + * rather than this class directly + */ +public class TrackerProvider extends ContentProvider { + + public static final Uri CONTENT_URI = Uri + .parse("content://com.android.locationtracker"); + + private static final String DB_NAME = "tracking.db"; + private static final String TABLE_NAME = "tracking"; + private static final int DB_VERSION = 1; + + private static final String LOG_TAG = "TrackerProvider"; + + /** + * This class helps open, create, and upgrade the database file. + */ + private static class DatabaseHelper extends SQLiteOpenHelper { + + DatabaseHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append(String.format("CREATE TABLE %s (", TABLE_NAME)); + TrackerEntry.buildCreationString(queryBuilder); + + queryBuilder.append(");"); + db.execSQL(queryBuilder.toString()); + db.setVersion(DB_VERSION); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // TODO: reimplement this when dB version changes + Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + + " to " + newVersion + + ", which will destroy all old data"); + db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); + onCreate(db); + } + } + + private DatabaseHelper mOpenHelper; + + @Override + public boolean onCreate() { + mOpenHelper = new DatabaseHelper(getContext()); + return true; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int result = db.delete(TABLE_NAME, selection, selectionArgs); + getContext().getContentResolver().notifyChange(uri, null); + return result; + } + + @Override + public String getType(Uri uri) { + throw new UnsupportedOperationException(); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + long rowId = db.insert(TABLE_NAME, null, values); + if (rowId > 0) { + Uri addedUri = ContentUris.withAppendedId(CONTENT_URI, rowId); + getContext().getContentResolver().notifyChange(addedUri, null); + return addedUri; + } + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + // TODO: extract limit from URI ? + Cursor cursor = db.query(TABLE_NAME, projection, selection, + selectionArgs, null, null, sortOrder); + getContext().getContentResolver().notifyChange(uri, null); + return cursor; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + throw new UnsupportedOperationException(); + } +} diff --git a/tests/SslLoad/src/com/android/sslload/SslLoad.java b/tests/SslLoad/src/com/android/sslload/SslLoad.java index 1470d48..62aa524 100644 --- a/tests/SslLoad/src/com/android/sslload/SslLoad.java +++ b/tests/SslLoad/src/com/android/sslload/SslLoad.java @@ -35,7 +35,7 @@ import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.util.Log; -import com.android.common.AndroidHttpClient; +import android.net.http.AndroidHttpClient; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 88c5441..ae4bd14 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -221,12 +221,12 @@ static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::START_TAG) { if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) { - ssize_t minSdkIndex = block.indexOfAttribute("android", + ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "minSdkVersion"); if (minSdkIndex >= 0) { - String8 minSdkString = String8( - block.getAttributeStringValue(minSdkIndex, &len)); - bundle->setMinSdkVersion(minSdkString.string()); + const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len); + const char* minSdk8 = strdup(String8(minSdk16).string()); + bundle->setMinSdkVersion(minSdk8); } } } diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index 51afc0a..a09cec0 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -25,8 +25,12 @@ void printStringPool(const ResStringPool* pool) const size_t NS = pool->size(); for (size_t s=0; s<NS; s++) { size_t len; - printf("String #%ld: %s\n", s, - String8(pool->stringAt(s, &len)).string()); + const char *str = (const char*)pool->string8At(s, &len); + if (str == NULL) { + str = String8(pool->stringAt(s, &len)).string(); + } + + printf("String #%ld: %s\n", s, str); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java index 8bf5e85..d5d315e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java @@ -236,11 +236,6 @@ public class Canvas extends _Original_Canvas { // OVERRIDEN METHODS // -------------------- - @Override - public void finalize() throws Throwable { - // pass - } - /* (non-Javadoc) * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap) */ diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java index 522415c..9e30671 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java @@ -52,11 +52,6 @@ public class Matrix extends _Original_Matrix { mValues = data; } - @Override - public void finalize() throws Throwable { - // pass - } - //---------- Custom Methods /** diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java index 2d03618..e4f9794 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java @@ -219,11 +219,6 @@ public class Paint extends _Original_Paint { } @Override - public void finalize() throws Throwable { - // pass - } - - @Override public void reset() { super.reset(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 41d9f9d..8e9e75f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,7 +16,7 @@ package com.android.layoutlib.bridge; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.ILayoutBridge; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.ILayoutResult; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index 73a3986..744bfbe 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -927,6 +927,7 @@ public final class BridgeContext extends Context { return null; } + @Override public File getExternalCacheDir() { // TODO Auto-generated method stub return null; @@ -964,6 +965,7 @@ public final class BridgeContext extends Context { return null; } + @Override public File getExternalFilesDir(String type) { // TODO Auto-generated method stub return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java index efd222e..70c5bd7 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java @@ -16,7 +16,7 @@ package com.android.layoutlib.bridge; -import com.android.common.XmlUtils; +import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; @@ -392,7 +392,8 @@ public final class BridgeTypedArray extends TypedArray { if (s == null) { return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT)) { + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { return LayoutParams.MATCH_PARENT; } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; @@ -460,7 +461,8 @@ public final class BridgeTypedArray extends TypedArray { if (s == null) { return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT)) { + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { return LayoutParams.MATCH_PARENT; } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; diff --git a/tools/preload/20100223.compiled b/tools/preload/20100223.compiled Binary files differnew file mode 100644 index 0000000..3056388 --- /dev/null +++ b/tools/preload/20100223.compiled diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java index bc21b6f..d8f95f4 100644 --- a/tools/preload/MemoryUsage.java +++ b/tools/preload/MemoryUsage.java @@ -166,7 +166,7 @@ class MemoryUsage implements Serializable { + ":/system/framework/loadclass.jar"; private static final String[] GET_DIRTY_PAGES = { - "adb", "-e", "shell", "dalvikvm", CLASS_PATH, "LoadClass" }; + "adb", "shell", "dalvikvm", CLASS_PATH, "LoadClass" }; /** * Measures memory usage for the given class. @@ -248,7 +248,7 @@ class MemoryUsage implements Serializable { String line = in.readLine(); if (line == null || !line.startsWith("DECAFBAD,")) { System.err.println("Got bad response for " + className - + ": " + line); + + ": " + line + "; command was " + Arrays.toString(commands)); errorCount += 1; return NOT_AVAILABLE; } diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java index a8d761d..f557365 100644 --- a/tools/preload/Policy.java +++ b/tools/preload/Policy.java @@ -43,9 +43,14 @@ public class Policy { "system_server", "com.google.process.content", "android.process.media", + "com.android.bluetooth", + "com.android.calendar", + "com.android.inputmethod.latin", "com.android.phone", - "com.google.android.apps.maps.FriendService", + "com.google.android.apps.maps.FriendService", // pre froyo + "com.google.android.apps.maps:FriendService", // froyo "com.google.android.apps.maps.LocationFriendService", + "com.google.android.deskclock", "com.google.process.gapps", "android.tts" )); diff --git a/tools/preload/Record.java b/tools/preload/Record.java index b2be4d4..9d45a26 100644 --- a/tools/preload/Record.java +++ b/tools/preload/Record.java @@ -19,6 +19,19 @@ */ class Record { + /** + * The delimiter character we use, {@code :}, conflicts with some other + * names. In that case, manually replace the delimiter with something else. + */ + private static final String[] REPLACE_CLASSES = { + "com.google.android.apps.maps:FriendService", + "com.google.android.apps.maps\\u003AFriendService", + "com.google.android.apps.maps:driveabout", + "com.google.android.apps.maps\\u003Adriveabout", + "com.google.android.apps.maps:LocationFriendService", + "com.google.android.apps.maps\\u003ALocationFriendService", + }; + enum Type { /** Start of initialization. */ START_LOAD, @@ -74,6 +87,10 @@ class Record { } sourceLineNumber = lineNum; + + for (int i = 0; i < REPLACE_CLASSES.length; i+= 2) { + line = line.replace(REPLACE_CLASSES[i], REPLACE_CLASSES[i+1]); + } line = line.substring(1); String[] parts = line.split(":"); diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index afaed24..810e4d2 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -943,7 +943,7 @@ public class WifiStateTracker extends NetworkStateTracker { } else { newDetailedState = DetailedState.FAILED; } - handleDisconnectedState(newDetailedState); + handleDisconnectedState(newDetailedState, true); /** * If we were associated with a network (networkId != -1), * assume we reached this state because of a failed attempt @@ -965,7 +965,7 @@ public class WifiStateTracker extends NetworkStateTracker { } else if (newState == SupplicantState.DISCONNECTED) { mHaveIpAddress = false; if (isDriverStopped() || mDisconnectExpected) { - handleDisconnectedState(DetailedState.DISCONNECTED); + handleDisconnectedState(DetailedState.DISCONNECTED, true); } else { scheduleDisconnect(); } @@ -1072,16 +1072,10 @@ public class WifiStateTracker extends NetworkStateTracker { */ if (wasDisconnectPending) { DetailedState saveState = getNetworkInfo().getDetailedState(); - handleDisconnectedState(DetailedState.DISCONNECTED); + handleDisconnectedState(DetailedState.DISCONNECTED, false); setDetailedStateInternal(saveState); - } else { - /** - * stop DHCP to ensure there is a new IP address - * even if the supplicant transitions without disconnect - * COMPLETED -> ASSOCIATED -> COMPLETED - */ - resetConnections(false); } + configureInterface(); mLastBssid = result.BSSID; mLastSsid = mWifiInfo.getSSID(); @@ -1116,7 +1110,7 @@ public class WifiStateTracker extends NetworkStateTracker { case EVENT_DEFERRED_DISCONNECT: if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { - handleDisconnectedState(DetailedState.DISCONNECTED); + handleDisconnectedState(DetailedState.DISCONNECTED, true); } break; @@ -1284,13 +1278,15 @@ public class WifiStateTracker extends NetworkStateTracker { * Reset our IP state and send out broadcasts following a disconnect. * @param newState the {@code DetailedState} to set. Should be either * {@code DISCONNECTED} or {@code FAILED}. + * @param disableInterface indicates whether the interface should + * be disabled */ - private void handleDisconnectedState(DetailedState newState) { + private void handleDisconnectedState(DetailedState newState, boolean disableInterface) { if (mDisconnectPending) { cancelDisconnect(); } mDisconnectExpected = false; - resetConnections(true); + resetConnections(disableInterface); setDetailedState(newState); sendNetworkStateChangeBroadcast(mLastBssid); mWifiInfo.setBSSID(null); |
