Merge "Don't clear the framebuffer when not needed."
diff --git a/api/current.xml b/api/current.xml
index 953c9f3..a866669 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -112283,6 +112283,348 @@
 </method>
 </class>
 </package>
+<package name="android.nfc"
+>
+<class name="NdefMessage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="NdefMessage"
+ type="android.nfc.NdefMessage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="records" type="android.nfc.NdefRecord[]">
+</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="getRecords"
+ return="android.nfc.NdefRecord[]"
+ 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>
+</class>
+<class name="NdefRecord"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="NdefRecord"
+ type="android.nfc.NdefRecord"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tnf" type="short">
+</parameter>
+<parameter name="type" type="byte[]">
+</parameter>
+<parameter name="id" type="byte[]">
+</parameter>
+<parameter name="payload" type="byte[]">
+</parameter>
+</constructor>
+<constructor name="NdefRecord"
+ type="android.nfc.NdefRecord"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</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="getId"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPayload"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTnf"
+ return="short"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="byte[]"
+ 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="RTD_ALTERNATIVE_CARRIER"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_HANDOVER_CARRIER"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_HANDOVER_REQUEST"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_HANDOVER_SELECT"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_SMART_POSTER"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_TEXT"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTD_URI"
+ type="byte[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_ABSOLUTE_URI"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_EMPTY"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_EXTERNAL_TYPE"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_MIME_MEDIA"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_UNCHANGED"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_UNKNOWN"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TNF_WELL_KNOWN"
+ type="short"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
 <package name="android.opengl"
 >
 <class name="ETC1"
@@ -201101,7 +201443,7 @@
  static="false"
  final="false"
  deprecated="not deprecated"
- visibility="protected"
+ visibility="public"
 >
 <parameter name="event" type="android.view.DragEvent">
 </parameter>
@@ -202843,6 +203185,23 @@
 <parameter name="animation" type="android.view.animation.Animation">
 </parameter>
 </method>
+<method name="startDrag"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+<parameter name="thumbBuilder" type="android.view.View.DragThumbnailBuilder">
+</parameter>
+<parameter name="myWindowOnly" type="boolean">
+</parameter>
+</method>
 <method name="unscheduleDrawable"
  return="void"
  abstract="false"
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 950d339..c9115c5 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -16,6 +16,18 @@
 
 package android.content;
 
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
 import com.android.internal.R;
@@ -36,17 +48,6 @@
 import android.content.pm.RegisteredServicesCacheListener;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.format.DateUtils;
@@ -58,11 +59,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
-import java.util.Collection;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -81,30 +84,17 @@
     private static final long MAX_TIME_PER_SYNC;
 
     static {
-        String localSyncDelayString = SystemProperties.get("sync.local_sync_delay");
-        long localSyncDelay = 30 * 1000; // 30 seconds
-        if (localSyncDelayString != null) {
-            try {
-                localSyncDelay = Long.parseLong(localSyncDelayString);
-            } catch (NumberFormatException nfe) {
-                // ignore, use default
-            }
-        }
-        LOCAL_SYNC_DELAY = localSyncDelay;
-
-        String maxTimePerSyncString = SystemProperties.get("sync.max_time_per_sync");
-        long maxTimePerSync = 5 * 60 * 1000; // 5 minutes
-        if (maxTimePerSyncString != null) {
-            try {
-                maxTimePerSync = Long.parseLong(maxTimePerSyncString);
-            } catch (NumberFormatException nfe) {
-                // ignore, use default
-            }
-        }
-        MAX_TIME_PER_SYNC = maxTimePerSync;
+        MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = SystemProperties.getInt("sync.max_init_syncs", 5);
+        MAX_SIMULTANEOUS_REGULAR_SYNCS = SystemProperties.getInt("sync.max_regular_syncs", 2);
+        LOCAL_SYNC_DELAY =
+                SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
+        MAX_TIME_PER_SYNC =
+                SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
+        SYNC_NOTIFICATION_DELAY =
+                SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
     }
 
-    private static final long SYNC_NOTIFICATION_DELAY = 30 * 1000; // 30 seconds
+    private static final long SYNC_NOTIFICATION_DELAY;
 
     /**
      * When retrying a sync for the first time use this delay. After that
@@ -123,21 +113,21 @@
      */
     private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
 
-    /**
-     * An error notification is sent if sync of any of the providers has been failing for this long.
-     */
-    private static final long ERROR_NOTIFICATION_DELAY_MS = 1000 * 60 * 10; // 10 minutes
-
     private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
 
     private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
+    private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
+
+    private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
+    private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
 
     private Context mContext;
 
     private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
 
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
+    volatile private PowerManager.WakeLock mSyncManagerWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
 
@@ -147,10 +137,8 @@
     private final SyncStorageEngine mSyncStorageEngine;
     public final SyncQueue mSyncQueue;
 
-    private ActiveSyncContext mActiveSyncContext = null;
+    private final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
 
-    // set if the sync error indicator should be reported.
-    private boolean mNeedSyncErrorNotification = false;
     // set if the sync active indicator should be reported
     private boolean mNeedSyncActiveNotification = false;
 
@@ -200,6 +188,9 @@
 
     private final PowerManager mPowerManager;
 
+    private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
+    private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
+
     public void onAccountsUpdated(Account[] accounts) {
         // remember if this was the first time this was called after an update
         final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY;
@@ -207,11 +198,10 @@
 
         // if a sync is in progress yet it is no longer in the accounts list,
         // cancel it
-        ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (activeSyncContext != null) {
-            if (!ArrayUtils.contains(accounts, activeSyncContext.mSyncOperation.account)) {
+        for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+            if (!ArrayUtils.contains(accounts, currentSyncContext.mSyncOperation.account)) {
                 Log.d(TAG, "canceling sync since the account has been removed");
-                sendSyncFinishedOrCanceledMessage(activeSyncContext,
+                sendSyncFinishedOrCanceledMessage(currentSyncContext,
                         null /* no result since this is a cancel */);
             }
         }
@@ -238,6 +228,7 @@
             // If this was the bootup case then don't sync everything, instead only
             // sync those that have an unknown syncable state, which will give them
             // a chance to set their syncable state.
+
             boolean onlyThoseWithUnkownSyncableState = justBootedUp;
             scheduleSync(null, null, null, 0 /* no delay */, onlyThoseWithUnkownSyncableState);
         }
@@ -371,6 +362,15 @@
                 HANDLE_SYNC_ALARM_WAKE_LOCK);
         mHandleAlarmWakeLock.setReferenceCounted(false);
 
+        // This WakeLock is used to ensure that we stay awake while running the sync loop
+        // message handler. Normally we will hold a sync adapter wake lock while it is being
+        // synced but during the execution of the sync loop it might finish a sync for
+        // one sync adapter before starting the sync for the other sync adapter and we
+        // don't want the device to go to sleep during that window.
+        mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                SYNC_LOOP_WAKE_LOCK);
+        mSyncManagerWakeLock.setReferenceCounted(false);
+
         mSyncStorageEngine.addStatusChangeListener(
                 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
             public void onStatusChanged(int which) {
@@ -427,8 +427,8 @@
         Intent intent = new Intent();
         intent.setAction("android.content.SyncAdapter");
         intent.setComponent(syncAdapterInfo.componentName);
-        if (!mContext.bindService(intent, new InitializerServiceConnection(account, authority, mContext,
-                mMainHandler),
+        if (!mContext.bindService(intent,
+                new InitializerServiceConnection(account, authority, mContext, mMainHandler),
                 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) {
             Log.w(TAG, "initializeSyncAdapter: failed to bind to " + intent);
         }
@@ -508,8 +508,8 @@
      * @param requestedAccount the account to sync, may be null to signify all accounts
      * @param requestedAuthority the authority to sync, may be null to indicate all authorities
      * @param extras a Map of SyncAdapter-specific information to control
-*          syncs of a specific provider. Can be null. Is ignored
-*          if the url is null.
+     *          syncs of a specific provider. Can be null. Is ignored
+     *          if the url is null.
      * @param delay how many milliseconds in the future to wait before performing this
      * @param onlyThoseWithUnkownSyncableState
      */
@@ -613,16 +613,29 @@
                         continue;
                     }
 
-                    if (isLoggable) {
-                        Log.v(TAG, "scheduleSync:"
-                                + " delay " + delay
-                                + ", source " + source
-                                + ", account " + account
-                                + ", authority " + authority
-                                + ", extras " + extras);
+                    Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority);
+                    long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority);
+                    final long backoffTime = backoff != null ? backoff.first : 0;
+                    if (isSyncable < 0) {
+                        Bundle newExtras = new Bundle();
+                        newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+                        scheduleSyncOperation(
+                                new SyncOperation(account, source, authority, newExtras, 0,
+                                        backoffTime, delayUntil));
                     }
-                    scheduleSyncOperation(
-                            new SyncOperation(account, source, authority, extras, delay));
+                    if (!onlyThoseWithUnkownSyncableState) {
+                        if (isLoggable) {
+                            Log.v(TAG, "scheduleSync:"
+                                    + " delay " + delay
+                                    + ", source " + source
+                                    + ", account " + account
+                                    + ", authority " + authority
+                                    + ", extras " + extras);
+                        }
+                        scheduleSyncOperation(
+                                new SyncOperation(account, source, authority, extras, delay,
+                                        backoffTime, delayUntil));
+                    }
                 }
             }
         }
@@ -636,7 +649,8 @@
     }
 
     public SyncAdapterType[] getSyncAdapterTypes() {
-        final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos =
+        final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>
+                serviceInfos =
                 mSyncAdapters.getAllServices();
         SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
         int i = 0;
@@ -666,6 +680,14 @@
         mSyncHandler.sendMessage(msg);
     }
 
+    private void sendCancelSyncsMessage(final Account account, final String authority) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
+        Message msg = mSyncHandler.obtainMessage();
+        msg.what = SyncHandler.MESSAGE_CANCEL;
+        msg.obj = Pair.create(account, authority);
+        mSyncHandler.sendMessage(msg);
+    }
+
     class SyncHandlerMessagePayload {
         public final ActiveSyncContext activeSyncContext;
         public final SyncResult syncResult;
@@ -683,11 +705,6 @@
         }
     }
 
-    private void clearBackoffSetting(SyncOperation op) {
-        mSyncStorageEngine.setBackoff(op.account, op.authority,
-                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
-    }
-
     private void increaseBackoffSetting(SyncOperation op) {
         final long now = SystemClock.elapsedRealtime();
 
@@ -713,6 +730,9 @@
 
         mSyncStorageEngine.setBackoff(op.account, op.authority,
                 now + newDelayInMs, newDelayInMs);
+        synchronized (mSyncQueue) {
+            mSyncQueue.onBackoffChanged(op.account, op.authority, now + newDelayInMs);
+        }
     }
 
     private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
@@ -725,6 +745,9 @@
             newDelayUntilTime = 0;
         }
         mSyncStorageEngine.setDelayUntilTime(op.account, op.authority, newDelayUntilTime);
+        synchronized (mSyncQueue) {
+            mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
+        }
     }
 
     /**
@@ -733,23 +756,7 @@
      * @param authority limit the cancelations to syncs with this authority, if non-null
      */
     public void cancelActiveSync(Account account, String authority) {
-        ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (activeSyncContext != null) {
-            // if an authority was specified then only cancel the sync if it matches
-            if (account != null) {
-                if (!account.equals(activeSyncContext.mSyncOperation.account)) {
-                    return;
-                }
-            }
-            // if an account was specified then only cancel the sync if it matches
-            if (authority != null) {
-                if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
-                    return;
-                }
-            }
-            sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                    null /* no result since this is a cancel */);
-        }
+        sendCancelSyncsMessage(account, authority);
     }
 
     /**
@@ -758,22 +765,6 @@
      * @param syncOperation the SyncOperation to schedule
      */
     public void scheduleSyncOperation(SyncOperation syncOperation) {
-        // If this operation is expedited and there is a sync in progress then
-        // reschedule the current operation and send a cancel for it.
-        final ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (syncOperation.expedited && activeSyncContext != null) {
-            final boolean hasSameKey =
-                    activeSyncContext.mSyncOperation.key.equals(syncOperation.key);
-            // This request is expedited and there is a sync in progress.
-            // Interrupt the current sync only if it is not expedited and if it has a different
-            // key than the one we are scheduling.
-            if (!activeSyncContext.mSyncOperation.expedited && !hasSameKey) {
-                scheduleSyncOperation(new SyncOperation(activeSyncContext.mSyncOperation));
-                sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                        null /* no result since this is a cancel */);
-            }
-        }
-
         boolean queueChanged;
         synchronized (mSyncQueue) {
             queueChanged = mSyncQueue.add(syncOperation);
@@ -798,11 +789,11 @@
      * @param authority limit the removals to operations with this authority, if non-null
      */
     public void clearScheduledSyncOperations(Account account, String authority) {
-        mSyncStorageEngine.setBackoff(account, authority,
-                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
         synchronized (mSyncQueue) {
             mSyncQueue.remove(account, authority);
         }
+        mSyncStorageEngine.setBackoff(account, authority,
+                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
     }
 
     void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
@@ -829,7 +820,8 @@
         if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
             Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
                     + operation);
-        } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {
+        } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
+                && !syncResult.syncAlreadyInProgress) {
             operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
             Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
                     + "encountered an error: " + operation);
@@ -850,7 +842,8 @@
             }
             scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource,
                     operation.authority, operation.extras,
-                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000));
+                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
+                    operation.backoff, operation.delayUntil));
         } else if (syncResult.hasSoftError()) {
             if (isLoggable) {
                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
@@ -873,15 +866,33 @@
         final long mStartTime;
         long mTimeoutStartTime;
         boolean mBound;
+        final PowerManager.WakeLock mSyncWakeLock;
+        final int mSyncAdapterUid;
+        SyncInfo mSyncInfo;
 
-        public ActiveSyncContext(SyncOperation syncOperation,
-                long historyRowId) {
+        /**
+         * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
+         * sync adapter. Since this grabs the wakelock you need to be sure to call
+         * close() when you are done with this ActiveSyncContext, whether the sync succeeded
+         * or not.
+         * @param syncOperation the SyncOperation we are about to sync
+         * @param historyRowId the row in which to record the history info for this sync
+         * @param syncAdapterUid the UID of the application that contains the sync adapter
+         * for this sync. This is used to attribute the wakelock hold to that application.
+         */
+        public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
+                int syncAdapterUid) {
             super();
+            mSyncAdapterUid = syncAdapterUid;
             mSyncOperation = syncOperation;
             mHistoryRowId = historyRowId;
             mSyncAdapter = null;
             mStartTime = SystemClock.elapsedRealtime();
             mTimeoutStartTime = mStartTime;
+            mSyncWakeLock = mSyncHandler.getSyncWakeLock(
+                    mSyncOperation.account.type, mSyncOperation.authority);
+            mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
+            mSyncWakeLock.acquire();
         }
 
         public void sendHeartbeat() {
@@ -889,6 +900,7 @@
         }
 
         public void onFinished(SyncResult result) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
             // include "this" in the message so that the handler can ignore it if this
             // ActiveSyncContext is no longer the mActiveSyncContext at message handling
             // time
@@ -936,6 +948,10 @@
             return bindResult;
         }
 
+        /**
+         * Performs the required cleanup, which is the releasing of the wakelock and
+         * unbinding from the sync adapter (if actually bound).
+         */
         protected void close() {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
@@ -944,6 +960,8 @@
                 mBound = false;
                 mContext.unbindService(this);
             }
+            mSyncWakeLock.setWorkSource(null);
+            mSyncWakeLock.release();
         }
 
         @Override
@@ -1003,62 +1021,28 @@
             pw.println("no alarm is scheduled (there had better not be any pending syncs)");
         }
 
-        final SyncManager.ActiveSyncContext activeSyncContext = mActiveSyncContext;
-
-        pw.print("active sync: "); pw.println(activeSyncContext);
-
         pw.print("notification info: ");
         sb.setLength(0);
         mSyncHandler.mSyncNotificationInfo.toString(sb);
         pw.println(sb.toString());
 
+        pw.println();
+        pw.println("Active Syncs: " + mActiveSyncContexts.size());
+        for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+            final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
+            pw.print("  ");
+            pw.print(DateUtils.formatElapsedTime(durationInSeconds));
+            pw.print(" - ");
+            pw.print(activeSyncContext.mSyncOperation.dump(false));
+            pw.println();
+        }
+
         synchronized (mSyncQueue) {
-            pw.print("sync queue: ");
             sb.setLength(0);
             mSyncQueue.dump(sb);
-            pw.println(sb.toString());
         }
-
-        SyncInfo active = mSyncStorageEngine.getCurrentSync();
-        if (active != null) {
-            SyncStorageEngine.AuthorityInfo authority
-                    = mSyncStorageEngine.getAuthority(active.authorityId);
-            final long durationInSeconds = (now - active.startTime) / 1000;
-            pw.print("Active sync: ");
-                    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 {
-            pw.println("No sync is in progress.");
-        }
-
-        ArrayList<SyncStorageEngine.PendingOperation> ops
-                = mSyncStorageEngine.getPendingOperations();
-        if (ops != null && ops.size() > 0) {
-            pw.println();
-            pw.println("Pending Syncs");
-            final int N = ops.size();
-            for (int i=0; i<N; i++) {
-                SyncStorageEngine.PendingOperation op = ops.get(i);
-                pw.print("  #"); pw.print(i); pw.print(": account=");
-                pw.print(op.account.name); pw.print(":");
-                pw.print(op.account.type); pw.print(" authority=");
-                pw.print(op.authority); pw.print(" expedited=");
-                pw.println(op.expedited);
-                if (op.extras != null && op.extras.size() > 0) {
-                    sb.setLength(0);
-                    SyncOperation.extrasToStringBuilder(op.extras, sb, false /* asKey */);
-                    pw.print("    extras: "); pw.println(sb.toString());
-                }
-            }
-        }
+        pw.println();
+        pw.print(sb.toString());
 
         // join the installed sync adapter with the accounts list and emit for everything
         pw.println();
@@ -1261,7 +1245,7 @@
 
         /** Call to let the tracker know that the sync state may have changed */
         public synchronized void update() {
-            final boolean isSyncInProgress = mActiveSyncContext != null;
+            final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
             if (isSyncInProgress == mLastWasSyncing) return;
             final long now = SystemClock.elapsedRealtime();
             if (isSyncInProgress) {
@@ -1301,17 +1285,14 @@
         private static final int MESSAGE_CHECK_ALARMS = 3;
         private static final int MESSAGE_SERVICE_CONNECTED = 4;
         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
+        private static final int MESSAGE_CANCEL = 6;
 
         public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
         private Long mAlarmScheduleTime = null;
         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
-        private PowerManager.WakeLock mSyncWakeLock;
-        private final HashMap<Pair<String, String>,  PowerManager.WakeLock> mWakeLocks =
+        private final HashMap<Pair<String, String>, PowerManager.WakeLock> mWakeLocks =
                 Maps.newHashMap();
 
-        // used to track if we have installed the error notification so that we don't reinstall
-        // it if sync is still failing
-        private boolean mErrorNotificationInstalled = false;
         private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
         public void onBootCompleted() {
             mBootCompleted = true;
@@ -1351,12 +1332,6 @@
          * Used to keep track of whether a sync notification is active and who it is for.
          */
         class SyncNotificationInfo {
-            // only valid if isActive is true
-            public Account account;
-
-            // only valid if isActive is true
-            public String authority;
-
             // true iff the notification manager has been asked to send the notification
             public boolean isActive = false;
 
@@ -1365,10 +1340,7 @@
             public Long startTime = null;
 
             public void toString(StringBuilder sb) {
-                sb.append("account ").append(account)
-                        .append(", authority ").append(authority)
-                        .append(", isActive ").append(isActive)
-                        .append(", startTime ").append(startTime);
+                sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
             }
 
             @Override
@@ -1384,60 +1356,72 @@
         }
 
         public void handleMessage(Message msg) {
-            Long earliestFuturePollTime = null;
+            long earliestFuturePollTime = Long.MAX_VALUE;
+            long nextPendingSyncTime = Long.MAX_VALUE;
             try {
                 waitUntilReadyToRun();
+                mSyncManagerWakeLock.acquire();
                 // Always do this first so that we be sure that any periodic syncs that
                 // are ready to run have been converted into pending syncs. This allows the
                 // logic that considers the next steps to take based on the set of pending syncs
                 // to also take into account the periodic syncs.
                 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                 switch (msg.what) {
+                    case SyncHandler.MESSAGE_CANCEL: {
+                        Pair<Account, String> payload = (Pair<Account, String>)msg.obj;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
+                                    + payload.first + ", " + payload.second);
+                        }
+                        cancelActiveSyncLocked(payload.first, payload.second);
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
+                        break;
+                    }
+
                     case SyncHandler.MESSAGE_SYNC_FINISHED:
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
                         }
                         SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj;
-                        if (mActiveSyncContext != payload.activeSyncContext) {
-                            Log.d(TAG, "handleSyncHandlerMessage: sync context doesn't match, "
-                                    + "dropping: mActiveSyncContext " + mActiveSyncContext
-                                    + " != " + payload.activeSyncContext);
-                            return;
+                        if (!isSyncStillActive(payload.activeSyncContext)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+                                    + "sync is no longer active: "
+                                    + payload.activeSyncContext);
+                            break;
                         }
-                        runSyncFinishedOrCanceled(payload.syncResult);
+                        runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext);
 
-                        // since we are no longer syncing, check if it is time to start a new sync
-                        runStateIdle();
+                        // since a sync just finished check if it is time to start a new sync
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
                         break;
 
                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
                         ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
-                                    + msgData.activeSyncContext
-                                    + " active is " + mActiveSyncContext);
+                                    + msgData.activeSyncContext);
                         }
                         // check that this isn't an old message
