Merge "Add a test to demonstrate parsing of trailing URI parameters."
diff --git a/api/current.xml b/api/current.xml
index e7d5ff4..9fc6e3e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20749,6 +20749,19 @@
  visibility="public"
 >
 </constructor>
+<method name="addOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
 <method name="addTab"
  return="void"
  abstract="true"
@@ -20998,6 +21011,19 @@
  visibility="public"
 >
 </method>
+<method name="removeOnMenuVisibilityListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.OnMenuVisibilityListener">
+</parameter>
+</method>
 <method name="removeTab"
  return="void"
  abstract="true"
@@ -21131,7 +21157,7 @@
 >
 <parameter name="adapter" type="android.widget.SpinnerAdapter">
 </parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
 </parameter>
 </method>
 <method name="setDropdownNavigationMode"
@@ -21146,7 +21172,7 @@
 >
 <parameter name="adapter" type="android.widget.SpinnerAdapter">
 </parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
 </parameter>
 <parameter name="defaultSelectedPosition" type="int">
 </parameter>
@@ -21163,7 +21189,7 @@
 >
 <parameter name="adapter" type="android.widget.SpinnerAdapter">
 </parameter>
-<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+<parameter name="callback" type="android.app.ActionBar.OnNavigationListener">
 </parameter>
 </method>
 <method name="setNavigationMode"
@@ -21475,7 +21501,28 @@
 >
 </field>
 </class>
-<interface name="ActionBar.NavigationCallback"
+<interface name="ActionBar.OnMenuVisibilityListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onMenuVisibilityChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
+</interface>
+<interface name="ActionBar.OnNavigationListener"
  abstract="true"
  static="true"
  final="false"
@@ -249389,7 +249436,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 7a6ad0f..2f69520 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -168,13 +168,13 @@
      * @param adapter An adapter that will provide views both to display
      *                the current navigation selection and populate views
      *                within the dropdown navigation menu.
-     * @param callback A NavigationCallback that will receive events when the user
+     * @param callback A OnNavigationListener that will receive events when the user
      *                 selects a navigation item.
      * @deprecated See setListNavigationCallbacks.
      */
     @Deprecated
     public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
-            NavigationCallback callback);
+            OnNavigationListener callback);
 
     /**
      * Set the adapter and navigation callback for list navigation mode.
@@ -182,17 +182,17 @@
      * The supplied adapter will provide views for the expanded list as well as
      * the currently selected item. (These may be displayed differently.)
      *
-     * The supplied NavigationCallback will alert the application when the user
+     * The supplied OnNavigationListener will alert the application when the user
      * changes the current list selection.
      *
      * @param adapter An adapter that will provide views both to display
      *                the current navigation selection and populate views
      *                within the dropdown navigation menu.
-     * @param callback A NavigationCallback that will receive events when the user
+     * @param callback An OnNavigationListener that will receive events when the user
      *                 selects a navigation item.
      */
     public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
-            NavigationCallback callback);
+            OnNavigationListener callback);
 
     /**
      * Set the action bar into dropdown navigation mode and supply an adapter that will
@@ -201,7 +201,7 @@
      * @param adapter An adapter that will provide views both to display the current
      *                navigation selection and populate views within the dropdown
      *                navigation menu.
-     * @param callback A NavigationCallback that will receive events when the user
+     * @param callback A OnNavigationListener that will receive events when the user
      *                 selects a navigation item.
      * @param defaultSelectedPosition Position within the provided adapter that should be
      *                                selected from the outset.
@@ -209,7 +209,7 @@
      */
     @Deprecated
     public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
-            NavigationCallback callback, int defaultSelectedPosition);
+            OnNavigationListener callback, int defaultSelectedPosition);
 
     /**
      * Set the selected navigation item in list or tabbed navigation modes.
@@ -532,9 +532,24 @@
     public abstract boolean isShowing();
 
     /**
-     * Callback interface for ActionBar navigation events. 
+     * Add a listener that will respond to menu visibility change events.
+     *
+     * @param listener The new listener to add
      */
