Merge "When SIM absent, keyguard should be considered non-secure." into jb-dev
diff --git a/api/current.txt b/api/current.txt
index 55c9e28..f5bc504 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3690,6 +3690,7 @@
     method public int getRouteTypes();
     method public void setExtendedSettingsClickListener(android.view.View.OnClickListener);
     method public void setRouteTypes(int);
+    method public void showDialog();
   }
 
   public class NativeActivity extends android.app.Activity implements android.view.InputQueue.Callback android.view.SurfaceHolder.Callback2 android.view.ViewTreeObserver.OnGlobalLayoutListener {
@@ -11575,11 +11576,13 @@
   }
 
   public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
+    method public java.lang.Object getTag();
     method public void setIconDrawable(android.graphics.drawable.Drawable);
     method public void setIconResource(int);
     method public void setName(java.lang.CharSequence);
     method public void setRemoteControlClient(android.media.RemoteControlClient);
     method public void setStatus(java.lang.CharSequence);
+    method public void setTag(java.lang.Object);
   }
 
   public class MediaScannerConnection implements android.content.ServiceConnection {
diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java
index 5fe08ec..4860182 100644
--- a/core/java/android/app/MediaRouteActionProvider.java
+++ b/core/java/android/app/MediaRouteActionProvider.java
@@ -16,7 +16,10 @@
 
 package android.app;
 
+import com.android.internal.app.MediaRouteChooserDialogFragment;
+
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.util.Log;
@@ -83,10 +86,37 @@
 
     @Override
     public boolean onPerformDefaultAction() {
-        // Show routing dialog
+        final FragmentManager fm = getActivity().getFragmentManager();
+        // See if one is already attached to this activity.
+        MediaRouteChooserDialogFragment dialogFragment =
+                (MediaRouteChooserDialogFragment) fm.findFragmentByTag(
+                MediaRouteChooserDialogFragment.FRAGMENT_TAG);
+        if (dialogFragment != null) {
+            Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!");
+            return false;
+        }
+
+        dialogFragment = new MediaRouteChooserDialogFragment();
+        dialogFragment.setExtendedSettingsClickListener(mExtendedSettingsListener);
+        dialogFragment.setRouteTypes(mRouteTypes);
+        dialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
         return true;
     }
 
+    private Activity getActivity() {
+        // Gross way of unwrapping the Activity so we can get the FragmentManager
+        Context context = mContext;
+        while (context instanceof ContextWrapper && !(context instanceof Activity)) {
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+        if (!(context instanceof Activity)) {
+            throw new IllegalStateException("The MediaRouteActionProvider's Context " +
+                    "is not an Activity.");
+        }
+
+        return (Activity) context;
+    }
+
     public void setExtendedSettingsClickListener(View.OnClickListener listener) {
         mExtendedSettingsListener = listener;
         if (mView != null) {
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 385241c..a4eebda 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -17,8 +17,10 @@
 package android.app;
 
 import com.android.internal.R;
+import com.android.internal.app.MediaRouteChooserDialogFragment;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -44,6 +46,7 @@
     private int mMinHeight;
 
     private OnClickListener mExtendedSettingsClickListener;
+    private MediaRouteChooserDialogFragment mDialogFragment;
 
     private static final int[] ACTIVATED_STATE_SET = {
         R.attr.state_activated
@@ -112,7 +115,7 @@
                 }
             }
         } else {
-            Log.d(TAG, "TODO: Implement the dialog!");
+            showDialog();
         }
 
         return handled;
@@ -263,8 +266,51 @@
     }
 
     public void setExtendedSettingsClickListener(OnClickListener listener) {
-        // TODO: if dialog is already open, propagate so that it updates live.
         mExtendedSettingsClickListener = listener;
+        if (mDialogFragment != null) {
+            mDialogFragment.setExtendedSettingsClickListener(listener);
+        }
+    }
+
+    /**
+     * Asynchronously show the route chooser dialog.
+     * This will attach a {@link DialogFragment} to the containing Activity.
+     */
+    public void showDialog() {
+        final FragmentManager fm = getActivity().getFragmentManager();
+        if (mDialogFragment == null) {
+            // See if one is already attached to this activity.
+            mDialogFragment = (MediaRouteChooserDialogFragment) fm.findFragmentByTag(
+                    MediaRouteChooserDialogFragment.FRAGMENT_TAG);
+        }
+        if (mDialogFragment != null) {
+            Log.w(TAG, "showDialog(): Already showing!");
+            return;
+        }
+
+        mDialogFragment = new MediaRouteChooserDialogFragment();
+        mDialogFragment.setExtendedSettingsClickListener(mExtendedSettingsClickListener);
+        mDialogFragment.setLauncherListener(new MediaRouteChooserDialogFragment.LauncherListener() {
+            @Override
+            public void onDetached(MediaRouteChooserDialogFragment detachedFragment) {
+                mDialogFragment = null;
+            }
+        });
+        mDialogFragment.setRouteTypes(mRouteTypes);
+        mDialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG);
+    }
+
+    private Activity getActivity() {
+        // Gross way of unwrapping the Activity so we can get the FragmentManager
+        Context context = getContext();
+        while (context instanceof ContextWrapper && !(context instanceof Activity)) {
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+        if (!(context instanceof Activity)) {
+            throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
+        }
+
+        return (Activity) context;
     }
 
     private class MediaRouteCallback extends MediaRouter.SimpleCallback {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ec35a3f..bb497c0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1670,6 +1670,9 @@
                 contentView.setTextViewText(R.id.text, overflowText);
                 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
                 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
+            } else {
+                contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
+                contentView.setViewVisibility(R.id.line3, View.GONE);
             }
 
             return contentView;
@@ -1812,6 +1815,7 @@
             // Remove the content text so line3 only shows if you have a summary
             final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
             mBuilder.mContentText = null;
+
             RemoteViews contentView = getStandardView(R.layout.notification_template_big_text);
             
             if (hadThreeLines) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 611742b..da61e74 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1316,7 +1316,7 @@
         case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
             HitTestResult hitTest = getHitTestResult();
             if (hitTest != null) {
-                performLongClick();
+                mWebView.performLongClick();
             }
             break;
         case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
@@ -7265,7 +7265,7 @@
                     // the states
                     mGotCenterDown = false;
                     mTrackballDown = false;
-                    performLongClick();
+                    mWebView.performLongClick();
                     break;
 
                 case WEBCORE_NEED_TOUCH_EVENTS:
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
new file mode 100644
index 0000000..000eee7
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2012 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 com.android.internal.app;
+
+import com.android.internal.R;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.MediaRouteActionProvider;
+import android.app.MediaRouteButton;
+import android.content.Context;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteCategory;
+import android.media.MediaRouter.RouteGroup;
+import android.media.MediaRouter.RouteInfo;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * This class implements the route chooser dialog for {@link MediaRouter}.
+ *
+ * @see MediaRouteButton
+ * @see MediaRouteActionProvider
+ */
+public class MediaRouteChooserDialogFragment extends DialogFragment {
+    private static final String TAG = "MediaRouteChooserDialogFragment";
+    public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment";
+
+    MediaRouter mRouter;
+    private int mRouteTypes;
+
+    private LauncherListener mLauncherListener;
+    private View.OnClickListener mExtendedSettingsListener;
+    private RouteAdapter mAdapter;
+    private ListView mListView;
+
+    public MediaRouteChooserDialogFragment() {
+        setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
+    }
+
+    public void setLauncherListener(LauncherListener listener) {
+        mLauncherListener = listener;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        if (mLauncherListener != null) {
+            mLauncherListener.onDetached(this);
+        }
+        if (mAdapter != null) {
+            mRouter.removeCallback(mAdapter);
+            mAdapter = null;
+        }
+        mRouter = null;
+    }
+
+    /**
+     * Implemented by the MediaRouteButton that launched this dialog
+     */
+    public interface LauncherListener {
+        public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
+    }
+
+    public void setExtendedSettingsClickListener(View.OnClickListener listener) {
+        mExtendedSettingsListener = listener;
+    }
+
+    public void setRouteTypes(int types) {
+        mRouteTypes = types;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
+        final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
+
+        if (mExtendedSettingsListener != null) {
+            extendedSettingsButton.setVisibility(View.VISIBLE);
+            extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
+        }
+
+        final ListView list = (ListView) layout.findViewById(R.id.list);
+        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        list.setAdapter(mAdapter = new RouteAdapter(inflater));
+        list.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
+        list.setOnItemClickListener(mAdapter);
+
+        mListView = list;
+        mRouter.addCallback(mRouteTypes, mAdapter);
+
+        return layout;
+    }
+
+    private static final int[] ITEM_LAYOUTS = new int[] {
+        R.layout.media_route_list_item_top_header,
+        R.layout.media_route_list_item_section_header,
+        R.layout.media_route_list_item
+    };
+
+    private class RouteAdapter extends BaseAdapter implements MediaRouter.Callback,
+            ListView.OnItemClickListener {
+        private static final int VIEW_TOP_HEADER = 0;
+        private static final int VIEW_SECTION_HEADER = 1;
+        private static final int VIEW_ROUTE = 2;
+
+        private int mSelectedItemPosition;
+        private final ArrayList<Object> mItems = new ArrayList<Object>();
+        private final LayoutInflater mInflater;
+
+        RouteAdapter(LayoutInflater inflater) {
+            mInflater = inflater;
+            update();
+        }
+
+        void update() {
+            // TODO this is kind of naive, but our data sets are going to be
+            // fairly small on average.
+            mItems.clear();
+
+            final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
+
+            final ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+            final int catCount = mRouter.getCategoryCount();
+            for (int i = 0; i < catCount; i++) {
+                final RouteCategory cat = mRouter.getCategoryAt(i);
+                cat.getRoutes(routes);
+
+                mItems.add(cat);
+
+                final int routeCount = routes.size();
+                for (int j = 0; j < routeCount; j++) {
+                    final RouteInfo info = routes.get(j);
+                    if (info == selectedRoute) {
+                        mSelectedItemPosition = mItems.size();
+                    }
+                    mItems.add(info);
+                }
+            }
+
+            notifyDataSetChanged();
+            if (mListView != null) {
+                mListView.setItemChecked(mSelectedItemPosition, true);
+            }
+        }
+
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 3;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            final Object item = getItem(position);
+            if (item instanceof RouteCategory) {
+                return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
+            } else {
+                return VIEW_ROUTE;
+            }
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItemViewType(position) == VIEW_ROUTE;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final int viewType = getItemViewType(position);
+
+            ViewHolder holder;
+            if (convertView == null) {
+                convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false);
+                holder = new ViewHolder();
+                holder.text1 = (TextView) convertView.findViewById(R.id.text1);
+                holder.text2 = (TextView) convertView.findViewById(R.id.text2);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            if (viewType == VIEW_ROUTE) {
+                bindItemView(position, holder);
+            } else {
+                bindHeaderView(position, holder);
+            }
+
+            return convertView;
+        }
+
+        void bindItemView(int position, ViewHolder holder) {
+            RouteInfo info = (RouteInfo) mItems.get(position);
+            holder.text1.setText(info.getName());
+            final CharSequence status = info.getStatus();
+            if (TextUtils.isEmpty(status)) {
+                holder.text2.setVisibility(View.GONE);
+            } else {
+                holder.text2.setVisibility(View.VISIBLE);
+                holder.text2.setText(status);
+            }
+        }
+
+        void bindHeaderView(int position, ViewHolder holder) {
+            RouteCategory cat = (RouteCategory) mItems.get(position);
+            holder.text1.setText(cat.getName());
+        }
+
+        public int getSelectedRoutePosition() {
+            return mSelectedItemPosition;
+        }
+
+        @Override
+        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+            update();
+        }
+
+        @Override
+        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+            update();
+        }
+
+        @Override
+        public void onRouteAdded(MediaRouter router, RouteInfo info) {
+            update();
+        }
+
+        @Override
+        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+            update();
+        }
+
+        @Override
+        public void onRouteChanged(MediaRouter router, RouteInfo info) {
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public void onRouteGrouped(MediaRouter router, RouteInfo info,
+                RouteGroup group, int index) {
+            update();
+        }
+
+        @Override
+        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
+            update();
+        }
+
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            ListView lv = (ListView) parent;
+            final Object item = getItem(lv.getCheckedItemPosition());
+            if (!(item instanceof RouteInfo)) {
+                // Oops. Stale event running around? Skip it.
+                return;
+            }
+            mRouter.selectRoute(mRouteTypes, (RouteInfo) item);
+            dismiss();
+        }
+    }
+
+    private static class ViewHolder {
+        public TextView text1;
+        public TextView text2;
+    }
+
+    private class GroupAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+    }
+}
diff --git a/core/res/res/layout/media_route_chooser_layout.xml b/core/res/res/layout/media_route_chooser_layout.xml
new file mode 100644
index 0000000..320d6de
--- /dev/null
+++ b/core/res/res/layout/media_route_chooser_layout.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:orientation="vertical"
+              android:showDividers="middle"
+              android:divider="?android:attr/dividerHorizontal">
+    <LinearLayout android:layout_width="match_parent"
+                  android:layout_height="?android:attr/listPreferredItemHeight"
+                  android:gravity="center_vertical"
+                  android:padding="8dp">
+        <ImageView android:id="@+id/volume_icon"
+                   android:layout_width="48dp"
+                   android:layout_height="48dp"
+                   android:src="@android:drawable/ic_audio_vol"
+                   android:gravity="center"
+                   android:scaleType="center" />
+        <SeekBar android:id="@+id/volume_slider"
+                 android:layout_width="0dp"
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:layout_marginLeft="8dp"
+                 android:layout_marginRight="8dp" />
+        <ImageButton android:id="@+id/extended_settings"
+                     android:layout_width="48dp"
+                     android:layout_height="48dp"
+                     android:background="?android:attr/selectableItemBackground"
+                     android:src="@android:drawable/ic_sysbar_quicksettings"
+                     android:visibility="gone" />
+    </LinearLayout>
+    <ListView android:id="@id/list"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
+    <Button android:id="@+id/done"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/borderlessButtonStyle"
+            android:text="@string/media_route_chooser_grouping_done"
+            android:visibility="gone" />
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml
new file mode 100644
index 0000000..d457c6c
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="?android:attr/listPreferredItemHeight"
+              android:background="?android:attr/activatedBackgroundIndicator"
+              android:gravity="center_vertical">
+
+    <ImageView android:layout_width="56dp"
+               android:layout_height="56dp"
+               android:scaleType="center"
+               android:id="@+id/icon"
+               android:visibility="gone" />
+
+    <LinearLayout android:layout_width="0dp"
+                  android:layout_height="match_parent"
+                  android:layout_weight="1"
+                  android:orientation="vertical"
+                  android:gravity="left|center_vertical"
+                  android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+                  android:paddingRight="?android:attr/listPreferredItemPaddingRight">
+
+        <TextView android:id="@android:id/text1"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <TextView android:id="@android:id/text2"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:textAppearance="?android:attr/textAppearanceSmall" />
+    </LinearLayout>
+
+    <!-- TODO Make this not glow when pressed from above, and give it a divider. -->
+    <ImageButton android:layout_width="56dp"
+                 android:layout_height="56dp"
+                 android:id="@+id/group_button"
+                 android:background="?android:attr/selectableItemBackground"
+                 android:scaleType="center"
+                 android:visibility="gone" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/media_route_list_item_section_header.xml b/core/res/res/layout/media_route_list_item_section_header.xml
new file mode 100644
index 0000000..04bd0ea
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_section_header.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:paddingTop="16dp">
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:background="#19ffffff"
+        android:textStyle="bold"
+        android:textAllCaps="true"
+        android:gravity="center_vertical"
+        android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+        android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+        android:minHeight="24dp"
+        />
+</FrameLayout>
diff --git a/core/res/res/layout/media_route_list_item_top_header.xml b/core/res/res/layout/media_route_list_item_top_header.xml
new file mode 100644
index 0000000..75decd3
--- /dev/null
+++ b/core/res/res/layout/media_route_list_item_top_header.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceSmall"
+    android:background="#19ffffff"
+    android:textStyle="bold"
+    android:textAllCaps="true"
+    android:gravity="center_vertical"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:minHeight="24dp"
+/>
diff --git a/core/res/res/layout/notification_template_big_text.xml b/core/res/res/layout/notification_template_big_text.xml
index d377882..0b3386b 100644
--- a/core/res/res/layout/notification_template_big_text.xml
+++ b/core/res/res/layout/notification_template_big_text.xml
@@ -108,9 +108,8 @@
                 android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
