Merge "Fix for the notification panel getting stuck open."
diff --git a/api/current.xml b/api/current.xml
index 86023fb..f915fad 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3265,6 +3265,17 @@
  visibility="public"
 >
 </field>
+<field name="customTokens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843593"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="cycles"
  type="int"
  transient="false"
@@ -18308,6 +18319,28 @@
  visibility="public"
 >
 </field>
+<field name="KEY_CALLER_PID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;callerPid&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEY_CALLER_UID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;callerUid&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEY_ERROR_CODE"
  type="java.lang.String"
  transient="false"
@@ -18555,6 +18588,28 @@
 </parameter>
 <parameter name="prefId" type="int">
 </parameter>
+<parameter name="customTokens" type="boolean">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorDescription"
+ type="android.accounts.AuthenticatorDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="labelId" type="int">
+</parameter>
+<parameter name="iconId" type="int">
+</parameter>
+<parameter name="smallIconId" type="int">
+</parameter>
+<parameter name="prefId" type="int">
+</parameter>
 </constructor>
 <method name="describeContents"
  return="int"
@@ -18615,6 +18670,16 @@
  visibility="public"
 >
 </field>
+<field name="customTokens"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="iconId"
  type="int"
  transient="false"
@@ -223231,7 +223296,7 @@
  abstract="false"
  static="false"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <constructor name="CacheManager"
@@ -223249,7 +223314,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -223271,7 +223336,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="url" type="java.lang.String">
@@ -223286,7 +223351,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -223297,7 +223362,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="url" type="java.lang.String">
@@ -223322,7 +223387,7 @@
  abstract="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <constructor name="CacheManager.CacheResult"
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index 524d3f4..7214c50 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -38,7 +38,7 @@
  * @hide
  */
 /* package private */ class AccountAuthenticatorCache
-        extends RegisteredServicesCache<AuthenticatorDescription> 
+        extends RegisteredServicesCache<AuthenticatorDescription>
         implements IAccountAuthenticatorCache {
     private static final String TAG = "Account";
     private static final MySerializer sSerializer = new MySerializer();
@@ -64,11 +64,13 @@
                     com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
             final int prefId = sa.getResourceId(
                     com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
+            final boolean customTokens = sa.getBoolean(
+                    com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);
             if (TextUtils.isEmpty(accountType)) {
                 return null;
             }
-            return new AuthenticatorDescription(accountType, packageName, labelId, iconId, 
-                    smallIconId, prefId);
+            return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
+                    smallIconId, prefId, customTokens);
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index fd3a0d0..6388dc5 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -188,6 +188,12 @@
     public static final String KEY_ERROR_CODE = "errorCode";
     public static final String KEY_ERROR_MESSAGE = "errorMessage";
     public static final String KEY_USERDATA = "userdata";
+    /**
+     * Authenticators using 'customTokens' option will also get the UID of the
+     * caller
+     */
+    public static final String KEY_CALLER_UID = "callerUid";
+    public static final String KEY_CALLER_PID = "callerPid";
 
     public static final String ACTION_AUTHENTICATOR_INTENT =
             "android.accounts.AccountAuthenticator";
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index a815b3a..f19b58b 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -893,13 +893,29 @@
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
         final int callerUid = Binder.getCallingUid();
-        final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+        final int callerPid = Binder.getCallingPid();
+
+        AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+            mAuthenticatorCache.getServiceInfo(
+                    AuthenticatorDescription.newKey(account.type));
+        final boolean customTokens =
+            authenticatorInfo != null && authenticatorInfo.type.customTokens;
+
+        // skip the check if customTokens
+        final boolean permissionGranted = customTokens ||
+            permissionIsGranted(account, authTokenType, callerUid);
+
+        if (customTokens) {
+            // let authenticator know the identity of the caller
+            loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
+            loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
+        }
 
         long identityToken = clearCallingIdentity();
         try {
             // if the caller has permission, do the peek. otherwise go the more expensive
             // route of starting a Session
-            if (permissionGranted) {
+            if (!customTokens && permissionGranted) {
                 String authToken = readAuthTokenFromCache(account, authTokenType);
                 if (authToken != null) {
                     Bundle result = new Bundle();
@@ -953,8 +969,10 @@
                                         "the type and name should not be empty");
                                 return;
                             }
-                            saveAuthTokenToDatabase(new Account(name, type),
-                                    authTokenType, authToken);
+                            if (!customTokens) {
+                                saveAuthTokenToDatabase(new Account(name, type),
+                                        authTokenType, authToken);
+                            }
                         }
 
                         Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index c6515672..5d9abb0 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -44,9 +44,12 @@
     /** The package name that can be used to lookup the resources from above. */
     final public String packageName;
 
+    /** Authenticator handles its own token caching and permission screen */
+    final public boolean customTokens;
+
     /** A constructor for a full AuthenticatorDescription */
     public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
-            int smallIconId, int prefId) {
+            int smallIconId, int prefId, boolean customTokens) {
         if (type == null) throw new IllegalArgumentException("type cannot be null");
         if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
         this.type = type;
@@ -55,6 +58,12 @@
         this.iconId = iconId;
         this.smallIconId = smallIconId;
         this.accountPreferencesId = prefId;
+        this.customTokens = customTokens;
+    }
+
+    public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
+            int smallIconId, int prefId) {
+        this(type, packageName, labelId, iconId, smallIconId, prefId, false);
     }
 
     /**
@@ -74,6 +83,7 @@
         this.iconId = 0;
         this.smallIconId = 0;
         this.accountPreferencesId = 0;
+        this.customTokens = false;
     }
 
     private AuthenticatorDescription(Parcel source) {
@@ -83,6 +93,7 @@
         this.iconId = source.readInt();
         this.smallIconId = source.readInt();
         this.accountPreferencesId = source.readInt();
+        this.customTokens = source.readByte() == 1;
     }
 
     /** @inheritDoc */