-    public interface NavigationCallback {
+    public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+    /**
+     * Remove a menu visibility listener. This listener will no longer receive menu
+     * visibility change events.
+     *
+     * @param listener A listener to remove that was previously added
+     */
+    public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
+
+    /**
+     * Listener interface for ActionBar navigation events.
+     */
+    public interface OnNavigationListener {
         /**
          * This method is called whenever a navigation item in your action bar
          * is selected.
@@ -547,6 +562,21 @@
     }
 
     /**
+     * Listener for receiving events when action bar menus are shown or hidden.
+     */
+    public interface OnMenuVisibilityListener {
+        /**
+         * Called when an action bar menu is shown or hidden. Applications may want to use
+         * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
+         * gameplay, or other activity within the main content area.
+         *
+         * @param isVisible True if an action bar menu is now visible, false if no action bar
+         *                  menus are visible.
+         */
+        public void onMenuVisibilityChanged(boolean isVisible);
+    }
+
+    /**
      * A tab in the action bar.
      *
      * <p>Tabs manage the hiding and showing of {@link Fragment}s.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d69a179..6b619fb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2362,6 +2362,9 @@
      * @return The default implementation returns true.
      */
     public boolean onMenuOpened(int featureId, Menu menu) {
+        if (featureId == Window.FEATURE_ACTION_BAR) {
+            mActionBar.dispatchMenuVisibilityChanged(true);
+        }
         return true;
     }
 
@@ -2392,7 +2395,7 @@
                     return true;
                 }
                 return mFragments.dispatchContextItemSelected(item);
-                
+
             default:
                 return false;
         }
@@ -2417,6 +2420,10 @@
             case Window.FEATURE_CONTEXT_MENU:
                 onContextMenuClosed(menu);
                 break;
+
+            case Window.FEATURE_ACTION_BAR:
+                mActionBar.dispatchMenuVisibilityChanged(false);
+                break;
         }
     }
 
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 64a4d7a..f90fc59 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -735,6 +735,9 @@
      * @see Activity#onMenuOpened(int, Menu)
      */
     public boolean onMenuOpened(int featureId, Menu menu) {
+        if (featureId == Window.FEATURE_ACTION_BAR) {
+            mActionBar.dispatchMenuVisibilityChanged(true);
+        }
         return true;
     }
 