-                android:layout_marginBottom="8dp"
+                android:layout_marginBottom="10dp"
                 android:layout_marginRight="8dp"
-                android:layout_marginTop="2dp"
                 android:singleLine="false"
                 android:visibility="gone"
                 android:maxLines="8"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1f15cf..0af8534 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1166,6 +1166,13 @@
 
   <java-symbol type="attr" name="mediaRouteButtonStyle" />
   <java-symbol type="attr" name="externalRouteEnabledDrawable" />
+  <java-symbol type="layout" name="media_route_chooser_layout" />
+  <java-symbol type="id" name="extended_settings" />
+  <java-symbol type="id" name="done" />
+  <java-symbol type="layout" name="media_route_list_item_top_header" />
+  <java-symbol type="layout" name="media_route_list_item_section_header" />
+  <java-symbol type="layout" name="media_route_list_item" />
+  <java-symbol type="id" name="group_button" />
 
   <!-- From android.policy -->
   <java-symbol type="anim" name="app_starting_exit" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 53914a8..967d700 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3551,24 +3551,26 @@
     <string name="activity_resolver_use_once">Just once</string>
 
     <!-- Name of the default audio route for tablets when nothing
-         is connected to a headphone or other wired audio output jack. [CHAR LIMIT=25] -->
+         is connected to a headphone or other wired audio output jack. [CHAR LIMIT=50] -->
     <string name="default_audio_route_name" product="tablet">Tablet speakers</string>
 
     <!-- Name of the default audio route when nothing is connected to