-                        if (mActiveSyncContext == msgData.activeSyncContext) {
-                            runBoundToSyncAdapter(msgData.syncAdapter);
+                        if (isSyncStillActive(msgData.activeSyncContext)) {
+                            runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
                         }
                         break;
                     }
 
                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
-                        ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+                        final ActiveSyncContext currentSyncContext =
+                                ((ServiceConnectionData)msg.obj).activeSyncContext;
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
-                                    + msgData.activeSyncContext
-                                    + " active is " + mActiveSyncContext);
+                                    + currentSyncContext);
                         }
                         // check that this isn't an old message
-                        if (mActiveSyncContext == msgData.activeSyncContext) {
+                        if (isSyncStillActive(currentSyncContext)) {
                             // cancel the sync if we have a syncadapter, which means one is
                             // outstanding
-                            if (mActiveSyncContext.mSyncAdapter != null) {
+                            if (currentSyncContext.mSyncAdapter != null) {
                                 try {
-                                    mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
+                                    currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
                                 } catch (RemoteException e) {
                                     // we don't need to retry this in this case
                                 }
@@ -1447,11 +1431,10 @@
                             // which is a soft error
                             SyncResult syncResult = new SyncResult();
                             syncResult.stats.numIoExceptions++;
-                            runSyncFinishedOrCanceled(syncResult);
+                            runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
 
-                            // since we are no longer syncing, check if it is time to start a new
-                            // sync
-                            runStateIdle();
+                            // since a sync just finished check if it is time to start a new sync
+                            nextPendingSyncTime = maybeStartNextSyncLocked();
                         }
 
                         break;
@@ -1464,22 +1447,7 @@
                         }
                         mAlarmScheduleTime = null;
                         try {
-                            if (mActiveSyncContext != null) {
-                                if (isLoggable) {
-                                    Log.v(TAG, "handleSyncHandlerMessage: sync context is active");
-                                }
-                                runStateSyncing();
-                            }
-
-                            // if the above call to runStateSyncing() resulted in the end of a sync,
-                            // check if it is time to start a new sync
-                            if (mActiveSyncContext == null) {
-                                if (isLoggable) {
-                                    Log.v(TAG, "handleSyncHandlerMessage: "
-                                            + "sync context is not active");
-                                }
-                                runStateIdle();
-                            }
+                            nextPendingSyncTime = maybeStartNextSyncLocked();
                         } finally {
                             mHandleAlarmWakeLock.release();
                         }
@@ -1490,19 +1458,14 @@
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
                         }
-                        // we do all the work for this case in the finally block
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
                         break;
                 }
             } finally {
-                final boolean isSyncInProgress = mActiveSyncContext != null;
-                if (!isSyncInProgress && mSyncWakeLock != null) {
-                    mSyncWakeLock.release();
-                    mSyncWakeLock = null;
-                }
-                manageSyncNotification();
-                manageErrorNotification();
-                manageSyncAlarm(earliestFuturePollTime);
+                manageSyncNotificationLocked();
+                manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
                 mSyncTimeTracker.update();
+                mSyncManagerWakeLock.release();
             }
         }
 
@@ -1511,10 +1474,10 @@
          * @return the desired start time of the earliest future  periodic sync operation,
          * in milliseconds since boot
          */
-        private Long scheduleReadyPeriodicSyncs() {
+        private long scheduleReadyPeriodicSyncs() {
             final boolean backgroundDataUsageAllowed =
                     getConnectivityManager().getBackgroundDataSetting();
-            Long earliestFuturePollTime = null;
+            long earliestFuturePollTime = Long.MAX_VALUE;
             if (!backgroundDataUsageAllowed || !mSyncStorageEngine.getMasterSyncAutomatically()) {
                 return earliestFuturePollTime;
             }
@@ -1544,23 +1507,27 @@
                     long nextPollTimeAbsolute = lastPollTimeAbsolute + periodInSeconds * 1000;
                     // if it is ready to run then schedule it and mark it as having been scheduled
                     if (nextPollTimeAbsolute <= nowAbsolute) {
+                        final Pair<Long, Long> backoff =
+                                mSyncStorageEngine.getBackoff(info.account, info.authority);
                         scheduleSyncOperation(
                                 new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC,
-                                        info.authority, extras, 0 /* delay */));
+                                        info.authority, extras, 0 /* delay */,
+                                        backoff != null ? backoff.first : 0,
+                                        mSyncStorageEngine.getDelayUntilTime(
+                                                info.account, info.authority)));
                         status.setPeriodicSyncTime(i, nowAbsolute);
                     } else {
                         // it isn't ready to run, remember this time if it is earlier than
                         // earliestFuturePollTime
-                        if (earliestFuturePollTime == null
-                                || nextPollTimeAbsolute < earliestFuturePollTime) {
+                        if (nextPollTimeAbsolute < earliestFuturePollTime) {
                             earliestFuturePollTime = nextPollTimeAbsolute;
                         }
                     }
                 }
             }
 
-            if (earliestFuturePollTime == null) {
-                return null;
+            if (earliestFuturePollTime == Long.MAX_VALUE) {
+                return Long.MAX_VALUE;
             }
 
             // convert absolute time to elapsed time
@@ -1570,47 +1537,23 @@
                       : (earliestFuturePollTime - nowAbsolute));
         }
 
-        private void runStateSyncing() {
-            // if the sync timeout has been reached then cancel it
-            ActiveSyncContext activeSyncContext = mActiveSyncContext;
-
-            final long now = SystemClock.elapsedRealtime();
-            if (now > activeSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC) {
-                Pair<SyncOperation, Long> nextOpAndRunTime;
-                synchronized (mSyncQueue) {
-                    nextOpAndRunTime = mSyncQueue.nextOperation();
-                }
-                if (nextOpAndRunTime != null && nextOpAndRunTime.second <= now) {
-                    Log.d(TAG, "canceling and rescheduling sync because it ran too long: "
-                            + activeSyncContext.mSyncOperation);
-                    scheduleSyncOperation(new SyncOperation(activeSyncContext.mSyncOperation));
-                    sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                            null /* no result since this is a cancel */);
-                } else {
-                    activeSyncContext.mTimeoutStartTime = now + MAX_TIME_PER_SYNC;
-                }
-            }
-
-            // no need to schedule an alarm, as that will be done by our caller.
-        }
-
-        private void runStateIdle() {
-            boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-            if (isLoggable) Log.v(TAG, "runStateIdle");
+        private long maybeStartNextSyncLocked() {
+            final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+            if (isLoggable) Log.v(TAG, "maybeStartNextSync");
 
             // If we aren't ready to run (e.g. the data connection is down), get out.
             if (!mDataConnectionIsConnected) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: no data connection, skipping");
+                    Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             if (mStorageIsLow) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: memory low, skipping");
+                    Log.v(TAG, "maybeStartNextSync: memory low, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
@@ -1618,46 +1561,56 @@
             Account[] accounts = mAccounts;
             if (accounts == INITIAL_ACCOUNTS_ARRAY) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: accounts not known, skipping");
+                    Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             // Otherwise consume SyncOperations from the head of the SyncQueue until one is
             // found that is runnable (not disabled, etc). If that one is ready to run then
             // start it, otherwise just get out.
-            SyncOperation op;
-            int syncableState;
             final boolean backgroundDataUsageAllowed =
                     getConnectivityManager().getBackgroundDataSetting();
             final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
 
-            synchronized (mSyncQueue) {
-                final long now = SystemClock.elapsedRealtime();
-                while (true) {
-                    Pair<SyncOperation, Long> nextOpAndRunTime = mSyncQueue.nextOperation();
-                    if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
-                        if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: no more ready sync operations, returning");
-                        }
-                        return;
-                    }
-                    op = nextOpAndRunTime.first;
+            final long now = SystemClock.elapsedRealtime();
 
-                    // we are either going to run this sync or drop it so go ahead and
-                    // remove it from the queue now
-                    mSyncQueue.remove(op);
+            // will be set to the next time that a sync should be considered for running
+            long nextReadyToRunTime = Long.MAX_VALUE;
+
+            // order the sync queue, dropping syncs that are not allowed
+            ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
+            synchronized (mSyncQueue) {
+                if (isLoggable) {
+                    Log.v(TAG, "build the operation array, syncQueue size is "
+                        + mSyncQueue.mOperationsMap.size());
+                }
+                Iterator<SyncOperation> operationIterator =
+                        mSyncQueue.mOperationsMap.values().iterator();
+                while (operationIterator.hasNext()) {
+                    final SyncOperation op = operationIterator.next();
 
                     // drop the sync if the account of this operation no longer exists
                     if (!ArrayUtils.contains(mAccounts, op.account)) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
                         continue;
                     }
 
-
-                    // drop this sync request if it isn't syncable, intializing the sync adapter
-                    // if the syncable state is set to "unknown"
-                    syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
+                    // drop this sync request if it isn't syncable
+                    int syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
                     if (syncableState == 0) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+                        continue;
+                    }
+
+                    // if the next run time is in the future, meaning there are no syncs ready
+                    // to run, return the time
+                    if (op.effectiveRunTime > now) {
+                        if (nextReadyToRunTime > op.effectiveRunTime) {
+                            nextReadyToRunTime = op.effectiveRunTime;
+                        }
                         continue;
                     }
 
@@ -1669,30 +1622,139 @@
                                 || !backgroundDataUsageAllowed
                                 || !mSyncStorageEngine.getSyncAutomatically(
                                        op.account, op.authority))) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
                         continue;
                     }
 
-                    // go ahead and try to sync this syncOperation
-                    break;
-                }
-
-                // We will do this sync. Run it outside of the synchronized block.
-                if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: we are going to sync " + op);
+                    operations.add(op);
                 }
             }
 
-            // convert the op into an initialization sync if the syncable state is "unknown" and
-            // op isn't already an initialization sync. If it is marked syncable then convert
-            // this into a regular sync
-            final boolean initializeIsSet =
-                    op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
-            if (syncableState < 0 && !initializeIsSet) {
-                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
-                op = new SyncOperation(op);
-            } else if (syncableState > 0 && initializeIsSet) {
-                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
-                op = new SyncOperation(op);
+            // find the next operation to dispatch, if one is ready
+            // iterate from the top, keep issuing (while potentially cancelling existing syncs)
+            // until the quotas are filled.
+            // once the quotas are filled iterate once more to find when the next one would be
+            // (also considering pre-emption reasons).
+            if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
+            Collections.sort(operations);
+            if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
+            for (int i = 0, N = operations.size(); i < N; i++) {
+                final SyncOperation candidate = operations.get(i);
+                final boolean candidateIsInitialization = candidate.isInitialization();
+
+                int numInit = 0;
+                int numRegular = 0;
+                ActiveSyncContext conflict = null;
+                ActiveSyncContext longRunning = null;
+                ActiveSyncContext toReschedule = null;
+
+                for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+                    final SyncOperation activeOp = activeSyncContext.mSyncOperation;
+                    if (activeOp.isInitialization()) {
+                        numInit++;
+                    } else {
+                        numRegular++;
+                    }
+                    if (activeOp.account.type.equals(candidate.account.type)
+                            && activeOp.authority.equals(candidate.authority)) {
+                        conflict = activeSyncContext;
+                        // don't break out since we want to do a full count of the varieties
+                    } else {
+                        if (candidateIsInitialization == activeOp.isInitialization()
+                                && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
+                            longRunning = activeSyncContext;
+                            // don't break out since we want to do a full count of the varieties
+                        }
+                    }
+                }
+
+                if (isLoggable) {
+                    Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
+                    Log.v(TAG, "  numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
+                    Log.v(TAG, "  longRunning: " + longRunning);
+                    Log.v(TAG, "  conflict: " + conflict);
+                }
+
+                if (conflict != null) {
+                    if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
+                            && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
+                        toReschedule = conflict;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since an initialization "
+                                    + "takes higher priority, " + conflict);
+                        }
+                    } else if (candidate.expedited && !conflict.mSyncOperation.expedited
+                            && (candidateIsInitialization
+                                == conflict.mSyncOperation.isInitialization())) {
+                        toReschedule = conflict;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since an expedited "
+                                    + "takes higher priority, " + conflict);
+                        }
+                    } else {
+                        continue;
+                    }
+                } else {
+                    final boolean roomAvailable = candidateIsInitialization 
+                            ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS 
+                            : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
+                    if (roomAvailable) {
+                        // dispatch candidate
+                    } else if (longRunning != null
+                            && (candidateIsInitialization
+                                == longRunning.mSyncOperation.isInitialization())) {
+                        toReschedule = longRunning;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
+                                    + longRunning);
+                        }
+                    } else {
+                        continue;
+                    }
+                }
+
+                if (toReschedule != null) {
+                    runSyncFinishedOrCanceledLocked(null, toReschedule);
+                    scheduleSyncOperation(toReschedule.mSyncOperation);
+                }
+    
+                synchronized (mSyncQueue){
+                    mSyncQueue.remove(candidate);
+                }
+                dispatchSyncOperation(candidate);
+            }
+
+            return nextReadyToRunTime;
+     }
+
+        private boolean dispatchSyncOperation(SyncOperation op) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "maybeStartNextSync: we are going to sync " + op);
+                Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
+                for (ActiveSyncContext syncContext : mActiveSyncContexts) {
+                    Log.v(TAG, syncContext.toString());
+                }
+            }
+
+            // if this is an initialization sync and there is already a sync running with
+            // the same account type and authority cancel that sync before starting this one
+            // since otherwise the syncadapter will likely reject this request
+            if (op.isInitialization()) {
+                Iterator<ActiveSyncContext> iterator = mActiveSyncContexts.iterator();
+                while (iterator.hasNext()) {
+                    ActiveSyncContext syncContext = iterator.next();
+                    if (!syncContext.mSyncOperation.isInitialization()
+                            && syncContext.mSyncOperation.account.type.equals(op.account.type)
+                            && syncContext.mSyncOperation.authority.equals(op.authority)) {
+                        Log.d(TAG, "canceling and rescheduling " + syncContext.mSyncOperation
+                                + " since we are about to start a sync that used the "
+                                + "same sync adapter, " + op);
+                        iterator.remove();
+                        runSyncFinishedOrCanceledLocked(null, syncContext);
+                        scheduleSyncOperation(syncContext.mSyncOperation);
+                    }
+                }
             }
 
             // connect to the sync adapter
@@ -1703,79 +1765,70 @@
                 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
                         + ", removing settings for it");
                 mSyncStorageEngine.removeAuthority(op.account, op.authority);
-                runStateIdle();
-                return;
+                return false;
             }
 
             ActiveSyncContext activeSyncContext =
-                    new ActiveSyncContext(op, insertStartSyncEvent(op));
-            mActiveSyncContext = activeSyncContext;
+                    new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
+            activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
+            mActiveSyncContexts.add(activeSyncContext);
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext);
+                Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
             }
-            mSyncStorageEngine.setActiveSync(mActiveSyncContext);
             if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
                 Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
-                runStateIdle();
-                return;
+                closeActiveSyncContext(activeSyncContext);
+                return false;
             }
 
-            // Find the wakelock for this account and authority and store it in mSyncWakeLock.
-            // Be sure to release the previous wakelock so that we don't end up with it being
-            // held until it is used again.
-            // There are a couple tricky things about this code:
-            // - make sure that we acquire the new wakelock before releasing the old one,
-            //   otherwise the device might go to sleep as soon as we release it.
-            // - since we use non-reference counted wakelocks we have to be sure not to do
-            //   the release if the wakelock didn't change. Othewise we would do an
-            //   acquire followed by a release on the same lock, resulting in no lock
-            //   being held.
-            PowerManager.WakeLock oldWakeLock = mSyncWakeLock;
-            try {
-                mSyncWakeLock = getSyncWakeLock(op.account.type, op.authority);
-				mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid));
-                mSyncWakeLock.acquire();
-            } finally {
-                if (oldWakeLock != null && oldWakeLock != mSyncWakeLock) {
-                    oldWakeLock.release();
-                }
-            }
-
-            // no need to schedule an alarm, as that will be done by our caller.
-
-            // the next step will occur when we get either a timeout or a
-            // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
+            return true;
         }
 
-        private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
-            mActiveSyncContext.mSyncAdapter = syncAdapter;
-            final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
+        private void runBoundToSyncAdapter(ActiveSyncContext activeSyncContext,
+              ISyncAdapter syncAdapter) {
+            activeSyncContext.mSyncAdapter = syncAdapter;
+            final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
             try {
-                syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
+                syncAdapter.startSync(activeSyncContext, syncOperation.authority,
                         syncOperation.account, syncOperation.extras);
             } catch (RemoteException remoteExc) {
-                Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+                Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
+                closeActiveSyncContext(activeSyncContext);
                 increaseBackoffSetting(syncOperation);
                 scheduleSyncOperation(new SyncOperation(syncOperation));
             } catch (RuntimeException exc) {
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+                closeActiveSyncContext(activeSyncContext);
                 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
             }
         }
 
-        private void runSyncFinishedOrCanceled(SyncResult syncResult) {
+        private void cancelActiveSyncLocked(Account account, String authority) {
+            ArrayList<ActiveSyncContext> activeSyncs =
+                    new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
+            for (ActiveSyncContext activeSyncContext : activeSyncs) {
+                if (activeSyncContext != null) {
+                    // if an authority was specified then only cancel the sync if it matches
+                    if (account != null) {
+                        if (!account.equals(activeSyncContext.mSyncOperation.account)) {
+                            return;
+                        }
+                    }
+                    // if an account was specified then only cancel the sync if it matches
+                    if (authority != null) {
+                        if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
+                            return;
+                        }
+                    }
+                    runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
+                            activeSyncContext);
+                }
+            }
+        }
+
+        private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
+                ActiveSyncContext activeSyncContext) {
             boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-            final ActiveSyncContext activeSyncContext = mActiveSyncContext;
-            mActiveSyncContext = null;
-            mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+            closeActiveSyncContext(activeSyncContext);
 
             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
 
@@ -1795,16 +1848,6 @@
                     // TODO: set these correctly when the SyncResult is extended to include it
                     downstreamActivity = 0;
                     upstreamActivity = 0;
-                    clearBackoffSetting(syncOperation);
-                    // if this was an initialization sync and the sync adapter is now
-                    // marked syncable then reschedule the sync. The next time it runs it
-                    // will be made into a regular sync.
-                    if (syncOperation.extras.getBoolean(
-                                ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
-                            && mSyncStorageEngine.getIsSyncable(
-                                syncOperation.account, syncOperation.authority) > 0) {
-                        scheduleSyncOperation(new SyncOperation(syncOperation));
-                    }
                 } else {
                     Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
                     // the operation failed so increase the backoff time
@@ -1839,8 +1882,6 @@
             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
                     upstreamActivity, downstreamActivity, elapsedTime);
 
-            activeSyncContext.close();
-
             if (syncResult != null && syncResult.tooManyDeletions) {
                 installHandleTooManyDeletesNotification(syncOperation.account,
                         syncOperation.authority, syncResult.stats.numDeletes);
@@ -1851,11 +1892,18 @@
 
             if (syncResult != null && syncResult.fullSyncRequested) {
                 scheduleSyncOperation(new SyncOperation(syncOperation.account,
-                        syncOperation.syncSource, syncOperation.authority, new Bundle(), 0));
+                        syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
+                        syncOperation.backoff, syncOperation.delayUntil));
             }
             // no need to schedule an alarm, as that will be done by our caller.
         }
 