@@ -749,6 +752,9 @@
      * @see Activity#onPanelClosed(int, Menu)
      */
     public void onPanelClosed(int featureId, Menu menu) {
+        if (featureId == Window.FEATURE_ACTION_BAR) {
+            mActionBar.dispatchMenuVisibilityChanged(false);
+        }
     }
 
     /**
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index 27043e0..641604e 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -43,7 +43,6 @@
     private Resources mResources;
     private boolean mMatchesFound;
     private int mNumberOfMatches;
-    private View mTitleBar;
     private ActionMode mActionMode;
 
     FindActionModeCallback(Context context) {
@@ -62,8 +61,6 @@
         mResources = context.getResources();
     }
 
-    void setTitleBar(View v) { mTitleBar = v; }
-
     void finish() {
         mActionMode.finish();
     }
@@ -174,7 +171,6 @@
 
     @Override
     public void onDestroyActionMode(ActionMode mode) {
-        if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
         mWebView.notifyFindDialogDismissed();
         mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b8ccf45..27a4b6d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2253,13 +2253,6 @@
      * @hide
      */
     public void setEmbeddedTitleBar(View v) {
-        if (null == v) {
-            // If one of our callbacks is holding onto the titlebar to replace
-            // it when its ActionMode ends, remove it.
-            if (mFindCallback != null) {
-                mFindCallback.setTitleBar(null);
-            }
-        }
         if (mTitleBar == v) return;
         if (mTitleBar != null) {
             removeView(mTitleBar);
@@ -2894,11 +2887,6 @@
         setFindIsUp(true);
         mFindCallback.setWebView(this);
         View titleBar = mTitleBar;
-        // We do not want to show the embedded title bar during find or
-        // select, but keep track of it so that it can be replaced when the
-        // mode is exited.
-        setEmbeddedTitleBar(null);
-        mFindCallback.setTitleBar(titleBar);
         startActionMode(mFindCallback);
         if (text == null) {
             text = mLastFind;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cfdcd32..1d3e4f4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8923,66 +8923,14 @@
                 TextView.this.requestFocus();
                 return true;
 
-            case DragEvent.ACTION_DRAG_LOCATION: {
+            case DragEvent.ACTION_DRAG_LOCATION:
                 final int offset = getOffset((int) event.getX(), (int) event.getY());
                 Selection.setSelection((Spannable)mText, offset);
                 return true;
-            }
 
-            case DragEvent.ACTION_DROP: {
-                StringBuilder content = new StringBuilder("");
-                ClipData clipData = event.getClipData();
-                final int itemCount = clipData.getItemCount();
-                for (int i=0; i < itemCount; i++) {
-                    Item item = clipData.getItem(i);
-                    content.append(item.coerceToText(TextView.this.mContext));
-                }
-
-                final int offset = getOffset((int) event.getX(), (int) event.getY());
-
-                if (mDragSourcePositions != -1) {
-                    final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
-                    final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
-                    if (offset >= dragSourceStart && offset < dragSourceEnd) {
-                        // A drop inside the original selection discards the drop.
-                        return true;
-                    }
-                }
-
-                final int originalLength = mText.length();
-                long minMax = prepareSpacesAroundPaste(offset, offset, content);
-                int min = extractRangeStartFromLong(minMax);
-                int max = extractRangeEndFromLong(minMax);
-
-                Selection.setSelection((Spannable) mText, max);
-                ((Editable) mText).replace(min, max, content);
-
-                if (mDragSourcePositions != -1) {
-                    int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
-                    int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
-                    if (max <= dragSourceStart) {
-                        // Inserting text before selection has shifted positions
-                        final int shift = mText.length() - originalLength;
-                        dragSourceStart += shift;
-                        dragSourceEnd += shift;
-                    }
-
-                    // Delete original selection
-                    ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
-
-                    // Make sure we do not leave two adjacent spaces.
-                    if ((dragSourceStart == 0 ||
-                            Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
-                            (dragSourceStart == mText.length() ||
-                            Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
-                        final int pos = dragSourceStart == mText.length() ?
-                                dragSourceStart - 1 : dragSourceStart;
-                        ((Editable) mText).delete(pos, pos + 1);
-                    }
-                }
-
+            case DragEvent.ACTION_DROP:
+                onDrop(event);
                 return true;
-            }
 
             case DragEvent.ACTION_DRAG_ENDED:
                 mDragSourcePositions = -1;
@@ -8994,6 +8942,59 @@
         }
     }
 
+    private void onDrop(DragEvent event) {
+        StringBuilder content = new StringBuilder("");
+        ClipData clipData = event.getClipData();
+        final int itemCount = clipData.getItemCount();
+        for (int i=0; i < itemCount; i++) {
+            Item item = clipData.getItem(i);
+            content.append(item.coerceToText(TextView.this.mContext));
+        }
+
+        final int offset = getOffset((int) event.getX(), (int) event.getY());
+
+        if (mDragSourcePositions != -1) {
+            final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+            final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+            if (offset >= dragSourceStart && offset < dragSourceEnd) {
+                // A drop inside the original selection discards the drop.
+                return;
+            }
+        }
+
+        final int originalLength = mText.length();
+        long minMax = prepareSpacesAroundPaste(offset, offset, content);
+        int min = extractRangeStartFromLong(minMax);
+        int max = extractRangeEndFromLong(minMax);
+
+        Selection.setSelection((Spannable) mText, max);
+        ((Editable) mText).replace(min, max, content);
+
+        if (mDragSourcePositions != -1) {
+            int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
+            int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+            if (max <= dragSourceStart) {
+                // Inserting text before selection has shifted positions
+                final int shift = mText.length() - originalLength;
+                dragSourceStart += shift;
+                dragSourceEnd += shift;
+            }
+
+            // Delete original selection
+            ((Editable) mText).delete(dragSourceStart, dragSourceEnd);
+
+            // Make sure we do not leave two adjacent spaces.
+            if ((dragSourceStart == 0 ||
+                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) &&
+                    (dragSourceStart == mText.length() ||
+                    Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) {
+                final int pos = dragSourceStart == mText.length() ?
+                        dragSourceStart - 1 : dragSourceStart;
+                ((Editable) mText).delete(pos, pos + 1);
+            }
+        }
+    }
+
     /**
      * @return True if this view supports insertion handles.
      */
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 20402a3..447a062 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -70,6 +70,10 @@
     
     private ActionMode mActionMode;
     
+    private boolean mLastMenuVisibility;
+    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
+            new ArrayList<OnMenuVisibilityListener>();
+
     private static final int CONTEXT_DISPLAY_NORMAL = 0;
     private static final int CONTEXT_DISPLAY_SPLIT = 1;
     
@@ -120,6 +124,26 @@
                 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
     }
 
+    public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+        mMenuVisibilityListeners.add(listener);
+    }
+
+    public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+        mMenuVisibilityListeners.remove(listener);
+    }
+
+    public void dispatchMenuVisibilityChanged(boolean isVisible) {
+        if (isVisible == mLastMenuVisibility) {
+            return;
+        }
+        mLastMenuVisibility = isVisible;
+
+        final int count = mMenuVisibilityListeners.size();
+        for (int i = 0; i < count; i++) {
+            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
+        }
+    }
+
     @Override
     public void setTitle(int resId) {
         setTitle(mContext.getString(resId));
@@ -138,11 +162,11 @@
         mActionView.setCallback(null);
     }
 
-    public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+    public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) {
         setDropdownNavigationMode(adapter, callback, -1);
     }
 