@@ -115,6 +126,7 @@
         dest.writeInt(iconId);
         dest.writeInt(smallIconId);
         dest.writeInt(accountPreferencesId);
+        dest.writeByte((byte) (customTokens ? 1 : 0));
     }
 
     /** Used to create the object from a parcel. */
diff --git a/core/java/android/content/SyncActivityTooManyDeletes.java b/core/java/android/content/SyncActivityTooManyDeletes.java
new file mode 100644
index 0000000..350f35e
--- /dev/null
+++ b/core/java/android/content/SyncActivityTooManyDeletes.java
@@ -0,0 +1,133 @@
+/*
+ * 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 com.android.internal.R;
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Presents multiple options for handling the case where a sync was aborted because there
+ * were too many pending deletes. One option is to force the delete, another is to rollback
+ * the deletes, the third is to do nothing.
+ * @hide
+ */
+public class SyncActivityTooManyDeletes extends Activity
+        implements AdapterView.OnItemClickListener {
+
+    private long mNumDeletes;
+    private Account mAccount;
+    private String mAuthority;
+    private String mProvider;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Bundle extras = getIntent().getExtras();
+        if (extras == null) {
+            finish();
+            return;
+        }
+
+        mNumDeletes = extras.getLong("numDeletes");
+        mAccount = (Account) extras.getParcelable("account");
+        mAuthority = extras.getString("authority");
+        mProvider = extras.getString("provider");
+
+        // the order of these must match up with the constants for position used in onItemClick
+        CharSequence[] options = new CharSequence[]{
+                getResources().getText(R.string.sync_really_delete),
+                getResources().getText(R.string.sync_undo_deletes),
+                getResources().getText(R.string.sync_do_nothing)
+        };
+
+        ListAdapter adapter = new ArrayAdapter<CharSequence>(this,
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1,
+                options);
+
+        ListView listView = new ListView(this);
+        listView.setAdapter(adapter);
+        listView.setItemsCanFocus(true);
+        listView.setOnItemClickListener(this);
+
+        TextView textView = new TextView(this);
+        CharSequence tooManyDeletesDescFormat =
+                getResources().getText(R.string.sync_too_many_deletes_desc);
+        textView.setText(String.format(tooManyDeletesDescFormat.toString(),
+                mNumDeletes, mProvider, mAccount.name));
+
+        final LinearLayout ll = new LinearLayout(this);
+        ll.setOrientation(LinearLayout.VERTICAL);
+        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+        ll.addView(textView, lp);
+        ll.addView(listView, lp);
+
+        // TODO: consider displaying the icon of the account type
+//        AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+//        for (AuthenticatorDescription desc : descs) {
+//            if (desc.type.equals(mAccount.type)) {
+//                try {
+//                    final Context authContext = createPackageContext(desc.packageName, 0);
+//                    ImageView imageView = new ImageView(this);
+//                    imageView.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
+//                    ll.addView(imageView, lp);
+//                } catch (PackageManager.NameNotFoundException e) {
+//                }
+//                break;
+//            }
+//        }
+
+        setContentView(ll);
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        // the constants for position correspond to the items options array in onCreate()
+        if (position == 0) startSyncReallyDelete();
+        else if (position == 1) startSyncUndoDeletes();
+        finish();
+    }
+
+    private void startSyncReallyDelete() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+
+    private void startSyncUndoDeletes() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 599429b..8b292c9 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -2157,9 +2157,7 @@
             }
             CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
 
