Merge "fix [2189862] Race condition in eglIntialize and eglDestroy"
diff --git a/Android.mk b/Android.mk
index 682f286..ab1e7ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -222,6 +222,7 @@
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 52648852..16ed39b 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=""android.permission.SET_TIME""
+ 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<android.content.PeriodicSync>"
+ 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"
@@ -192449,6 +192627,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"
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 2ec0b70..845c854 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -106,6 +106,9 @@
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 e65cdf1..f7cb227 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -431,6 +431,10 @@
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/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 414d963..19e741a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -271,7 +271,7 @@
}
/**
- * 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 @@
* 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 @@
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 @@
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 53c7935..9082003 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -277,7 +277,26 @@
} 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 cb42236..edb40ed 100755
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -27,6 +27,7 @@
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 eb2d7b1..b5587ed 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,6 +42,7 @@
import java.io.OutputStream;
import java.util.List;
import java.util.ArrayList;
+import java.util.Collection;
/**
@@ -966,6 +967,65 @@
}
/**
+ * 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 974a667..e0dfab5 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -32,6 +32,8 @@
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 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 b0f14c1..2d906ed 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -21,6 +21,7 @@
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 @@
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 @@
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 @@
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 @@
* 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/content/PeriodicSync.aidl b/core/java/android/content/PeriodicSync.aidl
new file mode 100644
index 0000000..4530591
--- /dev/null
+++ b/core/java/android/content/PeriodicSync.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable PeriodicSync;
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
new file mode 100644
index 0000000..17813ec
--- /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 699b61d..619c7d5 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -52,14 +52,7 @@
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 @@
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 @@
// 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 @@
// ignore the rest of the states -- leave our boolean alone.
}
if (mDataConnectionIsConnected) {
- initializeSyncPoll();
sendCheckAlarmsMessage();
}
}
@@ -291,14 +275,8 @@
};
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 @@
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 @@
}
}
- 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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;
+ // 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;
+ }
- if (processedAccounts.contains(curAccount)) {
- continue;
- }
-
- processedAccounts.add(curAccount);
-
- 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 @@
}
}
+ 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 a9f15d9..2eead3a 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -2,8 +2,6 @@
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 @@
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 @@
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 @@
* 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) {
+ if (best == 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) {
- return null;
- }
- return nextOpAndRunTime.second;
+ return Pair.create(best, bestRunTime);
}
/**
@@ -158,21 +138,25 @@
* 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 b8fda03..bb2b2da 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -20,10 +20,12 @@
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 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 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 @@
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 @@
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 @@
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 db70096..07a1f46 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -36,7 +36,6 @@
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.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 @@
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 @@
/** 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 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 @@
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 @@
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 @@
private int mYearInDays;
private final Context mContext;
+
private static volatile SyncStorageEngine sSyncStorageEngine = null;
/**
@@ -262,17 +272,15 @@
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 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 @@
}
} 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 @@
authority.backoffDelay = nextDelay;
changed = true;
}
- if (changed) {
- writeAccountInfoLocked();
- }
}
if (changed) {
@@ -499,12 +507,12 @@
+ " -> 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 @@
}
}
+ 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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 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 @@
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;
}
- 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() == 3) {
+ if ("periodicSync".equals(tagName) && authority != null) {
+ periodicSync = parsePeriodicSync(parser, authority);
+ }
+ } else if (parser.getDepth() == 4 && periodicSync != null) {
+ if ("extra".equals(tagName)) {
+ parseExtra(parser, periodicSync);
}
}
}
@@ -1249,6 +1375,105 @@
}
}
+ 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 @@
} 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 @@
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/provider/Calendar.java b/core/java/android/provider/Calendar.java
index f89ba91..cb42d73 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -184,6 +184,20 @@
* <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 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/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 66a7631..fd6af05 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -32,7 +32,6 @@
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 @@
* 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 @@
// 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 @@
} 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 @@
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 @@
// 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 @@
}
}
}
-
+
void startSpringback() {
if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) {
mTouchMode = TOUCH_MODE_OVERFLING;
@@ -2448,19 +2462,33 @@
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 @@
}
}
+
+
+ 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 @@
* @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 @@
incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
}
- final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
+ final int firstPosition = mFirstPosition;
- if (spaceAbove >= absIncrementalDeltaY && spaceBelow >= absIncrementalDeltaY) {
- hideSelector();
- offsetChildrenTopAndBottom(incrementalDeltaY);
- if (!awakenScrollBars()) {
- invalidate();
- }
- mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
- } else {
- 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 + childCount == mItemCount && lastBottom <= end && deltaY < 0) {
- // Don't need to move views up if the bottom of the last position is already visible
- return;
- }
-
- final boolean down = incrementalDeltaY < 0;
-
- hideSelector();
-
- final int headerViewsCount = getHeaderViewsCount();
- final int footerViewsStart = mItemCount - getFooterViewsCount();
-
- 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);
- }
- }
- }
- }
- } 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;
-
- mBlockLayoutRequests = true;
- detachViewsFromParent(start, count);
- offsetChildrenTopAndBottom(incrementalDeltaY);
-
- if (down) {
- mFirstPosition += count;
- }
-
- invalidate();
- fillGap(down);
- mBlockLayoutRequests = false;
-
- invokeOnItemScrollListener();
- awakenScrollBars();
+ 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 true;
+ }
+
+ final boolean down = incrementalDeltaY < 0;
+
+ hideSelector();
+
+ final int headerViewsCount = getHeaderViewsCount();
+ final int footerViewsStart = mItemCount - getFooterViewsCount();
+
+ 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);
+ }
+ }
+ }
+ }
+ } 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;
+
+ mBlockLayoutRequests = true;
+
+ if (count > 0) {
+ detachViewsFromParent(start, count);
+ }
+ offsetChildrenTopAndBottom(incrementalDeltaY);
+
+ if (down) {
+ mFirstPosition += count;
+ }
+
+ invalidate();
+
+ 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 405461a..a4b20da 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -18,14 +18,12 @@
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.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 @@
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 b4e2790..ea5841a 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -360,7 +360,8 @@
// 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 @@
}
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
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 @@
}
}
- 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 @@
// 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/com/android/internal/widget/WeightedLinearLayout.java b/core/java/com/android/internal/widget/WeightedLinearLayout.java
index b90204e..3d09f08 100644
--- a/core/java/com/android/internal/widget/WeightedLinearLayout.java
+++ b/core/java/com/android/internal/widget/WeightedLinearLayout.java
@@ -52,7 +52,8 @@
@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 @@
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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1406b66..713e7258 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 0000000..4bae258
--- /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 0000000..5f9ae69
--- /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 231e11c..7ae68f9 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 3dbfa25..cdc15c2 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 d1bfc68..4df570c 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 533338e..1505a7c 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -18,16 +18,23 @@
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 @@
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 @@
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/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 2735aa2..bd3113b 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -328,7 +328,8 @@
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 @@
#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;
}
}
- } else {
- err = INVALID_OPERATION;
+ 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();
+ }
+ }
+ }
}
}
#endif
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index e03f92c..3257b76 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -145,6 +145,7 @@
mutable LayerBase::Texture mTexture;
mutable NativeBuffer mTempBuffer;
mutable sp<GraphicBuffer> mTempGraphicBuffer;
+ mutable bool mUseEGLImageDirectly;
};
class OverlaySource : public Source {
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index d51ab30..b4c7212 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -54,7 +54,7 @@
LOCAL_SHARED_LIBRARIES += libdl
endif
-LOCAL_C_INCLUDES := external/tremor/Tremor \
+LOCAL_C_INCLUDES := external/tremolo/Tremolo \
$(JNI_H_INCLUDE) \
$(call include-path-for, graphics corecg) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index b25ce67..6b880e8 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -49,7 +49,7 @@
$(JNI_H_INCLUDE) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/external/opencore/android \
- $(TOP)/external/tremor/Tremor
+ $(TOP)/external/tremolo/Tremolo
LOCAL_SHARED_LIBRARIES := \
libbinder \
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 51fcaf5..c05d90a 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -512,7 +512,9 @@
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 @@
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 81864bd..b6e0aae 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -2092,7 +2092,20 @@
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 a1a776f..fa25fa9 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -1628,6 +1628,11 @@
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 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/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 1efa5a3..b7eea2e 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -802,8 +802,8 @@
}
if (synthAvailable) {
synthesizerLock.unlock();
+ processSpeechQueue();
}
- processSpeechQueue();
}
}
}
@@ -882,8 +882,8 @@
}
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 f330651..c28cf44 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -242,6 +242,14 @@
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 6795bdd..2f845e1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -258,8 +258,11 @@
// 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 @@
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 e14a973..1e7dd99 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -212,11 +212,13 @@
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 @@
}
}
+ 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 @@
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 @@
// 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 @@
}
};
+ 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/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index bff8fba..0d29c35 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -5,8 +5,6 @@
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 5f4ab78..1da59d1 100644
--- a/tests/CoreTests/android/content/SyncQueueTest.java
+++ b/tests/CoreTests/android/content/SyncQueueTest.java
@@ -66,22 +66,22 @@
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 @@
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);
}