+        private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
+            activeSyncContext.close();
+            mActiveSyncContexts.remove(activeSyncContext);
+            mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo);
+        }
+
         /**
          * Convert the error-containing SyncResult into the Sync.History error number. Since
          * the SyncResult may indicate multiple errors at once, this method just returns the
@@ -1885,11 +1933,11 @@
             throw new IllegalStateException("we are not in an error state, " + syncResult);
         }
 
-        private void manageSyncNotification() {
+        private void manageSyncNotificationLocked() {
             boolean shouldCancel;
             boolean shouldInstall;
 
-            if (mActiveSyncContext == null) {
+            if (mActiveSyncContexts.isEmpty()) {
                 mSyncNotificationInfo.startTime = null;
 
                 // we aren't syncing. if the notification is active then remember that we need
@@ -1898,34 +1946,38 @@
                 shouldInstall = false;
             } else {
                 // we are syncing
-                final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
-
                 final long now = SystemClock.elapsedRealtime();
                 if (mSyncNotificationInfo.startTime == null) {
                     mSyncNotificationInfo.startTime = now;
                 }
 
-                // cancel the notification if it is up and the authority or account is wrong
-                shouldCancel = mSyncNotificationInfo.isActive &&
-                        (!syncOperation.authority.equals(mSyncNotificationInfo.authority)
-                        || !syncOperation.account.equals(mSyncNotificationInfo.account));
-
-                // there are four cases:
-                // - the notification is up and there is no change: do nothing
-                // - the notification is up but we should cancel since it is stale:
-                //   need to install
+                // there are three cases:
+                // - the notification is up: do nothing
                 // - the notification is not up but it isn't time yet: don't install
                 // - the notification is not up and it is time: need to install
 
                 if (mSyncNotificationInfo.isActive) {
-                    shouldInstall = shouldCancel;
+                    shouldInstall = shouldCancel = false;
                 } else {
+                    // it isn't currently up, so there is nothing to cancel
+                    shouldCancel = false;
+
                     final boolean timeToShowNotification =
                             now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
-                    // show the notification immediately if this is a manual sync
-                    final boolean manualSync = syncOperation.extras
-                            .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
-                    shouldInstall = timeToShowNotification || manualSync;
+                    if (timeToShowNotification) {
+                        shouldInstall = true;
+                    } else {
+                        // show the notification immediately if this is a manual sync
+                        shouldInstall = false;
+                        for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+                            final boolean manualSync = activeSyncContext.mSyncOperation.extras
+                                    .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+                            if (manualSync) {
+                                shouldInstall = true;
+                                break;
+                            }
+                        }
+                    }
                 }
             }
 
@@ -1936,94 +1988,82 @@
             }
 
             if (shouldInstall) {
-                SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
                 mNeedSyncActiveNotification = true;
                 sendSyncStateIntent();
                 mSyncNotificationInfo.isActive = true;
-                mSyncNotificationInfo.account = syncOperation.account;
-                mSyncNotificationInfo.authority = syncOperation.authority;
             }
         }
 
-        /**
-         * Check if there were any long-lasting errors, if so install the error notification,
-         * otherwise cancel the error notification.
-         */
-        private void manageErrorNotification() {
-            //
-            long when = mSyncStorageEngine.getInitialSyncFailureTime();
-            if ((when > 0) && (when + ERROR_NOTIFICATION_DELAY_MS < System.currentTimeMillis())) {
-                if (!mErrorNotificationInstalled) {
-                    mNeedSyncErrorNotification = true;
-                    sendSyncStateIntent();
-                }
-                mErrorNotificationInstalled = true;
-            } else {
-                if (mErrorNotificationInstalled) {
-                    mNeedSyncErrorNotification = false;
-                    sendSyncStateIntent();
-                }
-                mErrorNotificationInstalled = false;
-            }
-        }
-
-        private void manageSyncAlarm(Long earliestFuturePollElapsedTime) {
+        private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
+                long nextPendingEventElapsedTime) {
             // in each of these cases the sync loop will be kicked, which will cause this
             // method to be called again
             if (!mDataConnectionIsConnected) return;
             if (mStorageIsLow) return;
 
-            final long now = SystemClock.elapsedRealtime();
+            // When the status bar notification should be raised
+            final long notificationTime =
+                    (!mSyncHandler.mSyncNotificationInfo.isActive
+                            && mSyncHandler.mSyncNotificationInfo.startTime != null)
+                            ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY
+                            : Long.MAX_VALUE;
 
-            // Compute the alarm fire time:
-            // - not syncing: time of the next sync operation
-            // - syncing, no notification: time from sync start to notification create time
-            // - syncing, with notification: time till timeout of the active sync operation
-            Long alarmTime;
-            ActiveSyncContext activeSyncContext = mActiveSyncContext;
-            if (activeSyncContext == null) {
-                synchronized (mSyncQueue) {
-                    final Pair<SyncOperation, Long> candidate = mSyncQueue.nextOperation();
-                    if (earliestFuturePollElapsedTime == null && candidate == null) {
-                        alarmTime = null;
-                    } else if (earliestFuturePollElapsedTime == null) {
-                        alarmTime = candidate.second;
-                    } else if (candidate == null) {
-                        alarmTime = earliestFuturePollElapsedTime;
-                    } else {
-                        alarmTime = Math.min(earliestFuturePollElapsedTime, candidate.second);
-                    }
+            // When we should consider canceling an active sync
+            long earliestTimeoutTime = Long.MAX_VALUE;
+            for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+                final long currentSyncTimeoutTime =
+                        currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
+                            + currentSyncTimeoutTime);
                 }
-            } else {
-                final long notificationTime =
-                        mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
-                final long timeoutTime =
-                        mActiveSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
-                if (mSyncHandler.mSyncNotificationInfo.isActive) {
-                    alarmTime = timeoutTime;
-                } else {
-                    alarmTime = Math.min(notificationTime, timeoutTime);
+                if (earliestTimeoutTime > currentSyncTimeoutTime) {
+                    earliestTimeoutTime = currentSyncTimeoutTime;
                 }
             }
 
-            // adjust the alarmTime so that we will wake up when it is time to
-            // install the error notification
-            if (!mErrorNotificationInstalled) {
-                long when = mSyncStorageEngine.getInitialSyncFailureTime();
-                if (when > 0) {
-                    when += ERROR_NOTIFICATION_DELAY_MS;
-                    // convert when fron absolute time to elapsed run time
-                    long delay = when - System.currentTimeMillis();
-                    when = now + delay;
-                    alarmTime = alarmTime != null ? Math.min(alarmTime, when) : when;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime);
+            }
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
+            }
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
+                        + nextPeriodicEventElapsedTime);
+            }
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
+                        + nextPendingEventElapsedTime);
+            }
+
+            long alarmTime = Math.min(notificationTime, earliestTimeoutTime);
+            alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime);
+            alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
+
+            // Bound the alarm time.
+            final long now = SystemClock.elapsedRealtime();
+            if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
+                            + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
                 }
+                alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
+            } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, "
+                            + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
+                }
+                alarmTime = now + SYNC_ALARM_TIMEOUT_MAX;
             }
 
             // determine if we need to set or cancel the alarm
             boolean shouldSet = false;
             boolean shouldCancel = false;
             final boolean alarmIsActive = mAlarmScheduleTime != null;
-            final boolean needAlarm = alarmTime != null;
+            final boolean needAlarm = alarmTime != Long.MAX_VALUE;
             if (needAlarm) {
                 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) {
                     shouldSet = true;
@@ -2035,6 +2075,11 @@
             // set or cancel the alarm as directed
             ensureAlarmService();
             if (shouldSet) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
+                            + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
+                            + " secs from now");
+                }
                 mAlarmScheduleTime = alarmTime;
                 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
                         mSyncAlarmIntent);
@@ -2048,7 +2093,7 @@
             Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
             syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
-            syncStateIntent.putExtra("failing", mNeedSyncErrorNotification);
+            syncStateIntent.putExtra("failing", false);
             mContext.sendBroadcast(syncStateIntent);
         }
 
@@ -2137,4 +2182,13 @@
                     resultMessage, downstreamActivity, upstreamActivity);
         }
     }
+
+    private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
+        for (ActiveSyncContext sync : mActiveSyncContexts) {
+            if (sync == activeSyncContext) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index b0160885..3d7f3fbf 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -33,9 +33,12 @@
     public long earliestRunTime;
     public boolean expedited;
     public SyncStorageEngine.PendingOperation pendingOperation;
+    public Long backoff;
+    public long delayUntil;
+    public long effectiveRunTime;
 
     public SyncOperation(Account account, int source, String authority, Bundle extras,
-            long delayInMs) {
+            long delayInMs, long backoff, long delayUntil) {
         this.account = account;
         this.syncSource = source;
         this.authority = authority;
@@ -48,6 +51,8 @@
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
+        this.delayUntil = delayUntil;
+        this.backoff = backoff;
         final long now = SystemClock.elapsedRealtime();
         if (delayInMs < 0) {
             this.expedited = true;
@@ -56,6 +61,7 @@
             this.expedited = false;
             this.earliestRunTime = now + delayInMs;
         }
+        updateEffectiveRunTime();
         this.key = toKey();
     }
 
@@ -72,49 +78,78 @@
         this.extras = new Bundle(other.extras);
         this.expedited = other.expedited;
         this.earliestRunTime = SystemClock.elapsedRealtime();
+        this.backoff = other.backoff;
+        this.delayUntil = other.delayUntil;
+        this.updateEffectiveRunTime();
         this.key = toKey();
     }
 
     public String toString() {
+        return dump(true);
+    }
+
+    public String dump(boolean useOneLine) {
         StringBuilder sb = new StringBuilder();
-        sb.append("authority: ").append(authority);
-        sb.append(" account: ").append(account);
-        sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb, false /* asKey */);
-        sb.append(" syncSource: ").append(syncSource);
-        sb.append(" when: ").append(earliestRunTime);
-        sb.append(" expedited: ").append(expedited);
+        sb.append(account.name);
+        sb.append(" (" + account.type + ")");
+        sb.append(", " + authority);
+        sb.append(", ");
+        sb.append(SyncStorageEngine.SOURCES[syncSource]);
+        sb.append(", earliestRunTime " + earliestRunTime);
+        if (expedited) {
+            sb.append(", EXPEDITED");
+        }
+        if (!useOneLine && !extras.keySet().isEmpty()) {
+            sb.append("\n    ");
+            extrasToStringBuilder(extras, sb);
+        }
         return sb.toString();
     }
 
+    public boolean isInitialization() {
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+    }
+
+    public boolean ignoreBackoff() {
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
+    }
+
     private String toKey() {
         StringBuilder sb = new StringBuilder();
         sb.append("authority: ").append(authority);
-	sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
+    	sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
         sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb, true /* asKey */);
+        extrasToStringBuilder(extras, sb);
         return sb.toString();
     }
 
-    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb, boolean asKey) {
+    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
         sb.append("[");
         for (String key : bundle.keySet()) {
-            // if we are writing this as a key don't consider whether this
-            // is an initialization sync or not when computing the key since
-            // we set this flag appropriately when dispatching the sync request.
-            if (asKey && ContentResolver.SYNC_EXTRAS_INITIALIZE.equals(key)) {
-                continue;
-            }
             sb.append(key).append("=").append(bundle.get(key)).append(" ");
         }
         sb.append("]");
     }
 
+    public void updateEffectiveRunTime() {
+        effectiveRunTime = ignoreBackoff()
+                ? earliestRunTime
+                : Math.max(
+                    Math.max(earliestRunTime, delayUntil),
+                    backoff);
+    }
+
     public int compareTo(Object o) {
         SyncOperation other = (SyncOperation)o;
-        if (earliestRunTime == other.earliestRunTime) {
+
+        if (expedited != other.expedited) {
+            return expedited ? -1 : 1;
+        }
+
+        if (effectiveRunTime == other.effectiveRunTime) {
             return 0;
         }
-        return (earliestRunTime < other.earliestRunTime) ? -1 : 1;
+
+        return effectiveRunTime < other.effectiveRunTime ? -1 : 1;
     }
 }
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 28baa0d..f826147 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -16,17 +16,18 @@
 
 package android.content;
 
-import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import android.os.SystemClock;
+import android.text.format.DateUtils;
 import android.util.Pair;
 import android.util.Log;
 import android.accounts.Account;
 
-import java.util.HashMap;
 import java.util.ArrayList;
-import java.util.Map;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  *
@@ -38,7 +39,7 @@
 
     // A Map of SyncOperations operationKey -> SyncOperation that is designed for
     // quick lookup of an enqueued SyncOperation.
-    private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
+    public final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
 
     public SyncQueue(SyncStorageEngine syncStorageEngine) {
         mSyncStorageEngine = syncStorageEngine;
@@ -47,8 +48,11 @@
         final int N = ops.size();
         for (int i=0; i<N; i++) {
             SyncStorageEngine.PendingOperation op = ops.get(i);
+            final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
             SyncOperation syncOperation = new SyncOperation(
-                    op.account, op.syncSource, op.authority, op.extras, 0 /* delay */);
+                    op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
+                    backoff != null ? backoff.first : 0,
+                    syncStorageEngine.getDelayUntilTime(op.account, op.authority));
             syncOperation.expedited = op.expedited;
             syncOperation.pendingOperation = op;
             add(syncOperation, op);
@@ -119,65 +123,26 @@
         }
     }
 
-    /**
-     * Find the operation that should run next. Operations are sorted by their earliestRunTime,
-     * prioritizing first those with a syncable state of "unknown" that aren't retries then
-     * expedited operations.
-     * The earliestRunTime is adjusted by the sync adapter's backoff and delayUntil times, if any.
-     * @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.
-     */
-    public Pair<SyncOperation, Long> nextOperation() {
-        SyncOperation best = null;
-        long bestRunTime = 0;
-        boolean bestSyncableIsUnknownAndNotARetry = false;
+    public void onBackoffChanged(Account account, String providerName, long backoff) {
+        // for each op that matches the account and provider update its
+        // backoff and effectiveStartTime
         for (SyncOperation op : mOperationsMap.values()) {
-            long opRunTime = op.earliestRunTime;
-            if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
-                Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(op.account, op.authority);
-                long delayUntil = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority);
-                opRunTime = Math.max(
-                        Math.max(opRunTime, delayUntil),
-                        backoff != null ? backoff.first : 0);
-            }
-            // we know a sync is a retry if the intialization flag is set, since that will only
-            // be set by the sync dispatching code, thus if it is set it must have already been
-            // dispatched
-            final boolean syncableIsUnknownAndNotARetry =
-                    !op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
-                    && mSyncStorageEngine.getIsSyncable(op.account, op.authority) < 0;
-            // if the unsyncable state differs, make the current the best if it is unsyncable
-            // else, if the expedited state differs, make the current the best if it is expedited
-            // else, make the current the best if it is earlier than the best
-            if (best == null
-                    || ((bestSyncableIsUnknownAndNotARetry == syncableIsUnknownAndNotARetry)
-                        ? (best.expedited == op.expedited
-                           ? opRunTime < bestRunTime
-                           : op.expedited)
-                        : syncableIsUnknownAndNotARetry)) {
-                best = op;
-                bestSyncableIsUnknownAndNotARetry = syncableIsUnknownAndNotARetry;
-                bestRunTime = opRunTime;
+            if (op.account.equals(account) && op.authority.equals(providerName)) {
+                op.backoff = backoff;
+                op.updateEffectiveRunTime();
             }
         }
-        if (best == null) {
-            return null;
-        }
-        return Pair.create(best, bestRunTime);
     }
 
-    /**
-     * Find and return the SyncOperation that should be run next and is ready to run.
-     * @param now the current {@link android.os.SystemClock#elapsedRealtime()}, used to
-     * decide if the sync operation is ready to run
-     * @return the SyncOperation that should be run next and is ready to run.
-     */
-    public Pair<SyncOperation, Long> nextReadyToRun(long now) {
-        Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation();
-        if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
-            return null;
+    public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) {
+        // for each op that matches the account and provider update its
+        // delayUntilTime and effectiveStartTime
+        for (SyncOperation op : mOperationsMap.values()) {
+            if (op.account.equals(account) && op.authority.equals(providerName)) {
+                op.delayUntil = delayUntil;
+                op.updateEffectiveRunTime();
+            }
         }
-        return nextOpAndRunTime;
     }
 
     public void remove(Account account, String authority) {
@@ -200,9 +165,17 @@
     }
 
     public void dump(StringBuilder sb) {
+        final long now = SystemClock.elapsedRealtime();
         sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
         for (SyncOperation operation : mOperationsMap.values()) {
-            sb.append(operation).append("\n");
+            sb.append("  ");
+            if (operation.effectiveRunTime <= now) {
+                sb.append("READY");
+            } else {
+                sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
+            }
+            sb.append(" - ");
+            sb.append(operation.dump(false)).append("\n");
         }
     }
 }
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 6413cec..487f6ce 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -229,7 +229,7 @@
     private final ArrayList<PendingOperation> mPendingOperations =
             new ArrayList<PendingOperation>();
 
-    private SyncInfo mCurrentSync;
+    private final ArrayList<SyncInfo> mCurrentSyncs = new ArrayList<SyncInfo>();
 
     private final SparseArray<SyncStatusInfo> mSyncStatus =
             new SparseArray<SyncStatusInfo>();
@@ -690,23 +690,12 @@
 
     /**
      * Returns true if there is currently a sync operation for the given
-     * account or authority in the pending list, or actively being processed.
+     * account or authority actively being processed.
      */
     public boolean isSyncActive(Account account, String authority) {
         synchronized (mAuthorities) {
-            int i = mPendingOperations.size();
-            while (i > 0) {
-                i--;
-                // TODO(fredq): this probably shouldn't be considering
-                // pending operations.
-                PendingOperation op = mPendingOperations.get(i);
-                if (op.account.equals(account) && op.authority.equals(authority)) {
-                    return true;
-                }
-            }
-
-            if (mCurrentSync != null) {
-                AuthorityInfo ainfo = getAuthority(mCurrentSync.authorityId);
+            for (SyncInfo syncInfo : mCurrentSyncs) {
+                AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
                 if (ainfo != null && ainfo.account.equals(account)
                         && ainfo.authority.equals(authority)) {
                     return true;
@@ -887,40 +876,47 @@
     }
 
     /**
-     * Called when the currently active sync is changing (there can only be
-     * one at a time).  Either supply a valid ActiveSyncContext with information
-     * about the sync, or null to stop the currently active sync.
+     * Called when a sync is starting. Supply a valid ActiveSyncContext with information
+     * about the sync.
      */
-    public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
+    public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
+        final SyncInfo syncInfo;
         synchronized (mAuthorities) {
-            if (activeSyncContext != null) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "setActiveSync: account="
-                        + activeSyncContext.mSyncOperation.account
-                        + " auth=" + activeSyncContext.mSyncOperation.authority
-                        + " src=" + activeSyncContext.mSyncOperation.syncSource
-                        + " extras=" + activeSyncContext.mSyncOperation.extras);
-                }
-                if (mCurrentSync != null) {
-                    Log.w(TAG, "setActiveSync called with existing active sync!");
-                }
-                AuthorityInfo authority = getAuthorityLocked(
-                        activeSyncContext.mSyncOperation.account,
-                        activeSyncContext.mSyncOperation.authority,
-                        "setActiveSync");
-                if (authority == null) {
-                    return;
-                }
-                mCurrentSync = new SyncInfo(authority.ident,
-                        authority.account, authority.authority,
-                        activeSyncContext.mStartTime);
-            } else {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "setActiveSync: null");
-                mCurrentSync = null;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "setActiveSync: account="
+                    + activeSyncContext.mSyncOperation.account
+                    + " auth=" + activeSyncContext.mSyncOperation.authority
+                    + " src=" + activeSyncContext.mSyncOperation.syncSource
+                    + " extras=" + activeSyncContext.mSyncOperation.extras);
             }
+            AuthorityInfo authority = getOrCreateAuthorityLocked(
+                    activeSyncContext.mSyncOperation.account,
+                    activeSyncContext.mSyncOperation.authority,
+                    -1 /* assign a new identifier if creating a new authority */,
+                    true /* write to storage if this results in a change */);
+            syncInfo = new SyncInfo(authority.ident,
+                    authority.account, authority.authority,
+                    activeSyncContext.mStartTime);
+            mCurrentSyncs.add(syncInfo);
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
+        reportActiveChange();
+        return syncInfo;
+    }
+
+    /**
+     * Called to indicate that a previously active sync is no longer active.
+     */
+    public void removeActiveSync(SyncInfo syncInfo) {
+        synchronized (mAuthorities) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "removeActiveSync: account="
+                        + syncInfo.account + " auth=" + syncInfo.authority);
+            }
+            mCurrentSyncs.remove(syncInfo);
+        }
+
+        reportActiveChange();
     }
 
     /**
@@ -1095,10 +1091,26 @@
      * Return the currently active sync information, or null if there is no
      * active sync.  Note that the returned object is the real, live active
      * sync object, so be careful what you do with it.
+     * <p>
+     * Since multiple concurrent syncs are now supported you should use
+     * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
+     * This method returns the first item from the list of current syncs
+     * or null if there are none.
+     * @deprecated use {@link #getCurrentSyncs()}
      */
     public SyncInfo getCurrentSync() {
         synchronized (mAuthorities) {
-            return mCurrentSync;
+            return !mCurrentSyncs.isEmpty() ? mCurrentSyncs.get(0) : null;
+        }
+    }
+
+    /**
+     * Return a list of the currently active syncs. Note that the returned items are the
+     * real, live active sync objects, so be careful what you do with it.
+     */
+    public List<SyncInfo> getCurrentSyncs() {
+        synchronized (mAuthorities) {
+            return new ArrayList<SyncInfo>(mCurrentSyncs);
         }
     }
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 38d897e..ba74d9b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -296,12 +296,12 @@
      * the normal application lifecycle.
      *
      * <p>Comes from the