-            Intent clickIntent = new Intent();
-            clickIntent.setClassName("com.android.providers.subscribedfeeds",
-                    "com.android.settings.SyncActivityTooManyDeletes");
+            Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
             clickIntent.putExtra("account", account);
             clickIntent.putExtra("authority", authority);
             clickIntent.putExtra("provider", authorityName.toString());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d27e99d..75aebc9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8206,8 +8206,9 @@
     /**
      * Manually render this view (and all of its children) to the given Canvas.
      * The view must have already done a full layout before this function is
-     * called.  When implementing a view, do not override this method; instead,
-     * you should implement {@link #onDraw}.
+     * called.  When implementing a view, implement {@link #onDraw} instead of
+     * overriding this method. If you do need to override this method, call
+     * the superclass version.
      *
      * @param canvas The Canvas to which the View is rendered.
      */
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index fd0a6d0..99be64b 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -44,7 +44,10 @@
  * this component and if they can not be resolved by the cache, the HTTP headers
  * are attached, as appropriate, to the request for revalidation of content. The
  * class also manages the cache size.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
  */
+@Deprecated
 public final class CacheManager {
 
     private static final String LOGTAG = "cache";
@@ -85,7 +88,10 @@
      * This class represents a resource retrieved from the HTTP cache.
      * Instances of this class can be obtained by invoking the
      * CacheManager.getCacheFile() method.
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static class CacheResult {
         // these fields are saved to the database
         int httpStatusCode;
@@ -220,9 +226,12 @@
     /**
      * get the base directory of the cache. With localPath of the CacheResult,
      * it identifies the cache file.
-     * 
+     *
      * @return File The base directory of the cache.
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static File getCacheFileBaseDir() {
         return mBaseDir;
     }
@@ -244,9 +253,12 @@
 
     /**
      * get the state of the current cache, enabled or disabled
-     * 
+     *
      * @return return if it is disabled
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static boolean cacheDisabled() {
         return mDisabled;
     }
@@ -314,8 +326,11 @@
      * HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in the
      * cached headers.
      * 
-     * @return the CacheResult for a given url
+     * @return the CacheResult for a given url.
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static CacheResult getCacheFile(String url,
             Map<String, String> headers) {
         return getCacheFile(url, 0, headers);
@@ -390,7 +405,10 @@
      * @return CacheResult for a given url
      * @hide - hide createCacheFile since it has a parameter of type headers, which is
      * in a hidden package.
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static CacheResult createCacheFile(String url, int statusCode,
             Headers headers, String mimeType, boolean forceCache) {
         return createCacheFile(url, statusCode, headers, mimeType, 0,
@@ -455,7 +473,10 @@
     /**
      * Save the info of a cache file for a given url to the CacheMap so that it
      * can be reused later
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
      */
+    @Deprecated
     public static void saveCacheFile(String url, CacheResult cacheRet) {
         saveCacheFile(url, 0, cacheRet);
     }
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 54c9d9a..86a67c7 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -34,7 +34,11 @@
     }
 
     void finish() {
-        mActionMode.finish();
+        // It is possible that onCreateActionMode was never called, in the case
+        // where there is no ActionBar, for example.
+        if (mActionMode != null) {
+            mActionMode.finish();
+        }
     }
 
     // ActionMode.Callback implementation
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7195f98..05bb19d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3790,7 +3790,9 @@
     public boolean selectText() {
         int x = viewToContentX((int) mLastTouchX + mScrollX);
         int y = viewToContentY((int) mLastTouchY + mScrollY);
-        setUpSelect();
+        if (!setUpSelect()) {
+            return false;
+        }
         if (mNativeClass != 0 && nativeWordSelection(x, y)) {
             nativeSetExtendSelection();
             mDrawSelectionPointer = false;
@@ -4675,10 +4677,15 @@
         return false;
     }
 
-    private void setUpSelect() {
-        if (0 == mNativeClass) return; // client isn't initialized
-        if (inFullScreenMode()) return;
-        if (mSelectingText) return;
+    /*
+     * Enter selecting text mode.  Returns true if the WebView is now in
+     * selecting text mode (including if it was already in that mode, and this
+     * method did nothing).
+     */
+    private boolean setUpSelect() {
+        if (0 == mNativeClass) return false; // client isn't initialized
+        if (inFullScreenMode()) return false;
+        if (mSelectingText) return true;
         mExtendSelection = false;
         mSelectingText = mDrawSelectionPointer = true;
         // don't let the picture change during text selection
@@ -4698,7 +4705,13 @@
         nativeHideCursor();
         mSelectCallback = new SelectActionModeCallback();
         mSelectCallback.setWebView(this);
-        startActionMode(mSelectCallback);
+        if (startActionMode(mSelectCallback) == null) {
+            // There is no ActionMode, so do not allow the user to modify a
+            // selection.
+            selectionDone();
+            return false;
+        }
+        return true;
     }
 
     /**
@@ -4715,7 +4728,9 @@
     void selectAll() {
         if (0 == mNativeClass) return; // client isn't initialized
         if (inFullScreenMode()) return;
-        if (!mSelectingText) setUpSelect();
+        if (!mSelectingText && !setUpSelect()) {
+            return;
+        }
         nativeSelectAll();
         mDrawSelectionPointer = false;
         mExtendSelection = true;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b8c8913..fdd0710 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1991,6 +1991,13 @@
                 .obtainMessage(WebCoreThread.RESUME_PRIORITY));
     }
 
+    static void sendStaticMessage(int messageType, Object argument) {
+        if (sWebCoreHandler == null)
+            return;
+
+        sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
+    }
+
     static void pauseUpdatePicture(WebViewCore core) {
         // Note: there is one possible failure mode. If pauseUpdatePicture() is
         // called from UI thread while WEBKIT_DRAW is just pulled out of the
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7b35b51..2d9a5f9 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -979,10 +979,7 @@
         // children
         // after we have completed drawing ourselves.
 
-        // Draw the selector wheel if needed
-        if (mDrawSelectorWheel) {
-            super.draw(canvas);
-        }
+        super.draw(canvas);
 
         // Draw our children if we are not showing the selector wheel of fading
         // it out
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9a2cc89..95a7222 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -864,7 +864,6 @@
             mInputType = EditorInfo.TYPE_NULL;
             mInput = null;
             bufferType = BufferType.SPANNABLE;
-            setFocusableInTouchMode(true);
             // So that selection can be changed using arrow keys and touch is handled.
             setMovementMethod(ArrowKeyMovementMethod.getInstance());
         } else if (editable) {
@@ -4073,7 +4072,7 @@
      *
      * Use {@link #setTextIsSelectable(boolean)} or the
      * {@link android.R.styleable#TextView_textIsSelectable} XML attribute to make this TextView
-     * selectable (the text is not selectable by default). 
+     * selectable (text is not selectable by default).
      *
      * Note that the content of an EditText is always selectable.
      *
@@ -4088,10 +4087,9 @@
     /**
      * Sets whether or not (default) the content of this view is selectable by the user.
      * 
-     * Note that this methods affect the {@link #setFocusableInTouchMode(boolean)},
-     * {@link #setFocusable(boolean)}, {@link #setClickable(boolean)} and
-     * {@link #setLongClickable(boolean)} states and you may want to restore these if they were
-     * customized.
+     * Note that this methods affect the {@link #setFocusable(boolean)},
+     * {@link #setClickable(boolean)} and {@link #setLongClickable(boolean)} states and you may want
+     * to restore these if they were customized.
      *
      * See {@link #isTextSelectable} for details.
      *
@@ -4102,7 +4100,6 @@
 
         mTextIsSelectable = selectable;
 
-        setFocusableInTouchMode(selectable);
         setFocusable(selectable);
         setClickable(selectable);
         setLongClickable(selectable);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92b50c7..981661a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1335,6 +1335,11 @@
                 android:exported="true">
         </activity>
 
+        <activity android:name="android.content.SyncActivityTooManyDeletes"
+               android:theme="@android:style/Theme.Holo.Dialog"
+               android:label="@string/sync_too_many_deletes">
+        </activity>
+
         <activity android:name="com.android.server.ShutdownActivity"
             android:permission="android.permission.SHUTDOWN"
             android:excludeFromRecents="true">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 98c9270..873f539 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -48,6 +48,7 @@
              theme does not set this value, meaning it is based on whether the
              window is floating. -->
         <attr name="backgroundDimEnabled" format="boolean" />
+
         <!-- =========== -->
         <!-- Text styles -->
         <!-- =========== -->
@@ -261,7 +262,7 @@
         <!-- Flag indicating whether this is a translucent window. -->
         <attr name="windowIsTranslucent" format="boolean" />
         <!-- Flag indicating that this window's background should be the
-        	 user's current wallpaper. -->
+           user's current wallpaper. -->
         <attr name="windowShowWallpaper" format="boolean" />
         <!-- This Drawable is overlaid over the foreground of the Window's content area, usually
              to place a shadow below the title.  -->
@@ -4310,7 +4311,7 @@
              If not supplied, then no activity will be launched. -->
         <attr name="configure" format="string" />
         <!-- A preview of what the AppWidget will look like after it's configured.
-       	     If not supplied, the AppWidget's icon will be used. -->
+              If not supplied, the AppWidget's icon will be used. -->
         <attr name="previewImage" format="reference" />
         <!-- The view id of the AppWidget subview which should be auto-advanced.
              by the widget's host. -->
@@ -4421,6 +4422,10 @@
         <attr name="smallIcon" format="reference"/>
         <!-- A preferences.xml file for authenticator-specific settings. -->
         <attr name="accountPreferences" format="reference"/>
+        <!-- Account handles its own token storage and permissions.
+             Default to false
+          -->
+        <attr name="customTokens" format="boolean"/>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3a5b238..7e06c86 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1407,6 +1407,7 @@
   <public type="attr" name="fastScrollPreviewBackgroundRight" />
   <public type="attr" name="fastScrollTrackDrawable" />
   <public type="attr" name="fastScrollOverlayPosition" />
+  <public type="attr" name="customTokens" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e48321c..92f3593 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -298,12 +298,12 @@
     <string name="shutdown_confirm_question">Would you like to shut down?</string>
 
     <!-- Recent Tasks dialog: title
-     TODO: this should move to SystemUI.apk, but the code for the old 
+     TODO: this should move to SystemUI.apk, but the code for the old
             recent dialog is still in the framework
      -->
     <string name="recent_tasks_title">Recent</string>
     <!-- Recent Tasks dialog: message when there are no recent applications
-     TODO: this should move to SystemUI.apk, but the code for the old 
+     TODO: this should move to SystemUI.apk, but the code for the old
             recent dialog is still in the framework
      -->
     <string name="no_recent_tasks">No recent applications.</string>
@@ -1363,17 +1363,17 @@
     <!-- Title of policy access to limiting the user's password choices -->
     <string name="policylab_limitPassword">Set password rules</string>
     <!-- Description of policy access to limiting the user's password choices -->
-    <string name="policydesc_limitPassword">Control the length and the characters 
+    <string name="policydesc_limitPassword">Control the length and the characters
     allowed in screen-unlock passwords</string>
     <!-- Title of policy access to watch user login attempts -->
     <string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
     <!-- Description of policy access to watch user login attempts -->
-    <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords 
-    entered when unlocking the screen, and lock the tablet or erase all the tablet\'s 
+    <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords
+    entered when unlocking the screen, and lock the tablet or erase all the tablet\'s
     data if too many incorrect passwords are entered</string>
     <!-- Description of policy access to watch user login attempts -->
-    <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords 
-    entered when unlocking the screen, and lock the phone or erase all the phone\'s 
+    <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords
+    entered when unlocking the screen, and lock the phone or erase all the phone\'s
     data if too many incorrect passwords are entered</string>
     <!-- Title of policy access to reset user's password -->
     <string name="policylab_resetPassword">Change the screen-unlock password</string>
@@ -1386,10 +1386,10 @@
     <!-- Title of policy access to wipe the user's data -->
     <string name="policylab_wipeData">Erase all data</string>
     <!-- Description of policy access to wipe the user's data -->
-    <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning, 
+    <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning,
     by performing a factory data reset</string>
     <!-- Description of policy access to wipe the user's data -->
-    <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning, 
+    <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning,
     by performing a factory data reset</string>
     <string name="policylab_setGlobalProxy">Set the device global proxy</string>
     <!-- Description of policy access to wipe the user's data -->
@@ -1602,7 +1602,7 @@
     <string name="relationTypeSister">Sister</string>
     <!-- Spouse relationship type [CHAR LIMIT=20] -->
     <string name="relationTypeSpouse">Spouse</string>
-    
+
     <!-- Custom SIP address type -->
     <string name="sipAddressTypeCustom">Custom</string>
     <!-- Home SIP address type -->
@@ -2640,4 +2640,15 @@
     <!-- Network positioning verification No. Button to push to deny sharing of location
          information. -->
     <string name="gpsVerifNo">No</string>
+
+    <!-- Error message when the sync tried to delete too many things -->
+    <string name="sync_too_many_deletes">Delete limit exceeded</string>
+    <!-- Dialog message for when there are too many deletes that would take place and we want user confirmation -->
+    <string name="sync_too_many_deletes_desc">There are <xliff:g id="number_of_deleted_items">%1$d</xliff:g> deleted items for <xliff:g id="type_of_sync">%2$s</xliff:g>, account <xliff:g id="account_name">%3$s</xliff:g>. What would you like to do?</string>
+    <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to delete the items -->
+    <string name="sync_really_delete">Delete the items.</string>
+    <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to undo the deletions -->
+    <string name="sync_undo_deletes">Undo the deletes.</string>
+    <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to do nothing for now -->
+    <string name="sync_do_nothing">Do nothing for now.</string>
 </resources>
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
new file mode 100644
index 0000000..ad3ec3d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import junit.framework.TestCase;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+import tests.http.SocketPolicy;
+import static tests.http.SocketPolicy.DISCONNECT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
+
+public final class DefaultHttpClientTest extends TestCase {
+
+    private MockWebServer server = new MockWebServer();
+
+    @Override protected void tearDown() throws Exception {
+        server.shutdown();
+        super.tearDown();
+    }
+
+    public void testServerClosesSocket() throws Exception {
+        testServerClosesOutput(DISCONNECT_AT_END);
+    }
+
+    public void testServerShutdownInput() throws Exception {
+        testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
+    }
+
+    /**
+     * DefaultHttpClient fails if the server shutdown the output after the
+     * response was sent. http://b/2612240
+     */
+    public void testServerShutdownOutput() throws Exception {
+        testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
+    }
+
+    private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
+        server.enqueue(new MockResponse()
+                .setBody("This connection won't pool properly")
+                .setSocketPolicy(socketPolicy));
+        server.enqueue(new MockResponse()
+                .setBody("This comes after a busted connection"));
+        server.play();
+
+        DefaultHttpClient client = new DefaultHttpClient();
+
+        HttpResponse a = client.execute(new HttpGet(server.getUrl("/a").toURI()));
+        assertEquals("This connection won't pool properly", contentToString(a));
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+
+        HttpResponse b = client.execute(new HttpGet(server.getUrl("/b").toURI()));
+        assertEquals("This comes after a busted connection", contentToString(b));
+        // sequence number 0 means the HTTP socket connection was not reused
+        assertEquals(0, server.takeRequest().getSequenceNumber());
+    }
+
+    private String contentToString(HttpResponse response) throws IOException {
+        StringWriter writer = new StringWriter();
+        char[] buffer = new char[1024];
+        Reader reader = new InputStreamReader(response.getEntity().getContent());
+        int length;
+        while ((length = reader.read(buffer)) != -1) {
+            writer.write(buffer, 0, length);
+        }
+        reader.close();
+        return writer.toString();
+    }
+}
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index 74c9d5d..df5be32 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -71,7 +71,8 @@
     bool addStringTag(const char* name, const char* value);
     void endFile();
 
-    virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
+    virtual bool scanFile(const char* path, long long lastModified,
+            long long fileSize, bool isDirectory) = 0;
     virtual bool handleStringTag(const char* name, const char* value) = 0;
     virtual bool setMimeType(const char* mimeType) = 0;
     virtual bool addNoMediaFolder(const char* path) = 0;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 63ec6b2..365fd65 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -407,55 +407,60 @@
         private long mFileSize;
         private String mWriter;
 
-        public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
-
-            // special case certain file names
-            // I use regionMatches() instead of substring() below
-            // to avoid memory allocation
-            int lastSlash = path.lastIndexOf('/');
-            if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
-                // ignore those ._* files created by MacOS
-                if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
-                    return null;
-                }
-
-                // ignore album art files created by Windows Media Player:
-                // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg and AlbumArt_{...}_Small.jpg
-                if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
-                    if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
-                            path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
-                        return null;
-                    }
-                    int length = path.length() - lastSlash - 1;
-                    if ((length == 17 && path.regionMatches(true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
-                            (length == 10 && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
-                        return null;
-                    }
-                }
-            }
-
+        public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
+                long fileSize, boolean isDirectory) {
             mMimeType = mimeType;
             mFileType = 0;
             mFileSize = fileSize;
 
-            // try mimeType first, if it is specified
-            if (mimeType != null) {
-                mFileType = MediaFile.getFileTypeForMimeType(mimeType);
-            }
+            if (!isDirectory) {
+                // special case certain file names
+                // I use regionMatches() instead of substring() below
+                // to avoid memory allocation
+                int lastSlash = path.lastIndexOf('/');
+                if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
+                    // ignore those ._* files created by MacOS
+                    if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
+                        return null;
+                    }
 
-            // if mimeType was not specified, compute file type based on file extension.
-            if (mFileType == 0) {
-                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
-                if (mediaFileType != null) {
-                    mFileType = mediaFileType.fileType;
-                    if (mMimeType == null) {
-                        mMimeType = mediaFileType.mimeType;
+                    // ignore album art files created by Windows Media Player:
+                    // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
+                    // and AlbumArt_{...}_Small.jpg
+                    if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
+                        if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
+                                path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
+                            return null;
+                        }
+                        int length = path.length() - lastSlash - 1;
+                        if ((length == 17 && path.regionMatches(
+                                true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
+                                (length == 10
+                                 && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
+                            return null;
+                        }
                     }
                 }
-            }
 
-            if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
-                mFileType = getFileTypeFromDrm(path);
+                // try mimeType first, if it is specified
+                if (mimeType != null) {
+                    mFileType = MediaFile.getFileTypeForMimeType(mimeType);
+                }
+
+                // if mimeType was not specified, compute file type based on file extension.
+                if (mFileType == 0) {
+                    MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+                    if (mediaFileType != null) {
+                        mFileType = mediaFileType.fileType;
+                        if (mMimeType == null) {
+                            mMimeType = mediaFileType.mimeType;
+                        }
+                    }
+                }
+
+                if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
+                    mFileType = getFileTypeFromDrm(path);
+                }
             }
 
             String key = path;
@@ -470,7 +475,9 @@
             FileCacheEntry entry = mFileCache.get(key);
             if (entry == null) {
                 Uri tableUri;
-                if (MediaFile.isVideoFileType(mFileType)) {
+                if (isDirectory) {
+                    tableUri = mFilesUri;
+                } else if (MediaFile.isVideoFileType(mFileType)) {
                     tableUri = mVideoUri;
                 } else if (MediaFile.isImageFileType(mFileType)) {
                     tableUri = mImagesUri;
@@ -479,7 +486,8 @@
                 } else {
                     tableUri = mFilesUri;
                 }
-                entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
+                entry = new FileCacheEntry(tableUri, 0, path, 0,
+                        (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
                 mFileCache.put(key, entry);
             }
             entry.mSeenInFileSystem = true;
@@ -514,22 +522,19 @@
             return entry;
         }
 
-        public void scanFile(String path, long lastModified, long fileSize) {
+        public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory) {
             // This is the callback funtion from native codes.
             // Log.v(TAG, "scanFile: "+path);
-            doScanFile(path, null, lastModified, fileSize, false);
-        }
-
-        public void scanFile(String path, String mimeType, long lastModified, long fileSize) {
-            doScanFile(path, mimeType, lastModified, fileSize, false);
+            doScanFile(path, null, lastModified, fileSize, isDirectory, false);
         }
 
         public Uri doScanFile(String path, String mimeType, long lastModified,
-                long fileSize, boolean scanAlways) {
+                long fileSize, boolean isDirectory, boolean scanAlways) {
             Uri result = null;
 //            long t1 = System.currentTimeMillis();
             try {
-                FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);
+                FileCacheEntry entry = beginFile(path, mimeType, lastModified,
+                        fileSize, isDirectory);
                 // rescan for metadata if file was modified since last scan
                 if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                     String lowpath = path.toLowerCase();
@@ -775,7 +780,11 @@
                     values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
                 }
                 if (tableUri == mFilesUri) {
-                    values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
+                    int format = entry.mFormat;
+                    if (format == 0) {
+                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
+                    }
+                    values.put(Files.FileColumns.FORMAT, format);
                 }
                 // new file, insert it
                 result = mMediaProvider.insert(tableUri, values);
@@ -1060,8 +1069,7 @@
             boolean fileMissing = false;
 
             if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
-                if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
-                        inScanDirectory(path, directories)) {
+                if (inScanDirectory(path, directories)) {
                     // we didn't see this file in the scan directory.
                     fileMissing = true;
                 } else {
@@ -1180,7 +1188,7 @@
             long lastModifiedSeconds = file.lastModified() / 1000;
 
             // always scan the file, so we can return the content://media Uri for existing files
-            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
+            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),false, true);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
             return null;
@@ -1227,7 +1235,8 @@
                 long lastModifiedSeconds = file.lastModified() / 1000;
 
                 // always scan the file, so we can return the content://media Uri for existing files
-                mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
+                mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
+                    (format == MtpConstants.FORMAT_ASSOCIATION), true);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java
index 258c3b4..ac326ef 100644
--- a/media/java/android/media/MediaScannerClient.java
+++ b/media/java/android/media/MediaScannerClient.java
@@ -21,9 +21,7 @@
  */
 public interface MediaScannerClient
 {    
-    public void scanFile(String path, long lastModified, long fileSize);
-    
-    public void scanFile(String path, String mimeType, long lastModified, long fileSize);
+    public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory);
 
     public void addNoMediaFolder(String path);
 
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index fd0b233..a5176fa 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -62,7 +62,7 @@
         }
         else {
             mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile",
-                                                     "(Ljava/lang/String;JJ)V");
+                                                     "(Ljava/lang/String;JJZ)V");
             mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag",
                                                      "(Ljava/lang/String;Ljava/lang/String;)V");
             mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