-    public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
+    public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback,
             int defaultSelectedPosition) {
         cleanupTabs();
         setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
@@ -516,7 +540,7 @@
 
         public void onMenuModeChange(MenuBuilder menu) {
             invalidate();
-            mUpperContextView.showOverflowMenu();
+            mUpperContextView.openOverflowMenu();
         }
     }
 
@@ -627,7 +651,7 @@
     }
 
     @Override
-    public void setListNavigationCallbacks(SpinnerAdapter adapter, NavigationCallback callback) {
+    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
         mActionView.setDropdownAdapter(adapter);
         mActionView.setCallback(callback);
     }
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index b54daba..2d067da 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -135,6 +135,6 @@
 
     public void onMenuModeChange(MenuBuilder menu) {
         invalidate();
-        mContextView.showOverflowMenu();
+        mContextView.openOverflowMenu();
     }
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 621defe..84067d0 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -59,6 +59,22 @@
         }
     };
     
+    private class OpenOverflowRunnable implements Runnable {
+        private MenuPopupHelper mPopup;
+
+        public OpenOverflowRunnable(MenuPopupHelper popup) {
+            mPopup = popup;
+        }
+
+        public void run() {
+            mOverflowPopup = mPopup;
+            mPopup.show();
+            mPostedOpenRunnable = null;
+        }
+    }
+
+    private OpenOverflowRunnable mPostedOpenRunnable;
+
     public ActionMenuView(Context context) {
         this(context, null);
     }
@@ -100,6 +116,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
         final int screen = newConfig.screenLayout;
         mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
                 Configuration.SCREENLAYOUT_SIZE_XLARGE;
@@ -115,6 +132,14 @@
         }
     }
 
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+            mOverflowPopup.dismiss();
+        }
+    }
+
     private int getMaxActionButtons() {
         return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
     }
@@ -193,30 +218,34 @@
     }
 
     public boolean showOverflowMenu() {
-        if (mOverflowButton != null) {
-            final MenuPopupHelper popup =
-                    new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
-            // Post this for later; we might still need a layout for the anchor to be right.
-            post(new Runnable() {
-                public void run() {
-                    popup.show();
-                }
-            });
-            mOverflowPopup = popup;
+        if (mOverflowButton != null && !isOverflowMenuShowing()) {
+            mMenu.getCallback().onMenuModeChange(mMenu);
             return true;
         }
         return false;
     }
 
+    public void openOverflowMenu() {
+        OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true);
+        mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+        // Post this for later; we might still need a layout for the anchor to be right.
+        post(mPostedOpenRunnable);
+    }
+
     public boolean isOverflowMenuShowing() {
-        MenuPopupHelper popup = mOverflowPopup;
-        if (popup != null) {
-            return popup.isShowing();
-        }
-        return false;
+        return mOverflowPopup != null && mOverflowPopup.isShowing();
+    }
+
+    public boolean isOverflowMenuOpen() {
+        return mOverflowPopup != null;
     }
 
     public boolean hideOverflowMenu() {
+        if (mPostedOpenRunnable != null) {
+            removeCallbacks(mPostedOpenRunnable);
+            return true;
+        }
+
         MenuPopupHelper popup = mOverflowPopup;
         if (popup != null) {
             popup.dismiss();
@@ -274,9 +303,22 @@
                 return true;
             }
 
-            // Change to overflow mode
-            mMenu.getCallback().onMenuModeChange(mMenu);
+            showOverflowMenu();
             return true;
         }
     }