-         a headphone or other wired audio output jack. [CHAR LIMIT=25] -->
+         a headphone or other wired audio output jack. [CHAR LIMIT=50] -->
     <string name="default_audio_route_name" product="default">Phone speaker</string>
 
     <!-- Name of the default audio route when wired headphones are
-         connected. [CHAR LIMIT=25] -->
+         connected. [CHAR LIMIT=50] -->
     <string name="default_audio_route_name_headphones">Headphones</string>
 
-    <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=25] -->
+    <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=50] -->
     <string name="default_audio_route_name_dock_speakers">Dock speakers</string>
 
-    <!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=25] -->
+    <!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=50] -->
     <string name="default_audio_route_name_hdmi">HDMI audio</string>
 
-    <!-- Name of the default audio route category. [CHAR LIMIT=25] -->
+    <!-- Name of the default audio route category. [CHAR LIMIT=50] -->
     <string name="default_audio_route_category_name">System</string>
 
+    <!-- "Done" button for MediaRouter chooser dialog when grouping routes. [CHAR LIMIT=NONE] -->
+    <string name="media_route_chooser_grouping_done">Done</string>
 </resources>
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 38fe363..b0657ff 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -581,6 +581,7 @@
      */
     public static class UserRouteInfo extends RouteInfo {
         RemoteControlClient mRcc;
+        private Object mTag;
 
         UserRouteInfo(RouteCategory category) {
             super(category);
@@ -638,6 +639,29 @@
         public void setIconResource(int resId) {
             setIconDrawable(sStatic.mResources.getDrawable(resId));
         }
+
+        /**
+         * Set an application-specific tag object for this route.
+         * The application may use this to store arbitrary data associated with the
+         * route for internal tracking.
+         *
+         * <p>Note that the lifespan of a route may be well past the lifespan of
+         * an Activity or other Context; take care that objects you store here
+         * will not keep more data in memory alive than you intend.</p>
+         *
+         * @param tag Arbitrary, app-specific data for this route to hold for later use
+         */
+        public void setTag(Object tag) {
+            mTag = tag;
+        }
+
+        /**
+         * @return The tag object previously set by the application
+         * @see #setTag(Object)
+         */
+        public Object getTag() {
+            return mTag;
+        }
     }
 
     /**
diff --git a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
index 52c9fda..78f7f3e 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
@@ -29,6 +29,7 @@
 import android.filterfw.format.ImageFormat;
 import android.opengl.GLES20;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import java.lang.ArrayIndexOutOfBoundsException;
@@ -510,6 +511,20 @@
         super(name);
 
         mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+        String adjStr = SystemProperties.get("ro.media.effect.bgdropper.adj");
+        if (adjStr.length() > 0) {
+            try {
+                mAcceptStddev += Float.parseFloat(adjStr);
+                if (mLogVerbose) {
+                    Log.v(TAG, "Adjusting accept threshold by " + adjStr +
+                            ", now " + mAcceptStddev);
+                }
+            } catch (NumberFormatException e) {
+                Log.e(TAG,
+                        "Badly formatted property ro.media.effect.bgdropper.adj: " + adjStr);
+            }
+        }
     }
 
     @Override
@@ -695,7 +710,6 @@
             mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
             mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
             mFrameCount = 0;
-            mStartLearning = false;
         }
 
         // Select correct pingpong buffers
@@ -720,6 +734,11 @@
         mBgInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
                                      GLES20.GL_LINEAR_MIPMAP_NEAREST);
 
+        if (mStartLearning) {
+            copyShaderProgram.process(mVideoInput, mBgMean[inputIndex]);
+            mStartLearning = false;
+        }
+
         // Process shaders
         Frame[] distInputs = { mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex] };
         mBgDistProgram.process(distInputs, mDistance);
@@ -765,7 +784,12 @@
                 ByteBuffer mMaskAverageByteBuffer = mMaskAverage.getData();
                 byte[] mask_average = mMaskAverageByteBuffer.array();
                 int bi = (int)(mask_average[3] & 0xFF);
-                if (mLogVerbose) Log.v(TAG, String.format("Mask_average is %d", bi));
+
+                if (mLogVerbose) {
+                    Log.v(TAG,
+                            String.format("Mask_average is %d, threshold is %d",
+                                    bi, DEFAULT_LEARNING_DONE_THRESHOLD));
+                }
 
                 if (bi >= DEFAULT_LEARNING_DONE_THRESHOLD) {
                     mStartLearning = true;                                      // Restart learning
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 18b8042..87ec16b 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -81,7 +81,7 @@
 
     private int mFailedAttempts = 0;
     private int mFailedBiometricUnlockAttempts = 0;
-    private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 5;
+    private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3;
 
     private boolean mClockVisible;
 
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index efed0a4..267bf82 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -93,6 +93,7 @@
                     final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
                     if (!winAnimator.mLastHidden) {
                         winAnimator.hide();
+                        mService.dispatchWallpaperVisibility(wallpaper, false);
                         mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
                 }
@@ -292,7 +293,7 @@
                             + " anim=" + win.mWinAnimator.mAnimation);
                 } else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
                     final boolean changed;
-                    if (mForceHiding) {
+                    if (mForceHiding && !winAnimator.isAnimating()) {
                         changed = win.hideLw(false, false);
                         if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
                                 "Now policy hidden: " + win);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b85573f..6d5ae71 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1870,16 +1870,7 @@
 
                 // First, make sure the client has the current visibility
                 // state.
-                if (wallpaper.mWallpaperVisible != visible) {
-                    wallpaper.mWallpaperVisible = visible;
-                    try {
-                        if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
-                                "Setting visibility of wallpaper " + wallpaper
-                                + ": " + visible);
-                        wallpaper.mClient.dispatchAppVisibility(visible);
-                    } catch (RemoteException e) {
-                    }
-                }
+                dispatchWallpaperVisibility(wallpaper, visible);
 
                 wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
                 if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "adjustWallpaper win "
@@ -2089,6 +2080,24 @@
         }
     }
 
+    /**
+     * Check wallpaper for visiblity change and notify window if so.
+     * @param wallpaper The wallpaper to test and notify.
+     * @param visible Current visibility.
+     */
+    void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
+        if (wallpaper.mWallpaperVisible != visible) {
+            wallpaper.mWallpaperVisible = visible;
+            try {
+                if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
+                        "Updating visibility of wallpaper " + wallpaper
+                        + ": " + visible + " Callers=" + Debug.getCallers(2));
+                wallpaper.mClient.dispatchAppVisibility(visible);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     void updateWallpaperVisibilityLocked() {
         final boolean visible = isWallpaperVisible(mWallpaperTarget);
         final int dw = mAppDisplayWidth;
@@ -2113,16 +2122,7 @@
                     updateWallpaperOffsetLocked(wallpaper, dw, dh, false);
                 }
 
-                if (wallpaper.mWallpaperVisible != visible) {
-                    wallpaper.mWallpaperVisible = visible;
-                    try {
-                        if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
-                                "Updating visibility of wallpaper " + wallpaper
-                                + ": " + visible);
-                        wallpaper.mClient.dispatchAppVisibility(visible);
-                    } catch (RemoteException e) {
-                    }
-                }
+                dispatchWallpaperVisibility(wallpaper, visible);
             }
         }
     }
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index bdacb6e..579cbb7 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -1122,6 +1122,9 @@
                                 + " during relayout");
                         if (showSurfaceRobustlyLocked()) {
                             mLastHidden = false;
+                            if (w.mIsWallpaper) {
+                                mService.dispatchWallpaperVisibility(w, true);
+                            }
                         } else {
                             w.mOrientationChanging = false;
                         }