@@ -78,12 +78,14 @@
     }
     
     // returns true if it succeeded, false if an exception occured in the Java code
-    virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
+    virtual bool scanFile(const char* path, long long lastModified,
+            long long fileSize, bool isDirectory)
     {
         jstring pathStr;
         if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
 
-        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
+        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
+                fileSize, isDirectory);
 
         mEnv->DeleteLocalRef(pathStr);
         return (!mEnv->ExceptionCheck());
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index c31b622..5ec573e 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -84,6 +84,7 @@
     // place to copy file or directory name
     char* fileSpot = path + strlen(path);
     struct dirent* entry;
+    struct stat statbuf;
 
     // ignore directories that contain a  ".nomedia" file
     if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
@@ -125,7 +126,6 @@
             // If the type is unknown, stat() the file instead.
             // This is sometimes necessary when accessing NFS mounted filesystems, but
             // could be needed in other cases well.
-            struct stat statbuf;
             if (stat(path, &statbuf) == 0) {
                 if (S_ISREG(statbuf.st_mode)) {
                     type = DT_REG;
@@ -142,8 +142,15 @@
                 // for example, the Mac ".Trashes" directory
                 if (name[0] == '.') continue;
 
+                // report the directory to the client
+                if (stat(path, &statbuf) == 0) {
+                    client.scanFile(path, statbuf.st_mtime, 0, true);
+                }
+
+                // and now process its contents
                 strcat(fileSpot, "/");
-                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
+                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
+                        exceptionCheck, exceptionEnv);
                 if (err) {
                     // pass exceptions up - ignore other errors
                     if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
@@ -151,11 +158,8 @@
                     continue;
                 }
             } else {
-                struct stat statbuf;
                 stat(path, &statbuf);
-                if (statbuf.st_size > 0) {
-                    client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
-                }
+                client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false);
                 if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
             }
         }
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
index aee01ab..d926cb1 100644
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ b/services/audioflinger/A2dpAudioInterface.cpp
@@ -260,6 +260,7 @@
     if (pRate) *pRate = lRate;
 
     mDevice = device;
+    mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
     return NO_ERROR;
 }
 
@@ -288,6 +289,7 @@
         if (mStandby) {
             acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
             mStandby = false;
+            mLastWriteTime = systemTime();
         }
 
         status = init();
@@ -308,6 +310,15 @@
             buffer = (char *)buffer + status;
         }
 
+        // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
+        // does no spin and starve other threads.
+        // NOTE: It is likely that the A2DP headset is being disconnected
+        nsecs_t now = systemTime();
+        if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
+            LOGV("A2DP sink runs too fast");
+            usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
+        }
+        mLastWriteTime = now;
         return bytes;
 
     }
@@ -316,7 +327,7 @@
     standby();
 
     // Simulate audio output timing in case of error
-    usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
+    usleep(mBufferDurationUs);
 
     return status;
 }
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
index cef1926..dbe2c6a 100644
--- a/services/audioflinger/A2dpAudioInterface.h
+++ b/services/audioflinger/A2dpAudioInterface.h
@@ -117,6 +117,8 @@
                 uint32_t    mDevice;
                 bool        mClosing;
                 bool        mSuspended;
+                nsecs_t     mLastWriteTime;
+                uint32_t    mBufferDurationUs;
     };
 
     friend class A2dpAudioStreamOut;