+
+    private class OverflowPopup extends MenuPopupHelper {
+        public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+                boolean overflowOnly) {
+            super(context, menu, anchorView, overflowOnly);
+        }
+
+        @Override
+        public void onDismiss() {
+            super.onDismiss();
+            mMenu.getCallback().onCloseMenu(mMenu, true);
+            mOverflowPopup = null;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 1406e4e..2cb78a5 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -35,7 +35,7 @@
  * @hide
  */
 public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
-        ViewTreeObserver.OnGlobalLayoutListener {
+        ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener {
     private static final String TAG = "MenuPopupHelper";
 
     private Context mContext;
@@ -46,12 +46,6 @@
     private boolean mOverflowOnly;
     private ViewTreeObserver mTreeObserver;
 
-    private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
-        public void onDismiss() {
-            mPopup = null;
-        }
-    };
-
     public MenuPopupHelper(Context context, MenuBuilder menu) {
         this(context, menu, null, false);
     }
@@ -77,7 +71,7 @@
     public void show() {
         mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
         mPopup.setOnItemClickListener(this);
-        mPopup.setOnDismissListener(mDismissListener);
+        mPopup.setOnDismissListener(this);
 
         final MenuAdapter adapter = mOverflowOnly ?
                 mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
@@ -110,8 +104,12 @@
         if (isShowing()) {
             mPopup.dismiss();
         }
+    }
+
+    public void onDismiss() {
+        mPopup = null;
         if (mTreeObserver != null) {
-            mTreeObserver.removeGlobalOnLayoutListener(this);
+            mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this);
             mTreeObserver = null;
         }
     }
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index e93c414..cbf12bf 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -180,6 +180,12 @@
         return false;
     }
 
