summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.xml489
-rw-r--r--cmds/stagefright/record.cpp3
-rw-r--r--cmds/stagefright/stagefright.cpp4
-rw-r--r--common/java/com/android/common/Base64.java29
-rw-r--r--core/java/android/accounts/AccountManager.java14
-rw-r--r--core/java/android/app/AlarmManager.java21
-rwxr-xr-xcore/java/android/app/IAlarmManager.aidl1
-rw-r--r--core/java/android/content/ContentResolver.java60
-rw-r--r--core/java/android/content/ContentService.java38
-rw-r--r--core/java/android/content/IContentService.aidl42
-rw-r--r--core/java/android/content/PeriodicSync.aidl (renamed from core/java/android/os/Base64Utils.java)18
-rw-r--r--core/java/android/content/PeriodicSync.java84
-rw-r--r--core/java/android/content/SyncManager.java453
-rw-r--r--core/java/android/content/SyncQueue.java86
-rw-r--r--core/java/android/content/SyncStatusInfo.java70
-rw-r--r--core/java/android/content/SyncStorageEngine.java393
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java185
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java72
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java76
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java27
-rwxr-xr-xcore/java/android/inputmethodservice/KeyboardView.java5
-rw-r--r--core/java/android/provider/Calendar.java28
-rw-r--r--core/java/android/speech/RecognitionManager.java27
-rw-r--r--core/java/android/view/animation/Animation.java2
-rw-r--r--core/java/android/view/animation/AnimationSet.java4
-rw-r--r--core/java/android/widget/AbsListView.java462
-rw-r--r--core/java/android/widget/ExpandableListView.java12
-rw-r--r--core/java/android/widget/LinearLayout.java41
-rw-r--r--core/java/android/widget/NumberPicker.java1
-rw-r--r--core/java/com/android/internal/widget/WeightedLinearLayout.java8
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_database_SQLiteDatabase.cpp58
-rw-r--r--core/jni/android_util_Base64.cpp160
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_secure.pngbin0 -> 923 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_secure.pngbin0 -> 631 bytes
-rw-r--r--core/res/res/layout/alert_dialog.xml4
-rw-r--r--core/res/res/values/arrays.xml1
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/tests/coretests/src/android/content/SyncStorageEngineTest.java163
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java47
-rw-r--r--keystore/java/android/security/KeyStore.java41
-rw-r--r--libs/surfaceflinger/LayerBuffer.cpp46
-rw-r--r--libs/surfaceflinger/LayerBuffer.h1
-rw-r--r--media/libstagefright/AudioPlayer.cpp4
-rw-r--r--media/libstagefright/omx/tests/OMXHarness.cpp8
-rw-r--r--opengl/libagl/egl.cpp15
-rw-r--r--opengl/libagl/texture.cpp27
-rw-r--r--opengl/libs/EGL/egl.cpp19
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java4
-rw-r--r--services/java/com/android/server/AlarmManagerService.java8
-rw-r--r--services/java/com/android/server/BackupManagerService.java27
-rw-r--r--services/java/com/android/server/PowerManagerService.java43
-rw-r--r--test-runner/android/test/TouchUtils.java4
-rw-r--r--tests/AndroidTests/Android.mk2
-rw-r--r--tests/CoreTests/android/content/SyncQueueTest.java30
60 files changed, 2145 insertions, 1341 deletions
diff --git a/Android.mk b/Android.mk
index 682f2865d1ad..ab1e7ea0ea12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -222,6 +222,7 @@ aidl_files := \
frameworks/base/core/java/android/content/ComponentName.aidl \
frameworks/base/core/java/android/content/Intent.aidl \
frameworks/base/core/java/android/content/IntentSender.aidl \
+ frameworks/base/core/java/android/content/PeriodicSync.aidl \
frameworks/base/core/java/android/content/SyncStats.aidl \
frameworks/base/core/java/android/content/res/Configuration.aidl \
frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
diff --git a/api/current.xml b/api/current.xml
index 66880e1108f6..c54fc98d2e27 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1035,6 +1035,17 @@
visibility="public"
>
</field>
+<field name="SET_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.SET_TIME&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SET_TIME_ZONE"
type="java.lang.String"
transient="false"
@@ -18808,6 +18819,19 @@
<parameter name="operation" type="android.app.PendingIntent">
</parameter>
</method>
+<method name="setTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="millis" type="long">
+</parameter>
+</method>
<method name="setTimeZone"
return="void"
abstract="false"
@@ -31083,6 +31107,25 @@
<parameter name="name" type="java.lang.String">
</parameter>
</method>
+<method name="addPeriodicSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<parameter name="pollFrequency" type="long">
+</parameter>
+</method>
<method name="addStatusChangeListener"
return="java.lang.Object"
abstract="false"
@@ -31203,6 +31246,21 @@
visibility="public"
>
</method>
+<method name="getPeriodicSyncs"
+ return="java.util.List&lt;android.content.PeriodicSync&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
<method name="getSyncAdapterTypes"
return="android.content.SyncAdapterType[]"
abstract="false"
@@ -31438,6 +31496,23 @@
<parameter name="observer" type="android.database.ContentObserver">
</parameter>
</method>
+<method name="removePeriodicSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
<method name="removeStatusChangeListener"
return="void"
abstract="false"
@@ -39721,6 +39796,109 @@
>
</method>
</class>
+<class name="PeriodicSync"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="PeriodicSync"
+ type="android.content.PeriodicSync"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+<parameter name="period" type="long">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="account"
+ type="android.accounts.Account"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="authority"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="extras"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="period"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="ReceiverCallNotAllowedException"
extends="android.util.AndroidRuntimeException"
abstract="false"
@@ -52892,40 +53070,40 @@
<parameter name="sleepAfterYieldDelay" type="long">
</parameter>
</method>
-<field name="CREATE_IF_NECESSARY"
+<field name="CONFLICT_ABORT"
type="int"
transient="false"
volatile="false"
- value="268435456"
+ value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="NO_LOCALIZED_COLLATORS"
+<field name="CONFLICT_FAIL"
type="int"
transient="false"
volatile="false"
- value="16"
+ value="3"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="OPEN_READONLY"
+<field name="CONFLICT_IGNORE"
type="int"
transient="false"
volatile="false"
- value="1"
+ value="4"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="OPEN_READWRITE"
+<field name="CONFLICT_NONE"
type="int"
transient="false"
volatile="false"
@@ -52936,86 +53114,77 @@
visibility="public"
>
</field>
-<field name="SQLITE_MAX_LIKE_PATTERN_LENGTH"
+<field name="CONFLICT_REPLACE"
type="int"
transient="false"
volatile="false"
- value="50000"
+ value="5"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-</class>
-<class name="SQLiteDatabase.ConflictAlgorithm"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<field name="ABORT"
+<field name="CONFLICT_ROLLBACK"
type="int"
transient="false"
volatile="false"
- value="2"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="FAIL"
+<field name="CREATE_IF_NECESSARY"
type="int"
transient="false"
volatile="false"
- value="3"
+ value="268435456"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="IGNORE"
+<field name="NO_LOCALIZED_COLLATORS"
type="int"
transient="false"
volatile="false"
- value="4"
+ value="16"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="NONE"
+<field name="OPEN_READONLY"
type="int"
transient="false"
volatile="false"
- value="0"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="REPLACE"
+<field name="OPEN_READWRITE"
type="int"
transient="false"
volatile="false"
- value="5"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="ROLLBACK"
+<field name="SQLITE_MAX_LIKE_PATTERN_LENGTH"
type="int"
transient="false"
volatile="false"
- value="1"
+ value="50000"
static="true"
final="true"
deprecated="not deprecated"
@@ -192449,6 +192618,49 @@
<parameter name="mode" type="int">
</parameter>
</method>
+<method name="smoothScrollBy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="distance" type="int">
+</parameter>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+<method name="smoothScrollToPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="smoothScrollToPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="boundPosition" type="int">
+</parameter>
+</method>
<method name="verifyDrawable"
return="boolean"
abstract="false"
@@ -200701,223 +200913,6 @@
</parameter>
</method>
</interface>
-<class name="NumberPicker"
- extends="android.widget.LinearLayout"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="NumberPicker"
- type="android.widget.NumberPicker"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</constructor>
-<constructor name="NumberPicker"
- type="android.widget.NumberPicker"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
-</constructor>
-<method name="changeCurrent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="current" type="int">
-</parameter>
-</method>
-<method name="getBeginRange"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="getCurrent"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getEndRange"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="setCurrent"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="current" type="int">
-</parameter>
-</method>
-<method name="setFormatter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="formatter" type="android.widget.NumberPicker.Formatter">
-</parameter>
-</method>
-<method name="setOnChangeListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.widget.NumberPicker.OnChangedListener">
-</parameter>
-</method>
-<method name="setRange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="setRange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-<parameter name="displayedValues" type="java.lang.String[]">
-</parameter>
-</method>
-<method name="setSpeed"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="speed" type="long">
-</parameter>
-</method>
-<field name="TWO_DIGIT_FORMATTER"
- type="android.widget.NumberPicker.Formatter"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="NumberPicker.Formatter"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="toString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<interface name="NumberPicker.OnChangedListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onChanged"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="picker" type="android.widget.NumberPicker">
-</parameter>
-<parameter name="oldVal" type="int">
-</parameter>
-<parameter name="newVal" type="int">
-</parameter>
-</method>
-</interface>
<class name="PopupWindow"
extends="java.lang.Object"
abstract="false"
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 2ec0b702d41f..845c85471f92 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -106,6 +106,9 @@ sp<MediaSource> createSource(const char *filename) {
sp<MediaExtractor> extractor =
MediaExtractor::Create(new FileSource(filename));
+ if (extractor == NULL) {
+ return NULL;
+ }
size_t num_tracks = extractor->countTracks();
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index e65cdf11545b..f7cb22730480 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -431,6 +431,10 @@ int main(int argc, char **argv) {
mediaSource = new JPEGSource(dataSource);
} else {
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ if (extractor == NULL) {
+ fprintf(stderr, "could not create data source\n");
+ return -1;
+ }
size_t numTracks = extractor->countTracks();
diff --git a/common/java/com/android/common/Base64.java b/common/java/com/android/common/Base64.java
index 0c8e7c103822..771875c038b6 100644
--- a/common/java/com/android/common/Base64.java
+++ b/common/java/com/android/common/Base64.java
@@ -22,6 +22,11 @@ package com.android.common;
*/
public class Base64 {
/**
+ * Default values for encoder/decoder flags.
+ */
+ public static final int DEFAULT = 0;
+
+ /**
* Encoder flag bit to indicate you want the padding '='
* characters at the end (if any) to be omitted.
*/
@@ -106,7 +111,7 @@ public class Base64 {
* @param input the input String to decode, which is converted to
* bytes using the default charset
* @param flags controls certain features of the decoded output.
- * Passing 0 to decode standard Base64.
+ * Pass {@code DEFAULT} to decode standard Base64.
*
* @throws IllegalArgumentException if the input contains
* incorrect padding
@@ -124,7 +129,7 @@ public class Base64 {
*
* @param input the input array to decode
* @param flags controls certain features of the decoded output.
- * Passing 0 to decode standard Base64.
+ * Pass {@code DEFAULT} to decode standard Base64.
*
* @throws IllegalArgumentException if the input contains
* incorrect padding
@@ -144,7 +149,7 @@ public class Base64 {
* @param offset the position within the input array at which to start
* @param len the number of bytes of input to decode
* @param flags controls certain features of the decoded output.
- * Passing 0 to decode standard Base64.
+ * Pass {@code DEFAULT} to decode standard Base64.
*
* @throws IllegalArgumentException if the input contains
* incorrect padding
@@ -362,8 +367,8 @@ public class Base64 {
*
* @param input the data to encode
* @param flags controls certain features of the encoded output.
- * Passing 0 results in output that adheres to RFC
- * 2045.
+ * Passing {@code DEFAULT} results in output that
+ * adheres to RFC 2045.
*/
public static String encodeToString(byte[] input, int flags) {
return new String(encode(input, flags));
@@ -378,8 +383,8 @@ public class Base64 {
* start
* @param len the number of bytes of input to encode
* @param flags controls certain features of the encoded output.
- * Passing 0 results in output that adheres to RFC
- * 2045.
+ * Passing {@code DEFAULT} results in output that
+ * adheres to RFC 2045.
*/
public static String encodeToString(byte[] input, int offset, int len, int flags) {
return new String(encode(input, offset, len, flags));
@@ -391,8 +396,8 @@ public class Base64 {
*
* @param input the data to encode
* @param flags controls certain features of the encoded output.
- * Passing 0 results in output that adheres to RFC
- * 2045.
+ * Passing {@code DEFAULT} results in output that
+ * adheres to RFC 2045.
*/
public static byte[] encode(byte[] input, int flags) {
return encode(input, 0, input.length, flags);
@@ -407,8 +412,8 @@ public class Base64 {
* start
* @param len the number of bytes of input to encode
* @param flags controls certain features of the encoded output.
- * Passing 0 results in output that adheres to RFC
- * 2045.
+ * Passing {@code DEFAULT} results in output that
+ * adheres to RFC 2045.
*/
public static byte[] encode(byte[] input, int offset, int len, int flags) {
final boolean do_padding = (flags & NO_PADDING) == 0;
@@ -494,4 +499,6 @@ public class Base64 {
assert op == output.length;
return output;
}
+
+ private Base64() { } // don't instantiate
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 414d9633b11e..19e741ab55ff 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -271,7 +271,7 @@ public class AccountManager {
}
/**
- * Add an account to the AccountManager's set of known accounts.
+ * Add an account to the AccountManager's set of known accounts.
* <p>
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
@@ -560,9 +560,13 @@ public class AccountManager {
* 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 for
+ * 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.
+ * 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
@@ -653,7 +657,7 @@ public class AccountManager {
if (accountType == null) {
Log.e(TAG, "the account must not be null");
// to unblock caller waiting on Future.get()
- set(new Bundle());
+ set(new Bundle());
return;
}
mService.addAcount(mResponse, accountType, authTokenType,
@@ -1372,7 +1376,7 @@ public class AccountManager {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
// To recover from disk-full.
- intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+ intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
}
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 53c79352d3f4..90820036d40b 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -277,7 +277,26 @@ public class AlarmManager
} catch (RemoteException ex) {
}
}
-
+
+ /**
+ * Set the system wall clock time.
+ * Requires the permission android.permission.SET_TIME.
+ *
+ * @param millis time in milliseconds since the Epoch
+ */
+ public void setTime(long millis) {
+ try {
+ mService.setTime(millis);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Set the system default time zone.
+ * Requires the permission android.permission.SET_TIME_ZONE.
+ *
+ * @param timeZone in the format understood by {@link java.util.TimeZone}
+ */
public void setTimeZone(String timeZone) {
try {
mService.setTimeZone(timeZone);
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index cb42236f413c..edb40ed77f51 100755
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -27,6 +27,7 @@ interface IAlarmManager {
void set(int type, long triggerAtTime, in PendingIntent operation);
void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
+ void setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index eb2d7b143dc7..b5587eda7fd6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,6 +42,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.ArrayList;
+import java.util.Collection;
/**
@@ -966,6 +967,65 @@ public abstract class ContentResolver {
}
/**
+ * Specifies that a sync should be requested with the specified the account, authority,
+ * and extras at the given frequency. If there is already another periodic sync scheduled
+ * with the account, authority and extras then a new periodic sync won't be added, instead
+ * the frequency of the previous one will be updated.
+ * <p>
+ * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
+ * Although these sync are scheduled at the specified frequency, it may take longer for it to
+ * actually be started if other syncs are ahead of it in the sync operation queue. This means
+ * that the actual start time may drift.
+ *
+ * @param account the account to specify in the sync
+ * @param authority the provider to specify in the sync request
+ * @param extras extra parameters to go along with the sync request
+ * @param pollFrequency how frequently the sync should be performed, in seconds.
+ */
+ public static void addPeriodicSync(Account account, String authority, Bundle extras,
+ long pollFrequency) {
+ validateSyncExtrasBundle(extras);
+ try {
+ getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
+ } catch (RemoteException e) {
+ // exception ignored; if this is thrown then it means the runtime is in the midst of
+ // being restarted
+ }
+ }
+
+ /**
+ * Remove a periodic sync. Has no affect if account, authority and extras don't match
+ * an existing periodic sync.
+ *
+ * @param account the account of the periodic sync to remove
+ * @param authority the provider of the periodic sync to remove
+ * @param extras the extras of the periodic sync to remove
+ */
+ public static void removePeriodicSync(Account account, String authority, Bundle extras) {
+ validateSyncExtrasBundle(extras);
+ try {
+ getContentService().removePeriodicSync(account, authority, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Get the list of information about the periodic syncs for the given account and authority.
+ *
+ * @param account the account whose periodic syncs we are querying
+ * @param authority the provider whose periodic syncs we are querying
+ * @return a list of PeriodicSync objects. This list may be empty but will never be null.
+ */
+ public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
+ try {
+ return getContentService().getPeriodicSyncs(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
* Check if this account/provider is syncable.
* @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
*/
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 974a6670aab4..e0dfab59ee22 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -32,6 +32,8 @@ import android.Manifest;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
/**
* {@hide}
@@ -273,6 +275,42 @@ public final class ContentService extends IContentService.Stub {
}
}
+ public void addPeriodicSync(Account account, String authority, Bundle extras,
+ long pollFrequency) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ long identityToken = clearCallingIdentity();
+ try {
+ getSyncManager().getSyncStorageEngine().addPeriodicSync(
+ account, authority, extras, pollFrequency);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void removePeriodicSync(Account account, String authority, Bundle extras) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ long identityToken = clearCallingIdentity();
+ try {
+ getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+ "no permission to read the sync settings");
+ long identityToken = clearCallingIdentity();
+ try {
+ return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
+ account, providerName);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
public int getIsSyncable(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index b0f14c15cbb4..2d906ed0d725 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -21,6 +21,7 @@ import android.content.ActiveSyncInfo;
import android.content.ISyncStatusObserver;
import android.content.SyncAdapterType;
import android.content.SyncStatusInfo;
+import android.content.PeriodicSync;
import android.net.Uri;
import android.os.Bundle;
import android.database.IContentObserver;
@@ -38,11 +39,11 @@ interface IContentService {
void requestSync(in Account account, String authority, in Bundle extras);
void cancelSync(in Account account, String authority);
-
+
/**
* Check if the provider should be synced when a network tickle is received
* @param providerName the provider whose setting we are querying
- * @return true of the provider should be synced when a network tickle is received
+ * @return true if the provider should be synced when a network tickle is received
*/
boolean getSyncAutomatically(in Account account, String providerName);
@@ -55,6 +56,33 @@ interface IContentService {
void setSyncAutomatically(in Account account, String providerName, boolean sync);
/**
+ * Get the frequency of the periodic poll, if any.
+ * @param providerName the provider whose setting we are querying
+ * @return the frequency of the periodic sync in seconds. If 0 then no periodic syncs
+ * will take place.
+ */
+ List<PeriodicSync> getPeriodicSyncs(in Account account, String providerName);
+
+ /**
+ * Set whether or not the provider is to be synced on a periodic basis.
+ *
+ * @param providerName the provider whose behavior is being controlled
+ * @param pollFrequency the period that a sync should be performed, in seconds. If this is
+ * zero or less then no periodic syncs will be performed.
+ */
+ void addPeriodicSync(in Account account, String providerName, in Bundle extras,
+ long pollFrequency);
+
+ /**
+ * Set whether or not the provider is to be synced on a periodic basis.
+ *
+ * @param providerName the provider whose behavior is being controlled
+ * @param pollFrequency the period that a sync should be performed, in seconds. If this is
+ * zero or less then no periodic syncs will be performed.
+ */
+ void removePeriodicSync(in Account account, String providerName, in Bundle extras);
+
+ /**
* Check if this account/provider is syncable.
* @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
*/
@@ -69,15 +97,15 @@ interface IContentService {
void setMasterSyncAutomatically(boolean flag);
boolean getMasterSyncAutomatically();
-
+
/**
* Returns true if there is currently a sync operation for the given
* account or authority in the pending list, or actively being processed.
*/
boolean isSyncActive(in Account account, String authority);
-
+
ActiveSyncInfo getActiveSync();
-
+
/**
* Returns the types of the SyncAdapters that are registered with the system.
* @return Returns the types of the SyncAdapters that are registered with the system.
@@ -96,8 +124,8 @@ interface IContentService {
* Return true if the pending status is true of any matching authorities.
*/
boolean isSyncPending(in Account account, String authority);
-
+
void addStatusChangeListener(int mask, ISyncStatusObserver callback);
-
+
void removeStatusChangeListener(ISyncStatusObserver callback);
}
diff --git a/core/java/android/os/Base64Utils.java b/core/java/android/content/PeriodicSync.aidl
index 684a469703ef..4530591bb551 100644
--- a/core/java/android/os/Base64Utils.java
+++ b/core/java/android/content/PeriodicSync.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 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.
@@ -14,18 +14,6 @@
* limitations under the License.
*/
-package android.os;
-
-/**
- * {@hide}
- */
-public class Base64Utils
-{
- // TODO add encode api here if possible
-
- public static byte [] decodeBase64(String data) {
- return decodeBase64Native(data);
- }
- private static native byte[] decodeBase64Native(String data);
-}
+package android.content;
+parcelable PeriodicSync;
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
new file mode 100644
index 000000000000..17813ec3aaf0
--- /dev/null
+++ b/core/java/android/content/PeriodicSync.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcelable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.accounts.Account;
+
+/**
+ * Value type that contains information about a periodic sync. Is parcelable, making it suitable
+ * for passing in an IPC.
+ */
+public class PeriodicSync implements Parcelable {
+ /** The account to be synced */
+ public final Account account;
+ /** The authority of the sync */
+ public final String authority;
+ /** Any extras that parameters that are to be passed to the sync adapter. */
+ public final Bundle extras;
+ /** How frequently the sync should be scheduled, in seconds. */
+ public final long period;
+
+ /** Creates a new PeriodicSync, copying the Bundle */
+ public PeriodicSync(Account account, String authority, Bundle extras, long period) {
+ this.account = account;
+ this.authority = authority;
+ this.extras = new Bundle(extras);
+ this.period = period;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ account.writeToParcel(dest, flags);
+ dest.writeString(authority);
+ dest.writeBundle(extras);
+ dest.writeLong(period);
+ }
+
+ public static final Creator<PeriodicSync> CREATOR = new Creator<PeriodicSync>() {
+ public PeriodicSync createFromParcel(Parcel source) {
+ return new PeriodicSync(Account.CREATOR.createFromParcel(source),
+ source.readString(), source.readBundle(), source.readLong());
+ }
+
+ public PeriodicSync[] newArray(int size) {
+ return new PeriodicSync[size];
+ }
+ };
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof PeriodicSync)) {
+ return false;
+ }
+
+ final PeriodicSync other = (PeriodicSync) o;
+
+ return account.equals(other.account)
+ && authority.equals(other.authority)
+ && period == other.period
+ && SyncStorageEngine.equals(extras, other.extras);
+ }
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 699b61d2b5d4..619c7d5e2067 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -52,14 +52,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -74,12 +67,6 @@ import java.util.concurrent.CountDownLatch;
public class SyncManager implements OnAccountsUpdateListener {
private static final String TAG = "SyncManager";
- // used during dumping of the Sync history
- private static final long MILLIS_IN_HOUR = 1000 * 60 * 60;
- private static final long MILLIS_IN_DAY = MILLIS_IN_HOUR * 24;
- private static final long MILLIS_IN_WEEK = MILLIS_IN_DAY * 7;
- private static final long MILLIS_IN_4WEEKS = MILLIS_IN_WEEK * 4;
-
/** Delay a sync due to local changes this long. In milliseconds */
private static final long LOCAL_SYNC_DELAY;
@@ -157,9 +144,7 @@ public class SyncManager implements OnAccountsUpdateListener {
// set if the sync active indicator should be reported
private boolean mNeedSyncActiveNotification = false;
- private volatile boolean mSyncPollInitialized;
private final PendingIntent mSyncAlarmIntent;
- private final PendingIntent mSyncPollAlarmIntent;
// Synchronized on "this". Instead of using this directly one should instead call
// its accessor, getConnManager().
private ConnectivityManager mConnManagerDoNotUseDirectly;
@@ -276,7 +261,6 @@ public class SyncManager implements OnAccountsUpdateListener {
// ignore the rest of the states -- leave our boolean alone.
}
if (mDataConnectionIsConnected) {
- initializeSyncPoll();
sendCheckAlarmsMessage();
}
}
@@ -291,14 +275,8 @@ public class SyncManager implements OnAccountsUpdateListener {
};
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
- private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM";
private final SyncHandler mSyncHandler;
- private static final int MAX_SYNC_POLL_DELAY_SECONDS = 36 * 60 * 60; // 36 hours
- private static final int MIN_SYNC_POLL_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
-
- private static final String SYNCMANAGER_PREFS_FILENAME = "/data/system/syncmanager.prefs";
-
private volatile boolean mBootCompleted = false;
private ConnectivityManager getConnectivityManager() {
@@ -338,9 +316,6 @@ public class SyncManager implements OnAccountsUpdateListener {
mSyncAlarmIntent = PendingIntent.getBroadcast(
mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
- mSyncPollAlarmIntent = PendingIntent.getBroadcast(
- mContext, 0 /* ignored */, new Intent(SYNC_POLL_ALARM), 0);
-
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
@@ -396,49 +371,6 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- private synchronized void initializeSyncPoll() {
- if (mSyncPollInitialized) return;
- mSyncPollInitialized = true;
-
- mContext.registerReceiver(new SyncPollAlarmReceiver(), new IntentFilter(SYNC_POLL_ALARM));
-
- // load the next poll time from shared preferences
- long absoluteAlarmTime = readSyncPollTime();
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "initializeSyncPoll: absoluteAlarmTime is " + absoluteAlarmTime);
- }
-
- // Convert absoluteAlarmTime to elapsed realtime. If this time was in the past then
- // schedule the poll immediately, if it is too far in the future then cap it at
- // MAX_SYNC_POLL_DELAY_SECONDS.
- long absoluteNow = System.currentTimeMillis();
- long relativeNow = SystemClock.elapsedRealtime();
- long relativeAlarmTime = relativeNow;
- if (absoluteAlarmTime > absoluteNow) {
- long delayInMs = absoluteAlarmTime - absoluteNow;
- final int maxDelayInMs = MAX_SYNC_POLL_DELAY_SECONDS * 1000;
- if (delayInMs > maxDelayInMs) {
- delayInMs = MAX_SYNC_POLL_DELAY_SECONDS * 1000;
- }
- relativeAlarmTime += delayInMs;
- }
-
- // schedule an alarm for the next poll time
- scheduleSyncPollAlarm(relativeAlarmTime);
- }
-
- private void scheduleSyncPollAlarm(long relativeAlarmTime) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "scheduleSyncPollAlarm: relativeAlarmTime is " + relativeAlarmTime
- + ", now is " + SystemClock.elapsedRealtime()
- + ", delay is " + (relativeAlarmTime - SystemClock.elapsedRealtime()));
- }
- ensureAlarmService();
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, relativeAlarmTime,
- mSyncPollAlarmIntent);
- }
-
/**
* Return a random value v that satisfies minValue <= v < maxValue. The difference between
* maxValue and minValue must be less than Integer.MAX_VALUE.
@@ -453,68 +385,6 @@ public class SyncManager implements OnAccountsUpdateListener {
return minValue + random.nextInt((int)spread);
}
- private void handleSyncPollAlarm() {
- // determine the next poll time
- long delayMs = jitterize(MIN_SYNC_POLL_DELAY_SECONDS, MAX_SYNC_POLL_DELAY_SECONDS) * 1000;
- long nextRelativePollTimeMs = SystemClock.elapsedRealtime() + delayMs;
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "handleSyncPollAlarm: delay " + delayMs);
-
- // write the absolute time to shared preferences
- writeSyncPollTime(System.currentTimeMillis() + delayMs);
-
- // schedule an alarm for the next poll time
- scheduleSyncPollAlarm(nextRelativePollTimeMs);
-
- // perform a poll
- scheduleSync(null /* sync all syncable accounts */, null /* sync all syncable providers */,
- new Bundle(), 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */);
- }
-
- private void writeSyncPollTime(long when) {
- File f = new File(SYNCMANAGER_PREFS_FILENAME);
- DataOutputStream str = null;
- try {
- str = new DataOutputStream(new FileOutputStream(f));
- str.writeLong(when);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "error writing to file " + f, e);
- } catch (IOException e) {
- Log.w(TAG, "error writing to file " + f, e);
- } finally {
- if (str != null) {
- try {
- str.close();
- } catch (IOException e) {
- Log.w(TAG, "error closing file " + f, e);
- }
- }
- }
- }
-
- private long readSyncPollTime() {
- File f = new File(SYNCMANAGER_PREFS_FILENAME);
-
- DataInputStream str = null;
- try {
- str = new DataInputStream(new FileInputStream(f));
- return str.readLong();
- } catch (FileNotFoundException e) {
- writeSyncPollTime(0);
- } catch (IOException e) {
- Log.w(TAG, "error reading file " + f, e);
- } finally {
- if (str != null) {
- try {
- str.close();
- } catch (IOException e) {
- Log.w(TAG, "error closing file " + f, e);
- }
- }
- }
- return 0;
- }
-
public ActiveSyncContext getActiveSyncContext() {
return mActiveSyncContext;
}
@@ -799,12 +669,6 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- class SyncPollAlarmReceiver extends BroadcastReceiver {
- public void onReceive(Context context, Intent intent) {
- handleSyncPollAlarm();
- }
- }
-
private void clearBackoffSetting(SyncOperation op) {
mSyncStorageEngine.setBackoff(op.account, op.authority,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
@@ -923,7 +787,7 @@ public class SyncManager implements OnAccountsUpdateListener {
mSyncStorageEngine.setBackoff(account, authority,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
synchronized (mSyncQueue) {
- mSyncQueue.clear(account, authority);
+ mSyncQueue.remove(account, authority);
}
}
@@ -1084,7 +948,8 @@ public class SyncManager implements OnAccountsUpdateListener {
pw.println("none");
}
final long now = SystemClock.elapsedRealtime();
- pw.print("now: "); pw.println(now);
+ pw.print("now: "); pw.print(now);
+ pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
pw.println(" (HH:MM:SS)");
pw.print("time spent syncing: ");
@@ -1102,7 +967,9 @@ public class SyncManager implements OnAccountsUpdateListener {
pw.println("no alarm is scheduled (there had better not be any pending syncs)");
}
- pw.print("active sync: "); pw.println(mActiveSyncContext);
+ final SyncManager.ActiveSyncContext activeSyncContext = mActiveSyncContext;
+
+ pw.print("active sync: "); pw.println(activeSyncContext);
pw.print("notification info: ");
sb.setLength(0);
@@ -1125,6 +992,11 @@ public class SyncManager implements OnAccountsUpdateListener {
pw.print(authority != null ? authority.account : "<no account>");
pw.print(" ");
pw.print(authority != null ? authority.authority : "<no account>");
+ if (activeSyncContext != null) {
+ pw.print(" ");
+ pw.print(SyncStorageEngine.SOURCES[
+ activeSyncContext.mSyncOperation.syncSource]);
+ }
pw.print(", duration is ");
pw.println(DateUtils.formatElapsedTime(durationInSeconds));
} else {
@@ -1152,80 +1024,76 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- HashSet<Account> processedAccounts = new HashSet<Account>();
- ArrayList<SyncStatusInfo> statuses
- = mSyncStorageEngine.getSyncStatus();
- if (statuses != null && statuses.size() > 0) {
- pw.println();
- pw.println("Sync Status");
- final int N = statuses.size();
- for (int i=0; i<N; i++) {
- SyncStatusInfo status = statuses.get(i);
- SyncStorageEngine.AuthorityInfo authority
- = mSyncStorageEngine.getAuthority(status.authorityId);
- if (authority != null) {
- Account curAccount = authority.account;
-
- if (processedAccounts.contains(curAccount)) {
- continue;
- }
-
- processedAccounts.add(curAccount);
+ // join the installed sync adapter with the accounts list and emit for everything
+ pw.println();
+ pw.println("Sync Status");
+ for (Account account : accounts) {
+ pw.print(" Account "); pw.print(account.name);
+ pw.print(" "); pw.print(account.type);
+ pw.println(":");
+ for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType :
+ mSyncAdapters.getAllServices()) {
+ if (!syncAdapterType.type.accountType.equals(account.type)) {
+ continue;
+ }
- pw.print(" Account "); pw.print(authority.account.name);
- pw.print(" "); pw.print(authority.account.type);
- pw.println(":");
- for (int j=i; j<N; j++) {
- status = statuses.get(j);
- authority = mSyncStorageEngine.getAuthority(status.authorityId);
- if (!curAccount.equals(authority.account)) {
- continue;
- }
- pw.print(" "); pw.print(authority.authority);
- pw.println(":");
- final String syncable = authority.syncable > 0
- ? "syncable"
- : (authority.syncable == 0 ? "not syncable" : "not initialized");
- final String enabled = authority.enabled ? "enabled" : "disabled";
- final String delayUntil = authority.delayUntil > now
- ? "delay for " + ((authority.delayUntil - now) / 1000) + " sec"
- : "no delay required";
- final String backoff = authority.backoffTime > now
- ? "backoff for " + ((authority.backoffTime - now) / 1000)
- + " sec"
- : "no backoff required";
- final String backoffDelay = authority.backoffDelay > 0
- ? ("the backoff increment is " + authority.backoffDelay / 1000
- + " sec")
- : "no backoff increment";
- pw.println(String.format(
- " settings: %s, %s, %s, %s, %s",
- enabled, syncable, backoff, backoffDelay, delayUntil));
- pw.print(" count: local="); pw.print(status.numSourceLocal);
- pw.print(" poll="); pw.print(status.numSourcePoll);
- pw.print(" server="); pw.print(status.numSourceServer);
- pw.print(" user="); pw.print(status.numSourceUser);
- pw.print(" total="); pw.println(status.numSyncs);
- pw.print(" total duration: ");
- pw.println(DateUtils.formatElapsedTime(
- status.totalElapsedTime/1000));
- if (status.lastSuccessTime != 0) {
- pw.print(" SUCCESS: source=");
- pw.print(SyncStorageEngine.SOURCES[
- status.lastSuccessSource]);
- pw.print(" time=");
- pw.println(formatTime(status.lastSuccessTime));
- } else {
- pw.print(" FAILURE: source=");
- pw.print(SyncStorageEngine.SOURCES[
- status.lastFailureSource]);
- pw.print(" initialTime=");
- pw.print(formatTime(status.initialFailureTime));
- pw.print(" lastTime=");
- pw.println(formatTime(status.lastFailureTime));
- pw.print(" message: "); pw.println(status.lastFailureMesg);
- }
- }
+ SyncStorageEngine.AuthorityInfo settings = mSyncStorageEngine.getAuthority(
+ account, syncAdapterType.type.authority);
+ SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
+ pw.print(" "); pw.print(settings.authority);
+ pw.println(":");
+ pw.print(" settings:");
+ pw.print(" " + (settings.syncable > 0
+ ? "syncable"
+ : (settings.syncable == 0 ? "not syncable" : "not initialized")));
+ pw.print(", " + (settings.enabled ? "enabled" : "disabled"));
+ if (settings.delayUntil > now) {
+ pw.print(", delay for "
+ + ((settings.delayUntil - now) / 1000) + " sec");
+ }
+ if (settings.backoffTime > now) {
+ pw.print(", backoff for "
+ + ((settings.backoffTime - now) / 1000) + " sec");
+ }
+ if (settings.backoffDelay > 0) {
+ pw.print(", the backoff increment is " + settings.backoffDelay / 1000
+ + " sec");
+ }
+ pw.println();
+ for (int periodicIndex = 0;
+ periodicIndex < settings.periodicSyncs.size();
+ periodicIndex++) {
+ Pair<Bundle, Long> info = settings.periodicSyncs.get(periodicIndex);
+ long lastPeriodicTime = status.getPeriodicSyncTime(periodicIndex);
+ long nextPeriodicTime = lastPeriodicTime + info.second * 1000;
+ pw.println(" periodic period=" + info.second
+ + ", extras=" + info.first
+ + ", next=" + formatTime(nextPeriodicTime));
+ }
+ pw.print(" count: local="); pw.print(status.numSourceLocal);
+ pw.print(" poll="); pw.print(status.numSourcePoll);
+ pw.print(" periodic="); pw.print(status.numSourcePeriodic);
+ pw.print(" server="); pw.print(status.numSourceServer);
+ pw.print(" user="); pw.print(status.numSourceUser);
+ pw.print(" total="); pw.print(status.numSyncs);
+ pw.println();
+ pw.print(" total duration: ");
+ pw.println(DateUtils.formatElapsedTime(status.totalElapsedTime/1000));
+ if (status.lastSuccessTime != 0) {
+ pw.print(" SUCCESS: source=");
+ pw.print(SyncStorageEngine.SOURCES[status.lastSuccessSource]);
+ pw.print(" time=");
+ pw.println(formatTime(status.lastSuccessTime));
+ }
+ if (status.lastFailureTime != 0) {
+ pw.print(" FAILURE: source=");
+ pw.print(SyncStorageEngine.SOURCES[
+ status.lastFailureSource]);
+ pw.print(" initialTime=");
+ pw.print(formatTime(status.initialFailureTime));
+ pw.print(" lastTime=");
+ pw.println(formatTime(status.lastFailureTime));
+ pw.print(" message: "); pw.println(status.lastFailureMesg);
}
}
}
@@ -1580,6 +1448,36 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
+ private boolean isSyncAllowed(Account account, String authority, boolean manualSync,
+ boolean backgroundDataUsageAllowed) {
+ Account[] accounts = mAccounts;
+
+ // Sync is disabled, drop this operation.
+ if (!isSyncEnabled()) {
+ return false;
+ }
+
+ // skip the sync if the account of this operation no longer exists
+ if (accounts == null || !ArrayUtils.contains(accounts, account)) {
+ return false;
+ }
+
+ // skip the sync if it isn't manual and auto sync is disabled
+ final boolean syncAutomatically =
+ mSyncStorageEngine.getSyncAutomatically(account, authority)
+ && mSyncStorageEngine.getMasterSyncAutomatically();
+ if (!(manualSync || (backgroundDataUsageAllowed && syncAutomatically))) {
+ return false;
+ }
+
+ if (mSyncStorageEngine.getIsSyncable(account, authority) <= 0) {
+ // if not syncable or if the syncable is unknown (< 0), don't allow
+ return false;
+ }
+
+ return true;
+ }
+
private void runStateSyncing() {
// if the sync timeout has been reached then cancel it
@@ -1589,7 +1487,7 @@ public class SyncManager implements OnAccountsUpdateListener {
if (now > activeSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC) {
SyncOperation nextSyncOperation;
synchronized (mSyncQueue) {
- nextSyncOperation = mSyncQueue.nextReadyToRun(now);
+ nextSyncOperation = getNextReadyToRunSyncOperation(now);
}
if (nextSyncOperation != null) {
Log.d(TAG, "canceling and rescheduling sync because it ran too long: "
@@ -1643,7 +1541,7 @@ public class SyncManager implements OnAccountsUpdateListener {
synchronized (mSyncQueue) {
final long now = SystemClock.elapsedRealtime();
while (true) {
- op = mSyncQueue.nextReadyToRun(now);
+ op = getNextReadyToRunSyncOperation(now);
if (op == null) {
if (isLoggable) {
Log.v(TAG, "runStateIdle: no more sync operations, returning");
@@ -1655,42 +1553,9 @@ public class SyncManager implements OnAccountsUpdateListener {
// from the queue now
mSyncQueue.remove(op);
- // Sync is disabled, drop this operation.
- if (!isSyncEnabled()) {
- if (isLoggable) {
- Log.v(TAG, "runStateIdle: sync disabled, dropping " + op);
- }
- continue;
- }
-
- // skip the sync if the account of this operation no longer exists
- if (!ArrayUtils.contains(accounts, op.account)) {
- if (isLoggable) {
- Log.v(TAG, "runStateIdle: account not present, dropping " + op);
- }
- continue;
- }
-
- // skip the sync if it isn't manual and auto sync is disabled
- final boolean manualSync =
- op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
- final boolean syncAutomatically =
- mSyncStorageEngine.getSyncAutomatically(op.account, op.authority)
- && mSyncStorageEngine.getMasterSyncAutomatically();
- if (!(manualSync || (backgroundDataUsageAllowed && syncAutomatically))) {
- if (isLoggable) {
- Log.v(TAG, "runStateIdle: sync of this operation is not allowed, "
- + "dropping " + op);
- }
- continue;
- }
-
- if (mSyncStorageEngine.getIsSyncable(op.account, op.authority) <= 0) {
- // if not syncable or if the syncable is unknown (< 0), don't allow
- if (isLoggable) {
- Log.v(TAG, "runStateIdle: sync of this operation is not allowed, "
- + "dropping " + op);
- }
+ if (!isSyncAllowed(op.account, op.authority,
+ op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false),
+ backgroundDataUsageAllowed)) {
continue;
}
@@ -1736,6 +1601,74 @@ public class SyncManager implements OnAccountsUpdateListener {
// MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
}
+ private SyncOperation getNextPeriodicSyncOperation() {
+ final boolean backgroundDataUsageAllowed =
+ getConnectivityManager().getBackgroundDataSetting();
+ SyncStorageEngine.AuthorityInfo best = null;
+ long bestPollTimeAbsolute = Long.MAX_VALUE;
+ Bundle bestExtras = null;
+ ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
+ for (SyncStorageEngine.AuthorityInfo info : infos) {
+ if (!isSyncAllowed(info.account, info.authority, false /* manualSync */,
+ backgroundDataUsageAllowed)) {
+ continue;
+ }
+ SyncStatusInfo status = mSyncStorageEngine.getStatusByAccountAndAuthority(
+ info.account, info.authority);
+ int i = 0;
+ for (Pair<Bundle, Long> periodicSync : info.periodicSyncs) {
+ long lastPollTimeAbsolute = status != null ? status.getPeriodicSyncTime(i) : 0;
+ final Bundle extras = periodicSync.first;
+ final Long periodInSeconds = periodicSync.second;
+ long nextPollTimeAbsolute = lastPollTimeAbsolute + periodInSeconds * 1000;
+ if (nextPollTimeAbsolute < bestPollTimeAbsolute) {
+ best = info;
+ bestPollTimeAbsolute = nextPollTimeAbsolute;
+ bestExtras = extras;
+ }
+ i++;
+ }
+ }
+
+ if (best == null) {
+ return null;
+ }
+
+ final long nowAbsolute = System.currentTimeMillis();
+ final SyncOperation syncOperation = new SyncOperation(best.account,
+ SyncStorageEngine.SOURCE_PERIODIC,
+ best.authority, bestExtras, 0 /* delay */);
+ syncOperation.earliestRunTime = SystemClock.elapsedRealtime()
+ + (bestPollTimeAbsolute - nowAbsolute);
+ if (syncOperation.earliestRunTime < 0) {
+ syncOperation.earliestRunTime = 0;
+ }
+ return syncOperation;
+ }
+
+ public Pair<SyncOperation, Long> bestSyncOperationCandidate() {
+ Pair<SyncOperation, Long> nextOpAndRunTime = mSyncQueue.nextOperation();
+ SyncOperation nextOp = nextOpAndRunTime != null ? nextOpAndRunTime.first : null;
+ Long nextRunTime = nextOpAndRunTime != null ? nextOpAndRunTime.second : null;
+ SyncOperation pollOp = getNextPeriodicSyncOperation();
+ if (nextOp != null
+ && (pollOp == null || nextOp.expedited
+ || nextRunTime <= pollOp.earliestRunTime)) {
+ return nextOpAndRunTime;
+ } else if (pollOp != null) {
+ return Pair.create(pollOp, pollOp.earliestRunTime);
+ } else {
+ return null;
+ }
+ }
+
+ private SyncOperation getNextReadyToRunSyncOperation(long now) {
+ Pair<SyncOperation, Long> nextOpAndRunTime = bestSyncOperationCandidate();
+ return nextOpAndRunTime != null && nextOpAndRunTime.second <= now
+ ? nextOpAndRunTime.first
+ : null;
+ }
+
private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
mActiveSyncContext.mSyncAdapter = syncAdapter;
final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
@@ -1961,7 +1894,8 @@ public class SyncManager implements OnAccountsUpdateListener {
ActiveSyncContext activeSyncContext = mActiveSyncContext;
if (activeSyncContext == null) {
synchronized (mSyncQueue) {
- alarmTime = mSyncQueue.nextRunTime(now);
+ Pair<SyncOperation, Long> candidate = bestSyncOperationCandidate();
+ alarmTime = candidate != null ? candidate.second : 0;
}
} else {
final long notificationTime =
@@ -2102,9 +2036,22 @@ public class SyncManager implements OnAccountsUpdateListener {
SyncStorageEngine.EVENT_STOP, syncOperation.syncSource,
syncOperation.account.name.hashCode());
- mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage,
- downstreamActivity, upstreamActivity);
+ mSyncStorageEngine.stopSyncEvent(rowId, syncOperation.extras, elapsedTime,
+ resultMessage, downstreamActivity, upstreamActivity);
}
}
+ public static long runTimeWithBackoffs(SyncStorageEngine syncStorageEngine,
+ Account account, String authority, boolean isManualSync, long runTime) {
+ // if this is a manual sync, the run time is unchanged
+ // otherwise, the run time is the max of the backoffs and the run time.
+ if (isManualSync) {
+ return runTime;
+ }
+
+ Pair<Long, Long> backoff = syncStorageEngine.getBackoff(account, authority);
+ long delayUntilTime = syncStorageEngine.getDelayUntilTime(account, authority);
+
+ return Math.max(Math.max(runTime, delayUntilTime), backoff != null ? backoff.first : 0);
+ }
}
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index a9f15d9f9c26..2eead3abdbf5 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -2,8 +2,6 @@ package android.content;
import com.google.android.collect.Maps;
-import android.os.Bundle;
-import android.os.SystemClock;
import android.util.Pair;
import android.util.Log;
import android.accounts.Account;
@@ -32,10 +30,9 @@ public class SyncQueue {
final int N = ops.size();
for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
- // -1 is a special value that means expedited
- final int delay = op.expedited ? -1 : 0;
SyncOperation syncOperation = new SyncOperation(
- op.account, op.syncSource, op.authority, op.extras, delay);
+ op.account, op.syncSource, op.authority, op.extras, 0 /* delay */);
+ syncOperation.expedited = op.expedited;
syncOperation.pendingOperation = op;
add(syncOperation, op);
}
@@ -90,8 +87,15 @@ public class SyncQueue {
return true;
}
+ /**
+ * Remove the specified operation if it is in the queue.
+ * @param operation the operation to remove
+ */
public void remove(SyncOperation operation) {
SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
+ if (operationToRemove == null) {
+ return;
+ }
if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
final String errorMessage = "unable to find pending row for " + operationToRemove;
Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
@@ -102,54 +106,30 @@ public class SyncQueue {
* Find the operation that should run next. Operations are sorted by their earliestRunTime,
* prioritizing expedited operations. The earliestRunTime is adjusted by the sync adapter's
* backoff and delayUntil times, if any.
- * @param now the current {@link android.os.SystemClock#elapsedRealtime()}
* @return the operation that should run next and when it should run. The time may be in
* the future. It is expressed in milliseconds since boot.
*/
- private Pair<SyncOperation, Long> nextOperation(long now) {
- SyncOperation lowestOp = null;
- long lowestOpRunTime = 0;
+ public Pair<SyncOperation, Long> nextOperation() {
+ SyncOperation best = null;
+ long bestRunTime = 0;
for (SyncOperation op : mOperationsMap.values()) {
- // effectiveRunTime:
- // - backoffTime > currentTime : backoffTime
- // - backoffTime <= currentTime : op.runTime
- Pair<Long, Long> backoff = null;
- long delayUntilTime = 0;
- final boolean isManualSync =
- op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
- if (!isManualSync) {
- backoff = mSyncStorageEngine.getBackoff(op.account, op.authority);
- delayUntilTime = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority);
- }
- long backoffTime = Math.max(backoff != null ? backoff.first : 0, delayUntilTime);
- long opRunTime = backoffTime > now ? backoffTime : op.earliestRunTime;
- if (lowestOp == null
- || (lowestOp.expedited == op.expedited
- ? opRunTime < lowestOpRunTime
- : op.expedited)) {
- lowestOp = op;
- lowestOpRunTime = opRunTime;
+ long opRunTime = SyncManager.runTimeWithBackoffs(mSyncStorageEngine, op.account,
+ op.authority,
+ op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false),
+ op.earliestRunTime);
+ // if the expedited state of both ops are the same then compare their runtime.
+ // Otherwise the candidate is only better than the current best if the candidate
+ // is expedited.
+ if (best == null
+ || (best.expedited == op.expedited ? opRunTime < bestRunTime : op.expedited)) {
+ best = op;
+ bestRunTime = opRunTime;
}
}
- if (lowestOp == null) {
- return null;
- }
- return Pair.create(lowestOp, lowestOpRunTime);
- }
-
- /**
- * Return when the next SyncOperation will be ready to run or null if there are
- * none.
- * @param now the current {@link android.os.SystemClock#elapsedRealtime()}, used to
- * decide if the sync operation is ready to run
- * @return when the next SyncOperation will be ready to run, expressed in elapsedRealtime()
- */
- public Long nextRunTime(long now) {
- Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation(now);
- if (nextOpAndRunTime == null) {
+ if (best == null) {
return null;
}
- return nextOpAndRunTime.second;
+ return Pair.create(best, bestRunTime);
}
/**
@@ -158,21 +138,25 @@ public class SyncQueue {
* decide if the sync operation is ready to run
* @return the SyncOperation that should be run next and is ready to run.
*/
- public SyncOperation nextReadyToRun(long now) {
- Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation(now);
+ public Pair<SyncOperation, Long> nextReadyToRun(long now) {
+ Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation();
if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
return null;
}
- return nextOpAndRunTime.first;
+ return nextOpAndRunTime;
}
- public void clear(Account account, String authority) {
+ public void remove(Account account, String authority) {
Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, SyncOperation> entry = entries.next();
SyncOperation syncOperation = entry.getValue();
- if (account != null && !syncOperation.account.equals(account)) continue;
- if (authority != null && !syncOperation.authority.equals(authority)) continue;
+ if (account != null && !syncOperation.account.equals(account)) {
+ continue;
+ }
+ if (authority != null && !syncOperation.authority.equals(authority)) {
+ continue;
+ }
entries.remove();
if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
final String errorMessage = "unable to find pending row for " + syncOperation;
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index b8fda030b11c..bb2b2dace8bf 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -20,10 +20,12 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import java.util.ArrayList;
+
/** @hide */
public class SyncStatusInfo implements Parcelable {
- static final int VERSION = 1;
-
+ static final int VERSION = 2;
+
public final int authorityId;
public long totalElapsedTime;
public int numSyncs;
@@ -31,6 +33,7 @@ public class SyncStatusInfo implements Parcelable {
public int numSourceServer;
public int numSourceLocal;
public int numSourceUser;
+ public int numSourcePeriodic;
public long lastSuccessTime;
public int lastSuccessSource;
public long lastFailureTime;
@@ -39,7 +42,10 @@ public class SyncStatusInfo implements Parcelable {
public long initialFailureTime;
public boolean pending;
public boolean initialize;
-
+ public ArrayList<Long> periodicSyncTimes;
+
+ private static final String TAG = "Sync";
+
SyncStatusInfo(int authorityId) {
this.authorityId = authorityId;
}
@@ -50,10 +56,11 @@ public class SyncStatusInfo implements Parcelable {
return Integer.parseInt(lastFailureMesg);
}
} catch (NumberFormatException e) {
+ Log.d(TAG, "error parsing lastFailureMesg of " + lastFailureMesg, e);
}
return def;
}
-
+
public int describeContents() {
return 0;
}
@@ -75,11 +82,19 @@ public class SyncStatusInfo implements Parcelable {
parcel.writeLong(initialFailureTime);
parcel.writeInt(pending ? 1 : 0);
parcel.writeInt(initialize ? 1 : 0);
+ if (periodicSyncTimes != null) {
+ parcel.writeInt(periodicSyncTimes.size());
+ for (long periodicSyncTime : periodicSyncTimes) {
+ parcel.writeLong(periodicSyncTime);
+ }
+ } else {
+ parcel.writeInt(-1);
+ }
}
SyncStatusInfo(Parcel parcel) {
int version = parcel.readInt();
- if (version != VERSION) {
+ if (version != VERSION && version != 1) {
Log.w("SyncStatusInfo", "Unknown version: " + version);
}
authorityId = parcel.readInt();
@@ -97,8 +112,51 @@ public class SyncStatusInfo implements Parcelable {
initialFailureTime = parcel.readLong();
pending = parcel.readInt() != 0;
initialize = parcel.readInt() != 0;
+ if (version == 1) {
+ periodicSyncTimes = null;
+ } else {
+ int N = parcel.readInt();
+ if (N < 0) {
+ periodicSyncTimes = null;
+ } else {
+ periodicSyncTimes = new ArrayList<Long>();
+ for (int i=0; i<N; i++) {
+ periodicSyncTimes.add(parcel.readLong());
+ }
+ }
+ }
}
-
+
+ public void setPeriodicSyncTime(int index, long when) {
+ ensurePeriodicSyncTimeSize(index);
+ periodicSyncTimes.set(index, when);
+ }
+
+ private void ensurePeriodicSyncTimeSize(int index) {
+ if (periodicSyncTimes == null) {
+ periodicSyncTimes = new ArrayList<Long>(0);
+ }
+
+ final int requiredSize = index + 1;
+ if (periodicSyncTimes.size() < requiredSize) {
+ for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
+ periodicSyncTimes.add((long) 0);
+ }
+ }
+ }
+
+ public long getPeriodicSyncTime(int index) {
+ if (periodicSyncTimes == null || periodicSyncTimes.size() < (index + 1)) {
+ return 0;
+ }
+ return periodicSyncTimes.get(index);
+ }
+
+ public void removePeriodicSyncTime(int index) {
+ ensurePeriodicSyncTimeSize(index);
+ periodicSyncTimes.remove(index);
+ }
+
public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index db70096f535c..07a1f46aa91b 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -36,7 +36,6 @@ import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
@@ -50,6 +49,7 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TimeZone;
+import java.util.List;
/**
* Singleton that tracks the sync data and overall sync
@@ -62,6 +62,8 @@ public class SyncStorageEngine extends Handler {
private static final boolean DEBUG = false;
private static final boolean DEBUG_FILE = false;
+ private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
+
// @VisibleForTesting
static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
@@ -89,6 +91,9 @@ public class SyncStorageEngine extends Handler {
/** Enum value for a user-initiated sync. */
public static final int SOURCE_USER = 3;
+ /** Enum value for a periodic sync. */
+ public static final int SOURCE_PERIODIC = 4;
+
public static final long NOT_IN_BACKOFF_MODE = -1;
private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
@@ -99,7 +104,8 @@ public class SyncStorageEngine extends Handler {
public static final String[] SOURCES = { "SERVER",
"LOCAL",
"POLL",
- "USER" };
+ "USER",
+ "PERIODIC" };
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
@@ -164,6 +170,7 @@ public class SyncStorageEngine extends Handler {
long backoffTime;
long backoffDelay;
long delayUntil;
+ final ArrayList<Pair<Bundle, Long>> periodicSyncs;
AuthorityInfo(Account account, String authority, int ident) {
this.account = account;
@@ -173,6 +180,8 @@ public class SyncStorageEngine extends Handler {
syncable = -1; // default to "unknown"
backoffTime = -1; // if < 0 then we aren't in backoff mode
backoffDelay = -1; // if < 0 then we aren't in backoff mode
+ periodicSyncs = new ArrayList<Pair<Bundle, Long>>();
+ periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS));
}
}
@@ -228,6 +237,7 @@ public class SyncStorageEngine extends Handler {
private int mYearInDays;
private final Context mContext;
+
private static volatile SyncStorageEngine sSyncStorageEngine = null;
/**
@@ -262,17 +272,15 @@ public class SyncStorageEngine extends Handler {
private int mNextHistoryId = 0;
private boolean mMasterSyncAutomatically = true;
- private SyncStorageEngine(Context context) {
+ private SyncStorageEngine(Context context, File dataDir) {
mContext = context;
sSyncStorageEngine = this;
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
- // This call will return the correct directory whether Encrypted File Systems is
- // enabled or not.
- File dataDir = Environment.getSecureDataDirectory();
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
+ syncDir.mkdirs();
mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
@@ -286,14 +294,17 @@ public class SyncStorageEngine extends Handler {
}
public static SyncStorageEngine newTestInstance(Context context) {
- return new SyncStorageEngine(context);
+ return new SyncStorageEngine(context, context.getFilesDir());
}
public static void init(Context context) {
if (sSyncStorageEngine != null) {
return;
}
- sSyncStorageEngine = new SyncStorageEngine(context);
+ // This call will return the correct directory whether Encrypted File Systems is
+ // enabled or not.
+ File dataDir = Environment.getSecureDataDirectory();
+ sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
}
public static SyncStorageEngine getSingleton() {
@@ -475,7 +486,7 @@ public class SyncStorageEngine extends Handler {
}
} else {
AuthorityInfo authority =
- getOrCreateAuthorityLocked(account, providerName, -1, false);
+ getOrCreateAuthorityLocked(account, providerName, -1 /* ident */, true);
if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
return;
}
@@ -483,9 +494,6 @@ public class SyncStorageEngine extends Handler {
authority.backoffDelay = nextDelay;
changed = true;
}
- if (changed) {
- writeAccountInfoLocked();
- }
}
if (changed) {
@@ -499,12 +507,12 @@ public class SyncStorageEngine extends Handler {
+ " -> delayUntil " + delayUntil);
}
synchronized (mAuthorities) {
- AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ AuthorityInfo authority = getOrCreateAuthorityLocked(
+ account, providerName, -1 /* ident */, true);
if (authority.delayUntil == delayUntil) {
return;
}
authority.delayUntil = delayUntil;
- writeAccountInfoLocked();
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -520,6 +528,90 @@ public class SyncStorageEngine extends Handler {
}
}
+ private void updateOrRemovePeriodicSync(Account account, String providerName, Bundle extras,
+ long period, boolean add) {
+ if (period <= 0) {
+ period = 0;
+ }
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", provider " + providerName
+ + " -> period " + period + ", extras " + extras);
+ }
+ synchronized (mAuthorities) {
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ if (add) {
+ boolean alreadyPresent = false;
+ for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
+ Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
+ final Bundle existingExtras = syncInfo.first;
+ if (equals(existingExtras, extras)) {
+ if (syncInfo.second == period) {
+ return;
+ }
+ authority.periodicSyncs.set(i, Pair.create(extras, period));
+ alreadyPresent = true;
+ break;
+ }
+ }
+ if (!alreadyPresent) {
+ authority.periodicSyncs.add(Pair.create(extras, period));
+ SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
+ status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
+ }
+ } else {
+ SyncStatusInfo status = mSyncStatus.get(authority.ident);
+ boolean changed = false;
+ Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ Pair<Bundle, Long> syncInfo = iterator.next();
+ if (equals(syncInfo.first, extras)) {
+ iterator.remove();
+ changed = true;
+ if (status != null) {
+ status.removePeriodicSyncTime(i);
+ }
+ } else {
+ i++;
+ }
+ }
+ if (!changed) {
+ return;
+ }
+ }
+ writeAccountInfoLocked();
+ writeStatusLocked();
+ }
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ }
+
+ public void addPeriodicSync(Account account, String providerName, Bundle extras,
+ long pollFrequency) {
+ updateOrRemovePeriodicSync(account, providerName, extras, pollFrequency, true /* add */);
+ }
+
+ public void removePeriodicSync(Account account, String providerName, Bundle extras) {
+ updateOrRemovePeriodicSync(account, providerName, extras, 0 /* period, ignored */,
+ false /* remove */);
+ }
+
+ public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
+ ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
+ synchronized (mAuthorities) {
+ AuthorityInfo authority = getAuthorityLocked(account, providerName, "getPeriodicSyncs");
+ if (authority != null) {
+ for (Pair<Bundle, Long> item : authority.periodicSyncs) {
+ syncs.add(new PeriodicSync(account, providerName, item.first, item.second));
+ }
+ }
+ }
+ return syncs;
+ }
+
public void setMasterSyncAutomatically(boolean flag) {
boolean old;
synchronized (mAuthorities) {
@@ -817,7 +909,25 @@ public class SyncStorageEngine extends Handler {
return id;
}
- public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
+ public static boolean equals(Bundle b1, Bundle b2) {
+ if (b1.size() != b2.size()) {
+ return false;
+ }
+ if (b1.isEmpty()) {
+ return true;
+ }
+ for (String key : b1.keySet()) {
+ if (!b2.containsKey(key)) {
+ return false;
+ }
+ if (!b1.get(key).equals(b2.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void stopSyncEvent(long historyId, Bundle extras, long elapsedTime, String resultMessage,
long downstreamActivity, long upstreamActivity) {
synchronized (mAuthorities) {
if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -860,6 +970,17 @@ public class SyncStorageEngine extends Handler {
case SOURCE_SERVER:
status.numSourceServer++;
break;
+ case SOURCE_PERIODIC:
+ status.numSourcePeriodic++;
+ AuthorityInfo authority = mAuthorities.get(item.authorityId);
+ for (int periodicSyncIndex = 0;
+ periodicSyncIndex < authority.periodicSyncs.size();
+ periodicSyncIndex++) {
+ if (equals(extras, authority.periodicSyncs.get(periodicSyncIndex).first)) {
+ status.setPeriodicSyncTime(periodicSyncIndex, item.eventTime);
+ }
+ }
+ break;
}
boolean writeStatisticsNow = false;
@@ -948,11 +1069,27 @@ public class SyncStorageEngine extends Handler {
}
/**
+ * Return an array of the current authorities. Note
+ * that the objects inside the array are the real, live objects,
+ * so be careful what you do with them.
+ */
+ public ArrayList<AuthorityInfo> getAuthorities() {
+ synchronized (mAuthorities) {
+ final int N = mAuthorities.size();
+ ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
+ for (int i=0; i<N; i++) {
+ infos.add(mAuthorities.valueAt(i));
+ }
+ return infos;
+ }
+ }
+
+ /**
* Returns the status that matches the authority and account.
*
* @param account the account we want to check
* @param authority the authority whose row should be selected
- * @return the SyncStatusInfo for the authority, or null if none exists
+ * @return the SyncStatusInfo for the authority
*/
public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
if (account == null || authority == null) {
@@ -1130,6 +1267,12 @@ public class SyncStorageEngine extends Handler {
return authority;
}
+ public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
+ synchronized (mAuthorities) {
+ return getOrCreateSyncStatusLocked(authority.ident);
+ }
+ }
+
private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
SyncStatusInfo status = mSyncStatus.get(authorityId);
if (status == null) {
@@ -1155,6 +1298,25 @@ public class SyncStorageEngine extends Handler {
}
/**
+ * public for testing
+ */
+ public void clearAndReadState() {
+ synchronized (mAuthorities) {
+ mAuthorities.clear();
+ mAccounts.clear();
+ mPendingOperations.clear();
+ mSyncStatus.clear();
+ mSyncHistory.clear();
+
+ readAccountInfoLocked();
+ readStatusLocked();
+ readPendingOperationsLocked();
+ readStatisticsLocked();
+ readLegacyAccountInfoLocked();
+ }
+ }
+
+ /**
* Read all account information back in to the initial engine state.
*/
private void readAccountInfoLocked() {
@@ -1175,59 +1337,23 @@ public class SyncStorageEngine extends Handler {
mMasterSyncAutomatically = listen == null
|| Boolean.parseBoolean(listen);
eventType = parser.next();
+ AuthorityInfo authority = null;
+ Pair<Bundle, Long> periodicSync = null;
do {
- if (eventType == XmlPullParser.START_TAG
- && parser.getDepth() == 2) {
+ if (eventType == XmlPullParser.START_TAG) {
tagName = parser.getName();
- if ("authority".equals(tagName)) {
- int id = -1;
- try {
- id = Integer.parseInt(parser.getAttributeValue(
- null, "id"));
- } catch (NumberFormatException e) {
- } catch (NullPointerException e) {
+ if (parser.getDepth() == 2) {
+ if ("authority".equals(tagName)) {
+ authority = parseAuthority(parser);
+ periodicSync = null;
+ }
+ } else if (parser.getDepth() == 3) {
+ if ("periodicSync".equals(tagName) && authority != null) {
+ periodicSync = parsePeriodicSync(parser, authority);
}
- if (id >= 0) {
- String accountName = parser.getAttributeValue(
- null, "account");
- String accountType = parser.getAttributeValue(
- null, "type");
- if (accountType == null) {
- accountType = "com.google";
- }
- String authorityName = parser.getAttributeValue(
- null, "authority");
- String enabled = parser.getAttributeValue(
- null, "enabled");
- String syncable = parser.getAttributeValue(null, "syncable");
- AuthorityInfo authority = mAuthorities.get(id);
- if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
- + accountName + " auth=" + authorityName
- + " enabled=" + enabled
- + " syncable=" + syncable);
- if (authority == null) {
- if (DEBUG_FILE) Log.v(TAG, "Creating entry");
- authority = getOrCreateAuthorityLocked(
- new Account(accountName, accountType),
- authorityName, id, false);
- }
- if (authority != null) {
- authority.enabled = enabled == null
- || Boolean.parseBoolean(enabled);
- if ("unknown".equals(syncable)) {
- authority.syncable = -1;
- } else {
- authority.syncable =
- (syncable == null || Boolean.parseBoolean(enabled))
- ? 1
- : 0;
- }
- } else {
- Log.w(TAG, "Failure adding authority: account="
- + accountName + " auth=" + authorityName
- + " enabled=" + enabled
- + " syncable=" + syncable);
- }
+ } else if (parser.getDepth() == 4 && periodicSync != null) {
+ if ("extra".equals(tagName)) {
+ parseExtra(parser, periodicSync);
}
}
}
@@ -1249,6 +1375,105 @@ public class SyncStorageEngine extends Handler {
}
}
+ private AuthorityInfo parseAuthority(XmlPullParser parser) {
+ AuthorityInfo authority = null;
+ int id = -1;
+ try {
+ id = Integer.parseInt(parser.getAttributeValue(
+ null, "id"));
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "error parsing the id of the authority", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "the id of the authority is null", e);
+ }
+ if (id >= 0) {
+ String accountName = parser.getAttributeValue(null, "account");
+ String accountType = parser.getAttributeValue(null, "type");
+ if (accountType == null) {
+ accountType = "com.google";
+ }
+ String authorityName = parser.getAttributeValue(null, "authority");
+ String enabled = parser.getAttributeValue(null, "enabled");
+ String syncable = parser.getAttributeValue(null, "syncable");
+ authority = mAuthorities.get(id);
+ if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
+ + accountName + " auth=" + authorityName
+ + " enabled=" + enabled
+ + " syncable=" + syncable);
+ if (authority == null) {
+ if (DEBUG_FILE) Log.v(TAG, "Creating entry");
+ authority = getOrCreateAuthorityLocked(
+ new Account(accountName, accountType), authorityName, id, false);
+ // clear this since we will read these later on
+ authority.periodicSyncs.clear();
+ }
+ if (authority != null) {
+ authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
+ if ("unknown".equals(syncable)) {
+ authority.syncable = -1;
+ } else {
+ authority.syncable =
+ (syncable == null || Boolean.parseBoolean(enabled)) ? 1 : 0;
+ }
+ } else {
+ Log.w(TAG, "Failure adding authority: account="
+ + accountName + " auth=" + authorityName
+ + " enabled=" + enabled
+ + " syncable=" + syncable);
+ }
+ }
+
+ return authority;
+ }
+
+ private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
+ Bundle extras = new Bundle();
+ String periodValue = parser.getAttributeValue(null, "period");
+ final long period;
+ try {
+ period = Long.parseLong(periodValue);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "error parsing the period of a periodic sync", e);
+ return null;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "the period of a periodic sync is null", e);
+ return null;
+ }
+ final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
+ authority.periodicSyncs.add(periodicSync);
+ return periodicSync;
+ }
+
+ private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
+ final Bundle extras = periodicSync.first;
+ String name = parser.getAttributeValue(null, "name");
+ String type = parser.getAttributeValue(null, "type");
+ String value1 = parser.getAttributeValue(null, "value1");
+ String value2 = parser.getAttributeValue(null, "value2");
+
+ try {
+ if ("long".equals(type)) {
+ extras.putLong(name, Long.parseLong(value1));
+ } else if ("integer".equals(type)) {
+ extras.putInt(name, Integer.parseInt(value1));
+ } else if ("double".equals(type)) {
+ extras.putDouble(name, Double.parseDouble(value1));
+ } else if ("float".equals(type)) {
+ extras.putFloat(name, Float.parseFloat(value1));
+ } else if ("boolean".equals(type)) {
+ extras.putBoolean(name, Boolean.parseBoolean(value1));
+ } else if ("string".equals(type)) {
+ extras.putString(name, value1);
+ } else if ("account".equals(type)) {
+ extras.putParcelable(name, new Account(value1, value2));
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "error parsing bundle value", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "error parsing bundle value", e);
+ }
+ }
+
/**
* Write all account information to the account file.
*/
@@ -1284,6 +1509,41 @@ public class SyncStorageEngine extends Handler {
} else if (authority.syncable == 0) {
out.attribute(null, "syncable", "false");
}
+ for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
+ out.startTag(null, "periodicSync");
+ out.attribute(null, "period", Long.toString(periodicSync.second));
+ final Bundle extras = periodicSync.first;
+ for (String key : extras.keySet()) {
+ out.startTag(null, "extra");
+ out.attribute(null, "name", key);
+ final Object value = extras.get(key);
+ if (value instanceof Long) {
+ out.attribute(null, "type", "long");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof Integer) {
+ out.attribute(null, "type", "integer");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof Boolean) {
+ out.attribute(null, "type", "boolean");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof Float) {
+ out.attribute(null, "type", "float");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof Double) {
+ out.attribute(null, "type", "double");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof String) {
+ out.attribute(null, "type", "string");
+ out.attribute(null, "value1", value.toString());
+ } else if (value instanceof Account) {
+ out.attribute(null, "type", "account");
+ out.attribute(null, "value1", ((Account)value).name);
+ out.attribute(null, "value2", ((Account)value).type);
+ }
+ out.endTag(null, "extra");
+ }
+ out.endTag(null, "periodicSync");
+ }
out.endTag(null, "authority");
}
@@ -1389,6 +1649,7 @@ public class SyncStorageEngine extends Handler {
st.numSourcePoll = getIntColumn(c, "numSourcePoll");
st.numSourceServer = getIntColumn(c, "numSourceServer");
st.numSourceUser = getIntColumn(c, "numSourceUser");
+ st.numSourcePeriodic = 0;
st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
st.lastFailureSource = getIntColumn(c, "lastFailureSource");
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index f64261c1d0f9..7776520f3cd7 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -29,7 +29,7 @@ public abstract class SQLiteClosable {
synchronized(mLock) {
if (mReferenceCount <= 0) {
throw new IllegalStateException(
- "attempt to acquire a reference on a close SQLiteClosable");
+ "attempt to acquire a reference on an already-closed SQLiteClosable obj.");
}
mReferenceCount++;
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index e4b0191c1446..540f4445ba12 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -66,64 +66,60 @@ public class SQLiteDatabase extends SQLiteClosable {
* Algorithms used in ON CONFLICT clause
* http://www.sqlite.org/lang_conflict.html
*/
- public static final class ConflictAlgorithm {
- /**
- * When a constraint violation occurs, an immediate ROLLBACK occurs,
- * thus ending the current transaction, and the command aborts with a
- * return code of SQLITE_CONSTRAINT. If no transaction is active
- * (other than the implied transaction that is created on every command)
- * then this algorithm works the same as ABORT.
- */
- public static final int ROLLBACK = 1;
-
- /**
- * When a constraint violation occurs,no ROLLBACK is executed
- * so changes from prior commands within the same transaction
- * are preserved. This is the default behavior.
- */
- public static final int ABORT = 2;
+ /**
+ * When a constraint violation occurs, an immediate ROLLBACK occurs,
+ * thus ending the current transaction, and the command aborts with a
+ * return code of SQLITE_CONSTRAINT. If no transaction is active
+ * (other than the implied transaction that is created on every command)
+ * then this algorithm works the same as ABORT.
+ */
+ public static final int CONFLICT_ROLLBACK = 1;
- /**
- * When a constraint violation occurs, the command aborts with a return
- * code SQLITE_CONSTRAINT. But any changes to the database that
- * the command made prior to encountering the constraint violation
- * are preserved and are not backed out.
- */
- public static final int FAIL = 3;
+ /**
+ * When a constraint violation occurs,no ROLLBACK is executed
+ * so changes from prior commands within the same transaction
+ * are preserved. This is the default behavior.
+ */
+ public static final int CONFLICT_ABORT = 2;
- /**
- * When a constraint violation occurs, the one row that contains
- * the constraint violation is not inserted or changed.
- * But the command continues executing normally. Other rows before and
- * after the row that contained the constraint violation continue to be
- * inserted or updated normally. No error is returned.
- */
- public static final int IGNORE = 4;
+ /**
+ * When a constraint violation occurs, the command aborts with a return
+ * code SQLITE_CONSTRAINT. But any changes to the database that
+ * the command made prior to encountering the constraint violation
+ * are preserved and are not backed out.
+ */
+ public static final int CONFLICT_FAIL = 3;
- /**
- * When a UNIQUE constraint violation occurs, the pre-existing rows that
- * are causing the constraint violation are removed prior to inserting
- * or updating the current row. Thus the insert or update always occurs.
- * The command continues executing normally. No error is returned.
- * If a NOT NULL constraint violation occurs, the NULL value is replaced
- * by the default value for that column. If the column has no default
- * value, then the ABORT algorithm is used. If a CHECK constraint
- * violation occurs then the IGNORE algorithm is used. When this conflict
- * resolution strategy deletes rows in order to satisfy a constraint,
- * it does not invoke delete triggers on those rows.
- * This behavior might change in a future release.
- */
- public static final int REPLACE = 5;
+ /**
+ * When a constraint violation occurs, the one row that contains
+ * the constraint violation is not inserted or changed.
+ * But the command continues executing normally. Other rows before and
+ * after the row that contained the constraint violation continue to be
+ * inserted or updated normally. No error is returned.
+ */
+ public static final int CONFLICT_IGNORE = 4;
- /**
- * use the following when no conflict action is specified.
- */
- public static final int NONE = 0;
- private static final String[] VALUES = new String[]
- {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
+ /**
+ * When a UNIQUE constraint violation occurs, the pre-existing rows that
+ * are causing the constraint violation are removed prior to inserting
+ * or updating the current row. Thus the insert or update always occurs.
+ * The command continues executing normally. No error is returned.
+ * If a NOT NULL constraint violation occurs, the NULL value is replaced
+ * by the default value for that column. If the column has no default
+ * value, then the ABORT algorithm is used. If a CHECK constraint
+ * violation occurs then the IGNORE algorithm is used. When this conflict
+ * resolution strategy deletes rows in order to satisfy a constraint,
+ * it does not invoke delete triggers on those rows.
+ * This behavior might change in a future release.
+ */
+ public static final int CONFLICT_REPLACE = 5;
- private ConflictAlgorithm() {} // disable instantiation of this class
- }
+ /**
+ * use the following when no conflict action is specified.
+ */
+ public static final int CONFLICT_NONE = 0;
+ private static final String[] CONFLICT_VALUES = new String[]
+ {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
/**
* Maximum Length Of A LIKE Or GLOB Pattern
@@ -290,10 +286,6 @@ public class SQLiteDatabase extends SQLiteClosable {
@Override
protected void onAllReferencesReleased() {
if (isOpen()) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " +
- getDatabaseName(mPath) + ";");
- }
if (SQLiteDebug.DEBUG_SQL_CACHE) {
mTimeClosed = getTime();
}
@@ -782,7 +774,14 @@ public class SQLiteDatabase extends SQLiteClosable {
SQLiteDatabase db = null;
try {
// Open the database.
- return new SQLiteDatabase(path, factory, flags);
+ SQLiteDatabase sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+ if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+ sqliteDatabase.enableSqlTracing(path);
+ }
+ if (SQLiteDebug.DEBUG_SQL_TIME) {
+ sqliteDatabase.enableSqlProfiling(path);
+ }
+ return sqliteDatabase;
} catch (SQLiteDatabaseCorruptException e) {
// Try to recover from this, if we can.
// TODO: should we do this for other open failures?
@@ -1338,7 +1337,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long insert(String table, String nullColumnHack, ContentValues values) {
try {
- return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE);
+ return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + values, e);
return -1;
@@ -1360,7 +1359,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
throws SQLException {
- return insertWithOnConflict(table, nullColumnHack, values, ConflictAlgorithm.NONE);
+ return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}
/**
@@ -1377,7 +1376,7 @@ public class SQLiteDatabase extends SQLiteClosable {
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
try {
return insertWithOnConflict(table, nullColumnHack, initialValues,
- ConflictAlgorithm.REPLACE);
+ CONFLICT_REPLACE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + initialValues, e);
return -1;
@@ -1399,7 +1398,7 @@ public class SQLiteDatabase extends SQLiteClosable {
public long replaceOrThrow(String table, String nullColumnHack,
ContentValues initialValues) throws SQLException {
return insertWithOnConflict(table, nullColumnHack, initialValues,
- ConflictAlgorithm.REPLACE);
+ CONFLICT_REPLACE);
}
/**
@@ -1412,10 +1411,10 @@ public class SQLiteDatabase extends SQLiteClosable {
* @param initialValues this map contains the initial column values for the
* row. The keys should be the column names and the values the
* column values
- * @param conflictAlgorithm {@link ConflictAlgorithm} for insert conflict resolver
+ * @param conflictAlgorithm for insert conflict resolver
* @return the row ID of the newly inserted row
* OR the primary key of the existing row if the input param 'conflictAlgorithm' =
- * {@link ConflictAlgorithm#IGNORE}
+ * {@link #CONFLICT_IGNORE}
* OR -1 if any error
*/
public long insertWithOnConflict(String table, String nullColumnHack,
@@ -1427,7 +1426,7 @@ public class SQLiteDatabase extends SQLiteClosable {
// Measurements show most sql lengths <= 152
StringBuilder sql = new StringBuilder(152);
sql.append("INSERT");
- sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]);
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
// Measurements show most values lengths < 40
@@ -1551,7 +1550,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @return the number of rows affected
*/
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
- return updateWithOnConflict(table, values, whereClause, whereArgs, ConflictAlgorithm.NONE);
+ return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
}
/**
@@ -1562,7 +1561,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* valid value that will be translated to NULL.
* @param whereClause the optional WHERE clause to apply when updating.
* Passing null will update all rows.
- * @param conflictAlgorithm {@link ConflictAlgorithm} for update conflict resolver
+ * @param conflictAlgorithm for update conflict resolver
* @return the number of rows affected
*/
public int updateWithOnConflict(String table, ContentValues values,
@@ -1577,7 +1576,7 @@ public class SQLiteDatabase extends SQLiteClosable {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
- sql.append(ConflictAlgorithm.VALUES[conflictAlgorithm]);
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(table);
sql.append(" SET ");
@@ -1652,9 +1651,6 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public void execSQL(String sql) throws SQLException {
long timeStart = Debug.threadCpuTimeNanos();
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, null));
- }
lock();
try {
native_execSQL(sql);
@@ -1680,9 +1676,6 @@ public class SQLiteDatabase extends SQLiteClosable {
if (bindArgs == null) {
throw new IllegalArgumentException("Empty bindArgs");
}
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(this.getPath(), sql, bindArgs));
- }
long timeStart = Debug.threadCpuTimeNanos();
lock();
SQLiteStatement statement = null;
@@ -1741,10 +1734,6 @@ public class SQLiteDatabase extends SQLiteClosable {
mLeakedException = new IllegalStateException(path +
" SQLiteDatabase created and never closed");
mFactory = factory;
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.d(TAG, "captured_sql|" + mPath + "|ATTACH DATABASE '" + mPath +
- "' as " + getDatabaseName(mPath) + ";");
- }
dbopen(mPath, mFlags);
if (SQLiteDebug.DEBUG_SQL_CACHE) {
mTimeOpened = getTime();
@@ -1754,10 +1743,6 @@ public class SQLiteDatabase extends SQLiteClosable {
setLocale(Locale.getDefault());
} catch (RuntimeException e) {
Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.d(TAG, "captured_sql|" + mPath + "|DETACH DATABASE " +
- getDatabaseName(mPath) + ";");
- }
dbclose();
if (SQLiteDebug.DEBUG_SQL_CACHE) {
mTimeClosed = getTime();
@@ -1770,20 +1755,6 @@ public class SQLiteDatabase extends SQLiteClosable {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
}
- private String getDatabaseName(String path) {
- if (path == null || path.trim().length() == 0) {
- return "db not specified?";
- }
-
- if (path.equalsIgnoreCase(":memory:")) {
- return "memorydb";
- }
- String[] tokens = path.split("/");
- String[] lastNodeTokens = tokens[tokens.length - 1].split("\\.", 2);
- return (lastNodeTokens.length == 1) ? lastNodeTokens[0]
- : lastNodeTokens[0] + lastNodeTokens[1];
- }
-
/**
* return whether the DB is opened as read only.
* @return true if DB is opened as read only
@@ -1922,8 +1893,9 @@ public class SQLiteDatabase extends SQLiteClosable {
mCacheFullWarnings = 0;
// clear the cache
mCompiledQueries.clear();
- Log.w(TAG, "compiled-sql statement cache cleared for the database " +
- getPath());
+ Log.w(TAG, "Compiled-sql statement cache for database: " +
+ getPath() + " hit MAX size-limit too many times. " +
+ "Removing all compiled-sql statements from the cache.");
} else {
// clear just a single entry from cache
Set<String> keySet = mCompiledQueries.keySet();
@@ -2061,6 +2033,23 @@ public class SQLiteDatabase extends SQLiteClosable {
private native void dbopen(String path, int flags);
/**
+ * Native call to setup tracing of all sql statements
+ *
+ * @param path the full path to the database
+ */
+ private native void enableSqlTracing(String path);
+
+ /**
+ * Native call to setup profiling of all sql statements.
+ * currently, sqlite's profiling = printing of execution-time
+ * (wall-clock time) of each of the sql statements, as they
+ * are executed.
+ *
+ * @param path the full path to the database
+ */
+ private native void enableSqlProfiling(String path);
+
+ /**
* Native call to execute a raw SQL statement. {@link #lock} must be held
* when calling this method.
*
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index b12034af1f60..4ea680e44b6f 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -31,17 +31,17 @@ public final class SQLiteDebug {
Log.isLoggable("SQLiteStatements", Log.VERBOSE);
/**
- * Controls the printing of compiled-sql-statement cache stats.
+ * Controls the printing of wall-clock time taken to execute SQL statements
+ * as they are executed.
*/
- public static final boolean DEBUG_SQL_CACHE =
- Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE);
+ public static final boolean DEBUG_SQL_TIME =
+ Log.isLoggable("SQLiteTime", Log.VERBOSE);
/**
- * Controls the capturing and printing of complete sql statement including the bind args and
- * the database name.
+ * Controls the printing of compiled-sql-statement cache stats.
*/
- public static final boolean DEBUG_CAPTURE_SQL =
- Log.isLoggable("SQLiteCaptureSql", Log.VERBOSE);
+ public static final boolean DEBUG_SQL_CACHE =
+ Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE);
/**
* Controls the stack trace reporting of active cursors being
@@ -121,62 +121,4 @@ public final class SQLiteDebug {
static synchronized void notifyActiveCursorFinalized() {
sNumActiveCursorsFinalized++;
}
-
- /**
- * returns a message containing the given database name (path) and the string built by
- * replacing "?" characters in the given sql string with the corresponding
- * positional values from the given param bindArgs.
- *
- * @param path the database name
- * @param sql sql string with possibly "?" for bindargs
- * @param bindArgs args for "?"s in the above string
- * @return the String to be logged
- */
- /* package */ static String captureSql(String path, String sql, Object[] bindArgs) {
- // how many bindargs in sql
- sql = sql.trim();
- String args[] = sql.split("\\?");
- // how many "?"s in the given sql string?
- int varArgsInSql = (sql.endsWith("?")) ? args.length : args.length - 1;
-
- // how many bind args do we have in the given input param bindArgs
- int bindArgsLen = (bindArgs == null) ? 0 : bindArgs.length;
- if (varArgsInSql < bindArgsLen) {
- return "too many bindArgs provided. " +
- "# of bindArgs = " + bindArgsLen + ", # of varargs = " + varArgsInSql +
- "; sql = " + sql;
- }
-
- // if there are no bindArgs, we are done. log the sql as is.
- if (bindArgsLen == 0 && varArgsInSql == 0) {
- return logSql(path, sql);
- }
-
- StringBuilder buf = new StringBuilder();
-
- // take the supplied bindArgs and plug them into sql
- for (int i = 0; i < bindArgsLen; i++) {
- buf.append(args[i]);
- buf.append(bindArgs[i]);
- }
-
- // does given sql have more varArgs than the supplied bindArgs
- // if so, assign nulls to the extra varArgs in sql
- for (int i = bindArgsLen; i < varArgsInSql; i ++) {
- buf.append(args[i]);
- buf.append("null");
- }
-
- // if there are any characters left in the given sql string AFTER the last "?"
- // log them also. for example, if the given sql = "select * from test where a=? and b=1
- // then the following code appends " and b=1" string to buf.
- if (varArgsInSql < args.length) {
- buf.append(args[varArgsInSql]);
- }
- return logSql(path, buf.toString());
- }
-
- private static String logSql(String path, String sql) {
- return "captured_sql|" + path + "|" + sql + ";";
- }
}
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 1159c1de74c5..2d0aa3967a46 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,19 +16,10 @@
package android.database.sqlite;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import android.util.Log;
-
/**
* A base class for compiled SQLite programs.
*/
public abstract class SQLiteProgram extends SQLiteClosable {
- private static final String TAG = "SQLiteProgram";
/** The database this program is compiled against. */
protected SQLiteDatabase mDatabase;
@@ -53,16 +44,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
*/
protected int nStatement = 0;
- /**
- * stores all bindargs for debugging purposes
- */
- private Map<Integer, String> mBindArgs = null;
-
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.d(TAG, "processing sql: " + sql);
- }
-
mDatabase = db;
mSql = sql;
db.acquireReference();
@@ -120,7 +102,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
}
/**
- * @deprecated use this.compiledStatement.compile instead
+ * @deprecated This method is deprecated and must not be used.
*
* @param sql the SQL string to compile
* @param forceCompilation forces the SQL to be recompiled in the event that there is an
@@ -138,9 +120,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @param index The 1-based index to the parameter to bind null to
*/
public void bindNull(int index) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- addToBindArgs(index, "null");
- }
acquireReference();
try {
native_bind_null(index);
@@ -157,9 +136,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @param value The value to bind
*/
public void bindLong(int index, long value) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- addToBindArgs(index, value + "");
- }
acquireReference();
try {
native_bind_long(index, value);
@@ -176,9 +152,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @param value The value to bind
*/
public void bindDouble(int index, double value) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- addToBindArgs(index, value + "");
- }
acquireReference();
try {
native_bind_double(index, value);
@@ -195,9 +168,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @param value The value to bind
*/
public void bindString(int index, String value) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- addToBindArgs(index, "'" + value + "'");
- }
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
@@ -217,9 +187,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @param value The value to bind
*/
public void bindBlob(int index, byte[] value) {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- addToBindArgs(index, "blob");
- }
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
@@ -235,9 +202,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* Clears all existing bindings. Unset bindings are treated as NULL.
*/
public void clearBindings() {
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- mBindArgs = null;
- }
acquireReference();
try {
native_clear_bindings();
@@ -259,39 +223,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
}
/**
- * this method is called under the debug flag {@link SQLiteDebug.DEBUG_CAPTURE_SQL} only.
- * it collects the bindargs as they are called by the callers the bind... methods in this
- * class.
- */
- private void addToBindArgs(int index, String obj) {
- if (mBindArgs == null) {
- mBindArgs = new HashMap<Integer, String>();
- }
- mBindArgs.put(index, obj);
- }
-
- /**
- * constructs all the bindargs in sequence and returns a String Array of the values.
- * it uses the HashMap built up by the above method.
- *
- * @return the string array of bindArgs with the args arranged in sequence
- */
- /* package */ String[] getBindArgs() {
- if (mBindArgs == null) {
- return null;
- }
- Set<Integer> indexSet = mBindArgs.keySet();
- ArrayList<Integer> indexList = new ArrayList<Integer>(indexSet);
- Collections.sort(indexList);
- int len = indexList.size();
- String[] bindObjs = new String[len];
- for (int i = 0; i < len; i++) {
- bindObjs[i] = mBindArgs.get(indexList.get(i));
- }
- return bindObjs;
- }
-
- /**
+ * @deprecated This method is deprecated and must not be used.
* Compiles SQL into a SQLite program.
*
* <P>The database lock must be held when calling this method.
@@ -299,6 +231,10 @@ public abstract class SQLiteProgram extends SQLiteClosable {
*/
@Deprecated
protected final native void native_compile(String sql);
+
+ /**
+ * @deprecated This method is deprecated and must not be used.
+ */
@Deprecated
protected final native void native_finalize();
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index c34661da8385..5bcad4b9a56e 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -18,7 +18,6 @@ package android.database.sqlite;
import android.database.CursorWindow;
import android.os.Debug;
-import android.os.SystemClock;
import android.util.Log;
/**
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 0cee3c526a08..f1f5a2ad2034 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -17,7 +17,6 @@
package android.database.sqlite;
import android.os.Debug;
-import android.util.Log;
/**
* A pre-compiled statement against a {@link SQLiteDatabase} that can be reused.
@@ -27,8 +26,6 @@ import android.util.Log;
*/
public class SQLiteStatement extends SQLiteProgram
{
- private static final String TAG = "SQLiteStatement";
-
/**
* Don't use SQLiteStatement constructor directly, please use
* {@link SQLiteDatabase#compileStatement(String)}
@@ -52,12 +49,6 @@ public class SQLiteStatement extends SQLiteProgram
acquireReference();
try {
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.v(TAG, "execute() for [" + mSql + "]");
- }
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
- }
native_execute();
mDatabase.logTimeStat(mSql, timeStart);
} finally {
@@ -82,12 +73,6 @@ public class SQLiteStatement extends SQLiteProgram
acquireReference();
try {
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.v(TAG, "executeInsert() for [" + mSql + "]");
- }
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
- }
native_execute();
mDatabase.logTimeStat(mSql, timeStart);
return mDatabase.lastInsertRow();
@@ -111,12 +96,6 @@ public class SQLiteStatement extends SQLiteProgram
acquireReference();
try {
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.v(TAG, "simpleQueryForLong() for [" + mSql + "]");
- }
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
- }
long retValue = native_1x1_long();
mDatabase.logTimeStat(mSql, timeStart);
return retValue;
@@ -140,12 +119,6 @@ public class SQLiteStatement extends SQLiteProgram
acquireReference();
try {
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- Log.v(TAG, "simpleQueryForString() for [" + mSql + "]");
- }
- if (SQLiteDebug.DEBUG_CAPTURE_SQL) {
- Log.v(TAG, SQLiteDebug.captureSql(mDatabase.getPath(), mSql, getBindArgs()));
- }
String retValue = native_1x1_string();
mDatabase.logTimeStat(mSql, timeStart);
return retValue;
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index b0c39093976b..cecacaa88503 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -620,7 +620,10 @@ public class KeyboardView extends View implements View.OnClickListener {
if (mBuffer == null || mKeyboardChanged) {
if (mBuffer == null || mKeyboardChanged &&
(mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
- mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+ // Make sure our bitmap is at least 1x1
+ final int width = Math.max(1, getWidth());
+ final int height = Math.max(1, getHeight());
+ mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBuffer);
}
invalidateAllKeys();
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index f89ba9195ead..cb42d73d1aef 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -184,6 +184,20 @@ public final class Calendar {
* <P>Type: INTEGER (long)</P>
*/
public static final String _SYNC_DIRTY = "_sync_dirty";
+
+ /**
+ * The name of the account instance to which this row belongs, which when paired with
+ * {@link #ACCOUNT_TYPE} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
}
/**
@@ -579,20 +593,6 @@ public final class Calendar {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/event_entities");
- /**
- * The name of the account instance to which this row belongs, which when paired with
- * {@link #ACCOUNT_TYPE} identifies a specific account.
- * <P>Type: TEXT</P>
- */
- public static final String ACCOUNT_NAME = "_sync_account";
-
- /**
- * The type of account to which this row belongs, which when paired with
- * {@link #ACCOUNT_NAME} identifies a specific account.
- * <P>Type: TEXT</P>
- */
- public static final String ACCOUNT_TYPE = "_sync_account_type";
-
public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
return new EntityIteratorImpl(cursor, resolver);
}
diff --git a/core/java/android/speech/RecognitionManager.java b/core/java/android/speech/RecognitionManager.java
index 0d25b2fdd833..7915208ef2c1 100644
--- a/core/java/android/speech/RecognitionManager.java
+++ b/core/java/android/speech/RecognitionManager.java
@@ -216,7 +216,6 @@ public class RecognitionManager {
throw new IllegalArgumentException("intent must not be null");
}
checkIsCalledFromMainThread();
- checkIsCommandAllowed();
if (mConnection == null) { // first time connection
mConnection = new Connection();
if (!mContext.bindService(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
@@ -243,7 +242,6 @@ public class RecognitionManager {
*/
public void stopListening() {
checkIsCalledFromMainThread();
- checkIsCommandAllowed();
putMessage(Message.obtain(mHandler, MSG_STOP));
}
@@ -254,7 +252,6 @@ public class RecognitionManager {
*/
public void cancel() {
checkIsCalledFromMainThread();
- checkIsCommandAllowed();
putMessage(Message.obtain(mHandler, MSG_CANCEL));
}
@@ -265,12 +262,6 @@ public class RecognitionManager {
}
}
- private void checkIsCommandAllowed() {
- if (mService == null && mPendingTasks.isEmpty()) { // setListener message must be there
- throw new IllegalStateException("Listener must be set before any command is called");
- }
- }
-
private void putMessage(Message msg) {
if (mService == null) {
mPendingTasks.offer(msg);
@@ -281,6 +272,9 @@ public class RecognitionManager {
/** sends the actual message to the service */
private void handleStartListening(Intent recognizerIntent) {
+ if (!checkOpenConnection()) {
+ return;
+ }
try {
mService.startListening(recognizerIntent, mListener);
if (DBG) Log.d(TAG, "service start listening command succeded");
@@ -292,6 +286,9 @@ public class RecognitionManager {
/** sends the actual message to the service */
private void handleStopMessage() {
+ if (!checkOpenConnection()) {
+ return;
+ }
try {
mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeded");
@@ -303,6 +300,9 @@ public class RecognitionManager {
/** sends the actual message to the service */
private void handleCancelMessage() {
+ if (!checkOpenConnection()) {
+ return;
+ }
try {
mService.cancel(mListener);
if (DBG) Log.d(TAG, "service cancel command succeded");
@@ -311,6 +311,15 @@ public class RecognitionManager {
mListener.onError(ERROR_CLIENT);
}
}
+
+ private boolean checkOpenConnection() {
+ if (mService != null) {
+ return true;
+ }
+ mListener.onError(ERROR_CLIENT);
+ Log.e(TAG, "not connected to the recognition service");
+ return false;
+ }
/** changes the listener */
private void handleChangeListener(RecognitionListener listener) {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index c8396c4d4979..000e4ceded22 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -880,7 +880,7 @@ public abstract class Animation implements Cloneable {
region.inset(-1.0f, -1.0f);
if (mFillBefore) {
final Transformation previousTransformation = mPreviousTransformation;
- applyTransformation(0.0f, previousTransformation);
+ applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation);
}
}
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 98b259451ae1..1546dcd763bd 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -282,7 +282,9 @@ public class AnimationSet extends Animation {
final Animation a = animations.get(i);
temp.clear();
- a.applyTransformation(0.0f, temp);
+ final Interpolator interpolator = a.mInterpolator;
+ a.applyTransformation(interpolator != null ? interpolator.getInterpolation(0.0f)
+ : 0.0f, temp);
previousTransformation.compose(temp);
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 66a76315af10..fd6af05bd1d6 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -32,7 +32,6 @@ import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -307,6 +306,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* Handles one frame of a fling
*/
private FlingRunnable mFlingRunnable;
+
+ /**
+ * Handles scrolling between positions within the list.
+ */
+ private PositionScroller mPositionScroller;
/**
* The offset in pixels form the top of the AdapterView to the top
@@ -1588,6 +1592,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// let the fling runnable report it's new state which
// should be idle
mFlingRunnable.endFling();
+ if (mScrollY != 0) {
+ mScrollY = 0;
+ invalidate();
+ }
}
// Always hide the type filter
dismissPopup();
@@ -1935,9 +1943,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
} else {
int touchMode = mTouchMode;
if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
- mScrollY = 0;
if (mFlingRunnable != null) {
mFlingRunnable.endFling();
+
+ if (mScrollY != 0) {
+ mScrollY = 0;
+ invalidate();
+ }
}
}
}
@@ -2052,9 +2064,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (motionView != null) {
motionViewPrevTop = motionView.getTop();
}
+
// No need to do all this work if we're not going to move anyway
+ boolean atEdge = false;
if (incrementalDeltaY != 0) {
- trackMotionScroll(deltaY, incrementalDeltaY);
+ atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
}
// Check to see if we have bumped into the scroll limit
@@ -2064,7 +2078,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// supposed to be
final int motionViewRealTop = motionView.getTop();
final int motionViewNewTop = mMotionViewNewTop;
- if (motionViewRealTop != motionViewNewTop) {
+ if (atEdge) {
// Apply overscroll
mScrollY -= incrementalDeltaY - (motionViewRealTop - motionViewPrevTop);
@@ -2440,7 +2454,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
}
-
+
void startSpringback() {
if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) {
mTouchMode = TOUCH_MODE_OVERFLING;
@@ -2448,19 +2462,33 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
post(this);
}
}
-
+
void startOverfling(int initialVelocity) {
mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, 0, 0, 0, getHeight());
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
}
-
+
+ void startScroll(int distance, int duration) {
+ int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
+ mLastFlingY = initialY;
+ mScroller.startScroll(0, initialY, 0, distance, duration);
+ mTouchMode = TOUCH_MODE_FLING;
+ post(this);
+ }
+
private void endFling() {
mTouchMode = TOUCH_MODE_REST;
+
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
clearScrollingCache();
+
removeCallbacks(this);
+
+ if (mPositionScroller != null) {
+ removeCallbacks(mPositionScroller);
+ }
}
public void run() {
@@ -2553,6 +2581,278 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+
+
+ class PositionScroller implements Runnable {
+ private static final int SCROLL_DURATION = 400;
+
+ private static final int MOVE_DOWN_POS = 1;
+ private static final int MOVE_UP_POS = 2;
+ private static final int MOVE_DOWN_BOUND = 3;
+ private static final int MOVE_UP_BOUND = 4;
+
+ private int mMode;
+ private int mTargetPos;
+ private int mBoundPos;
+ private int mLastSeenPos;
+ private int mScrollDuration;
+ private int mExtraScroll;
+
+ PositionScroller() {
+ mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
+ }
+
+ void start(int position) {
+ final int firstPos = mFirstPosition;
+ final int lastPos = firstPos + getChildCount() - 1;
+
+ int viewTravelCount = 0;
+ if (position <= firstPos) {
+ viewTravelCount = firstPos - position + 1;
+ mMode = MOVE_UP_POS;
+ } else if (position >= lastPos) {
+ viewTravelCount = position - lastPos + 1;
+ mMode = MOVE_DOWN_POS;
+ } else {
+ // Already on screen, nothing to do
+ return;
+ }
+
+ if (viewTravelCount > 0) {
+ mScrollDuration = SCROLL_DURATION / viewTravelCount;
+ } else {
+ mScrollDuration = SCROLL_DURATION;
+ }
+ mTargetPos = position;
+ mBoundPos = INVALID_POSITION;
+ mLastSeenPos = INVALID_POSITION;
+
+ post(this);
+ }
+
+ void start(int position, int boundPosition) {
+ if (boundPosition == INVALID_POSITION) {
+ start(position);
+ return;
+ }
+
+ final int firstPos = mFirstPosition;
+ final int lastPos = firstPos + getChildCount() - 1;
+
+ int viewTravelCount = 0;
+ if (position < firstPos) {
+ final int boundPosFromLast = lastPos - boundPosition;
+ if (boundPosFromLast < 1) {
+ // Moving would shift our bound position off the screen. Abort.
+ return;
+ }
+
+ final int posTravel = firstPos - position + 1;
+ final int boundTravel = boundPosFromLast - 1;
+ if (boundTravel < posTravel) {
+ viewTravelCount = boundTravel;
+ mMode = MOVE_UP_BOUND;
+ } else {
+ viewTravelCount = posTravel;
+ mMode = MOVE_UP_POS;
+ }
+ } else if (position > lastPos) {
+ final int boundPosFromFirst = boundPosition - firstPos;
+ if (boundPosFromFirst < 1) {
+ // Moving would shift our bound position off the screen. Abort.
+ return;
+ }
+
+ final int posTravel = position - lastPos + 1;
+ final int boundTravel = boundPosFromFirst - 1;
+ if (boundTravel < posTravel) {
+ viewTravelCount = boundTravel;
+ mMode = MOVE_DOWN_BOUND;
+ } else {
+ viewTravelCount = posTravel;
+ mMode = MOVE_DOWN_POS;
+ }
+ } else {
+ // Already on screen, nothing to do
+ return;
+ }
+
+ if (viewTravelCount > 0) {
+ mScrollDuration = SCROLL_DURATION / viewTravelCount;
+ } else {
+ mScrollDuration = SCROLL_DURATION;
+ }
+ mTargetPos = position;
+ mBoundPos = boundPosition;
+ mLastSeenPos = INVALID_POSITION;
+
+ post(this);
+ }
+
+ void stop() {
+ removeCallbacks(this);
+ }
+
+ public void run() {
+ final int listHeight = getHeight();
+ final int firstPos = mFirstPosition;
+
+ switch (mMode) {
+ case MOVE_DOWN_POS: {
+ final int lastViewIndex = getChildCount() - 1;
+ final int lastPos = firstPos + lastViewIndex;
+
+ if (lastPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ post(this);
+ return;
+ }
+
+ final View lastView = getChildAt(lastViewIndex);
+ final int lastViewHeight = lastView.getHeight();
+ final int lastViewTop = lastView.getTop();
+ final int lastViewPixelsShowing = listHeight - lastViewTop;
+
+ smoothScrollBy(lastViewHeight - lastViewPixelsShowing + mExtraScroll,
+ mScrollDuration);
+
+ mLastSeenPos = lastPos;
+ if (lastPos != mTargetPos) {
+ post(this);
+ }
+ break;
+ }
+
+ case MOVE_DOWN_BOUND: {
+ final int nextViewIndex = 1;
+ if (firstPos == mBoundPos || getChildCount() <= nextViewIndex) {
+ return;
+ }
+ final int nextPos = firstPos + nextViewIndex;
+
+ if (nextPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ post(this);
+ return;
+ }
+
+ final View nextView = getChildAt(nextViewIndex);
+ final int nextViewHeight = nextView.getHeight();
+ final int nextViewTop = nextView.getTop();
+ final int extraScroll = mExtraScroll;
+ if (nextPos != mBoundPos) {
+ smoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll),
+ mScrollDuration);
+
+ mLastSeenPos = nextPos;
+
+ post(this);
+ } else {
+ if (nextViewTop > extraScroll) {
+ smoothScrollBy(nextViewTop - extraScroll, mScrollDuration);
+ }
+ }
+ break;
+ }
+
+ case MOVE_UP_POS: {
+ if (firstPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ post(this);
+ return;
+ }
+
+ final View firstView = getChildAt(0);
+ final int firstViewTop = firstView.getTop();
+
+ smoothScrollBy(firstViewTop - mExtraScroll, mScrollDuration);
+
+ mLastSeenPos = firstPos;
+
+ if (firstPos != mTargetPos) {
+ post(this);
+ }
+ break;
+ }
+
+ case MOVE_UP_BOUND: {
+ final int lastViewIndex = getChildCount() - 2;
+ if (lastViewIndex < 0) {
+ return;
+ }
+ final int lastPos = firstPos + lastViewIndex;
+
+ if (lastPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ post(this);
+ return;
+ }
+
+ final View lastView = getChildAt(lastViewIndex);
+ final int lastViewHeight = lastView.getHeight();
+ final int lastViewTop = lastView.getTop();
+ final int lastViewPixelsShowing = listHeight - lastViewTop;
+ mLastSeenPos = lastPos;
+ if (lastPos != mBoundPos) {
+ smoothScrollBy(-(lastViewPixelsShowing - mExtraScroll), mScrollDuration);
+ post(this);
+ } else {
+ final int bottom = listHeight - mExtraScroll;
+ final int lastViewBottom = lastViewTop + lastViewHeight;
+ if (bottom > lastViewBottom) {
+ smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Smoothly scroll to the specified adapter position. The view will
+ * scroll such that the indicated position is displayed.
+ * @param position Scroll to this adapter position.
+ */
+ public void smoothScrollToPosition(int position) {
+ if (mPositionScroller == null) {
+ mPositionScroller = new PositionScroller();
+ }
+ mPositionScroller.start(position);
+ }
+
+ /**
+ * Smoothly scroll to the specified adapter position. The view will
+ * scroll such that the indicated position is displayed, but it will
+ * stop early if scrolling further would scroll boundPosition out of
+ * view.
+ * @param position Scroll to this adapter position.
+ * @param boundPosition Do not scroll if it would move this adapter
+ * position out of view.
+ */
+ public void smoothScrollToPosition(int position, int boundPosition) {
+ if (mPositionScroller == null) {
+ mPositionScroller = new PositionScroller();
+ }
+ mPositionScroller.start(position, boundPosition);
+ }
+
+ /**
+ * Smoothly scroll by distance pixels over duration milliseconds.
+ * @param distance Distance to scroll in pixels.
+ * @param duration Duration of the scroll animation in milliseconds.
+ */
+ public void smoothScrollBy(int distance, int duration) {
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ } else {
+ mFlingRunnable.endFling();
+ }
+ mFlingRunnable.startScroll(distance, duration);
+ }
private void createScrollingCache() {
if (mScrollingCacheEnabled && !mCachingStarted) {
@@ -2588,11 +2888,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion
* began. Positive numbers mean the user's finger is moving down the screen.
* @param incrementalDeltaY Change in deltaY from the previous event.
+ * @return true if we're already at the beginning/end of the list and have nothing to do.
*/
- void trackMotionScroll(int deltaY, int incrementalDeltaY) {
+ boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {
final int childCount = getChildCount();
if (childCount == 0) {
- return;
+ return true;
}
final int firstTop = getChildAt(0).getTop();
@@ -2618,98 +2919,99 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
}
- final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
-
- if (spaceAbove >= absIncrementalDeltaY && spaceBelow >= absIncrementalDeltaY) {
- hideSelector();
- offsetChildrenTopAndBottom(incrementalDeltaY);
- if (!awakenScrollBars()) {
- invalidate();
- }
- mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
- } else {
- final int firstPosition = mFirstPosition;
+ final int firstPosition = mFirstPosition;
- if (firstPosition == 0 && firstTop >= listPadding.top && deltaY > 0) {
- // Don't need to move views down if the top of the first position is already visible
- return;
- }
+ if (firstPosition == 0 && firstTop >= listPadding.top && deltaY > 0) {
+ // Don't need to move views down if the top of the first position
+ // is already visible
+ return true;
+ }
- if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY < 0) {
- // Don't need to move views up if the bottom of the last position is already visible
- return;
- }
+ if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY < 0) {
+ // Don't need to move views up if the bottom of the last position
+ // is already visible
+ return true;
+ }
- final boolean down = incrementalDeltaY < 0;
+ final boolean down = incrementalDeltaY < 0;
- hideSelector();
+ hideSelector();
- final int headerViewsCount = getHeaderViewsCount();
- final int footerViewsStart = mItemCount - getFooterViewsCount();
+ final int headerViewsCount = getHeaderViewsCount();
+ final int footerViewsStart = mItemCount - getFooterViewsCount();
- int start = 0;
- int count = 0;
+ int start = 0;
+ int count = 0;
- if (down) {
- final int top = listPadding.top - incrementalDeltaY;
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getBottom() >= top) {
- break;
- } else {
- count++;
- int position = firstPosition + i;
- if (position >= headerViewsCount && position < footerViewsStart) {
- mRecycler.addScrapView(child);
-
- if (ViewDebug.TRACE_RECYCLER) {
- ViewDebug.trace(child,
- ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
- firstPosition + i, -1);
- }
+ if (down) {
+ final int top = listPadding.top - incrementalDeltaY;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getBottom() >= top) {
+ break;
+ } else {
+ count++;
+ int position = firstPosition + i;
+ if (position >= headerViewsCount && position < footerViewsStart) {
+ mRecycler.addScrapView(child);
+
+ if (ViewDebug.TRACE_RECYCLER) {
+ ViewDebug.trace(child,
+ ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
+ firstPosition + i, -1);
}
}
}
- } else {
- final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;
- for (int i = childCount - 1; i >= 0; i--) {
- final View child = getChildAt(i);
- if (child.getTop() <= bottom) {
- break;
- } else {
- start = i;
- count++;
- int position = firstPosition + i;
- if (position >= headerViewsCount && position < footerViewsStart) {
- mRecycler.addScrapView(child);
-
- if (ViewDebug.TRACE_RECYCLER) {
- ViewDebug.trace(child,
- ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
- firstPosition + i, -1);
- }
+ }
+ } else {
+ final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = getChildAt(i);
+ if (child.getTop() <= bottom) {
+ break;
+ } else {
+ start = i;
+ count++;
+ int position = firstPosition + i;
+ if (position >= headerViewsCount && position < footerViewsStart) {
+ mRecycler.addScrapView(child);
+
+ if (ViewDebug.TRACE_RECYCLER) {
+ ViewDebug.trace(child,
+ ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
+ firstPosition + i, -1);
}
}
}
}
+ }
+
+ mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
- mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
+ mBlockLayoutRequests = true;
- mBlockLayoutRequests = true;
+ if (count > 0) {
detachViewsFromParent(start, count);
- offsetChildrenTopAndBottom(incrementalDeltaY);
+ }
+ offsetChildrenTopAndBottom(incrementalDeltaY);
- if (down) {
- mFirstPosition += count;
- }
+ if (down) {
+ mFirstPosition += count;
+ }
- invalidate();
- fillGap(down);
- mBlockLayoutRequests = false;
+ invalidate();
- invokeOnItemScrollListener();
- awakenScrollBars();
+ final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
+ if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
+ fillGap(down);
}
+
+ mBlockLayoutRequests = false;
+
+ invokeOnItemScrollListener();
+ awakenScrollBars();
+
+ return false;
}
/**
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 405461a12bda..a4b20da1e2d3 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -18,14 +18,12 @@ package android.widget;
import com.android.internal.R;
-import java.util.ArrayList;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -35,6 +33,8 @@ import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ExpandableListConnector.PositionMetadata;
+import java.util.ArrayList;
+
/**
* A view that shows items in a vertically scrolling two-level list. This
* differs from the {@link ListView} by allowing two levels: groups which can
@@ -541,6 +541,12 @@ public class ExpandableListView extends ListView {
if (mOnGroupExpandListener != null) {
mOnGroupExpandListener.onGroupExpand(posMetadata.position.groupPos);
}
+
+ final int groupPos = posMetadata.position.groupPos;
+ final int groupFlatPos = posMetadata.position.flatListPos;
+
+ smoothScrollToPosition(groupFlatPos + mAdapter.getChildrenCount(groupPos),
+ groupFlatPos);
}
returnValue = true;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index b4e2790ae56e..ea5841a01641 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -360,7 +360,8 @@ 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.
- mTotalLength += lp.topMargin + lp.bottomMargin;
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
} else {
int oldHeight = Integer.MIN_VALUE;
@@ -385,8 +386,9 @@ public class LinearLayout extends ViewGroup {
}
final int childHeight = child.getMeasuredHeight();
- mTotalLength += childHeight + lp.topMargin +
- lp.bottomMargin + getNextLocationOffset(child);
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
+ lp.bottomMargin + getNextLocationOffset(child));
if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
@@ -459,8 +461,10 @@ public class LinearLayout extends ViewGroup {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
- mTotalLength += largestChildHeight + lp.topMargin+ lp.bottomMargin +
- getNextLocationOffset(child);
+ // Account for negative margins
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
+ lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
}
@@ -536,12 +540,14 @@ public class LinearLayout extends ViewGroup {
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
- mTotalLength += child.getMeasuredHeight() + lp.topMargin +
- lp.bottomMargin + getNextLocationOffset(child);
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
+ lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
// Add in our padding
- mTotalLength += mPaddingTop + mPaddingBottom;
+ mTotalLength += mPaddingTop + mPaddingBottom;
+ // TODO: Should we recompute the heightSpec based on the new total length?
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);
@@ -651,7 +657,8 @@ 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.
- mTotalLength += lp.leftMargin + lp.rightMargin;
+ 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).
@@ -686,8 +693,9 @@ public class LinearLayout extends ViewGroup {
}
final int childWidth = child.getMeasuredWidth();
- mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
- getNextLocationOffset(child);
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
+ lp.rightMargin + getNextLocationOffset(child));
if (useLargestChild) {
largestChildWidth = Math.max(childWidth, largestChildWidth);
@@ -772,8 +780,9 @@ public class LinearLayout extends ViewGroup {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
- mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
- getNextLocationOffset(child);
+ final int totalLength = mTotalLength;
+ mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
+ lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
@@ -843,8 +852,9 @@ public class LinearLayout extends ViewGroup {
}
}
- mTotalLength += child.getMeasuredWidth() + lp.leftMargin +
- lp.rightMargin + getNextLocationOffset(child);
+ 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;
@@ -875,6 +885,7 @@ public class LinearLayout extends ViewGroup {
// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;
+ // TODO: Should we update widthSize with the new total length?
// Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
// the most common case
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 2d36bc82f68e..fd18db491b12 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -39,6 +39,7 @@ import com.android.internal.R;
* A view for selecting a number
*
* For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ * @hide
*/
@Widget
public class NumberPicker extends LinearLayout {
diff --git a/core/java/com/android/internal/widget/WeightedLinearLayout.java b/core/java/com/android/internal/widget/WeightedLinearLayout.java
index b90204e7cd0d..3d09f08826f9 100644
--- a/core/java/com/android/internal/widget/WeightedLinearLayout.java
+++ b/core/java/com/android/internal/widget/WeightedLinearLayout.java
@@ -52,7 +52,8 @@ public class WeightedLinearLayout extends LinearLayout {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
- final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+ final int screenWidth = metrics.widthPixels;
+ final boolean isPortrait = screenWidth < metrics.heightPixels;
final int widthMode = getMode(widthMeasureSpec);
@@ -62,14 +63,13 @@ public class WeightedLinearLayout extends LinearLayout {
int height = getMeasuredHeight();
boolean measure = false;
- final int widthSize = getSize(widthMeasureSpec);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, EXACTLY);
final float widthWeight = isPortrait ? mMinorWeight : mMajorWeight;
if (widthMode == AT_MOST && widthWeight > 0.0f) {
- if (width < (widthSize * widthWeight)) {
- widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (widthSize * widthWeight),
+ if (width < (screenWidth * widthWeight)) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (screenWidth * widthWeight),
EXACTLY);
measure = true;
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 7fd58e89707d..1ffd265dd221 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -70,7 +70,6 @@ LOCAL_SRC_FILES:= \
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
- android_util_Base64.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1d22de340376..7c8df0366748 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -153,7 +153,6 @@ extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
-extern int register_android_util_Base64(JNIEnv* env);
extern int register_android_location_GpsLocationProvider(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
@@ -1266,7 +1265,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_server_BluetoothA2dpService),
REG_JNI(register_android_message_digest_sha1),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
- REG_JNI(register_android_util_Base64),
REG_JNI(register_android_location_GpsLocationProvider),
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 020aff4a5c3b..c19701073e82 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -143,12 +143,68 @@ done:
if (handle != NULL) sqlite3_close(handle);
}
+void sqlTrace(void *databaseName, const char *sql) {
+ LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
+}
+
+/* public native void enableSqlTracing(); */
+static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName)
+{
+ sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
+ char const *path = env->GetStringUTFChars(databaseName, NULL);
+ if (path == NULL) {
+ LOGE("Failure in enableSqlTracing(). VM ran out of memory?\n");
+ return; // VM would have thrown OutOfMemoryError
+ }
+ int len = strlen(path);
+ char *traceFuncArg = (char *)malloc(len + 1);
+ strncpy(traceFuncArg, path, len);
+ traceFuncArg[len-1] = NULL;
+ env->ReleaseStringUTFChars(databaseName, path);
+ sqlite3_trace(handle, &sqlTrace, (void *)traceFuncArg);
+ LOGI("will be printing all sql statements executed on database = %s\n", traceFuncArg);
+}
+
+void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
+ double d = tm/1000000.0;
+ LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
+}
+
+/* public native void enableSqlProfiling(); */
+static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName)
+{
+ sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
+ char const *path = env->GetStringUTFChars(databaseName, NULL);
+ if (path == NULL) {
+ LOGE("Failure in enableSqlProfiling(). VM ran out of memory?\n");
+ return; // VM would have thrown OutOfMemoryError
+ }
+ int len = strlen(path);
+ char *traceFuncArg = (char *)malloc(len + 1);
+ strncpy(traceFuncArg, path, len);
+ traceFuncArg[len-1] = NULL;
+ env->ReleaseStringUTFChars(databaseName, path);
+ sqlite3_profile(handle, &sqlProfile, (void *)traceFuncArg);
+ LOGI("will be printing execution time of all sql statements executed on database = %s\n",
+ traceFuncArg);
+}
+
/* public native void close(); */
static void dbclose(JNIEnv* env, jobject object)
{
sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
if (handle != NULL) {
+ // release the memory associated with the traceFuncArg in enableSqlTracing function
+ void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
+ if (traceFuncArg != NULL) {
+ free(traceFuncArg);
+ }
+ // release the memory associated with the traceFuncArg in enableSqlProfiling function
+ traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
+ if (traceFuncArg != NULL) {
+ free(traceFuncArg);
+ }
LOGV("Closing database: handle=%p\n", handle);
int result = sqlite3_close(handle);
if (result == SQLITE_OK) {
@@ -357,6 +413,8 @@ static JNINativeMethod sMethods[] =
/* name, signature, funcPtr */
{"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
{"dbclose", "()V", (void *)dbclose},
+ {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing},
+ {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling},
{"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
{"lastInsertRow", "()J", (void *)lastInsertRow},
{"lastChangeCount", "()I", (void *)lastChangeCount},
diff --git a/core/jni/android_util_Base64.cpp b/core/jni/android_util_Base64.cpp
deleted file mode 100644
index bc69747c1bcf..000000000000
--- a/core/jni/android_util_Base64.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/* //device/libs/android_runtime/android_util_Base64.cpp
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-/*********************************************************
-*
-* This code was copied from
-* system/extra/ssh/dropbear-0.49/libtomcrypt/src/misc/base64/base64_decode.c
-*
-*********************************************************/
-
-#define LOG_TAG "Base64"
-
-#include <utils/Log.h>
-
-#include <android_runtime/AndroidRuntime.h>
-
-#include "JNIHelp.h"
-
-#include <sys/errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <signal.h>
-
-namespace android {
-
-static const unsigned char map[256] = {
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
-255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
-255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-255, 255, 255, 255 };
-
-/**
- base64 decode a block of memory
- @param in The base64 data to decode
- @param inlen The length of the base64 data
- @param out [out] The destination of the binary decoded data
- @param outlen [in/out] The max size and resulting size of the decoded data
- @return 0 if successful
-*/
-int base64_decode(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen)
-{
- unsigned long t, x, y, z;
- unsigned char c;
- int g;
-
- g = 3;
- for (x = y = z = t = 0; x < inlen; x++) {
- c = map[in[x]&0xFF];
- if (c == 255) continue;
- /* the final = symbols are read and used to trim the remaining bytes */
- if (c == 254) {
- c = 0;
- /* prevent g < 0 which would potentially allow an overflow later */
- if (--g < 0) {
- return -3;
- }
- } else if (g != 3) {
- /* we only allow = to be at the end */
- return -4;
- }
-
- t = (t<<6)|c;
-
- if (++y == 4) {
- if (z + g > *outlen) {
- return -2;
- }
- out[z++] = (unsigned char)((t>>16)&255);
- if (g > 1) out[z++] = (unsigned char)((t>>8)&255);
- if (g > 2) out[z++] = (unsigned char)(t&255);
- y = t = 0;
- }
- }
- if (y != 0) {
- return -5;
- }
- *outlen = z;
- return 0;
-}
-
-static jbyteArray decodeBase64(JNIEnv *env, jobject jobj, jstring jdata)
-{
- const char * rawData = env->GetStringUTFChars(jdata, NULL);
- int stringLength = env->GetStringUTFLength(jdata);
-
- int resultLength = stringLength / 4 * 3;
- if (rawData[stringLength-1] == '=') {
- resultLength -= 1;
- if (rawData[stringLength-2] == '=') {
- resultLength -= 1;
- }
- }
-
- jbyteArray byteArray = env->NewByteArray(resultLength);
- jbyte* byteArrayData = env->GetByteArrayElements(byteArray, NULL);
-
- unsigned long outlen = resultLength;
- int result = base64_decode((const unsigned char*)rawData, stringLength, (unsigned char *)byteArrayData, &outlen);
- if (result != 0)
- memset((unsigned char *)byteArrayData, -result, resultLength);
-
- env->ReleaseStringUTFChars(jdata, rawData);
- env->ReleaseByteArrayElements(byteArray, byteArrayData, 0);
-
- return byteArray;
-}
-
-static const JNINativeMethod methods[] = {
- {"decodeBase64Native", "(Ljava/lang/String;)[B", (void*)decodeBase64 }
-};
-
-static const char* const kBase64PathName = "android/os/Base64Utils";
-
-int register_android_util_Base64(JNIEnv* env)
-{
- jclass clazz;
-
- clazz = env->FindClass(kBase64PathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Base64Utils");
-
- return AndroidRuntime::registerNativeMethods(
- env, kBase64PathName,
- methods, NELEM(methods));
-}
-
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1406b66459b6..713e725878e0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -679,6 +679,12 @@
android:label="@string/permlab_setWallpaperHints"
android:description="@string/permdesc_setWallpaperHints" />
+ <!-- Allows applications to set the system time -->
+ <permission android:name="android.permission.SET_TIME"
+ android:protectionLevel="signatureOrSystem"
+ android:label="@string/permlab_setTime"
+ android:description="@string/permdesc_setTime" />
+
<!-- Allows applications to set the system time zone -->
<permission android:name="android.permission.SET_TIME_ZONE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
diff --git a/core/res/res/drawable-hdpi/stat_sys_secure.png b/core/res/res/drawable-hdpi/stat_sys_secure.png
new file mode 100644
index 000000000000..4bae258ae823
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_secure.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_secure.png b/core/res/res/drawable-mdpi/stat_sys_secure.png
new file mode 100644
index 000000000000..5f9ae69cb215
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_secure.png
Binary files differ
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 231e11c2c327..7ae68f900b45 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -28,8 +28,8 @@
android:paddingBottom="3dip"
android:paddingLeft="3dip"
android:paddingRight="1dip"
- android:majorWeight="0.5"
- android:minorWeight="0.8">
+ android:majorWeight="0.65"
+ android:minorWeight="0.9">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 3dbfa259f2d8..cdc15c29ccc0 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -107,6 +107,7 @@
icons in the status bar that are not notifications. -->
<string-array name="status_bar_icon_order">
<item><xliff:g id="id">clock</xliff:g></item>
+ <item><xliff:g id="id">secure</xliff:g></item>
<item><xliff:g id="id">alarm_clock</xliff:g></item>
<item><xliff:g id="id">battery</xliff:g></item>
<item><xliff:g id="id">phone_signal</xliff:g></item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d1bfc683001b..4df570c30066 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1017,6 +1017,12 @@
configuration, and installed applications.</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_setTime">set time</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_setTime">Allows an application to change
+ the phone\'s clock time.</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_setTimeZone">set time zone</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_setTimeZone">Allows an application to change
diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
index 533338e027e2..1505a7c0ff3a 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -18,16 +18,23 @@ package android.content;
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
+import android.test.suitebuilder.annotation.SmallTest;
import android.test.mock.MockContext;
import android.test.mock.MockContentResolver;
import android.accounts.Account;
+import android.os.Bundle;
+
+import java.util.List;
+import java.io.File;
public class SyncStorageEngineTest extends AndroidTestCase {
/**
* Test that we handle the case of a history row being old enough to purge before the
* correcponding sync is finished. This can happen if the clock changes while we are syncing.
+ *
*/
+ @SmallTest
public void testPurgeActiveSync() throws Exception {
final Account account = new Account("a@example.com", "example.type");
final String authority = "testprovider";
@@ -41,7 +48,150 @@ public class SyncStorageEngineTest extends AndroidTestCase {
long historyId = engine.insertStartSyncEvent(
account, authority, time0, SyncStorageEngine.SOURCE_LOCAL);
long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
- engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
+ engine.stopSyncEvent(historyId, new Bundle(), time1 - time0, "yay", 0, 0);
+ }
+
+ /**
+ * Test that we can create, remove and retrieve periodic syncs
+ */
+ @SmallTest
+ public void testPeriodics() throws Exception {
+ final Account account1 = new Account("a@example.com", "example.type");
+ final Account account2 = new Account("b@example.com", "example.type.2");
+ final String authority = "testprovider";
+ final Bundle extras1 = new Bundle();
+ extras1.putString("a", "1");
+ final Bundle extras2 = new Bundle();
+ extras2.putString("a", "2");
+ final int period1 = 200;
+ final int period2 = 1000;
+
+ PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1);
+ PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1);
+ PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2);
+ PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2);
+
+ MockContentResolver mockResolver = new MockContentResolver();
+
+ SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
+ new TestContext(mockResolver, getContext()));
+
+ removePeriodicSyncs(engine, account1, authority);
+ removePeriodicSyncs(engine, account2, authority);
+
+ // this should add two distinct periodic syncs for account1 and one for account2
+ engine.addPeriodicSync(sync1.account, sync1.authority, sync1.extras, sync1.period);
+ engine.addPeriodicSync(sync2.account, sync2.authority, sync2.extras, sync2.period);
+ engine.addPeriodicSync(sync3.account, sync3.authority, sync3.extras, sync3.period);
+ engine.addPeriodicSync(sync4.account, sync4.authority, sync4.extras, sync4.period);
+
+ List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, authority);
+
+ assertEquals(2, syncs.size());
+
+ assertEquals(sync1, syncs.get(0));
+ assertEquals(sync3, syncs.get(1));
+
+ engine.removePeriodicSync(sync1.account, sync1.authority, sync1.extras);
+
+ syncs = engine.getPeriodicSyncs(account1, authority);
+ assertEquals(1, syncs.size());
+ assertEquals(sync3, syncs.get(0));
+
+ syncs = engine.getPeriodicSyncs(account2, authority);
+ assertEquals(1, syncs.size());
+ assertEquals(sync4, syncs.get(0));
+ }
+
+ private void removePeriodicSyncs(SyncStorageEngine engine, Account account, String authority) {
+ engine.setIsSyncable(account, authority, engine.getIsSyncable(account, authority));
+ List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority);
+ for (PeriodicSync sync : syncs) {
+ engine.removePeriodicSync(sync.account, sync.authority, sync.extras);
+ }
+ }
+
+ @SmallTest
+ public void testAuthorityPersistence() throws Exception {
+ final Account account1 = new Account("a@example.com", "example.type");
+ final Account account2 = new Account("b@example.com", "example.type.2");
+ final String authority1 = "testprovider1";
+ final String authority2 = "testprovider2";
+ final Bundle extras1 = new Bundle();
+ extras1.putString("a", "1");
+ final Bundle extras2 = new Bundle();
+ extras2.putString("a", "2");
+ extras2.putLong("b", 2);
+ extras2.putInt("c", 1);
+ extras2.putBoolean("d", true);
+ extras2.putDouble("e", 1.2);
+ extras2.putFloat("f", 4.5f);
+ extras2.putParcelable("g", account1);
+ final int period1 = 200;
+ final int period2 = 1000;
+
+ PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1);
+ PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1);
+ PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1);
+ PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2);
+ PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1);
+
+ MockContentResolver mockResolver = new MockContentResolver();
+
+ SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
+ new TestContext(mockResolver, getContext()));
+
+ removePeriodicSyncs(engine, account1, authority1);
+ removePeriodicSyncs(engine, account2, authority1);
+ removePeriodicSyncs(engine, account1, authority2);
+ removePeriodicSyncs(engine, account2, authority2);
+
+ engine.setMasterSyncAutomatically(false);
+
+ engine.setIsSyncable(account1, authority1, 1);
+ engine.setSyncAutomatically(account1, authority1, true);
+
+ engine.setIsSyncable(account2, authority1, 1);
+ engine.setSyncAutomatically(account2, authority1, true);
+
+ engine.setIsSyncable(account1, authority2, 1);
+ engine.setSyncAutomatically(account1, authority2, false);
+
+ engine.setIsSyncable(account2, authority2, 0);
+ engine.setSyncAutomatically(account2, authority2, true);
+
+ engine.addPeriodicSync(sync1.account, sync1.authority, sync1.extras, sync1.period);
+ engine.addPeriodicSync(sync2.account, sync2.authority, sync2.extras, sync2.period);
+ engine.addPeriodicSync(sync3.account, sync3.authority, sync3.extras, sync3.period);
+ engine.addPeriodicSync(sync4.account, sync4.authority, sync4.extras, sync4.period);
+ engine.addPeriodicSync(sync5.account, sync5.authority, sync5.extras, sync5.period);
+
+ engine.writeAllState();
+ engine.clearAndReadState();
+
+ List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, authority1);
+ assertEquals(2, syncs.size());
+ assertEquals(sync1, syncs.get(0));
+ assertEquals(sync2, syncs.get(1));
+
+ syncs = engine.getPeriodicSyncs(account1, authority2);
+ assertEquals(2, syncs.size());
+ assertEquals(sync3, syncs.get(0));
+ assertEquals(sync4, syncs.get(1));
+
+ syncs = engine.getPeriodicSyncs(account2, authority1);
+ assertEquals(1, syncs.size());
+ assertEquals(sync5, syncs.get(0));
+
+ assertEquals(true, engine.getSyncAutomatically(account1, authority1));
+ assertEquals(true, engine.getSyncAutomatically(account2, authority1));
+ assertEquals(false, engine.getSyncAutomatically(account1, authority2));
+ assertEquals(true, engine.getSyncAutomatically(account2, authority2));
+
+ assertEquals(1, engine.getIsSyncable(account1, authority1));
+ assertEquals(1, engine.getIsSyncable(account2, authority1));
+ assertEquals(1, engine.getIsSyncable(account1, authority2));
+ assertEquals(0, engine.getIsSyncable(account2, authority2));
}
}
@@ -49,15 +199,26 @@ class TestContext extends ContextWrapper {
ContentResolver mResolver;
+ private final Context mRealContext;
+
public TestContext(ContentResolver resolver, Context realContext) {
super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
+ mRealContext = realContext;
mResolver = resolver;
}
@Override
+ public File getFilesDir() {
+ return mRealContext.getFilesDir();
+ }
+
+ @Override
public void enforceCallingOrSelfPermission(String permission, String message) {
}
+ @Override
+ public void sendBroadcast(Intent intent) {
+ }
@Override
public ContentResolver getContentResolver() {
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java
deleted file mode 100644
index ea807bd64cb5..000000000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDebugTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-import junit.framework.TestCase;
-
-/**
- * Tests for the SQLiteDebug
- */
-public class SQLiteDebugTest extends TestCase {
- private static final String TEST_DB = "test.db";
-
- public void testCaptureSql() {
- String rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=? and b=1",
- new Object[] {"blah"});
- String expectedVal = "select * from t1 where a='blah' and b=1";
- assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
-
- rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=?",
- new Object[] {"blah"});
- expectedVal = "select * from t1 where a='blah'";
- assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
-
- rslt = SQLiteDebug.captureSql(TEST_DB, "select * from t1 where a=1",
- new Object[] {"blah"});
- assertTrue(rslt.startsWith("too many bindArgs provided."));
-
- rslt = SQLiteDebug.captureSql(TEST_DB, "update t1 set a=? where b=?",
- new Object[] {"blah", "foo"});
- expectedVal = "update t1 set a='blah' where b='foo'";
- assertTrue(rslt.equals("captured_sql|" + TEST_DB + "|" + expectedVal));
- }
-}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 0a2fe4c633a7..6041b837730e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -28,16 +28,16 @@ import java.util.ArrayList;
* {@hide}
*/
public class KeyStore {
- public static int NO_ERROR = 1;
- public static int LOCKED = 2;
- public static int UNINITIALIZED = 3;
- public static int SYSTEM_ERROR = 4;
- public static int PROTOCOL_ERROR = 5;
- public static int PERMISSION_DENIED = 6;
- public static int KEY_NOT_FOUND = 7;
- public static int VALUE_CORRUPTED = 8;
- public static int UNDEFINED_ACTION = 9;
- public static int WRONG_PASSWORD = 10;
+ public static final int NO_ERROR = 1;
+ public static final int LOCKED = 2;
+ public static final int UNINITIALIZED = 3;
+ public static final int SYSTEM_ERROR = 4;
+ public static final int PROTOCOL_ERROR = 5;
+ public static final int PERMISSION_DENIED = 6;
+ public static final int KEY_NOT_FOUND = 7;
+ public static final int VALUE_CORRUPTED = 8;
+ public static final int UNDEFINED_ACTION = 9;
+ public static final int WRONG_PASSWORD = 10;
private static final LocalSocketAddress sAddress = new LocalSocketAddress(
"keystore", LocalSocketAddress.Namespace.RESERVED);
@@ -56,8 +56,8 @@ public class KeyStore {
}
public byte[] get(byte[] key) {
- byte[][] values = execute('g', key);
- return (values == null) ? null : values[0];
+ ArrayList<byte[]> values = execute('g', key);
+ return (values == null || values.size() == 0) ? null : values.get(0);
}
public String get(String key) {
@@ -93,7 +93,8 @@ public class KeyStore {
}
public byte[][] saw(byte[] prefix) {
- return execute('s', prefix);
+ ArrayList<byte[]> values = execute('s', prefix);
+ return (values == null) ? null : values.toArray(new byte[values.size()][]);
}
public String[] saw(String prefix) {
@@ -148,7 +149,7 @@ public class KeyStore {
return mError;
}
- private byte[][] execute(int code, byte[]... parameters) {
+ private ArrayList<byte[]> execute(int code, byte[]... parameters) {
mError = PROTOCOL_ERROR;
for (byte[] parameter : parameters) {
@@ -179,7 +180,7 @@ public class KeyStore {
return null;
}
- ArrayList<byte[]> results = new ArrayList<byte[]>();
+ ArrayList<byte[]> values = new ArrayList<byte[]>();
while (true) {
int i, j;
if ((i = in.read()) == -1) {
@@ -188,16 +189,16 @@ public class KeyStore {
if ((j = in.read()) == -1) {
return null;
}
- byte[] result = new byte[i << 8 | j];
- for (i = 0; i < result.length; i += j) {
- if ((j = in.read(result, i, result.length - i)) == -1) {
+ byte[] value = new byte[i << 8 | j];
+ for (i = 0; i < value.length; i += j) {
+ if ((j = in.read(value, i, value.length - i)) == -1) {
return null;
}
}
- results.add(result);
+ values.add(value);
}
mError = NO_ERROR;
- return results.toArray(new byte[results.size()][]);
+ return values;
} catch (IOException e) {
// ignore
} finally {
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 2735aa2a31e2..bd3113b4da02 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -328,7 +328,8 @@ bool LayerBuffer::Source::transformed() const {
LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
const ISurface::BufferHeap& buffers)
- : Source(layer), mStatus(NO_ERROR), mBufferSize(0)
+ : Source(layer), mStatus(NO_ERROR), mBufferSize(0),
+ mUseEGLImageDirectly(true)
{
if (buffers.heap == NULL) {
// this is allowed, but in this case, it is illegal to receive
@@ -466,25 +467,38 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
#if defined(EGL_ANDROID_image_native_buffer)
if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
- copybit_device_t* copybit = mLayer.mBlitEngine;
- if (copybit && ourBuffer->supportsCopybit()) {
- // create our EGLImageKHR the first time
- err = initTempBuffer();
- if (err == NO_ERROR) {
+ err = INVALID_OPERATION;
+ if (ourBuffer->supportsCopybit()) {
+ // First, try to use the buffer as an EGLImage directly
+ if (mUseEGLImageDirectly) {
// NOTE: Assume the buffer is allocated with the proper USAGE flags
- const NativeBuffer& dst(mTempBuffer);
- region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
- err = copybit->stretch(copybit, &dst.img, &src.img,
- &dst.crop, &src.crop, &clip);
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ src.img.w, src.img.h, src.img.format,
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ src.img.w, src.img.handle, false);
+ err = mLayer.initializeEglImage(buffer, &mTexture);
if (err != NO_ERROR) {
- clearTempBufferImage();
+ mUseEGLImageDirectly = false;
+ }
+ }
+ copybit_device_t* copybit = mLayer.mBlitEngine;
+ if (copybit && err != NO_ERROR) {
+ // create our EGLImageKHR the first time
+ err = initTempBuffer();
+ if (err == NO_ERROR) {
+ // NOTE: Assume the buffer is allocated with the proper USAGE flags
+ const NativeBuffer& dst(mTempBuffer);
+ region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
+ err = copybit->stretch(copybit, &dst.img, &src.img,
+ &dst.crop, &src.crop, &clip);
+ if (err != NO_ERROR) {
+ clearTempBufferImage();
+ }
}
}
- } else {
- err = INVALID_OPERATION;
}
}
#endif
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index e03f92c7b1a4..3257b76c45e7 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -145,6 +145,7 @@ private:
mutable LayerBase::Texture mTexture;
mutable NativeBuffer mTempBuffer;
mutable sp<GraphicBuffer> mTempGraphicBuffer;
+ mutable bool mUseEGLImageDirectly;
};
class OverlaySource : public Source {
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index e7351dcbf926..492692047384 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -229,6 +229,8 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
mInputBuffer->release();
mInputBuffer = NULL;
}
+
+ mSeeking = false;
}
}
@@ -240,8 +242,6 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
- mSeeking = false;
-
if (err != OK) {
mReachedEOS = true;
memset((char *)data + size_done, 0, size_remaining);
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 51fcaf5f4438..c05d90a48248 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -512,7 +512,9 @@ static sp<MediaSource> CreateSourceForMime(const char *mime) {
sp<MediaExtractor> extractor = CreateExtractorFromURI(url);
- CHECK(extractor != NULL);
+ if (extractor == NULL) {
+ return NULL;
+ }
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
@@ -571,6 +573,10 @@ status_t Harness::testSeek(
sp<MediaSource> source = CreateSourceForMime(mime);
sp<MediaSource> seekSource = CreateSourceForMime(mime);
+ if (source == NULL || seekSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
CHECK_EQ(seekSource->start(), OK);
sp<MediaSource> codec = OMXCodec::Create(
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 81864bd791bc..b6e0aae74ee8 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -2092,7 +2092,20 @@ EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
if (native_buffer->common.version != sizeof(android_native_buffer_t))
return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
-
+
+ switch (native_buffer->format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGBA_5551:
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ break;
+ default:
+ return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+ }
+
native_buffer->common.incRef(&native_buffer->common);
return (EGLImageKHR)native_buffer;
}
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index a1a776f99029..fa25fa92f0ca 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -1628,6 +1628,11 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
return;
}
+ if (image == EGL_NO_IMAGE_KHR) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+
android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
ogles_error(c, GL_INVALID_VALUE);
@@ -1652,4 +1657,26 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
{
+ ogles_context_t* c = ogles_context_t::get();
+ if (target != GL_RENDERBUFFER_OES) {
+ ogles_error(c, GL_INVALID_ENUM);
+ return;
+ }
+
+ if (image == EGL_NO_IMAGE_KHR) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+
+ android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+ if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+
+ // well, we're not supporting this extension anyways
}
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index d2f8ced55cb0..145e25eaadc9 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -166,7 +166,8 @@ struct egl_display_t {
uint32_t magic;
DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS];
EGLint numTotalConfigs;
- volatile int32_t refs;
+ uint32_t refs;
+ Mutex lock;
egl_display_t() : magic('_dpy'), numTotalConfigs(0) { }
~egl_display_t() { magic = 0; }
@@ -644,7 +645,9 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
egl_display_t * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (android_atomic_inc(&dp->refs) > 0) {
+ Mutex::Autolock _l(dp->lock);
+
+ if (dp->refs > 0) {
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
return EGL_TRUE;
@@ -728,6 +731,7 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
}
if (res == EGL_TRUE) {
+ dp->refs++;
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
return EGL_TRUE;
@@ -743,7 +747,15 @@ EGLBoolean eglTerminate(EGLDisplay dpy)
egl_display_t* const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (android_atomic_dec(&dp->refs) != 1)
+
+ Mutex::Autolock _l(dp->lock);
+
+ if (dp->refs == 0) {
+ return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+ }
+
+ // this is specific to Android, display termination is ref-counted.
+ if (dp->refs > 1)
return EGL_TRUE;
EGLBoolean res = EGL_FALSE;
@@ -767,6 +779,7 @@ EGLBoolean eglTerminate(EGLDisplay dpy)
// TODO: all egl_object_t should be marked for termination
+ dp->refs--;
dp->numTotalConfigs = 0;
clearTLS();
return res;
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 1efa5a3e5179..b7eea2e3d5bc 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -802,8 +802,8 @@ public class TtsService extends Service implements OnCompletionListener {
}
if (synthAvailable) {
synthesizerLock.unlock();
+ processSpeechQueue();
}
- processSpeechQueue();
}
}
}
@@ -882,8 +882,8 @@ public class TtsService extends Service implements OnCompletionListener {
}
if (synthAvailable) {
synthesizerLock.unlock();
+ processSpeechQueue();
}
- processSpeechQueue();
}
}
}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index f3306513d815..c28cf440a4cd 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -242,6 +242,14 @@ class AlarmManagerService extends IAlarmManager.Stub {
setRepeating(type, bucketTime, interval, operation);
}
+ public void setTime(long millis) {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.SET_TIME",
+ "setTime");
+
+ SystemClock.setCurrentTimeMillis(millis);
+ }
+
public void setTimeZone(String tz) {
mContext.enforceCallingOrSelfPermission(
"android.permission.SET_TIME_ZONE",
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6795bdd20b0d..2f845e15e849 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -258,8 +258,11 @@ class BackupManagerService extends IBackupManager.Stub {
// snapshot the pending-backup set and work on that
ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
+ File oldJournal = mJournal;
synchronized (mQueueLock) {
- // Do we have any work to do?
+ // Do we have any work to do? Construct the work queue
+ // then release the synchronization lock to actually run
+ // the backup.
if (mPendingBackups.size() > 0) {
for (BackupRequest b: mPendingBackups.values()) {
queue.add(b);
@@ -268,20 +271,22 @@ class BackupManagerService extends IBackupManager.Stub {
mPendingBackups.clear();
// Start a new backup-queue journal file too
- File oldJournal = mJournal;
mJournal = null;
- // At this point, we have started a new journal file, and the old
- // file identity is being passed to the backup processing thread.
- // When it completes successfully, that old journal file will be
- // deleted. If we crash prior to that, the old journal is parsed
- // at next boot and the journaled requests fulfilled.
- (new PerformBackupTask(transport, queue, oldJournal)).run();
- } else {
- Log.v(TAG, "Backup requested but nothing pending");
- mWakelock.release();
}
}
+
+ if (queue.size() > 0) {
+ // At this point, we have started a new journal file, and the old
+ // file identity is being passed to the backup processing thread.
+ // When it completes successfully, that old journal file will be
+ // deleted. If we crash prior to that, the old journal is parsed
+ // at next boot and the journaled requests fulfilled.
+ (new PerformBackupTask(transport, queue, oldJournal)).run();
+ } else {
+ Log.v(TAG, "Backup requested but nothing pending");
+ mWakelock.release();
+ }
break;
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index e14a9734c8c9..1e7dd992c4a0 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -212,11 +212,13 @@ class PowerManagerService extends IPowerManager.Stub
private Sensor mLightSensor;
private boolean mLightSensorEnabled;
private float mLightSensorValue = -1;
+ private int mHighestLightSensorValue = -1;
private float mLightSensorPendingValue = -1;
private int mLightSensorScreenBrightness = -1;
private int mLightSensorButtonBrightness = -1;
private int mLightSensorKeyboardBrightness = -1;
private boolean mDimScreen = true;
+ private boolean mIsDocked = false;
private long mNextTimeout;
private volatile int mPokey = 0;
private volatile boolean mPokeAwakeOnSet = false;
@@ -363,6 +365,15 @@ class PowerManagerService extends IPowerManager.Stub
}
}
+ private final class DockReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ dockStateChanged(state);
+ }
+ }
+
/**
* Set the setting that determines whether the device stays on when plugged in.
* The argument is a bit string, with each bit specifying a power source that,
@@ -527,6 +538,9 @@ class PowerManagerService extends IPowerManager.Stub
filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(new BootCompletedReceiver(), filter);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+ mContext.registerReceiver(new DockReceiver(), filter);
// Listen for secure settings changes
mContext.getContentResolver().registerContentObserver(
@@ -1389,6 +1403,8 @@ class PowerManagerService extends IPowerManager.Stub
// clear current value so we will update based on the new conditions
// when the sensor is reenabled.
mLightSensorValue = -1;
+ // reset our highest light sensor value when the screen turns off
+ mHighestLightSensorValue = -1;
}
}
}
@@ -2059,15 +2075,40 @@ class PowerManagerService extends IPowerManager.Stub
}
};
+ private void dockStateChanged(int state) {
+ synchronized (mLocks) {
+ mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ if (mIsDocked) {
+ mHighestLightSensorValue = -1;
+ }
+ if ((mPowerState & SCREEN_ON_BIT) != 0) {
+ // force lights recalculation
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ lightSensorChangedLocked(value);
+ }
+ }
+ }
+
private void lightSensorChangedLocked(int value) {
if (mDebugLightSensor) {
Log.d(TAG, "lightSensorChangedLocked " + value);
}
+ // do not allow light sensor value to decrease
+ if (mHighestLightSensorValue < value) {
+ mHighestLightSensorValue = value;
+ }
+
if (mLightSensorValue != value) {
mLightSensorValue = value;
if ((mPowerState & BATTERY_LOW_BIT) == 0) {
- int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues);
+ // use maximum light sensor value seen since screen went on for LCD to avoid flicker
+ // we only do this if we are undocked, since lighting should be stable when
+ // stationary in a dock.
+ int lcdValue = getAutoBrightnessValue(
+ (mIsDocked ? value : mHighestLightSensorValue),
+ mLcdBacklightValues);
int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues);
int keyboardValue;
if (mKeyboardVisible) {
diff --git a/test-runner/android/test/TouchUtils.java b/test-runner/android/test/TouchUtils.java
index 962b2f9aab30..69c6d2d57d00 100644
--- a/test-runner/android/test/TouchUtils.java
+++ b/test-runner/android/test/TouchUtils.java
@@ -773,7 +773,7 @@ public class TouchUtils {
float xStep = (toX - fromX) / stepCount;
MotionEvent event = MotionEvent.obtain(downTime, eventTime,
- MotionEvent.ACTION_DOWN, fromX, y, 0);
+ MotionEvent.ACTION_DOWN, x, y, 0);
inst.sendPointerSync(event);
inst.waitForIdleSync();
@@ -787,7 +787,7 @@ public class TouchUtils {
}
eventTime = SystemClock.uptimeMillis();
- event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, fromX, y, 0);
+ event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
inst.sendPointerSync(event);
inst.waitForIdleSync();
}
diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index bff8fba2c6ac..0d29c358afde 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -5,8 +5,6 @@ LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner services
-LOCAL_STATIC_JAVA_LIBRARIES := gsf-client
-
# Resource unit tests use a private locale
LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c 160dpi -c 32dpi -c 240dpi
diff --git a/tests/CoreTests/android/content/SyncQueueTest.java b/tests/CoreTests/android/content/SyncQueueTest.java
index 5f4ab789bd0a..1da59d18e1a8 100644
--- a/tests/CoreTests/android/content/SyncQueueTest.java
+++ b/tests/CoreTests/android/content/SyncQueueTest.java
@@ -66,22 +66,22 @@ public class SyncQueueTest extends AndroidTestCase {
long now = SystemClock.elapsedRealtime() + 200;
- assertEquals(op6, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op6);
- assertEquals(op1, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op1);
- assertEquals(op4, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op4);
- assertEquals(op5, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op5);
- assertEquals(op2, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op2);
- assertEquals(op3, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op3);
}
@@ -109,32 +109,32 @@ public class SyncQueueTest extends AndroidTestCase {
long now = SystemClock.elapsedRealtime() + 200;
- assertEquals(op6, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op6);
- assertEquals(op1, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op1);
mSettings.setBackoff(ACCOUNT2, AUTHORITY3, now + 200, 5);
- assertEquals(op5, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
mSettings.setBackoff(ACCOUNT2, AUTHORITY3, SyncStorageEngine.NOT_IN_BACKOFF_MODE, 0);
- assertEquals(op4, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
mSettings.setDelayUntilTime(ACCOUNT2, AUTHORITY3, now + 200);
- assertEquals(op5, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
mSettings.setDelayUntilTime(ACCOUNT2, AUTHORITY3, 0);
- assertEquals(op4, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op4);
- assertEquals(op5, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op5);
- assertEquals(op2, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op2);
- assertEquals(op3, mSyncQueue.nextReadyToRun(now));
+ assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
mSyncQueue.remove(op3);
}