-     * {@link android.R.styleable#AndroidManifestApplication_heavyWeight android:heavyWeight}
+     * {@link android.R.styleable#AndroidManifestApplication_cantSaveState android:cantSaveState}
      * attribute of the &lt;application&gt; tag.
      *
      * {@hide}
      */
-    public static final int CANT_SAVE_STATE = 1<<27;
+    public static final int FLAG_CANT_SAVE_STATE = 1<<27;
 
     /**
      * Flags associated with the application.  Any combination of
@@ -380,6 +380,12 @@
      */
     public boolean enabled = true;
 
+    /**
+     * For convenient access to package's install location.
+     * @hide
+     */
+    public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+    
     public void dump(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
         if (className != null) {
@@ -456,6 +462,7 @@
         uid = orig.uid;
         targetSdkVersion = orig.targetSdkVersion;
         enabled = orig.enabled;
+        installLocation = orig.installLocation;
         manageSpaceActivityName = orig.manageSpaceActivityName;
         descriptionRes = orig.descriptionRes;
     }
@@ -488,6 +495,7 @@
         dest.writeInt(uid);
         dest.writeInt(targetSdkVersion);
         dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(installLocation);
         dest.writeString(manageSpaceActivityName);
         dest.writeString(backupAgentName);
         dest.writeInt(descriptionRes);
@@ -520,6 +528,7 @@
         uid = source.readInt();
         targetSdkVersion = source.readInt();
         enabled = source.readInt() != 0;
+        installLocation = source.readInt();
         manageSpaceActivityName = source.readString();
         backupAgentName = source.readString();
         descriptionRes = source.readInt();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eaf1e33..6cbc9b5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -783,7 +783,8 @@
         pkg.installLocation = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_installLocation,
                 PARSE_DEFAULT_INSTALL_LOCATION);
-
+        pkg.applicationInfo.installLocation = pkg.installLocation;
+        
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
         int supportsNormalScreens = 1;
@@ -1612,7 +1613,7 @@
                 if (sa.getBoolean(
                         com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
                         false)) {
-                    ai.flags |= ApplicationInfo.CANT_SAVE_STATE;
+                    ai.flags |= ApplicationInfo.FLAG_CANT_SAVE_STATE;
 
                     // A heavy-weight application can not be in a custom process.
                     // We can do direct compare because we intern all strings.
@@ -1929,7 +1930,7 @@
 
         sa.recycle();
 
-        if (receiver && (owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+        if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have receives in its main process
             // We can do direct compare because we intern all strings.
             if (a.info.processName == owner.packageName) {
@@ -2219,7 +2220,7 @@
 
         sa.recycle();
 
-        if ((owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+        if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have providers in its main process
             // We can do direct compare because we intern all strings.
             if (p.info.processName == owner.packageName) {
@@ -2460,7 +2461,7 @@
 
         sa.recycle();
 
-        if ((owner.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+        if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
             // A heavy-weight application can not have services in its main process
             // We can do direct compare because we intern all strings.
             if (s.info.processName == owner.packageName) {
diff --git a/core/java/android/net/LinkAddress.aidl b/core/java/android/net/LinkAddress.aidl
new file mode 100644
index 0000000..e7d8646
--- /dev/null
+++ b/core/java/android/net/LinkAddress.aidl
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable LinkAddress;
+
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
new file mode 100644
index 0000000..cb302da
--- /dev/null
+++ b/core/java/android/net/LinkAddress.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Identifies an address of a network link
+ * @hide
+ */
+public class LinkAddress implements Parcelable {
+    /**
+     * IPv4 or IPv6 address.
+     */
+    private final InetAddress address;
+
+    /**
+     * Network prefix
+     */
+    private final int prefix;
+
+    public LinkAddress(InetAddress address, InetAddress mask) {
+        this.address = address;
+        this.prefix = computeprefix(mask);
+    }
+
+    public LinkAddress(InetAddress address, int prefix) {
+        this.address = address;
+        this.prefix = prefix;
+    }
+
+    public LinkAddress(InterfaceAddress interfaceAddress) {
+        this.address = interfaceAddress.getAddress();
+        this.prefix = interfaceAddress.getNetworkPrefixLength();
+    }
+
+    private static int computeprefix(InetAddress mask) {
+        int count = 0;
+        for (byte b : mask.getAddress()) {
+            for (int i = 0; i < 8; ++i) {
+                if ((b & (1 << i)) != 0) {
+                    ++count;
+                }
+            }
+        }
+        return count;
+    }
+
+    @Override
+    public String toString() {
+        return (address == null ? "" : (address.getHostAddress() + "/" + prefix));
+    }
+
+    /**
+     * Compares this {@code LinkAddress} instance against the specified address
+     * in {@code obj}. Two addresses are equal if their InetAddress and prefix
+     * are equal
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LinkAddress)) {
+            return false;
+        }
+        LinkAddress linkAddress = (LinkAddress) obj;
+        return this.address.equals(linkAddress.address) &&
+            this.prefix == linkAddress.prefix;
+    }
+
+    /**
+     * Returns the InetAddress for this address.
+     */
+    public InetAddress getAddress() {
+        return address;
+    }
+
+    /**
+     * Get network prefix length
+     */
+    public int getNetworkPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        if (address != null) {
+            dest.writeByte((byte)1);
+            dest.writeByteArray(address.getAddress());
+            dest.writeInt(prefix);
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkAddress> CREATOR =
+        new Creator<LinkAddress>() {
+            public LinkAddress createFromParcel(Parcel in) {
+                InetAddress address = null;
+                int prefix = 0;
+                if (in.readByte() == 1) {
+                    try {
+                        address = InetAddress.getByAddress(in.createByteArray());
+                        prefix = in.readInt();
+                    } catch (UnknownHostException e) { }
+                }
+                return new LinkAddress(address, prefix);
+            }
+
+            public LinkAddress[] newArray(int size) {
+                return new LinkAddress[size];
+            }
+        };
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index f411eac..f1545ea 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -36,8 +36,8 @@
  */
 public class LinkProperties implements Parcelable {
 
-    private NetworkInterface mIface;
-    private Collection<InetAddress> mAddresses;
+    String mIfaceName;
+    private Collection<LinkAddress> mLinkAddresses;
     private Collection<InetAddress> mDnses;
     private InetAddress mGateway;
     private ProxyProperties mHttpProxy;
@@ -49,34 +49,42 @@
     // copy constructor instead of clone
     public LinkProperties(LinkProperties source) {
         if (source != null) {
-            mIface = source.getInterface();
-            mAddresses = source.getAddresses();
+            mIfaceName = source.getInterfaceName();
+            mLinkAddresses = source.getLinkAddresses();
             mDnses = source.getDnses();
             mGateway = source.getGateway();
             mHttpProxy = new ProxyProperties(source.getHttpProxy());
         }
     }
 
-    public void setInterface(NetworkInterface iface) {
-        mIface = iface;
-    }
-    public NetworkInterface getInterface() {
-        return mIface;
-    }
-    public String getInterfaceName() {
-        return (mIface == null ? null : mIface.getName());
+    public void setInterfaceName(String iface) {
+        mIfaceName = iface;
     }
 
-    public void addAddress(InetAddress address) {
-        mAddresses.add(address);
+    public String getInterfaceName() {
+        return mIfaceName;
     }
+
     public Collection<InetAddress> getAddresses() {
-        return Collections.unmodifiableCollection(mAddresses);
+        Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+        for (LinkAddress linkAddress : mLinkAddresses) {
+            addresses.add(linkAddress.getAddress());
+        }
+        return Collections.unmodifiableCollection(addresses);
+    }
+
+    public void addLinkAddress(LinkAddress address) {
+        mLinkAddresses.add(address);
+    }
+
+    public Collection<LinkAddress> getLinkAddresses() {
+        return Collections.unmodifiableCollection(mLinkAddresses);
     }
 
     public void addDns(InetAddress dns) {
         mDnses.add(dns);
     }
+
     public Collection<InetAddress> getDnses() {
         return Collections.unmodifiableCollection(mDnses);
     }
@@ -96,8 +104,8 @@
     }
 
     public void clear() {
-        mIface = null;
-        mAddresses = new ArrayList<InetAddress>();
+        mIfaceName = null;
+        mLinkAddresses = new ArrayList<LinkAddress>();
         mDnses = new ArrayList<InetAddress>();
         mGateway = null;
         mHttpProxy = null;
@@ -113,11 +121,11 @@
 
     @Override
     public String toString() {
-        String ifaceName = (mIface == null ? "" : "InterfaceName: " + mIface.getName() + " ");
+        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
 
-        String ip = "IpAddresses: [";
-        for (InetAddress addr : mAddresses) ip +=  addr.getHostAddress() + ",";
-        ip += "] ";
+        String linkAddresses = "LinkAddresses: [";
+        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString();
+        linkAddresses += "] ";
 
         String dns = "DnsAddresses: [";
         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
@@ -126,7 +134,7 @@
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
         String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.getHostAddress() + " ");
 
-        return ifaceName + ip + gateway + dns + proxy;
+        return ifaceName + linkAddresses + gateway + dns + proxy;
     }
 
     /**
@@ -135,12 +143,11 @@
      */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(getInterfaceName());
-        dest.writeInt(mAddresses.size());
-        //TODO: explore an easy alternative to preserve hostname
-        // without doing a lookup
-        for(InetAddress a : mAddresses) {
-            dest.writeByteArray(a.getAddress());
+        dest.writeInt(mLinkAddresses.size());
+        for(LinkAddress linkAddress : mLinkAddresses) {
+            dest.writeParcelable(linkAddress, flags);
         }
+
         dest.writeInt(mDnses.size());
         for(InetAddress d : mDnses) {
             dest.writeByteArray(d.getAddress());
@@ -170,16 +177,14 @@
                 String iface = in.readString();
                 if (iface != null) {
                     try {
-                        netProp.setInterface(NetworkInterface.getByName(iface));
+                        netProp.setInterfaceName(iface);
                     } catch (Exception e) {
                         return null;
                     }
                 }
                 int addressCount = in.readInt();
                 for (int i=0; i<addressCount; i++) {
-                    try {
-                        netProp.addAddress(InetAddress.getByAddress(in.createByteArray()));
-                    } catch (UnknownHostException e) { }
+                    netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
                 }
                 addressCount = in.readInt();
                 for (int i=0; i<addressCount; i++) {
diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java
new file mode 100644
index 0000000..557f651
--- /dev/null
+++ b/core/java/android/nfc/NdefMessage.java
@@ -0,0 +1,89 @@
+/*
+ * 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.nfc;
+
+import android.nfc.NdefRecord;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.UnsupportedOperationException;
+
+/**
+ * NDEF Message data.
+ * <p>
+ * Immutable data class. An NDEF message always contains zero or more NDEF
+ * records.
+ */
+public class NdefMessage implements Parcelable {
+    /**
+     * Create an NDEF message from raw bytes.
+     * <p>
+     * Validation is performed to make sure the Record format headers are valid,
+     * and the ID + TYPE + PAYLOAD fields are of the correct size.
+     *
+     * @hide
+     */
+    public NdefMessage(byte[] data) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Create an NDEF message from NDEF records.
+     */
+    public NdefMessage(NdefRecord[] records) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get the NDEF records inside this NDEF message.
+     *
+     * @return array of zero or more NDEF records.
+     */
+    public NdefRecord[] getRecords() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get a byte array representation of this NDEF message.
+     *
+     * @return byte array
+     * @hide
+     */
+    public byte[] toByteArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static final Parcelable.Creator<NdefMessage> CREATOR =
+            new Parcelable.Creator<NdefMessage>() {
+        public NdefMessage createFromParcel(Parcel in) {
+            throw new UnsupportedOperationException();
+        }
+        public NdefMessage[] newArray(int size) {
+            throw new UnsupportedOperationException();
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
new file mode 100644
index 0000000..54cbbeb
--- /dev/null
+++ b/core/java/android/nfc/NdefRecord.java
@@ -0,0 +1,231 @@
+/*
+ * 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.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.UnsupportedOperationException;
+
+/**
+ * NDEF Record data.
+ * <p>
+ * Immutable data class. An NDEF record always contains
+ * <ul>
+ * <li>3-bit TNF field
+ * <li>Variable length type
+ * <li>Variable length ID
+ * <li>Variable length payload
+ * </ul>
+ * The TNF (Type Name Format) field indicates how to interpret the type field.
+ * <p>
+ * This class represents a logical (unchunked) NDEF record. The underlying
+ * representation may be chunked across several NDEF records when the payload is
+ * large.
+ */
+public class NdefRecord implements Parcelable {
+    /**
+     * Indicates no type, id, or payload is associated with this NDEF Record.
+     * <p>
+     * Type, id and payload fields must all be empty to be a valid TNF_EMPTY
+     * record.
+     */
+    public static final short TNF_EMPTY = 0x00;
+
+    /**
+     * Indicates the type field uses the RTD type name format.
+     * <p>
+     * Use this TNF with RTD types such as RTD_TEXT, RTD_URI.
+     */
+    public static final short TNF_WELL_KNOWN = 0x01;
+
+    /**
+     * Indicates the type field contains a value that follows the media-type BNF
+     * construct defined by RFC 2046.
+     */
+    public static final short TNF_MIME_MEDIA = 0x02;
+
+    /**
+     * Indicates the type field contains a value that follows the absolute-URI
+     * BNF construct defined by RFC 3986.
+     */
+    public static final short TNF_ABSOLUTE_URI = 0x03;
+
+    /**
+     * Indicates the type field contains a value that follows the RTD external
+     * name specification.
+     * <p>
+     * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
+     * Those are well known RTD constants, not external RTD constants.
+     */
+    public static final short TNF_EXTERNAL_TYPE = 0x04;
+
+    /**
+     * Indicates the payload type is unknown.
+     * <p>
+     * This is similar to the "application/octet-stream" MIME type. The payload
+     * type is not explicitly encoded within the NDEF Message.
+     * <p>
+     * The type field must be empty to be a valid TNF_UNKNOWN record.
+     */
+    public static final short TNF_UNKNOWN = 0x05;
+
+    /**
+     * Indicates the payload is an intermediate or final chunk of a chunked
+     * NDEF Record.
+     * <p>
+     * The payload type is specified in the first chunk, and subsequent chunks
+     * must use TNF_UNCHANGED with an empty type field. TNF_UNCHANGED must not
+     * be used in any other situation.
+     */
+    public static final short TNF_UNCHANGED = 0x06;
+
+    /**
+     * Reserved TNF type.
+     * <p>
+     * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
+     * value like TNF_UNKNOWN.
+     * @hide
+     */
+    public static final short TNF_RESERVED = 0x07;
+
+    /**
+     * RTD Text type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_TEXT = {0x54};  // "T"
+
+    /**
+     * RTD URI type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_URI = {0x55};   // "U"
+
+    /**
+     * RTD Smart Poster type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_SMART_POSTER = {0x53, 0x70};  // "Sp"
+
+    /**
+     * RTD Alternative Carrier type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63};  // "ac"
+
+    /**
+     * RTD Handover Carrier type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63};  // "Hc"
+
+    /**
+     * RTD Handover Request type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72};  // "Hr"
+
+    /**
+     * RTD Handover Select type. For use with TNF_WELL_KNOWN.
+     */
+    public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
+
+    /**
+     * Construct an NDEF Record.
+     * <p>
+     * Applications should not attempt to manually chunk NDEF Records - the
+     * implementation of android.nfc will automatically chunk an NDEF Record
+     * when necessary (and only present a single logical NDEF Record to the
+     * application). So applications should not use TNF_UNCHANGED.
+     *
+     * @param tnf  a 3-bit TNF constant
+     * @param type byte array, containing zero to 255 bytes, must not be null
+     * @param id   byte array, containing zero to 255 bytes, must not be null
+     * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
+     *                must not be null
+     */
+    public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Construct an NDEF Record from raw bytes.
+     * <p>
+     * Validation is performed to make sure the header is valid, and that
+     * the id, type and payload sizes appear to be valid.
+     *
+     * @throws FormatException if the data is not a valid NDEF record
+     */
+    public NdefRecord(byte[] data) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the 3-bit TNF.
+     * <p>
+     * TNF is the top-level type.
+     */
+    public short getTnf() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the variable length Type field.
+     * <p>
+     * This should be used in conjunction with the TNF field to determine the
+     * payload format.
+     */
+    public byte[] getType() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the variable length ID.
+     */
+    public byte[] getId() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the variable length payload.
+     */
+    public byte[] getPayload() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return this NDEF Record as a byte array.
+     * @hide
+     */
+    public byte[] toByteArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    public static final Parcelable.Creator<NdefRecord> CREATOR =
+            new Parcelable.Creator<NdefRecord>() {
+        public NdefRecord createFromParcel(Parcel in) {
+            throw new UnsupportedOperationException();
+        }
+        public NdefRecord[] newArray(int size) {
+            throw new UnsupportedOperationException();
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 54cb4ca..b45aa99 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -287,10 +287,12 @@
         setMeasuredDimension(width, height);
     }
     
+    /** @hide */
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        boolean result = super.setFrame(left, top, right, bottom);
         updateWindow(false, false);
+        return result;
     }
 
     @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2afbe81..472f7b4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8543,7 +8543,7 @@
         if ((privateFlags & PRESSED) != 0) viewStateIndex |= VIEW_STATE_PRESSED;
         if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= VIEW_STATE_ENABLED;
         if (isFocused()) viewStateIndex |= VIEW_STATE_FOCUSED;
-        if ((privateFlags & SELECTED) != 0) viewStateIndex |= VIEW_STATE_PRESSED;
+        if ((privateFlags & SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED;
         if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED;
         if ((privateFlags & ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED;
 
@@ -9898,11 +9898,10 @@
     }
 
     /**
-     * Drag and drop.  App calls startDrag(), then callbacks to onMeasureDragThumbnail()
-     * and onDrawDragThumbnail() happen, then the drag operation is handed over to the
-     * OS.
+     * Drag and drop.  App calls startDrag(), then callbacks to the thumbnail builder's
+     * onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag
+     * operation is handed over to the OS.
      * !!! TODO: real docs
-     * @hide
      */
     public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder,
             boolean myWindowOnly) {
@@ -10027,7 +10026,7 @@
      * For DRAG_ENDED_EVENT, the 'event' argument may be null.  The view should return
      * to its normal visual state.
      */
-    protected boolean onDragEvent(DragEvent event) {
+    public boolean onDragEvent(DragEvent event) {
         return false;
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5ebc981..5b3a091 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -938,11 +938,16 @@
             if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
             View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
             if (target != null) {
+                if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "   dispatch drop to " + target);
                 event.mX = mLocalPoint.x;
                 event.mY = mLocalPoint.y;
                 retval = target.dispatchDragEvent(event);
                 event.mX = tx;
                 event.mY = ty;
+            } else {
+                if (ViewDebug.DEBUG_DRAG) {
+                    Log.d(View.VIEW_LOG_TAG, "   not dropped on an accepting view");
+                }
             }
         } break;
         }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 7462668..7c089d8 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -22,6 +22,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.media.MediaFile;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -252,6 +253,13 @@
         return mSettings;
     }
 
+    /*
+     * Given mimeType, check whether it's supported in Android media framework.
+     * mimeType could be such as "audio/ogg" and "video/mp4".
+     */
+    /* package */ static boolean supportsMimeType(String mimeType) {
+        return MediaFile.getFileTypeForMimeType(mimeType) > 0;
+    }
     /**
      * Add an error message to the client's console.
      * @param message The message to add
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 1d1e601..b7b1a23 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -324,7 +324,11 @@
     }
 
     private int modulo(int pos, int size) {
-        return (size + (pos % size)) % size;
+        if (size > 0) {
+            return (size + (pos % size)) % size;
+        } else {
+            return 0;
+        }
     }
 
     /**
@@ -383,6 +387,8 @@
 
     void showOnly(int childIndex, boolean animate, boolean onLayout) {
         if (mAdapter == null) return;
+        final int adapterCount = mAdapter.getCount();
+        if (adapterCount == 0) return;
 
         for (int i = 0; i < mPreviousViews.size(); i++) {
             View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
@@ -399,7 +405,6 @@
             removeViewInLayout(viewToRemove);
         }
         mPreviousViews.clear();
-        int adapterCount = mAdapter.getCount();
         int newWindowStartUnbounded = childIndex - mActiveOffset;
         int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
         int newWindowStart = Math.max(0, newWindowStartUnbounded);
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 839de7d..f0954e2 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -749,7 +749,9 @@
         if (mAdapter != null && mWhichChild == -1) {
             mWhichChild = mAdapter.getCount() - 1;
         }
-        setDisplayedChild(mWhichChild);
+        if (mWhichChild >= 0) {
+            setDisplayedChild(mWhichChild);
+        }
     }
 
     LayoutParams createOrReuseLayoutParams(View v) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 70c6378..d63af4e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -97,6 +97,7 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
 import android.view.ViewRoot;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
@@ -198,7 +199,8 @@
 
     private int mCurrentAlpha = 255;    
 
-    private final int[] mTempCoords = new int[2];
+    final int[] mTempCoords = new int[2];
+    Rect mTempRect;
 
     private ColorStateList mTextColor;
     private int mCurTextColor;
@@ -7792,6 +7794,8 @@
         private float mOffsetY;
         private float mHotspotX;
         private float mHotspotY;
+        private int mLastParentX;
+        private int mLastParentY;
 
         public HandleView(CursorController controller, Drawable handle) {
             super(TextView.this.mContext);
@@ -7801,8 +7805,11 @@
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
             mContainer.setSplitTouchEnabled(true);
             mContainer.setClippingEnabled(false);
-            mHotspotX = mDrawable.getIntrinsicWidth() * 0.5f;
-            mHotspotY = -mDrawable.getIntrinsicHeight() * 0.2f;
+
+            final int handleWidth = mDrawable.getIntrinsicWidth();
+            final int handleHeight = mDrawable.getIntrinsicHeight();
+            mHotspotX = handleWidth * 0.5f;
+            mHotspotY = -handleHeight * 0.2f;
         }
 
         @Override
@@ -7812,7 +7819,7 @@
         }
 
         public void show() {
-            if (!isPositionInBounds()) {
+            if (!isPositionVisible()) {
                 hide();
                 return;
             }
@@ -7833,7 +7840,12 @@
             return mContainer.isShowing();
         }
 
-        private boolean isPositionInBounds() {
+        private boolean isPositionVisible() {
+            // Always show a dragging handle.
+            if (mIsDragging) {
+                return true;
+            }
+
             final int extendedPaddingTop = getExtendedPaddingTop();
             final int extendedPaddingBottom = getExtendedPaddingBottom();
             final int compoundPaddingLeft = getCompoundPaddingLeft();
@@ -7845,28 +7857,55 @@
             final int top = 0;
             final int bottom = hostView.getHeight();
 
-            final int clipLeft = left + compoundPaddingLeft;
-            final int clipTop = top + extendedPaddingTop;
-            final int clipRight = right - compoundPaddingRight;
-            final int clipBottom = bottom - extendedPaddingBottom;
+            if (mTempRect == null) {
+                mTempRect = new Rect();
+            }
+            final Rect clip = mTempRect;
+            clip.left = left + compoundPaddingLeft;
+            clip.top = top + extendedPaddingTop;
+            clip.right = right - compoundPaddingRight;
+            clip.bottom = bottom - extendedPaddingBottom;
 
-            return mPositionX + mHotspotX >= clipLeft && mPositionX + mHotspotX <= clipRight &&
-                    mPositionY + mHotspotY >= clipTop && mPositionY + mHotspotY <= clipBottom;
+            final ViewParent parent = hostView.getParent();
+            if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) {
+                return false;
+            }
+
+            final int[] coords = mTempCoords;
+            hostView.getLocationInWindow(coords);
+            final int posX = coords[0] + mPositionX + (int) mHotspotX;
+            final int posY = coords[1] + mPositionY;
+
+            return posX >= clip.left && posX <= clip.right &&
+                    posY >= clip.top && posY + mHotspotY <= clip.bottom;
         }
 
         private void moveTo(int x, int y) {
             mPositionX = x - TextView.this.mScrollX;
             mPositionY = y - TextView.this.mScrollY;
-            if (isPositionInBounds()) {
+            if (isPositionVisible()) {
+                int[] coords = null;
                 if (mContainer.isShowing()){
-                    final int[] coords = mTempCoords;
+                    coords = mTempCoords;
                     TextView.this.getLocationInWindow(coords);
-                    coords[0] += mPositionX;
-                    coords[1] += mPositionY;
-                    mContainer.update(coords[0], coords[1], mRight - mLeft, mBottom - mTop);
+                    mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY,
+                            mRight - mLeft, mBottom - mTop);
                 } else {
                     show();
                 }
+
+                if (mIsDragging) {
+                    if (coords == null) {
+                        coords = mTempCoords;
+                        TextView.this.getLocationInWindow(coords);
+                    }
+                    if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
+                        mOffsetX += coords[0] - mLastParentX;
+                        mOffsetY += coords[1] - mLastParentY;
+                        mLastParentX = coords[0];
+                        mLastParentY = coords[1];
+                    }
+                }
             } else {
                 hide();
             }
@@ -7893,6 +7932,10 @@
                 final float rawY = ev.getRawY();
                 mOffsetX = rawX - mPositionX;
                 mOffsetY = rawY - mPositionY;
+                final int[] coords = mTempCoords;
+                TextView.this.getLocationInWindow(coords);
+                mLastParentX = coords[0];
+                mLastParentY = coords[1];
                 mIsDragging = true;
                 break;
             }
diff --git a/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png b/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png
deleted file mode 100644
index ef8a877..0000000
--- a/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_close_on.9.png b/core/res/res/drawable-hdpi/status_bar_close_on.9.png
deleted file mode 100644
index f313ffb..0000000
--- a/core/res/res/drawable-hdpi/status_bar_close_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_menu_generic.png b/core/res/res/drawable-mdpi/ic_dialog_menu_generic.png
deleted file mode 100755
index de07bda..0000000
--- a/core/res/res/drawable-mdpi/ic_dialog_menu_generic.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_close_on.9.png b/core/res/res/drawable-mdpi/status_bar_close_on.9.png
deleted file mode 100644
index 9cbd9fe..0000000
--- a/core/res/res/drawable-mdpi/status_bar_close_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index ed42e64..37fc6c7 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -66,6 +66,7 @@
     protected MockWebServer mServer = null;
     protected String mFileType = "text/plain";
     protected Context mContext = null;
+    protected MultipleDownloadsCompletedReceiver mReceiver = null;
     protected static final int DEFAULT_FILE_SIZE = 130 * 1024;  // 130kb
     protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
 
@@ -131,12 +132,15 @@
          */
         @Override
         public void onReceive(Context context, Intent intent) {
+            Log.i(LOG_TAG, "Received Notification:");
             if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
-                ++mNumDownloadsCompleted;
-                Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
-                        intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
-                Bundle extras = intent.getExtras();
-                downloadIds.add(new Long(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID)));
+                synchronized(this) {
+                    ++mNumDownloadsCompleted;
+                    Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
+                            intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
+                    Bundle extras = intent.getExtras();
+                    downloadIds.add(new Long(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID)));
+                }
             }
         }
 
@@ -212,6 +216,7 @@
         mContext = getInstrumentation().getContext();
         mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
         mServer = new MockWebServer();
+        mReceiver = registerNewMultipleDownloadsReceiver();
         // Note: callers overriding this should call mServer.play() with the desired port #
     }
 
@@ -712,8 +717,9 @@
             Cursor cursor = mDownloadManager.query(query);
 
             try {
-                // If we've finished the downloads then we're done
-                if (cursor.getCount() == 0) {
+                // @TODO: there may be a little cleaner way to check for success, perhaps
+                // via STATUS_SUCCESSFUL and/or STATUS_FAILED
+                if (cursor.getCount() == 0 && mReceiver.numDownloadsCompleted() > 0) {
                     break;
                 }
                 currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
diff --git a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
index 27eea4d..cb7c2d2 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerIntegrationTest.java
@@ -61,7 +61,6 @@
             Environment.getRootDirectory().getAbsolutePath();
     private final static String CACHE_DIR =
             Environment.getDownloadCacheDirectory().getAbsolutePath();
-    protected MultipleDownloadsCompletedReceiver mReceiver = null;
 
     /**
      * {@inheritDoc}
@@ -72,7 +71,6 @@
         setWiFiStateOn(true);
         mServer.play();
         removeAllCurrentDownloads();
-        mReceiver = registerNewMultipleDownloadsReceiver();
     }
 
     /**
@@ -270,7 +268,7 @@
             try {
                 verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
                 verifyInt(cursor, DownloadManager.COLUMN_REASON,
-                        DownloadManager.ERROR_FILE_ERROR);
+                        DownloadManager.ERROR_FILE_ALREADY_EXISTS);
             } finally {
                 cursor.close();
             }
@@ -429,6 +427,7 @@
                 }
             }
 
+            Log.i(LOG_TAG, "Done creating filler file.");
             assertTrue(DOWNLOAD_FILE_SIZE > (fs.getAvailableBlocks() * blockSize));
             byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
             long dlRequest = doBasicDownload(blobData);
diff --git a/core/tests/coretests/src/android/content/SyncQueueTest.java b/core/tests/coretests/src/android/content/SyncQueueTest.java
deleted file mode 100644
index 1da59d1..0000000
--- a/core/tests/coretests/src/android/content/SyncQueueTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContext;
-import android.test.mock.MockContentResolver;
-import android.accounts.Account;
-import android.os.Bundle;
-import android.os.SystemClock;
-
-public class SyncQueueTest extends AndroidTestCase {
-    private static final Account ACCOUNT1 = new Account("test.account1", "test.type1");
-    private static final Account ACCOUNT2 = new Account("test.account2", "test.type2");
-    private static final String AUTHORITY1 = "test.authority1";
-    private static final String AUTHORITY2 = "test.authority2";
-    private static final String AUTHORITY3 = "test.authority3";
-
-    private SyncStorageEngine mSettings;
-    private SyncQueue mSyncQueue;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        MockContentResolver mockResolver = new MockContentResolver();
-        mSettings = SyncStorageEngine.newTestInstance(new TestContext(mockResolver, getContext()));
-        mSyncQueue = new SyncQueue(mSettings);
-    }
-
-    public void testSyncQueueOrder() throws Exception {
-        final SyncOperation op1 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
-        final SyncOperation op2 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
-        final SyncOperation op3 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
-        final SyncOperation op4 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("4"), 60);
-        final SyncOperation op5 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
-        final SyncOperation op6 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
-        op6.expedited = true;
-
-        mSyncQueue.add(op1);
-        mSyncQueue.add(op2);
-        mSyncQueue.add(op3);
-        mSyncQueue.add(op4);
-        mSyncQueue.add(op5);
-        mSyncQueue.add(op6);
-
-        long now = SystemClock.elapsedRealtime() + 200;
-
-        assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op6);
-
-        assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op1);
-
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op4);
-
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op5);
-
-        assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op2);
-
-        assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op3);
-    }
-
-    public void testOrderWithBackoff() throws Exception {
-        final SyncOperation op1 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
-        final SyncOperation op2 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
-        final SyncOperation op3 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
-        final SyncOperation op4 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY3, newTestBundle("4"), 60);
-        final SyncOperation op5 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
-        final SyncOperation op6 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
-        op6.expedited = true;
-
-        mSyncQueue.add(op1);
-        mSyncQueue.add(op2);
-        mSyncQueue.add(op3);
-        mSyncQueue.add(op4);
-        mSyncQueue.add(op5);
-        mSyncQueue.add(op6);
-
-        long now = SystemClock.elapsedRealtime() + 200;
-
-        assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op6);
-
-        assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op1);
-
-        mSettings.setBackoff(ACCOUNT2,  AUTHORITY3, now + 200, 5);
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setBackoff(ACCOUNT2,  AUTHORITY3, SyncStorageEngine.NOT_IN_BACKOFF_MODE, 0);
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setDelayUntilTime(ACCOUNT2,  AUTHORITY3, now + 200);
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setDelayUntilTime(ACCOUNT2,  AUTHORITY3, 0);
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op4);
-
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op5);
-
-        assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op2);
-
-        assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op3);
-    }
-
-    Bundle newTestBundle(String val) {
-        Bundle bundle = new Bundle();
-        bundle.putString("test", val);
-        return bundle;
-    }
-
-    static class TestContext extends ContextWrapper {
-        ContentResolver mResolver;
-
-        public TestContext(ContentResolver resolver, Context realContext) {
-            super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
-            mResolver = resolver;
-        }
-
-        @Override
-        public void enforceCallingOrSelfPermission(String permission, String message) {
-        }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mResolver;
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
index 0b494a7..ae41409 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -39,7 +39,8 @@
      * correcponding sync is finished. This can happen if the clock changes while we are syncing.
      *
      */
-    @SmallTest
+    // TODO: this test causes AidlTest to fail. Omit for now
+    // @SmallTest
     public void testPurgeActiveSync() throws Exception {
         final Account account = new Account("a@example.com", "example.type");
         final String authority = "testprovider";
diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
index 4d016d1..fe62764 100644
--- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.Suppress;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -25,6 +25,10 @@
 import java.util.Collections;
 import java.util.List;
 
+import junit.framework.TestCase;
+
+// this test causes a IllegalAccessError: superclass not accessible
+@Suppress
 public class LoggingPrintStreamTest extends TestCase {
 
     TestPrintStream out = new TestPrintStream();
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index c1d6428..8858b74 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -104,7 +104,7 @@
         private void buildShaderString() {
             mShader  = "//rs_shader_internal\n";
             mShader += "varying lowp vec4 varColor;\n";
-            mShader += "varying vec4 varTex0;\n";
+            mShader += "varying vec2 varTex0;\n";
 
             mShader += "void main() {\n";
             if (mVaryingColorEnable) {
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 63e2598..65a0af2 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -101,7 +101,7 @@
 
             mShader  = "//rs_shader_internal\n";
             mShader += "varying vec4 varColor;\n";
-            mShader += "varying vec4 varTex0;\n";
+            mShader += "varying vec2 varTex0;\n";
 
             mShader += "void main() {\n";
             mShader += "  gl_Position = UNI_MVP * ATTRIB_position;\n";
@@ -109,7 +109,7 @@
 
             mShader += "  varColor = ATTRIB_color;\n";
             if (mTextureMatrixEnable) {
-                mShader += "  varTex0 = UNI_TexMatrix * ATTRIB_texture0;\n";
+                mShader += "  varTex0 = (UNI_TexMatrix * vec4(ATTRIB_texture0, 0.0, 1.0)).xy;\n";
             } else {
                 mShader += "  varTex0 = ATTRIB_texture0;\n";
             }
@@ -126,7 +126,7 @@
             b.add(Element.F32_4(mRS), "position");
             b.add(Element.F32_4(mRS), "color");
             b.add(Element.F32_3(mRS), "normal");
-            b.add(Element.F32_4(mRS), "texture0");
+            b.add(Element.F32_2(mRS), "texture0");
             addInput(b.create());
 
             return super.create();
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 70bd8e8..cff38b2 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -64,6 +64,7 @@
     FILE *mFile;
     bool mUse4ByteNalLength;
     bool mUse32BitOffset;
+    bool mIsFileSizeLimitExplicitlyRequested;
     bool mPaused;
     bool mStarted;
     off_t mOffset;
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index d689667..d6ae5e9 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -114,8 +114,9 @@
 
     int32_t     identity;       // surface's identity (const)
     int32_t     token;          // surface's token (for debugging)
-    int32_t     reserved32[1];
     Statistics  stats;
+    int8_t      headBuf;        // last retired buffer
+    uint8_t     reservedBytes[3];
     int32_t     reserved;
     BufferData  buffers[NUM_BUFFER_MAX];     // 1024 bytes
 };
@@ -201,6 +202,7 @@
     status_t undoDequeue(int buf);
     
     status_t lock(int buf);
+    status_t cancel(int buf);
     status_t queue(int buf);
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
@@ -230,8 +232,9 @@
         inline ssize_t operator()();
     };
 
-    struct UndoDequeueUpdate : public UpdateBase {
-        inline UndoDequeueUpdate(SharedBufferBase* sbb);
+    struct CancelUpdate : public UpdateBase {
+        int tail, buf;
+        inline CancelUpdate(SharedBufferBase* sbb, int tail, int buf);
         inline ssize_t operator()();
     };
 
@@ -256,7 +259,6 @@
     int mNumBuffers;
 
     int32_t tail;
-    int32_t undoDequeueTail;
     int32_t queued_head;
     // statistics...
     nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX];
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index a210880..cef439c 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -200,6 +200,7 @@
      */
     static int setSwapInterval(ANativeWindow* window, int interval);
     static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+    static int cancelBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
     static int query(ANativeWindow* window, int what, int* value);
@@ -208,6 +209,7 @@
     int dequeueBuffer(android_native_buffer_t** buffer);
     int lockBuffer(android_native_buffer_t* buffer);
     int queueBuffer(android_native_buffer_t* buffer);
+    int cancelBuffer(android_native_buffer_t* buffer);
     int query(int what, int* value);
     int perform(int operation, va_list args);
 
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index d59d72b..654d0f3 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -218,7 +218,17 @@
     int     (*perform)(struct ANativeWindow* window,
                 int operation, ... );
     
-    void* reserved_proc[3];
+    /*
+     * hook used to cancel a buffer that has been dequeued.
+     * No synchronization is performed between dequeue() and cancel(), so
+     * either external synchronization is needed, or these functions must be
+     * called from the same thread.
+     */
+    int     (*cancelBuffer)(struct ANativeWindow* window,
+                struct android_native_buffer_t* buffer);
+
+
+    void* reserved_proc[2];
 };
 
 // Backwards compatibility...  please switch to ANativeWindow.
diff --git a/libs/rs/java/Samples/res/raw/multitexf.glsl b/libs/rs/java/Samples/res/raw/multitexf.glsl
index 91151ad..351ff9b 100644
--- a/libs/rs/java/Samples/res/raw/multitexf.glsl
+++ b/libs/rs/java/Samples/res/raw/multitexf.glsl
@@ -1,4 +1,4 @@
-varying vec4 varTex0;
+varying vec2 varTex0;
 
 void main() {
    vec2 t0 = varTex0.xy;
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index b9de7e1..a951005 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -497,7 +497,7 @@
 
 void FontState::initRenderState()
 {
-    String8 shaderString("varying vec4 varTex0;\n");
+    String8 shaderString("varying vec2 varTex0;\n");
     shaderString.append("void main() {\n");
     shaderString.append("  lowp vec4 col = UNI_Color;\n");
     shaderString.append("  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index c94f294..81b4fa4 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -190,7 +190,7 @@
 {
     String8 shaderString(RS_SHADER_INTERNAL);
     shaderString.append("varying lowp vec4 varColor;\n");
-    shaderString.append("varying vec4 varTex0;\n");
+    shaderString.append("varying vec2 varTex0;\n");
     shaderString.append("void main() {\n");
     shaderString.append("  lowp vec4 col = UNI_Color;\n");
     shaderString.append("  gl_FragColor = col;\n");
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index d3dbfb2..a785262 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -242,6 +242,7 @@
 void ProgramVertexState::init(Context *rsc)
 {
     const Element *matrixElem = Element::create(rsc, RS_TYPE_MATRIX_4X4, RS_KIND_USER, false, 1);
+    const Element *f2Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
     const Element *f3Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
     const Element *f4Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
 
@@ -256,7 +257,7 @@
     rsc->mStateElement.elementBuilderAdd(f4Elem, "position", 1);
     rsc->mStateElement.elementBuilderAdd(f4Elem, "color", 1);
     rsc->mStateElement.elementBuilderAdd(f3Elem, "normal", 1);
-    rsc->mStateElement.elementBuilderAdd(f4Elem, "texture0", 1);
+    rsc->mStateElement.elementBuilderAdd(f2Elem, "texture0", 1);
     const Element *attrElem = rsc->mStateElement.elementBuilderCreate(rsc);
 
     Type *inputType = new Type(rsc);
@@ -266,7 +267,7 @@
 
     String8 shaderString(RS_SHADER_INTERNAL);
     shaderString.append("varying vec4 varColor;\n");
-    shaderString.append("varying vec4 varTex0;\n");
+    shaderString.append("varying vec2 varTex0;\n");
     shaderString.append("void main() {\n");
     shaderString.append("  gl_Position = UNI_MVP * ATTRIB_position;\n");
     shaderString.append("  gl_PointSize = 1.0;\n");
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index a43b4402..8f583f06 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -285,10 +285,12 @@
     return NO_ERROR;
 }
 
-SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
-    : UpdateBase(sbb) {    
+SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb,
+        int tail, int buf)
+    : UpdateBase(sbb), tail(tail), buf(buf) {
 }
-ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ssize_t SharedBufferClient::CancelUpdate::operator()() {
+    stack.index[tail] = buf;
     android_atomic_inc(&stack.available);
     return NO_ERROR;
 }
@@ -319,7 +321,7 @@
         return BAD_VALUE;
 
     // Preventively lock the current buffer before updating queued.
-    android_atomic_write(stack.index[head], &stack.inUse);
+    android_atomic_write(stack.headBuf, &stack.inUse);
 
     // Decrement the number of queued buffers 
     int32_t queued;
@@ -334,7 +336,9 @@
     // the buffer we preventively locked upon entering this function
 
     head = (head + 1) % numBuffers;
-    android_atomic_write(stack.index[head], &stack.inUse);
+    const int8_t headBuf = stack.index[head];
+    stack.headBuf = headBuf;
+    android_atomic_write(headBuf, &stack.inUse);
 
     // head is only modified here, so we don't need to use cmpxchg
     android_atomic_write(head, &stack.head);
@@ -359,7 +363,7 @@
 SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
         int surface, int num, int32_t identity)
     : SharedBufferBase(sharedClient, surface, identity),
-      mNumBuffers(num), tail(0), undoDequeueTail(0)
+      mNumBuffers(num), tail(0)
 {
     SharedBufferStack& stack( *mSharedStack );
     tail = computeTail();
@@ -390,7 +394,6 @@
     DequeueUpdate update(this);
     updateCondition( update );
 
-    undoDequeueTail = tail;
     int dequeued = stack.index[tail];
     tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
     LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s",
@@ -403,14 +406,19 @@
 
 status_t SharedBufferClient::undoDequeue(int buf)
 {
+    return cancel(buf);
+}
+
+status_t SharedBufferClient::cancel(int buf)
+{
     RWLock::AutoRLock _rd(mLock);
 
-    // TODO: we can only undo the previous dequeue, we should
-    // enforce that in the api
-    UndoDequeueUpdate update(this);
+    // calculate the new position of the tail index (essentially tail--)
+    int localTail = (tail + mNumBuffers - 1) % mNumBuffers;
+    CancelUpdate update(this, localTail, buf);
     status_t err = updateCondition( update );
     if (err == NO_ERROR) {
-        tail = undoDequeueTail;
+        tail = localTail;
     }
     return err;
 }
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index c77d48e..ebb0cc9 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -416,6 +416,7 @@
 {
     ANativeWindow::setSwapInterval  = setSwapInterval;
     ANativeWindow::dequeueBuffer    = dequeueBuffer;
+    ANativeWindow::cancelBuffer     = cancelBuffer;
     ANativeWindow::lockBuffer       = lockBuffer;
     ANativeWindow::queueBuffer      = queueBuffer;
     ANativeWindow::query            = query;
@@ -527,6 +528,12 @@
     return self->dequeueBuffer(buffer);
 }
 
+int Surface::cancelBuffer(ANativeWindow* window,
+        android_native_buffer_t* buffer) {
+    Surface* self = getSelf(window);
+    return self->cancelBuffer(buffer);
+}
+
 int Surface::lockBuffer(ANativeWindow* window, 
         android_native_buffer_t* buffer) {
     Surface* self = getSelf(window);
@@ -627,6 +634,33 @@
     return err;
 }
 
+int Surface::cancelBuffer(android_native_buffer_t* buffer)
+{
+    status_t err = validate();
+    switch (err) {
+    case NO_ERROR:
+        // no error, common case
+        break;
+    case INVALID_OPERATION:
+        // legitimate errors here
+        return err;
+    default:
+        // other errors happen because the surface is now invalid,
+        // for instance because it has been destroyed. In this case,
+        // we just fail silently (canceling a buffer is not technically
+        // an error at this point)
+        return NO_ERROR;
+    }
+
+    int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+    err = mSharedBufferClient->cancel(bufIdx);
+
+    LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err));
+    return err;
+}
+
+
 int Surface::lockBuffer(android_native_buffer_t* buffer)
 {
     status_t err = validate();
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
index 8da7eaa..3ebad00 100755
--- a/media/java/android/media/videoeditor/AudioTrack.java
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -183,11 +183,13 @@
      */

     @SuppressWarnings("unused")

     private AudioTrack() throws IOException {

-        this(null, null);

+        this(null, null, null);

     }

 

     /**

      * Constructor

+     *

+     * @param editor The video editor reference

      * @param audioTrackId The audio track id

      * @param filename The absolute file name

      *

@@ -195,7 +197,8 @@
      * @throws IllegalArgumentException if file format is not supported or if

      *             the codec is not supported

      */

-    public AudioTrack(String audioTrackId, String filename) throws IOException {

+    public AudioTrack(VideoEditor editor, String audioTrackId, String filename)

+            throws IOException {

         mUniqueId = audioTrackId;

         mFilename = filename;

         mStartTimeMs = 0;

@@ -233,6 +236,7 @@
     /**

      * Constructor

      *

+     * @param editor The video editor reference

      * @param audioTrackId The audio track id

      * @param filename The audio filename

      * @param startTimeMs the start time in milliseconds (relative to the

@@ -248,8 +252,9 @@
      *

      * @throws IOException if file is not found

      */

-    AudioTrack(String audioTrackId, String filename, long startTimeMs, long beginMs, long endMs,

-            boolean loop, int volume, boolean muted, String audioWaveformFilename) throws IOException {

+    AudioTrack(VideoEditor editor, String audioTrackId, String filename, long startTimeMs,

+            long beginMs, long endMs, boolean loop, int volume, boolean muted,

+            String audioWaveformFilename) throws IOException {

         mUniqueId = audioTrackId;

         mFilename = filename;

         mStartTimeMs = startTimeMs;

diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index ae4d359..df3c5fb 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -53,12 +53,13 @@
      */

     @SuppressWarnings("unused")

     private MediaImageItem() throws IOException {

-        this(null, null, 0, RENDERING_MODE_BLACK_BORDER);

+        this(null, null, null, 0, RENDERING_MODE_BLACK_BORDER);

     }

 

     /**

      * Constructor

      *

+     * @param editor The video editor reference

      * @param mediaItemId The media item id

      * @param filename The image file name

      * @param durationMs The duration of the image on the storyboard

@@ -66,9 +67,10 @@
      *

      * @throws IOException

      */

-    public MediaImageItem(String mediaItemId, String filename, long durationMs, int renderingMode)

+    public MediaImageItem(VideoEditor editor, String mediaItemId, String filename, long durationMs,

+            int renderingMode)

             throws IOException {

-        super(mediaItemId, filename, renderingMode);

+        super(editor, mediaItemId, filename, renderingMode);

 

         // Determine the dimensions of the image

         final BitmapFactory.Options dbo = new BitmapFactory.Options();

diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
index b5561e9..d9c38af 100755
--- a/media/java/android/media/videoeditor/MediaItem.java
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -70,6 +70,7 @@
     /**

      * Constructor

      *

+     * @param editor The video editor reference

      * @param mediaItemId The MediaItem id

      * @param filename name of the media file.

      * @param renderingMode The rendering mode

@@ -79,7 +80,8 @@
      *             supported the exception object contains the unsupported

      *             capability

      */

-    protected MediaItem(String mediaItemId, String filename, int renderingMode) throws IOException {

+    protected MediaItem(VideoEditor editor, String mediaItemId, String filename,

+            int renderingMode) throws IOException {

         mUniqueId = mediaItemId;

         mFilename = filename;

         mRenderingMode = renderingMode;

diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index 8ac5bdd..dd12336 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -192,26 +192,29 @@
      */

     @SuppressWarnings("unused")

     private MediaVideoItem() throws IOException {

-        this(null, null, RENDERING_MODE_BLACK_BORDER);

+        this(null, null, null, RENDERING_MODE_BLACK_BORDER);

     }

 

     /**

      * Constructor

      *

+     * @param editor The video editor reference

      * @param mediaItemId The MediaItem id

      * @param filename The image file name

      * @param renderingMode The rendering mode

      *

      * @throws IOException if the file cannot be opened for reading

      */

-    public MediaVideoItem(String mediaItemId, String filename, int renderingMode)

+    public MediaVideoItem(VideoEditor editor, String mediaItemId, String filename,

+            int renderingMode)

         throws IOException {

-        this(mediaItemId, filename, renderingMode, 0, END_OF_FILE, 100, false, null);

+        this(editor, mediaItemId, filename, renderingMode, 0, END_OF_FILE, 100, false, null);

     }

 

     /**

      * Constructor

      *

+     * @param editor The video editor reference

      * @param mediaItemId The MediaItem id

      * @param filename The image file name

      * @param renderingMode The rendering mode

@@ -226,10 +229,10 @@
      *

      * @throws IOException if the file cannot be opened for reading

      */

-    MediaVideoItem(String mediaItemId, String filename, int renderingMode,

+    MediaVideoItem(VideoEditor editor, String mediaItemId, String filename, int renderingMode,

             long beginMs, long endMs, int volumePercent, boolean muted,

             String audioWaveformFilename)  throws IOException {

-        super(mediaItemId, filename, renderingMode);

+        super(editor, mediaItemId, filename, renderingMode);

         // TODO: Set these variables correctly

         mWidth = 1080;

         mHeight = 720;

diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
index b39d9d8..c3cb82a 100644
--- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -765,7 +765,7 @@
                         if (MediaImageItem.class.getSimpleName().equals(type)) {
                             final long durationMs = Long.parseLong(parser.getAttributeValue("",
                                     ATTR_DURATION));
-                            currentMediaItem = new MediaImageItem(mediaItemId, filename,
+                            currentMediaItem = new MediaImageItem(this, mediaItemId, filename,
                                     durationMs, renderingMode);
                         } else if (MediaVideoItem.class.getSimpleName().equals(type)) {
                             final long beginMs = Long.parseLong(parser.getAttributeValue("",
@@ -778,7 +778,7 @@
                                     ATTR_MUTED));
                             final String audioWaveformFilename = parser.getAttributeValue("",
                                     ATTR_AUDIO_WAVEFORM_FILENAME);
-                            currentMediaItem = new MediaVideoItem(mediaItemId, filename,
+                            currentMediaItem = new MediaVideoItem(this, mediaItemId, filename,
                                     renderingMode, beginMs, endMs, volume, muted,
                                     audioWaveformFilename);
 
@@ -1017,7 +1017,7 @@
         final boolean loop = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_LOOP));
         final String waveformFilename = parser.getAttributeValue("", ATTR_AUDIO_WAVEFORM_FILENAME);
         try {
-            final AudioTrack audioTrack = new AudioTrack(audioTrackId, filename, startTimeMs,
+            final AudioTrack audioTrack = new AudioTrack(this, audioTrackId, filename, startTimeMs,
                     beginMs, endMs, loop, volume, muted, waveformFilename);
 
             return audioTrack;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 90b1aab..6d00d7c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -212,6 +212,7 @@
     : mFile(fopen(filename, "wb")),
       mUse4ByteNalLength(true),
       mUse32BitOffset(true),
+      mIsFileSizeLimitExplicitlyRequested(false),
       mPaused(false),
       mStarted(false),
       mOffset(0),
@@ -225,6 +226,7 @@
     : mFile(fdopen(fd, "wb")),
       mUse4ByteNalLength(true),
       mUse32BitOffset(true),
+      mIsFileSizeLimitExplicitlyRequested(false),
       mPaused(false),
       mStarted(false),
       mOffset(0),
@@ -322,7 +324,7 @@
     static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
     int64_t size = MIN_MOOV_BOX_SIZE;
 
-    if (mMaxFileSizeLimitBytes != 0) {
+    if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
         size = mMaxFileSizeLimitBytes * 4 / 1000;
     } else if (mMaxFileDurationLimitUs != 0) {
         if (bitRate <= 0) {
@@ -342,7 +344,7 @@
         size = MAX_MOOV_BOX_SIZE;
     }
 
-    LOGV("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
+    LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
          " moov size %lld bytes",
          mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
     return factor * size;
@@ -353,6 +355,16 @@
         return UNKNOWN_ERROR;
     }
 
+    /*
+     * Check mMaxFileSizeLimitBytes at the beginning
+     * since mMaxFileSizeLimitBytes may be implicitly
+     * changed later for 32-bit file offset even if
+     * user does not ask to set it explicitly.
+     */
+    if (mMaxFileSizeLimitBytes != 0) {
+        mIsFileSizeLimitExplicitlyRequested = true;
+    }
+
     int32_t use64BitOffset;
     if (param &&
         param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index e442c85..3908d71 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -23,10 +23,13 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.Writer;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 import android.hardware.Camera;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
+import android.os.Handler;
 import android.os.Looper;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -51,27 +54,75 @@
     private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
     private static final long WAIT_TIME_CAMERA_TEST = 3000;  // 3 second
     private static final long WAIT_TIME_RECORDER_TEST = 6000;  // 6 second
-    private static final long WAIT_TIME_RECORD = 10000;  // 10 seconds
-    private static final long WAIT_TIME_PLAYBACK = 6000;  // 6 second
     private static final String OUTPUT_FILE = "/sdcard/temp";
     private static final String OUTPUT_FILE_EXT = ".3gp";
     private static final String MEDIA_STRESS_OUTPUT =
         "/sdcard/mediaStressOutput.txt";
-    private Looper mCameraLooper = null;
-    private Looper mRecorderLooper = null;
-    private final Object lock = new Object();
-    private final Object recorderlock = new Object();
-    private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000;  // Milliseconds.
     private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
     private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
 
+    private final static int WAIT_TIMEOUT = 10000;
+    private Thread mLooperThread;
+    private Handler mHandler;
+
     public MediaRecorderStressTest() {
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
     }
 
     protected void setUp() throws Exception {
+        final Semaphore sem = new Semaphore(0);
+        mLooperThread = new Thread() {
+            @Override
+            public void run() {
+                Log.v(TAG, "starting looper");
+                Looper.prepare();
+                mHandler = new Handler();
+                sem.release();
+                Looper.loop();
+                Log.v(TAG, "quit looper");
+            }
+        };
+        mLooperThread.start();
+        if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+            fail("Failed to start the looper.");
+        }
+
         getActivity();
-        super.setUp();      
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHandler != null) {
+            mHandler.getLooper().quit();
+            mHandler = null;
+        }
+        if (mLooperThread != null) {
+            mLooperThread.join(WAIT_TIMEOUT);
+            if (mLooperThread.isAlive()) {
+                fail("Failed to stop the looper.");
+            }
+            mLooperThread = null;
+        }
+
+        super.tearDown();
+    }
+
+    private void runOnLooper(final Runnable command) throws InterruptedException {
+        final Semaphore sem = new Semaphore(0);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    command.run();
+                } finally {
+                    sem.release();
+                }
+            }
+        });
+        if (! sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+            fail("Failed to run the command on the looper.");
+        }
     }
 
     private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
@@ -89,69 +140,6 @@
         }
     }
 
-    private void initializeCameraMessageLooper() {
-        Log.v(TAG, "start looper");
-        new Thread() {
-            @Override
-            public void run() {
-                // Set up a looper to be used by camera.
-                Looper.prepare();
-                Log.v(TAG, "start loopRun");
-                mCameraLooper = Looper.myLooper();
-                mCamera = Camera.open();
-                synchronized (lock) {
-                    lock.notify();
-                }
-                Looper.loop();
-                Log.v(TAG, "initializeMessageLooper: quit.");
-            }
-        }.start();
-    }
-
-    private void initializeRecorderMessageLooper() {
-        Log.v(TAG, "start looper");
-        new Thread() {
-            @Override
-            public void run() {
-                Looper.prepare();
-                Log.v(TAG, "start loopRun");
-                mRecorderLooper = Looper.myLooper();
-                mRecorder = new MediaRecorder();
-                synchronized (recorderlock) {
-                    recorderlock.notify();
-                }
-                Looper.loop();  // Blocks forever until Looper.quit() is called.
-                Log.v(TAG, "initializeMessageLooper: quit.");
-            }
-        }.start();
-    }
-
-    /*
-     * Terminates the message looper thread.
-     */
-    private void terminateCameraMessageLooper() {
-        mCameraLooper.quit();
-        try {
-            Thread.sleep(1000);
-        } catch (Exception e){
-            Log.v(TAG, e.toString());
-        }
-        mCamera.release();
-    }
-
-    /*
-     * Terminates the message looper thread.
-     */
-    private void terminateRecorderMessageLooper() {
-        mRecorderLooper.quit();
-        try {
-            Thread.sleep(1000);
-        } catch (Exception e){
-            Log.v(TAG, e.toString());
-        }
-        mRecorder.release();
-    }
-
     //Test case for stressing the camera preview.
     @LargeTest
     public void testStressCamera() throws Exception {
@@ -166,21 +154,19 @@
             Log.v(TAG, "Start preview");
             output.write("No of loop: ");
 
-            for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++){
-                synchronized (lock) {
-                    initializeCameraMessageLooper();
-                    try {
-                        lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
-                    } catch(Exception e) {
-                        Log.v(TAG, "wait was interrupted.");
+            for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) {
+                runOnLooper(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCamera = Camera.open();
                     }
-                }
+                });
                 mCamera.setErrorCallback(mCameraErrorCallback);
                 mCamera.setPreviewDisplay(mSurfaceHolder);
                 mCamera.startPreview();
                 Thread.sleep(WAIT_TIME_CAMERA_TEST);
                 mCamera.stopPreview();
-                terminateCameraMessageLooper();
+                mCamera.release();
                 output.write(" ," + i);
             }
         } catch (Exception e) {
@@ -205,15 +191,13 @@
         try {
             output.write("No of loop: ");
             Log.v(TAG, "Start preview");
-            for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++){
-                synchronized (recorderlock) {
-                    initializeRecorderMessageLooper();
-                    try {
-                        recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
-                    } catch(Exception e) {
-                        Log.v(TAG, "wait was interrupted.");
+            for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) {
+                runOnLooper(new Runnable() {
+                    @Override
+                    public void run() {
+                        mRecorder = new MediaRecorder();
                     }
-                }
+                });
                 Log.v(TAG, "counter = " + i);
                 filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
                 Log.v(TAG, filename);
@@ -233,7 +217,7 @@
                 Log.v(TAG, "before release");
                 Thread.sleep(WAIT_TIME_RECORDER_TEST);
                 mRecorder.reset();
-                terminateRecorderMessageLooper();
+                mRecorder.release();
                 output.write(", " + i);
             }
         } catch (Exception e) {
@@ -258,33 +242,29 @@
         try {
             Log.v(TAG, "Start preview");
             output.write("No of loop: ");
-            for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++){
-                synchronized (lock) {
-                    initializeCameraMessageLooper();
-                    try {
-                        lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
-                    } catch(Exception e) {
-                        Log.v(TAG, "wait was interrupted.");
+            for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) {
+                runOnLooper(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCamera = Camera.open();
                     }
-                }
+                });
                 mCamera.setErrorCallback(mCameraErrorCallback);
                 mCamera.setPreviewDisplay(mSurfaceHolder);
                 mCamera.startPreview();
                 Thread.sleep(WAIT_TIME_CAMERA_TEST);
                 mCamera.stopPreview();
-                terminateCameraMessageLooper();
+                mCamera.release();
                 mCamera = null;
                 Log.v(TAG, "release camera");
                 filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
                 Log.v(TAG, filename);
-                synchronized (recorderlock) {
-                    initializeRecorderMessageLooper();
-                    try {
-                        recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
-                    } catch(Exception e) {
-                        Log.v(TAG, "wait was interrupted.");
+                runOnLooper(new Runnable() {
+                    @Override
+                    public void run() {
+                        mRecorder = new MediaRecorder();
                     }
-                }
+                });
                 mRecorder.setOnErrorListener(mRecorderErrorCallback);
                 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
@@ -299,7 +279,7 @@
                 mRecorder.prepare();
                 Log.v(TAG, "before release");
                 Thread.sleep(WAIT_TIME_CAMERA_TEST);
-                terminateRecorderMessageLooper();
+                mRecorder.release();
                 Log.v(TAG, "release video recorder");
                 output.write(", " + i);
             }
@@ -358,14 +338,12 @@
             for (int i = 0; i < iterations; i++){
                 filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
                 Log.v(TAG, filename);
-                synchronized (recorderlock) {
-                    initializeRecorderMessageLooper();
-                    try {
-                        recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
-                    } catch(Exception e) {
-                        Log.v(TAG, "wait was interrupted.");
+                runOnLooper(new Runnable() {
+                    @Override
+                    public void run() {
+                        mRecorder = new MediaRecorder();
                     }
-                }
+                });
                 Log.v(TAG, "iterations : " + iterations);
                 Log.v(TAG, "video_encoder : " + video_encoder);
                 Log.v(TAG, "audio_encoder : " + audio_encoder);
@@ -391,7 +369,7 @@
                 Thread.sleep(record_duration);
                 Log.v(TAG, "Before stop");
                 mRecorder.stop();
-                terminateRecorderMessageLooper();
+                mRecorder.release();
                 //start the playback
                 MediaPlayer mp = new MediaPlayer();
                 mp.setDataSource(filename);
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 460b74f..239dc05 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -1969,7 +1969,7 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     // TODO: eglSwapInterval()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    return EGL_TRUE;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index a33b7c294..2b4845b 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -584,9 +584,9 @@
         }
         if (linkProperties != null) {
             intent.putExtra(Phone.DATA_LINK_PROPERTIES_KEY, linkProperties);
-            NetworkInterface iface = linkProperties.getInterface();
+            String iface = linkProperties.getInterfaceName();
             if (iface != null) {
-                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface.getName());
+                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface);
             }
         }
         if (linkCapabilities != null) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 30aed69..59f7434 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -505,7 +505,7 @@
         InputChannel mServerChannel, mClientChannel;
         WindowState mTargetWindow;
         ArrayList<WindowState> mNotifiedWindows;
-        boolean mDragEnded;
+        boolean mDragInProgress;
 
         private final Rect tmpRect = new Rect();
 
@@ -562,6 +562,7 @@
             // works correctly in calling out to the apps.
             mDataDescription = new ClipDescription(mData);
             mNotifiedWindows.clear();
+            mDragInProgress = true;
 
             if (DEBUG_DRAG) {
                 Slog.d(TAG, "broadcasting DRAG_STARTED of " + mDataDescription);
@@ -586,7 +587,7 @@
          * process, so it's safe for the caller to call recycle() on the event afterwards.
          */
         private void sendDragStartedLw(WindowState newWin, DragEvent event) {
-            if (!mDragEnded && newWin.isPotentialDragTarget()) {
+            if (mDragInProgress && newWin.isPotentialDragTarget()) {
                 try {
                     // clone for local callees since dispatch will recycle the event
                     if (Process.myPid() == newWin.mSession.mPid) {
@@ -606,20 +607,22 @@
          * was begun.  This is a rare case.
          */
         private void sendDragStartedIfNeededLw(WindowState newWin) {
-            // If we have sent the drag-started, we needn't do so again
-            for (WindowState ws : mNotifiedWindows) {
-                if (ws == newWin) {
-                    return;
+            if (mDragInProgress) {
+                // If we have sent the drag-started, we needn't do so again
+                for (WindowState ws : mNotifiedWindows) {
+                    if (ws == newWin) {
+                        return;
+                    }
                 }
+                if (DEBUG_DRAG) {
+                    Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
+                }
+                DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+                        mDataDescription, null);
+                // sendDragStartedLw() clones 'event' if the window is process-local
+                sendDragStartedLw(newWin, event);
+                event.recycle();
             }
-            if (DEBUG_DRAG) {
-                Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
-            }
-            DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
-                    mDataDescription, null);
-            // sendDragStartedLw() clones 'event' if the window is process-local
-            sendDragStartedLw(newWin, event);
-            event.recycle();
         }
 
         void broadcastDragEnded() {
@@ -636,7 +639,7 @@
                     }
                 }
                 mNotifiedWindows.clear();
-                mDragEnded = true;
+                mDragInProgress = false;
             }
             evt.recycle();
         }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 30395c0..9ed1242 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -515,7 +515,7 @@
                     r.info, r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward());
             
-            if ((app.info.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+            if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Note that the package
                 // manager will ensure that only activity can run in the main
                 // process of the .apk, which is the only thing that will be
@@ -2442,7 +2442,7 @@
             final long origId = Binder.clearCallingIdentity();
             
             if (mMainStack && aInfo != null &&
-                    (aInfo.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Check to see if we already
                 // have another, different heavy-weight process running.
                 if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index 0d983b5..07f90cd 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -40,6 +40,7 @@
         MMI,                            /* not presently used; dial() returns null */
         INVALID_NUMBER,                 /* invalid dial string */
         NUMBER_UNREACHABLE,             /* cannot reach the peer */
+        SERVER_UNREACHABLE,             /* cannot reach the server */
         INVALID_CREDENTIALS,            /* invalid credentials */
         OUT_OF_NETWORK,                 /* calling from out of network is not allowed */
         SERVER_ERROR,                   /* server error */
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 3030481..185d413 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -21,6 +21,7 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
+import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
@@ -29,10 +30,10 @@
 import android.util.EventLog;
 
 import java.net.InetAddress;
+import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.net.UnknownHostException;
-import java.util.HashMap;
 
 /**
  * {@hide}
@@ -68,7 +69,7 @@
  *        EVENT_GET_LAST_FAIL_DONE,
  *        EVENT_DEACTIVATE_DONE.
  *     }
- *   ++ # mInactiveState 
+ *   ++ # mInactiveState
  *        e(doNotifications)
  *        x(clearNotifications) {
  *            EVENT_RESET { notifiyDisconnectCompleted }.
@@ -428,26 +429,25 @@
                 try {
                     String prefix = "net." + interfaceName + ".";
 
-                    linkProperties.setInterface(NetworkInterface.getByName(interfaceName));
+                    NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName);
+                    linkProperties.setInterfaceName(interfaceName);
 
                     // TODO: Get gateway and dns via RIL interface not property?
                     String gatewayAddress = SystemProperties.get(prefix + "gw");
                     linkProperties.setGateway(InetAddress.getByName(gatewayAddress));
 
-                    if (response.length > 2) {
-                        String ipAddress = response[2];
-                        linkProperties.addAddress(InetAddress.getByName(ipAddress));
-
-                        // TODO: Get gateway and dns via RIL interface not property?
-                        String dnsServers[] = new String[2];
-                        dnsServers[0] = SystemProperties.get(prefix + "dns1");
-                        dnsServers[1] = SystemProperties.get(prefix + "dns2");
-                        if (isDnsOk(dnsServers)) {
-                            linkProperties.addDns(InetAddress.getByName(dnsServers[0]));
-                            linkProperties.addDns(InetAddress.getByName(dnsServers[1]));
-                        } else {
-                            result = SetupResult.ERR_BadDns;
-                        }
+                    for (InterfaceAddress addr : networkInterface.getInterfaceAddresses()) {
+                        linkProperties.addLinkAddress(new LinkAddress(addr));
+                    }
+                    // TODO: Get gateway and dns via RIL interface not property?
+                    String dnsServers[] = new String[2];
+                    dnsServers[0] = SystemProperties.get(prefix + "dns1");
+                    dnsServers[1] = SystemProperties.get(prefix + "dns2");
+                    if (isDnsOk(dnsServers)) {
+                        linkProperties.addDns(InetAddress.getByName(dnsServers[0]));
+                        linkProperties.addDns(InetAddress.getByName(dnsServers[1]));
+                    } else {
+                        result = SetupResult.ERR_BadDns;
                     }
                 } catch (UnknownHostException e1) {
                     log("onSetupCompleted: UnknowHostException " + e1);
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 55e3002..6ed9295 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -874,6 +874,9 @@
         public void onError(SipAudioCall call, int errorCode,
                 String errorMessage) {
             switch (errorCode) {
+                case SipErrorCode.SERVER_UNREACHABLE:
+                    onError(Connection.DisconnectCause.SERVER_UNREACHABLE);
+                    break;
                 case SipErrorCode.PEER_NOT_REACHABLE:
                     onError(Connection.DisconnectCause.NUMBER_UNREACHABLE);
                     break;
diff --git a/tests/CoreTests/android/core/MiscRegressionTest.java b/tests/CoreTests/android/core/MiscRegressionTest.java
index 8281db0..7734397 100644
--- a/tests/CoreTests/android/core/MiscRegressionTest.java
+++ b/tests/CoreTests/android/core/MiscRegressionTest.java
@@ -66,37 +66,6 @@
         }
     }
 
-    // Regression test for #1061945: negative Shorts do not
-    // serialize/deserialize correctly
-    @SmallTest
-    public void testShortSerialization() throws Exception {
-        // create an instance of ObjectInputStream
-        String x = new String("serialize_foobar");
-        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
-        (new java.io.ObjectOutputStream(baos)).writeObject(x);
-        ObjectInputStream ois = new java.io.ObjectInputStream(
-                new java.io.ByteArrayInputStream(baos.toByteArray()));
-
-        // get the setField(...,, short val) method in question
-        Class<ObjectInputStream> oClass = ObjectInputStream.class;
-        Method m = oClass.getDeclaredMethod("setField", new Class[] { Object.class, Class.class, String.class, short.class});
-        // compose args
-        short start = 123;
-        short origval = -1; // 0xffff
-        Short obj = new Short(start);
-        Class<Short> declaringClass = Short.class;
-        String fieldDescName = "value";
-
-        // test the initial value
-        assertEquals(obj.shortValue(), start);
-        // invoke native method to set the field "value" of type short to the newval
-        m.setAccessible(true); // since the method is private
-        m.invoke(ois, new Object[]{ obj, declaringClass, fieldDescName, new Short(origval)} );
-        // test the set value
-        short res = obj.shortValue();
-        assertEquals("Read and written values must be equal", origval, res);
-    }
-    
     // Regression test for #951285: Suitable LogHandler should be chosen
     // depending on the environment.
     @MediumTest
diff --git a/voip/java/android/net/sip/ISipService.aidl b/voip/java/android/net/sip/ISipService.aidl
index 6c68213..3250bf9 100644
--- a/voip/java/android/net/sip/ISipService.aidl
+++ b/voip/java/android/net/sip/ISipService.aidl
@@ -16,6 +16,7 @@
 
 package android.net.sip;
 
+import android.app.PendingIntent;
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
 import android.net.sip.SipProfile;
@@ -26,7 +27,7 @@
 interface ISipService {
     void open(in SipProfile localProfile);
     void open3(in SipProfile localProfile,
-            String incomingCallBroadcastAction,
+            in PendingIntent incomingCallPendingIntent,
             in ISipSessionListener listener);
     void close(in String localProfileUri);
     boolean isOpened(String localProfileUri);
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index eb7a1ae..a55ab25 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -61,6 +61,9 @@
     /** Cross-domain authentication required. */
     public static final int CROSS_DOMAIN_AUTHENTICATION = -11;
 
+    /** When the server is not reachable. */
+    public static final int SERVER_UNREACHABLE = -12;
+
     public static String toString(int errorCode) {
         switch (errorCode) {
             case NO_ERROR:
@@ -87,6 +90,8 @@
                 return "DATA_CONNECTION_LOST";
             case CROSS_DOMAIN_AUTHENTICATION:
                 return "CROSS_DOMAIN_AUTHENTICATION";
+            case SERVER_UNREACHABLE:
+                return "SERVER_UNREACHABLE";
             default:
                 return "UNKNOWN";
         }
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index bd859e8..80c35fb 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -16,6 +16,7 @@
 
 package android.net.sip;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -34,7 +35,7 @@
  * <li>open a {@link SipProfile} to get ready for making outbound calls or have
  *      the background SIP service listen to incoming calls and broadcast them
  *      with registered command string. See
- *      {@link #open(SipProfile, String, SipRegistrationListener)},
+ *      {@link #open(SipProfile, PendingIntent, SipRegistrationListener)},
  *      {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
  *      {@link #isRegistered}. It also facilitates handling of the incoming call
  *      broadcast intent. See
@@ -51,6 +52,19 @@
  */
 public class SipManager {
     /**
+     * The result code to be sent back with the incoming call
+     * {@link PendingIntent}.
+     * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
+     */
+    public static final int INCOMING_CALL_RESULT_CODE = 101;
+
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_CALL_ID = "android:sipCallID";
+
+    /** Part of the incoming call intent. */
+    public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
+
+    /**
      * Action string for the incoming call intent for the Phone app.
      * Internal use only.
      * @hide
@@ -78,12 +92,6 @@
      */
     public static final String EXTRA_LOCAL_URI = "android:localSipUri";
 
-    /** Part of the incoming call intent. */
-    public static final String EXTRA_CALL_ID = "android:sipCallID";
-
-    /** Part of the incoming call intent. */
-    public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
-
     private static final String TAG = "SipManager";
 
     private ISipService mSipService;
@@ -142,7 +150,8 @@
     /**
      * Opens the profile for making calls. The caller may make subsequent calls
      * through {@link #makeAudioCall}. If one also wants to receive calls on the
-     * profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
+     * profile, use
+     * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}
      * instead.
      *
      * @param localProfile the SIP profile to make calls from
@@ -165,17 +174,29 @@
      * in order to receive calls from the provider.
      *
      * @param localProfile the SIP profile to receive incoming calls for
-     * @param incomingCallBroadcastAction the action to be broadcast when an
-     *      incoming call is received
+     * @param incomingCallPendingIntent When an incoming call is received, the
+     *      SIP service will call
+     *      {@link PendingIntent#send(Context, int, Intent)} to send back the
+     *      intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the
+     *      result code and the intent to fill in the call ID and session
+     *      description information. It cannot be null.
      * @param listener to listen to registration events; can be null
+     * @see #getCallId
+     * @see #getOfferSessionDescription
+     * @see #takeAudioCall
+     * @throws NullPointerException if {@code incomingCallPendingIntent} is null
      * @throws SipException if the profile contains incorrect settings or
      *      calling the SIP service results in an error
      */
     public void open(SipProfile localProfile,
-            String incomingCallBroadcastAction,
+            PendingIntent incomingCallPendingIntent,
             SipRegistrationListener listener) throws SipException {
+        if (incomingCallPendingIntent == null) {
+            throw new NullPointerException(
+                    "incomingCallPendingIntent cannot be null");
+        }
         try {
-            mSipService.open3(localProfile, incomingCallBroadcastAction,
+            mSipService.open3(localProfile, incomingCallPendingIntent,
                     createRelay(listener, localProfile.getUriString()));
         } catch (RemoteException e) {
             throw new SipException("open()", e);
@@ -184,7 +205,8 @@
 
     /**
      * Sets the listener to listen to registration events. No effect if the
-     * profile has not been opened to receive calls (see {@link #open}).
+     * profile has not been opened to receive calls (see
+     * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}).
      *
      * @param localProfileUri the URI of the profile
      * @param listener to listen to registration events; can be null
@@ -282,9 +304,9 @@
     }
 
     /**
-     * Creates a {@link SipAudioCall} to make a call. To use this method, one
-     * must call {@link #open(SipProfile)} first. The attempt will be timed out
-     * if the call is not established within {@code timeout} seconds and
+     * Creates a {@link SipAudioCall} to make an audio call. The attempt will be
+     * timed out if the call is not established within {@code timeout} seconds
+     * and
      * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
      * will be called.
      *
@@ -416,9 +438,11 @@
 
     /**
      * Manually registers the profile to the corresponding SIP provider for
-     * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
-     * is still needed to be called at least once in order for the SIP service
-     * to broadcast an intent when an incoming call is received.
+     * receiving calls.
+     * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is
+     * still needed to be called at least once in order for the SIP service to
+     * notify the caller with the {@code PendingIntent} when an incoming call is
+     * received.
      *
      * @param localProfile the SIP profile to register with
      * @param expiryTime registration expiration time (in seconds)
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 405dff8..a7c61e5 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -134,14 +134,6 @@
 
     public void open(SipProfile localProfile) {
         localProfile.setCallingUid(Binder.getCallingUid());
-        if (localProfile.getAutoRegistration() && isCallerRadio()) {
-            openToReceiveCalls(localProfile);
-        } else {
-            openToMakeCalls(localProfile);
-        }
-    }
-
-    private void openToMakeCalls(SipProfile localProfile) {
         try {
             createGroup(localProfile);
         } catch (SipException e) {
@@ -150,28 +142,20 @@
         }
     }
 
-    private void openToReceiveCalls(SipProfile localProfile) {
-        open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
-    }
-
     public synchronized void open3(SipProfile localProfile,
-            String incomingCallBroadcastAction, ISipSessionListener listener) {
+            PendingIntent incomingCallPendingIntent,
+            ISipSessionListener listener) {
         localProfile.setCallingUid(Binder.getCallingUid());
-        if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
-            Log.w(TAG, "empty broadcast action for incoming call");
-            return;
-        }
-        if (incomingCallBroadcastAction.equals(
-                SipManager.ACTION_SIP_INCOMING_CALL) && !isCallerRadio()) {
-            Log.w(TAG, "failed to open the profile; "
-                    + "the action string is reserved");
+        if (incomingCallPendingIntent == null) {
+            Log.w(TAG, "incomingCallPendingIntent cannot be null; "
+                    + "the profile is not opened");
             return;
         }
         if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
-                + incomingCallBroadcastAction + ": " + listener);
+                + incomingCallPendingIntent + ": " + listener);
         try {
             SipSessionGroupExt group = createGroup(localProfile,
-                    incomingCallBroadcastAction, listener);
+                    incomingCallPendingIntent, listener);
             if (localProfile.getAutoRegistration()) {
                 group.openToReceiveCalls();
                 if (isWifiOn()) grabWifiLock();
@@ -287,20 +271,19 @@
     }
 
     private SipSessionGroupExt createGroup(SipProfile localProfile,
-            String incomingCallBroadcastAction, ISipSessionListener listener)
-            throws SipException {
+            PendingIntent incomingCallPendingIntent,
+            ISipSessionListener listener) throws SipException {
         String key = localProfile.getUriString();
         SipSessionGroupExt group = mSipGroups.get(key);
         if (group != null) {
             if (!isCallerCreator(group)) {
                 throw new SipException("only creator can access the profile");
             }
-            group.setIncomingCallBroadcastAction(
-                    incomingCallBroadcastAction);
+            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
             group.setListener(listener);
         } else {
             group = new SipSessionGroupExt(localProfile,
-                    incomingCallBroadcastAction, listener);
+                    incomingCallPendingIntent, listener);
             mSipGroups.put(key, group);
             notifyProfileAdded(localProfile);
         }
@@ -405,19 +388,19 @@
 
     private class SipSessionGroupExt extends SipSessionAdapter {
         private SipSessionGroup mSipGroup;
-        private String mIncomingCallBroadcastAction;
+        private PendingIntent mIncomingCallPendingIntent;
         private boolean mOpened;
 
         private AutoRegistrationProcess mAutoRegistration =
                 new AutoRegistrationProcess();
 
         public SipSessionGroupExt(SipProfile localProfile,
-                String incomingCallBroadcastAction,
+                PendingIntent incomingCallPendingIntent,
                 ISipSessionListener listener) throws SipException {
             String password = localProfile.getPassword();
             SipProfile p = duplicate(localProfile);
             mSipGroup = createSipSessionGroup(mLocalIp, p, password);
-            mIncomingCallBroadcastAction = incomingCallBroadcastAction;
+            mIncomingCallPendingIntent = incomingCallPendingIntent;
             mAutoRegistration.setListener(listener);
         }
 
@@ -458,8 +441,8 @@
             mAutoRegistration.setListener(listener);
         }
 
-        public void setIncomingCallBroadcastAction(String action) {
-            mIncomingCallBroadcastAction = action;
+        public void setIncomingCallPendingIntent(PendingIntent pIntent) {
+            mIncomingCallPendingIntent = pIntent;
         }
 
         public void openToReceiveCalls() throws SipException {
@@ -469,7 +452,7 @@
                 mAutoRegistration.start(mSipGroup);
             }
             if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
-                    + mIncomingCallBroadcastAction);
+                    + mIncomingCallPendingIntent);
         }
 
         public void onConnectivityChanged(boolean connected)
@@ -481,7 +464,7 @@
             } else {
                 // close mSipGroup but remember mOpened
                 if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
-                        + getUri() + ": " + mIncomingCallBroadcastAction);
+                        + getUri() + ": " + mIncomingCallPendingIntent);
                 mSipGroup.close();
                 mAutoRegistration.stop();
             }
@@ -508,7 +491,7 @@
             mSipGroup.close();
             mAutoRegistration.stop();
             if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
-                    + mIncomingCallBroadcastAction);
+                    + mIncomingCallPendingIntent);
         }
 
         public ISipSession createSession(ISipSessionListener listener) {
@@ -516,8 +499,10 @@
         }
 
         @Override
-        public void onRinging(ISipSession session, SipProfile caller,
+        public void onRinging(ISipSession s, SipProfile caller,
                 String sessionDescription) {
+            SipSessionGroup.SipSessionImpl session =
+                    (SipSessionGroup.SipSessionImpl) s;
             synchronized (SipService.this) {
                 try {
                     if (!isRegistered()) {
@@ -528,15 +513,15 @@
                     // send out incoming call broadcast
                     addPendingSession(session);
                     Intent intent = SipManager.createIncomingCallBroadcast(
-                            session.getCallId(), sessionDescription)
-                            .setAction(mIncomingCallBroadcastAction);
+                            session.getCallId(), sessionDescription);
                     if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
                             + caller.getUri() + ": " + session.getCallId()
-                            + " " + mIncomingCallBroadcastAction);
-                    mContext.sendBroadcast(intent);
-                } catch (RemoteException e) {
-                    // should never happen with a local call
-                    Log.e(TAG, "processCall()", e);
+                            + " " + mIncomingCallPendingIntent);
+                    mIncomingCallPendingIntent.send(mContext,
+                            SipManager.INCOMING_CALL_RESULT_CODE, intent);
+                } catch (PendingIntent.CanceledException e) {
+                    Log.w(TAG, "pendingIntent is canceled, drop incoming call");
+                    session.endCall();
                 }
             }
         }
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index bc377cf..37fffa8 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -480,7 +480,7 @@
                     public void run() {
                         try {
                             processCommand(command);
-                        } catch (SipException e) {
+                        } catch (Throwable e) {
                             Log.w(TAG, "command error: " + command, e);
                             onError(e);
                         }
@@ -1218,7 +1218,7 @@
         private int getErrorCode(Throwable exception) {
             String message = exception.getMessage();
             if (exception instanceof UnknownHostException) {
-                return SipErrorCode.INVALID_REMOTE_URI;
+                return SipErrorCode.SERVER_UNREACHABLE;
             } else if (exception instanceof IOException) {
                 return SipErrorCode.SOCKET_ERROR;
             } else {
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
index f3ecac2..84c7166 100644
--- a/voip/jni/rtp/AmrCodec.cpp
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -73,7 +73,7 @@
     }
 
     // Handle mode-set and octet-align.
-    char *modes = strcasestr(fmtp, "mode-set=");
+    const char *modes = strcasestr(fmtp, "mode-set=");
     if (modes) {
         mMode = 0;
         mModeSet = 0;
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
index 92015a9..2ceebdc 100644
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ b/voip/jni/rtp/EchoSuppressor.cpp
@@ -16,6 +16,7 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>
 #include <math.h>
 
 #define LOG_TAG "Echo"
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 7ea4872..9634157 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -20,19 +20,24 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.DhcpInfo;
+import android.net.ProxyProperties;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.ProxySettings;
 import android.net.wifi.WifiConfiguration.Status;
 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
 import android.os.Environment;
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.BufferedWriter;
-import java.io.File;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.FileInputStream;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.HashMap;
@@ -46,8 +51,31 @@
  * It deals with the following
  * - Add/update/remove a WifiConfiguration
  *   The configuration contains two types of information.
- *     = IP configuration that is handled by WifiConfigStore and
+ *     = IP and proxy configuration that is handled by WifiConfigStore and
  *       is saved to disk on any change.
+ *
+ *       The format of configuration file is as follows:
+ *       <version>
+ *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
+ *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
+ *       ..
+ *
+ *       (key, value) pairs for a given network are grouped together and can
+ *       be in any order. A "EOS" at the end of a set of (key, value) pairs
+ *       indicates that the next set of (key, value) pairs are for a new
+ *       network. A network is identified by a unique "id". If there is no
+ *       "id" key in the (key, value) pairs, the data is discarded. An IP
+ *       configuration includes the keys - "ipAssignment", "ipAddress", "gateway",
+ *       "netmask", "dns1" and "dns2". A proxy configuration includes "proxySettings",
+ *       "proxyHost", "proxyPort" and "exclusionList"
+ *
+ *       An invalid version on read would result in discarding the contents of
+ *       the file. On the next write, the latest version is written to file.
+ *
+ *       Any failures during read or write to the configuration file are ignored
+ *       without reporting to the user since the likelihood of these errors are
+ *       low and the impact on connectivity is low.
+ *
  *     = SSID & security details that is pushed to the supplicant.
  *       supplicant saves these details to the disk on calling
  *       saveConfigCommand().
@@ -59,10 +87,9 @@
  *          to the disk. (TODO: deprecate these calls in WifiManager)
  *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
  *          These calls persist the supplicant config to disk.
+ *
  * - Maintain a list of configured networks for quick access
  *
- * TODO:
- * - handle proxy per configuration
  */
 class WifiConfigStore {
 
@@ -110,7 +137,7 @@
         List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
         synchronized (sConfiguredNetworks) {
             for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                networks.add(config.clone());
+                networks.add(new WifiConfiguration(config));
             }
         }
         return networks;
@@ -229,6 +256,7 @@
             synchronized (sConfiguredNetworks) {
                 sConfiguredNetworks.remove(netId);
             }
+            writeIpAndProxyConfigurations();
             sendConfigChangeBroadcast();
         } else {
             Log.e(TAG, "Failed to remove network " + netId);
@@ -353,6 +381,19 @@
     }
 
     /**
+     * Fetch the proxy properties for a given network id
+     */
+    static ProxyProperties getProxyProperties(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null && config.proxySettings == ProxySettings.STATIC) {
+                return new ProxyProperties(config.proxyProperties);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Return if the specified network is using static IP
      */
     static boolean isUsingStaticIp(int netId) {
@@ -411,7 +452,7 @@
                 sNetworkIds.put(configKey(config), config.networkId);
             }
         }
-        readIpConfigurations();
+        readIpAndProxyConfigurations();
         sendConfigChangeBroadcast();
     }
 
@@ -430,38 +471,89 @@
         markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
     }
 
-    private static void writeIpConfigurations() {
-        StringBuilder builder = new StringBuilder();
-        BufferedWriter out = null;
+    private static void writeIpAndProxyConfigurations() {
 
-        builder.append(IPCONFIG_FILE_VERSION);
-        builder.append("\n");
+        DataOutputStream out = null;
+        try {
+            out = new DataOutputStream(new BufferedOutputStream(
+                    new FileOutputStream(ipConfigFile)));
 
-        synchronized (sConfiguredNetworks) {
-            for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                if (config.ipAssignment == WifiConfiguration.IpAssignment.STATIC) {
-                    builder.append("id=" + configKey(config));
-                    builder.append(":");
-                    builder.append("ip=" + config.ipConfig.ipAddress);
-                    builder.append(":");
-                    builder.append("gateway=" + config.ipConfig.gateway);
-                    builder.append(":");
-                    builder.append("netmask=" + config.ipConfig.netmask);
-                    builder.append(":");
-                    builder.append("dns1=" + config.ipConfig.dns1);
-                    builder.append(":");
-                    builder.append("dns2=" + config.ipConfig.dns2);
-                    builder.append("\n");
+            out.writeInt(IPCONFIG_FILE_VERSION);
+
+            synchronized (sConfiguredNetworks) {
+                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                    boolean writeToFile = false;
+
+                    switch (config.ipAssignment) {
+                        case STATIC:
+                            out.writeUTF("ipAssignment");
+                            out.writeUTF(config.ipAssignment.toString());
+                            out.writeUTF("ipAddress");
+                            out.writeInt(config.ipConfig.ipAddress);
+                            out.writeUTF("gateway");
+                            out.writeInt(config.ipConfig.gateway);
+                            out.writeUTF("netmask");
+                            out.writeInt(config.ipConfig.netmask);
+                            out.writeUTF("dns1");
+                            out.writeInt(config.ipConfig.dns1);
+                            out.writeUTF("dns2");
+                            out.writeInt(config.ipConfig.dns2);
+                            writeToFile = true;
+                            break;
+                        case DHCP:
+                            out.writeUTF("ipAssignment");
+                            out.writeUTF(config.ipAssignment.toString());
+                            writeToFile = true;
+                            break;
+                        case UNASSIGNED:
+                            /* Ignore */
+                            break;
+                        default:
+                            Log.e(TAG, "Ignore invalid ip assignment while writing");
+                            break;
+                    }
+
+                    switch (config.proxySettings) {
+                        case STATIC:
+                            out.writeUTF("proxySettings");
+                            out.writeUTF(config.proxySettings.toString());
+                            InetSocketAddress proxy = config.proxyProperties.getSocketAddress();
+                            if (proxy != null) {
+                                out.writeUTF("proxyHost");
+                                out.writeUTF(proxy.getHostName());
+                                out.writeUTF("proxyPort");
+                                out.writeInt(proxy.getPort());
+                                String exclusionList = config.proxyProperties.getExclusionList();
+                                if (exclusionList != null && exclusionList.length() > 0) {
+                                    out.writeUTF("exclusionList");
+                                    out.writeUTF(exclusionList);
+                                }
+                            }
+                            writeToFile = true;
+                            break;
+                        case NONE:
+                            out.writeUTF("proxySettings");
+                            out.writeUTF(config.proxySettings.toString());
+                            writeToFile = true;
+                            break;
+                        case UNASSIGNED:
+                            /* Ignore */
+                            break;
+                        default:
+                            Log.e(TAG, "Ignore invalid proxy settings while writing");
+                            break;
+                    }
+
+                    if (writeToFile) {
+                        out.writeUTF("id");
+                        out.writeInt(configKey(config));
+                        out.writeUTF("EOS");
+                    }
                 }
             }
-        }
 
-        try {
-            out = new BufferedWriter(new FileWriter(ipConfigFile), builder.length());
-            out.write(builder.toString());
         } catch (IOException e) {
             Log.e(TAG, "Error writing data file");
-            return;
         } finally {
             if (out != null) {
                 try {
@@ -471,80 +563,116 @@
         }
     }
 
-    private static void readIpConfigurations() {
-        File f = new File(ipConfigFile);
-        byte[] buffer;
-        FileInputStream s = null;
-        try {
-            buffer = new byte[(int)f.length()];
-            s = new FileInputStream(f);
-            s.read(buffer);
-        } catch (IOException e) {
-            Log.e(TAG, "Error reading data file");
-            return;
-        } finally {
-            if (s != null) {
-                try {
-                    s.close();
-                } catch (Exception e) {}
-            }
-        }
+    private static void readIpAndProxyConfigurations() {
 
-        String data = new String(buffer);
-        if (data == null || data.length() == 0) {
-            Log.d(TAG, "IP configuration file empty");
-            return;
-        }
-
-        String[] parsed = data.split("\n");
+        DataInputStream in = null;
         try {
-            if (Integer.parseInt(parsed[0]) != IPCONFIG_FILE_VERSION) {
+            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
+                    ipConfigFile)));
+
+            if (in.readInt() != IPCONFIG_FILE_VERSION) {
                 Log.e(TAG, "Bad version on IP configuration file, ignore read");
                 return;
             }
 
-            for (String line : parsed) {
-                int hashKey = -1;
+            while (true) {
+                int id = -1;
+                IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
                 DhcpInfo ipConfig = new DhcpInfo();
-                String[] keyVals = line.split(":");
+                ProxySettings proxySettings = ProxySettings.UNASSIGNED;
+                String proxyHost = null;
+                int proxyPort = -1;
+                String exclusionList = null;
+                String key;
 
-                for (String keyVal : keyVals) {
-                    String[] keyValPair = keyVal.split("=");
-                    if (keyValPair[0].equals("id")) {
-                        hashKey = Integer.parseInt(keyValPair[1]);
-                    } else if (keyValPair[0].equals("ip")) {
-                        ipConfig.ipAddress = Integer.parseInt(keyValPair[1]);
-                    } else if (keyValPair[0].equals("gateway")) {
-                        ipConfig.gateway = Integer.parseInt(keyValPair[1]);
-                    } else if (keyValPair[0].equals("netmask")) {
-                        ipConfig.netmask = Integer.parseInt(keyValPair[1]);
-                    } else if (keyValPair[0].equals("dns1")) {
-                        ipConfig.dns1 = Integer.parseInt(keyValPair[1]);
-                    } else if (keyValPair[0].equals("dns2")) {
-                        ipConfig.dns2 = Integer.parseInt(keyValPair[1]);
+                do {
+                    key = in.readUTF();
+                    if (key.equals("id")) {
+                        id = in.readInt();
+                    } else if (key.equals("ipAssignment")) {
+                        ipAssignment = IpAssignment.valueOf(in.readUTF());
+                    } else if (key.equals("ipAddress")) {
+                        ipConfig.ipAddress = in.readInt();
+                    } else if (key.equals("gateway")) {
+                        ipConfig.gateway = in.readInt();
+                    } else if (key.equals("netmask")) {
+                        ipConfig.netmask = in.readInt();
+                    } else if (key.equals("dns1")) {
+                        ipConfig.dns1 = in.readInt();
+                    } else if (key.equals("dns2")) {
+                        ipConfig.dns2 = in.readInt();
+                    } else if (key.equals("proxySettings")) {
+                        proxySettings = ProxySettings.valueOf(in.readUTF());
+                    } else if (key.equals("proxyHost")) {
+                        proxyHost = in.readUTF();
+                    } else if (key.equals("proxyPort")) {
+                        proxyPort = in.readInt();
+                    } else if (key.equals("exclusionList")) {
+                        exclusionList = in.readUTF();
+                    } else if (key.equals("EOS")) {
+                        break;
                     } else {
-                        Log.w(TAG, "Ignoring " + keyVal);
+                        Log.e(TAG, "Ignore unknown key " + key + "while reading");
                     }
-                }
+                } while (true);
 
-                if (hashKey != -1) {
+                if (id != -1) {
                     synchronized (sConfiguredNetworks) {
                         WifiConfiguration config = sConfiguredNetworks.get(
-                                sNetworkIds.get(hashKey));
+                                sNetworkIds.get(id));
 
                         if (config == null) {
-                            Log.e(TAG, "IP configuration found for missing network, ignored");
+                            Log.e(TAG, "configuration found for missing network, ignored");
                         } else {
-                            config.ipAssignment = WifiConfiguration.IpAssignment.STATIC;
-                            config.ipConfig = ipConfig;
+                            switch (ipAssignment) {
+                                case STATIC:
+                                    config.ipAssignment = ipAssignment;
+                                    config.ipConfig = ipConfig;
+                                    break;
+                                case DHCP:
+                                    config.ipAssignment = ipAssignment;
+                                    break;
+                                case UNASSIGNED:
+                                    //Ignore
+                                    break;
+                                default:
+                                    Log.e(TAG, "Ignore invalid ip assignment while reading");
+                                    break;
+                            }
+
+                            switch (proxySettings) {
+                                case STATIC:
+                                    config.proxySettings = proxySettings;
+                                    ProxyProperties proxyProperties = new ProxyProperties();
+                                    proxyProperties.setSocketAddress(
+                                            new InetSocketAddress(proxyHost, proxyPort));
+                                    proxyProperties.setExclusionList(exclusionList);
+                                    config.proxyProperties = proxyProperties;
+                                    break;
+                                case NONE:
+                                    config.proxySettings = proxySettings;
+                                    break;
+                                case UNASSIGNED:
+                                    //Ignore
+                                    break;
+                                default:
+                                    Log.e(TAG, "Ignore invalid proxy settings while reading");
+                                    break;
+                            }
                         }
                     }
                 } else {
-                    Log.e(TAG,"Missing id while parsing configuration" + line);
+                    Log.e(TAG,"Missing id while parsing configuration");
                 }
             }
-        } catch (NumberFormatException e) {
+        } catch (IOException e) {
             Log.e(TAG, "Error parsing configuration");
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception e) {}
+            }
         }
     }
 
@@ -759,21 +887,68 @@
             }
         }
         readNetworkVariables(sConfig);
+        writeIpAndProxyConfigurationsOnChange(sConfig, config);
+        return netId;
+    }
 
-        if (config.ipAssignment != IpAssignment.UNASSIGNED) {
+    /* Compare current and new configuration and write to file on change */
+    private static void writeIpAndProxyConfigurationsOnChange(WifiConfiguration currentConfig,
+            WifiConfiguration newConfig) {
+        boolean newNetwork = (newConfig.networkId == INVALID_NETWORK_ID);
+        boolean writeConfigToFile = false;
+
+        if (newConfig.ipAssignment != IpAssignment.UNASSIGNED) {
             if (newNetwork ||
-                    (sConfig.ipAssignment != config.ipAssignment) ||
-                    (sConfig.ipConfig.ipAddress != config.ipConfig.ipAddress) ||
-                    (sConfig.ipConfig.gateway != config.ipConfig.gateway) ||
-                    (sConfig.ipConfig.netmask != config.ipConfig.netmask) ||
-                    (sConfig.ipConfig.dns1 != config.ipConfig.dns1) ||
-                    (sConfig.ipConfig.dns2 != config.ipConfig.dns2)) {
-                sConfig.ipAssignment = config.ipAssignment;
-                sConfig.ipConfig = config.ipConfig;
-                writeIpConfigurations();
+                    (currentConfig.ipAssignment != newConfig.ipAssignment) ||
+                    (currentConfig.ipConfig.ipAddress != newConfig.ipConfig.ipAddress) ||
+                    (currentConfig.ipConfig.gateway != newConfig.ipConfig.gateway) ||
+                    (currentConfig.ipConfig.netmask != newConfig.ipConfig.netmask) ||
+                    (currentConfig.ipConfig.dns1 != newConfig.ipConfig.dns1) ||
+                    (currentConfig.ipConfig.dns2 != newConfig.ipConfig.dns2)) {
+                currentConfig.ipAssignment = newConfig.ipAssignment;
+                currentConfig.ipConfig = newConfig.ipConfig;
+                writeConfigToFile = true;
             }
         }
-        return netId;
+
+        if (newConfig.proxySettings != ProxySettings.UNASSIGNED) {
+            InetSocketAddress newSockAddr = newConfig.proxyProperties.getSocketAddress();
+            String newExclusionList = newConfig.proxyProperties.getExclusionList();
+
+            InetSocketAddress currentSockAddr = currentConfig.proxyProperties.getSocketAddress();
+            String currentExclusionList = currentConfig.proxyProperties.getExclusionList();
+
+            boolean socketAddressDiffers = false;
+            boolean exclusionListDiffers = false;
+
+            if (newSockAddr != null && currentSockAddr != null ) {
+                socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
+            } else if (newSockAddr != null || currentSockAddr != null) {
+                socketAddressDiffers = true;
+            }
+
+            if (newExclusionList != null && currentExclusionList != null) {
+                exclusionListDiffers = currentExclusionList.equals(newExclusionList);
+            } else if (newExclusionList != null || currentExclusionList != null) {
+                exclusionListDiffers = true;
+            }
+
+            if (newNetwork ||
+                    (currentConfig.proxySettings != newConfig.proxySettings) ||
+                    socketAddressDiffers ||
+                    exclusionListDiffers) {
+                currentConfig.proxySettings = newConfig.proxySettings;
+                currentConfig.proxyProperties = newConfig.proxyProperties;
+                Log.d(TAG, "proxy change SSID = " + currentConfig.SSID + " proxyProperties: " +
+                        currentConfig.proxyProperties.toString());
+                writeConfigToFile = true;
+            }
+        }
+
+        if (writeConfigToFile) {
+            writeIpAndProxyConfigurations();
+            sendConfigChangeBroadcast();
+        }
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 57e9bad..c4a1310 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -17,6 +17,7 @@
 package android.net.wifi;
 
 import android.net.DhcpInfo;
+import android.net.ProxyProperties;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -301,8 +302,13 @@
      * @hide
      */
     public enum IpAssignment {
+        /* Use statically configured IP settings. Configuration can be accessed
+         * with ipConfig */
         STATIC,
+        /* Use dynamically configured IP settigns */
         DHCP,
+        /* no IP details are assigned, this is used to indicate
+         * that any existing IP settings should be retained */
         UNASSIGNED
     }
     /**
@@ -314,6 +320,29 @@
      */
     public DhcpInfo ipConfig;
 
+    /**
+     * @hide
+     */
+    public enum ProxySettings {
+        /* No proxy is to be used. Any existing proxy settings
+         * should be cleared. */
+        NONE,
+        /* Use statically configured proxy. Configuration can be accessed
+         * with proxyProperties */
+        STATIC,
+        /* no proxy details are assigned, this is used to indicate
+         * that any existing proxy settings should be retained */
+        UNASSIGNED
+    }
+    /**
+     * @hide
+     */
+    public ProxySettings proxySettings;
+    /**
+     * @hide
+     */
+    public ProxyProperties proxyProperties;
+
     public WifiConfiguration() {
         networkId = INVALID_NETWORK_ID;
         SSID = null;
@@ -333,6 +362,8 @@
         }
         ipAssignment = IpAssignment.UNASSIGNED;
         ipConfig = new DhcpInfo();
+        proxySettings = ProxySettings.UNASSIGNED;
+        proxyProperties = new ProxyProperties();
     }
 
     public String toString() {
@@ -419,6 +450,12 @@
             sbuf.append(" ").append(ipConfig);
         }
         sbuf.append('\n');
+
+        if (proxySettings == ProxySettings.STATIC) {
+            sbuf.append(" ").append("Proxy configuration:").append('\n');
+            sbuf.append(" ").append(proxyProperties);
+        }
+        sbuf.append('\n');
         return sbuf.toString();
     }
 
@@ -458,38 +495,36 @@
         return 0;
     }
 
-    /**
-     * Returns a copy of this WifiConfiguration.
-     *
-     * @return a copy of this WifiConfiguration.
-     * @hide
-     */
-    public WifiConfiguration clone() {
-        WifiConfiguration config = new WifiConfiguration();
-        config.networkId = networkId;
-        config.status = status;
-        config.SSID = SSID;
-        config.BSSID = BSSID;
-        config.preSharedKey = preSharedKey;
+    /** copy constructor {@hide} */
+    public WifiConfiguration(WifiConfiguration source) {
+        if (source != null) {
+            networkId = source.networkId;
+            status = source.status;
+            SSID = source.SSID;
+            BSSID = source.BSSID;
+            preSharedKey = source.preSharedKey;
 
-        for (int i = 0; i < wepKeys.length; i++)
-            config.wepKeys[i] = wepKeys[i];
+            wepKeys = new String[4];
+            for (int i = 0; i < wepKeys.length; i++)
+                wepKeys[i] = source.wepKeys[i];
 
-        config.wepTxKeyIndex = wepTxKeyIndex;
-        config.priority = priority;
-        config.hiddenSSID = hiddenSSID;
-        config.allowedKeyManagement   = (BitSet) allowedKeyManagement.clone();
-        config.allowedProtocols       = (BitSet) allowedProtocols.clone();
-        config.allowedAuthAlgorithms  = (BitSet) allowedAuthAlgorithms.clone();
-        config.allowedPairwiseCiphers = (BitSet) allowedPairwiseCiphers.clone();
-        config.allowedGroupCiphers    = (BitSet) allowedGroupCiphers.clone();
+            wepTxKeyIndex = source.wepTxKeyIndex;
+            priority = source.priority;
+            hiddenSSID = source.hiddenSSID;
+            allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
+            allowedProtocols       = (BitSet) source.allowedProtocols.clone();
+            allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
+            allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
+            allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
 
-        for (int i = 0; i < enterpriseFields.length; i++) {
-            config.enterpriseFields[i].setValue(enterpriseFields[i].value());
+            for (int i = 0; i < source.enterpriseFields.length; i++) {
+                enterpriseFields[i].setValue(source.enterpriseFields[i].value());
+            }
+            ipAssignment = source.ipAssignment;
+            ipConfig = new DhcpInfo(source.ipConfig);
+            proxySettings = source.proxySettings;
+            proxyProperties = new ProxyProperties(source.proxyProperties);
         }
-        config.ipAssignment = ipAssignment;
-        config.ipConfig = new DhcpInfo(ipConfig);
-        return config;
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -522,6 +557,8 @@
         dest.writeInt(ipConfig.dns2);
         dest.writeInt(ipConfig.serverAddress);
         dest.writeInt(ipConfig.leaseDuration);
+        dest.writeString(proxySettings.name());
+        dest.writeParcelable(proxyProperties, flags);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -557,6 +594,8 @@
                 config.ipConfig.dns2 = in.readInt();
                 config.ipConfig.serverAddress = in.readInt();
                 config.ipConfig.leaseDuration = in.readInt();
+                config.proxySettings = ProxySettings.valueOf(in.readString());
+                config.proxyProperties = in.readParcelable(null);
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e82c003..572abc0 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -38,12 +38,14 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
 
 import android.app.ActivityManagerNative;
+import android.net.LinkAddress;
 import android.net.NetworkInfo;
 import android.net.DhcpInfo;
 import android.net.NetworkUtils;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo.DetailedState;
 import android.net.LinkProperties;
+import android.net.ProxyProperties;
 import android.net.wifi.WifiConfiguration.Status;
 import android.os.Binder;
 import android.os.Message;
@@ -1251,24 +1253,25 @@
     }
 
     private void configureLinkProperties() {
-        try {
-            mLinkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
-        } catch (SocketException e) {
-            Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
-                    ". e=" + e);
-            return;
-        } catch (NullPointerException e) {
-            Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
-            return;
-        }
+
+        mLinkProperties.setInterfaceName(mInterfaceName);
+
         // TODO - fix this for v6
         synchronized (mDhcpInfo) {
-            mLinkProperties.addAddress(NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress));
+            mLinkProperties.addLinkAddress(new LinkAddress(
+                    NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress),
+                    NetworkUtils.intToInetAddress(mDhcpInfo.netmask)));
             mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
             mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
             mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
         }
-        // TODO - add proxy info
+
+        ProxyProperties proxyProperties = WifiConfigStore.getProxyProperties(mLastNetworkId);
+        if (proxyProperties != null) {
+            mLinkProperties.setHttpProxy(proxyProperties);
+            Log.d(TAG, "netId=" + mLastNetworkId  + " proxy configured: "
+                    + proxyProperties.toString());
+        }
     }
 
     private int getMaxDhcpRetries() {