+    public void openOverflowMenu() {
+        if (mMenuView != null) {
+            mMenuView.openOverflowMenu();
+        }
+    }
+
     public boolean hideOverflowMenu() {
         if (mMenuView != null) {
             return mMenuView.hideOverflowMenu();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index f931217..07a65fc 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -22,7 +22,7 @@
 import com.android.internal.view.menu.MenuBuilder;
 
 import android.app.ActionBar;
-import android.app.ActionBar.NavigationCallback;
+import android.app.ActionBar.OnNavigationListener;
 import android.app.Activity;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -114,7 +114,7 @@
     private ActionMenuItem mLogoNavItem;
 
     private SpinnerAdapter mSpinnerAdapter;
-    private NavigationCallback mCallback;
+    private OnNavigationListener mCallback;
 
     private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
             new AdapterView.OnItemSelectedListener() {
@@ -243,7 +243,7 @@
         return null;
     }
 
-    public void setCallback(NavigationCallback callback) {
+    public void setCallback(OnNavigationListener callback) {
         mCallback = callback;
     }
 
@@ -269,6 +269,12 @@
         return false;
     }
 
+    public void openOverflowMenu() {
+        if (mMenuView != null) {
+            mMenuView.openOverflowMenu();
+        }
+    }
+
     public void postShowOverflowMenu() {
         post(new Runnable() {
             public void run() {
@@ -291,6 +297,13 @@
         return false;
     }
 
+    public boolean isOverflowMenuOpen() {
+        if (mMenuView != null) {
+            return mMenuView.isOverflowMenuOpen();
+        }
+        return false;
+    }
+
     public boolean isOverflowReserved() {
         return mMenuView != null && mMenuView.isOverflowReserved();
     }
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index c3d3482f..44d75c1 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -455,7 +455,7 @@
 <ol>
   <li>Create a {@link android.widget.SpinnerAdapter} that provides the
 list of selectable items for the list and the layout to use when drawing each item in the list.</li>
-  <li>Implement {@link android.app.ActionBar.NavigationCallback} to define the behavior when the
+  <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the
 user selects an item from the list.</li>
   <li>Turn on navigation mode for the Action Bar with {@link
 android.app.ActionBar#setNavigationMode setNavigationMode()}. For example:
@@ -472,17 +472,17 @@
 actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
 </pre>
 <p>This method takes your {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback}. More about these next.</p>
+android.app.ActionBar.OnNavigationListener}. More about these next.</p>
 </li>
 </ol>
 
 <p>That's the basic setup. The {@link android.widget.SpinnerAdapter} and {@link
-android.app.ActionBar.NavigationCallback} is where most of the work is done. There are many ways 
+android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many ways
 you can implement these to define the functionality for your drop-down navigation. Implementing
 various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this
 document&mdash;you should refer to the class refrence for more information about implementing it or
 extending an existing implementation. However, below is a simple example for a {@link
-android.widget.SpinnerAdapter} and {@link android.app.ActionBar.NavigationCallback} to get you
+android.widget.SpinnerAdapter} and {@link android.app.ActionBar.OnNavigationListener} to get you
 started.</p>
 
 
@@ -520,24 +520,24 @@
 </pre>
 
 
-<h3 id="NavigationCallback">Example: simple NavigationCallback</h3>
+<h3 id="OnNavigationListener">Example: simple OnNavigationListener</h3>
 
-<p>Your implementation of {@link android.app.ActionBar.NavigationCallback} is where you handle
+<p>Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle
 fragment changes or other modifications to your activity when the user selects an item from the
 drop-down list. There's only one callback method to implement: {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}.</p>
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.</p>
 
 <p>The {@link
-android.app.ActionBar.NavigationCallback#onNavigationItemSelected onNavigationItemSelected()}
+android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}
 method receives the position of the item in the list and an item ID provided by the {@link
 android.widget.SpinnerAdapter}.</p>
 
 <p>Here's an example that instantiates an anonymous implementation of {@link
-android.app.ActionBar.NavigationCallback}, which inserts a {@link android.app.Fragment} into the
+android.app.ActionBar.OnNavigationListener}, which inserts a {@link android.app.Fragment} into the
 layout container identified by {@code R.id.fragment_container}:</p>
 
 <pre>
-mNavigationCallback = new NavigationCallback() {
+mOnNavigationListener = new OnNavigationListener() {
   // Get the same strings provided for the drop-down's ArrayAdapter
   String[] strings = getResources().getStringArray(R.array.action_list);
 
@@ -556,7 +556,7 @@
 };
 </pre>
 
-<p>This instance of {@link android.app.ActionBar.NavigationCallback} can be given to {@link
+<p>This instance of {@link android.app.ActionBar.OnNavigationListener} can be given to {@link
 android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from
 above.</p>
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index cd8a065..138dff7 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -160,6 +160,8 @@
 
     private ContextMenuBuilder mContextMenu;
     private MenuDialogHelper mContextMenuHelper;
+    private ActionButtonSubmenu mActionButtonPopup;
+    private boolean mClosingActionMenu;
 
     private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
 
@@ -542,6 +544,9 @@
             if (doCallback) {
                 callOnPanelClosed(st.featureId, st, null);
             }
+        } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback &&
+                mActionBar != null) {
+            checkCloseActionMenu(st.menu);
         }
         st.isPrepared = false;
         st.isHandled = false;
@@ -563,6 +568,27 @@
         }
     }
 
+    private void checkCloseActionMenu(Menu menu) {
+        if (mClosingActionMenu) {
+            return;
+        }
+
+        boolean closed = false;
+        mClosingActionMenu = true;
+        if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) {
+            closed = true;
+        }
+        if (mActionButtonPopup != null) {
+            mActionButtonPopup.dismiss();
+            closed = true;
+        }
+        Callback cb = getCallback();
+        if (cb != null && closed) {
+            cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
+        }
+        mClosingActionMenu = false;
+    }
+
     @Override
     public final void togglePanel(int featureId, KeyEvent event) {
         PanelFeatureState st = getPanelState(featureId, true);
@@ -842,7 +868,12 @@
             // The window manager will give us a valid window token
             new MenuDialogHelper(subMenu).show(null);
         } else {
-            new MenuPopupHelper(getContext(), subMenu).show();
+            mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu);
+            mActionButtonPopup.show();
+            Callback cb = getCallback();
+            if (cb != null) {
+                cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
+            }
         }
 
         return true;
@@ -854,16 +885,21 @@
 
     private void reopenMenu(boolean toggleMenuMode) {
         if (mActionBar != null) {
+            final Callback cb = getCallback();
             if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
-                final Callback cb = getCallback();
                 if (cb != null) {
                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
                     if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
-                        mActionBar.showOverflowMenu();
+                        cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
+                        mActionBar.openOverflowMenu();
                     }
                 }
             } else {
                 mActionBar.hideOverflowMenu();
+                if (cb != null) {
+                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+                    cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
+                }
             }
             return;
         }
@@ -2042,8 +2078,23 @@
             if (cb != null && mFeatureId < 0) {
                 cb.onDetachedFromWindow();
             }
+
+            if (mActionButtonPopup != null) {
+                if (mActionButtonPopup.isShowing()) {
+                    mActionButtonPopup.dismiss();
+                }
+                mActionButtonPopup = null;
+            }
         }
-        
+
+        @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            if (mActionButtonPopup != null) {
+                mActionButtonPopup.dismiss();
+                post(mActionButtonPopup);
+            }
+        }
+
         @Override
         public void onCloseSystemDialogs(String reason) {
             if (mFeatureId >= 0) {
@@ -2921,4 +2972,29 @@
     void sendCloseSystemWindows(String reason) {
         PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
     }
+
+    private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable {
+        private SubMenuBuilder mSubMenu;
+
+        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+            super(context, subMenu);
+            mSubMenu = subMenu;
+        }
+
+        @Override
+        public void onDismiss() {
+            super.onDismiss();
+            mSubMenu.getCallback().onCloseSubMenu(mSubMenu);
+            mActionButtonPopup = null;
+        }
+
+        @Override
+        public void run() {
+            show();
+            Callback cb = getCallback();
+            if (cb != null) {
+                cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu);
+            }
+        }
+    }
 }
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index e3b5db1..175f613 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -81,12 +81,6 @@
                     LOGV("setDeviceConnectionState() BT SCO  device, address %s", device_address);
                     // keep track of SCO device address
                     mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
-#ifdef WITH_A2DP
-                    if (mA2dpOutput != 0 &&
-                        mPhoneState != AudioSystem::MODE_NORMAL) {
-                        mpClientInterface->suspendOutput(mA2dpOutput);
-                    }
-#endif
                 }
             }
             break;
@@ -115,12 +109,6 @@
             {
                 if (AudioSystem::isBluetoothScoDevice(device)) {
                     mScoDeviceAddress = "";
-#ifdef WITH_A2DP
-                    if (mA2dpOutput != 0 &&
-                        mPhoneState != AudioSystem::MODE_NORMAL) {
-                        mpClientInterface->restoreOutput(mA2dpOutput);
-                    }
-#endif
                 }
             }
             } break;
@@ -138,6 +126,7 @@
         if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
             closeA2dpOutputs();
         }
+        checkA2dpSuspend();
 #endif
         updateDeviceForStrategy();
         setOutputDevice(mHardwareOutput, newDevice);
@@ -280,14 +269,7 @@
     newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
     checkOutputForAllStrategies();
-    // suspend A2DP output if a SCO device is present.
-    if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
-        if (oldState == AudioSystem::MODE_NORMAL) {
-            mpClientInterface->suspendOutput(mA2dpOutput);
-        } else if (state == AudioSystem::MODE_NORMAL) {
-            mpClientInterface->restoreOutput(mA2dpOutput);
-        }
-    }
+    checkA2dpSuspend();
 #endif
     updateDeviceForStrategy();
 
@@ -397,6 +379,7 @@
     uint32_t newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
     checkOutputForAllStrategies();
+    checkA2dpSuspend();
 #endif
     updateDeviceForStrategy();
     setOutputDevice(mHardwareOutput, newDevice);
@@ -1025,8 +1008,10 @@
 #ifdef AUDIO_POLICY_TEST
     Thread(false),
 #endif //AUDIO_POLICY_TEST
-    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),
-    mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
+    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
+    mMusicStopTime(0), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
+    mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
+    mA2dpSuspended(false)
 {
     mpClientInterface = clientInterface;
 
@@ -1322,17 +1307,6 @@
     }
     AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
 
-    if (mScoDeviceAddress != "") {
-        // It is normal to suspend twice if we are both in call,
-        // and have the hardware audio output routed to BT SCO
-        if (mPhoneState != AudioSystem::MODE_NORMAL) {
-            mpClientInterface->suspendOutput(mA2dpOutput);
-        }
-        if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
-            mpClientInterface->suspendOutput(mA2dpOutput);
-        }
-    }
-
     if (!a2dpUsedForSonification()) {
         // mute music on A2DP output if a notification or ringtone is playing
         uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
@@ -1340,6 +1314,9 @@
             setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
         }
     }
+
+    mA2dpSuspended = false;
+
     return NO_ERROR;
 }
 
@@ -1369,6 +1346,7 @@
         }
     }
     mA2dpDeviceAddress = "";
+    mA2dpSuspended = false;
     return NO_ERROR;
 }
 
@@ -1467,6 +1445,48 @@
     checkOutputForStrategy(STRATEGY_DTMF);
 }
 
+void AudioPolicyManagerBase::checkA2dpSuspend()
+{
+    // suspend A2DP output if:
+    //      (NOT already suspended) &&
+    //      ((SCO device is connected &&
+    //       (forced usage for communication || for record is SCO))) ||
+    //      (phone state is ringing || in call)
+    //
+    // restore A2DP output if:
+    //      (Already suspended) &&
+    //      ((SCO device is NOT connected ||
+    //       (forced usage NOT for communication && NOT for record is SCO))) &&
+    //      (phone state is NOT ringing && NOT in call)
+    //
+    if (mA2dpOutput == 0) {
+        return;
+    }
+
+    if (mA2dpSuspended) {
+        if (((mScoDeviceAddress == "") ||
+             ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
+              (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
+             ((mPhoneState != AudioSystem::MODE_IN_CALL) &&
+              (mPhoneState != AudioSystem::MODE_RINGTONE))) {
+
+            mpClientInterface->restoreOutput(mA2dpOutput);
+            mA2dpSuspended = false;
+        }
+    } else {
+        if (((mScoDeviceAddress != "") &&
+             ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+              (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
+             ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
+              (mPhoneState == AudioSystem::MODE_RINGTONE))) {
+
+            mpClientInterface->suspendOutput(mA2dpOutput);
+            mA2dpSuspended = true;
+        }
+    }
+}
+
+
 #endif
 
 uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
@@ -1714,14 +1734,7 @@
         // wait for the PCM output buffers to empty before proceeding with the rest of the command
         usleep(outputDesc->mLatency*2*1000);
     }
-#ifdef WITH_A2DP
-    // suspend A2DP output if SCO device is selected
-    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
-         if (mA2dpOutput != 0) {
-             mpClientInterface->suspendOutput(mA2dpOutput);
-         }
-    }
-#endif
+
     // do the routing
     AudioParameter param = AudioParameter();
     param.addInt(String8(AudioParameter::keyRouting), (int)device);
@@ -1729,15 +1742,6 @@
     // update stream volumes according to new device
     applyStreamVolumes(output, device, delayMs);
 
-#ifdef WITH_A2DP
-    // if disconnecting SCO device, restore A2DP output
-    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
-         if (mA2dpOutput != 0) {
-             LOGV("restore A2DP output");
-             mpClientInterface->restoreOutput(mA2dpOutput);
-         }
-    }
-#endif
     // if changing from a combined headset + speaker route, unmute media streams
     if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
         setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);