Merge "Import translations. DO NOT MERGE"
diff --git a/api/current.txt b/api/current.txt
index d1e1d47..f15e17b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11582,6 +11582,8 @@
     method public java.lang.CharSequence getName(android.content.Context);
     method public java.lang.CharSequence getStatus();
     method public int getSupportedTypes();
+    method public java.lang.Object getTag();
+    method public void setTag(java.lang.Object);
   }
 
   public static class MediaRouter.SimpleCallback extends android.media.MediaRouter.Callback {
@@ -11596,14 +11598,12 @@
   }
 
   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 setName(int);
     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/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index c783e6a..2411cec 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -29,9 +29,12 @@
 import android.content.IntentFilter;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.os.Binder;
 import android.os.Process;
+import android.os.UserId;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.util.List;
 
@@ -48,7 +51,7 @@
     private final Context mContext;
 
     // This field is initialized lazily in getSearchables(), and then never modified.
-    private Searchables mSearchables;
+    private SparseArray<Searchables> mSearchables;
 
     private ContentObserver mGlobalSearchObserver;
 
@@ -66,14 +69,24 @@
                 mContext.getContentResolver());
     }
 
-    private synchronized Searchables getSearchables() {
+    private synchronized Searchables getSearchables(int userId) {
         if (mSearchables == null) {
-            Log.i(TAG, "Building list of searchable activities");
             new MyPackageMonitor().register(mContext, null, true);
-            mSearchables = new Searchables(mContext);
-            mSearchables.buildSearchableList();
+            mSearchables = new SparseArray<Searchables>();
         }
-        return mSearchables;
+        Searchables searchables = mSearchables.get(userId);
+
+        long origId = Binder.clearCallingIdentity();
+        boolean userExists = mContext.getPackageManager().getUser(userId) != null;
+        Binder.restoreCallingIdentity(origId);
+
+        if (searchables == null && userExists) {
+            Log.i(TAG, "Building list of searchable activities for userId=" + userId);
+            searchables = new Searchables(mContext, userId);
+            searchables.buildSearchableList();
+            mSearchables.append(userId, searchables);
+        }
+        return searchables;
     }
 
     /**
@@ -87,7 +100,7 @@
                 public void run() {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                     mContext.unregisterReceiver(BootCompletedReceiver.this);
-                    getSearchables();
+                    getSearchables(0);
                 }
             }.start();
         }
@@ -109,8 +122,12 @@
         }
 
         private void updateSearchables() {
-            // Update list of searchable activities
-            getSearchables().buildSearchableList();
+            synchronized (SearchManagerService.this) {
+                // Update list of searchable activities
+                for (int i = 0; i < mSearchables.size(); i++) {
+                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                }
+            }
             // Inform all listeners that the list of searchables has been updated.
             Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -132,7 +149,11 @@
 
         @Override
         public void onChange(boolean selfChange) {
-            getSearchables().buildSearchableList();
+            synchronized (SearchManagerService.this) {
+                for (int i = 0; i < mSearchables.size(); i++) {
+                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                }
+            }
             Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             mContext.sendBroadcast(intent);
@@ -156,32 +177,32 @@
             Log.e(TAG, "getSearchableInfo(), activity == null");
             return null;
         }
-        return getSearchables().getSearchableInfo(launchActivity);
+        return getSearchables(UserId.getCallingUserId()).getSearchableInfo(launchActivity);
     }
 
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
     public List<SearchableInfo> getSearchablesInGlobalSearch() {
-        return getSearchables().getSearchablesInGlobalSearchList();
+        return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList();
     }
 
     public List<ResolveInfo> getGlobalSearchActivities() {
-        return getSearchables().getGlobalSearchActivities();
+        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities();
     }
 
     /**
      * Gets the name of the global search activity.
      */
     public ComponentName getGlobalSearchActivity() {
-        return getSearchables().getGlobalSearchActivity();
+        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity();
     }
 
     /**
      * Gets the name of the web search activity.
      */
     public ComponentName getWebSearchActivity() {
-        return getSearchables().getWebSearchActivity();
+        return getSearchables(UserId.getCallingUserId()).getWebSearchActivity();
     }
 
 }
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index f24d52f..4e00ef9 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,6 +16,7 @@
 
 package android.server.search;
 
+import android.app.AppGlobals;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
 import android.content.ComponentName;
@@ -23,9 +24,11 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -38,6 +41,7 @@
 
 /**
  * This class maintains the information about all searchable activities.
+ * This is a hidden class.
  */
 public class Searchables {
 
@@ -65,12 +69,18 @@
     public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME =
             "com.google.android.providers.enhancedgooglesearch/.Launcher";
 
+    // Cache the package manager instance
+    private IPackageManager mPm;
+    // User for which this Searchables caches information
+    private int mUserId;
+
     /**
      *
      * @param context Context to use for looking up activities etc.
      */
-    public Searchables (Context context) {
+    public Searchables (Context context, int userId) {
         mContext = context;
+        mUserId = userId;
     }
 
     /**
@@ -195,16 +205,14 @@
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
 
-        final PackageManager pm = mContext.getPackageManager();
-
         // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
         List<ResolveInfo> searchList;
         final Intent intent = new Intent(Intent.ACTION_SEARCH);
-        searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+        searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA);
 
         List<ResolveInfo> webSearchInfoList;
         final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
-        webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
+        webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
 
         // analyze each one, generate a Searchables record, and record
         if (searchList != null || webSearchInfoList != null) {
@@ -262,10 +270,8 @@
         // Step 1 : Query the package manager for a list
         // of activities that can handle the GLOBAL_SEARCH intent.
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
+                    queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
         if (activities != null && !activities.isEmpty()) {
             // Step 2: Rank matching activities according to our heuristics.
             Collections.sort(activities, GLOBAL_SEARCH_RANKER);
@@ -301,10 +307,8 @@
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
         intent.setComponent(globalSearch);
 
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
+        List<ResolveInfo> activities = queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY);
         if (activities != null && !activities.isEmpty()) {
             return true;
         }
@@ -374,9 +378,8 @@
         }
         Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
         intent.setPackage(globalSearchActivity.getPackageName());
-        PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+                queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
 
         if (activities != null && !activities.isEmpty()) {
             ActivityInfo ai = activities.get(0).activityInfo;
@@ -387,6 +390,22 @@
         return null;
     }
 
+    private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        if (mPm == null) {
+            mPm = AppGlobals.getPackageManager();
+        }
+        List<ResolveInfo> activities = null;
+        try {
+            activities =
+                    mPm.queryIntentActivities(intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags, mUserId);
+        } catch (RemoteException re) {
+            // Local call
+        }
+        return activities;
+    }
+
     /**
      * Returns the list of searchable activities.
      */
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
index 6c9a047..e1a48be 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -36,6 +36,8 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.Checkable;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -44,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * This class implements the route chooser dialog for {@link MediaRouter}.
@@ -58,11 +61,7 @@
     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 static final int[] GROUP_ITEM_LAYOUTS = new int[] {
-        R.layout.media_route_list_item_top_header,
+        R.layout.media_route_list_item,
         R.layout.media_route_list_item_checkable,
         R.layout.media_route_list_item_collapse_group
     };
@@ -74,7 +73,6 @@
     private LauncherListener mLauncherListener;
     private View.OnClickListener mExtendedSettingsListener;
     private RouteAdapter mAdapter;
-    private GroupAdapter mGroupAdapter;
     private ListView mListView;
 
     final RouteComparator mComparator = new RouteComparator();
@@ -99,10 +97,6 @@
         if (mLauncherListener != null) {
             mLauncherListener.onDetached(this);
         }
-        if (mGroupAdapter != null) {
-            mRouter.removeCallback(mGroupAdapter.mCallback);
-            mGroupAdapter = null;
-        }
         if (mAdapter != null) {
             mRouter.removeCallback(mAdapter.mCallback);
             mAdapter = null;
@@ -139,45 +133,18 @@
         }
 
         final ListView list = (ListView) layout.findViewById(R.id.list);
-        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
         list.setItemsCanFocus(true);
         list.setAdapter(mAdapter = new RouteAdapter());
-        list.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
         list.setOnItemClickListener(mAdapter);
 
         mListView = list;
         mRouter.addCallback(mRouteTypes, mAdapter.mCallback);
 
+        mAdapter.scrollToSelectedItem();
+
         return layout;
     }
 
-    void onExpandGroup(RouteGroup info) {
-        mGroupAdapter = new GroupAdapter(info);
-        mRouter.addCallback(mRouteTypes, mGroupAdapter.mCallback);
-        mListView.setAdapter(mGroupAdapter);
-        mListView.setOnItemClickListener(mGroupAdapter);
-        mListView.setItemsCanFocus(false);
-        mListView.clearChoices();
-        mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        mGroupAdapter.initCheckedItems();
-
-        getDialog().setCanceledOnTouchOutside(false);
-    }
-
-    void onDoneGrouping() {
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setItemsCanFocus(true);
-        mListView.clearChoices();
-        mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
-
-        mRouter.removeCallback(mGroupAdapter.mCallback);
-        mGroupAdapter = null;
-
-        getDialog().setCanceledOnTouchOutside(true);
-    }
-
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new RouteChooserDialog(getActivity(), getTheme());
@@ -186,14 +153,6 @@
     @Override
     public void onResume() {
         super.onResume();
-
-        if (mListView != null) {
-            if (mGroupAdapter != null) {
-                mGroupAdapter.initCheckedItems();
-            } else {
-                mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
-            }
-        }
     }
 
     private static class ViewHolder {
@@ -203,52 +162,139 @@
         public ImageButton expandGroupButton;
         public RouteAdapter.ExpandGroupListener expandGroupListener;
         public int position;
+        public CheckBox check;
     }
 
     private class RouteAdapter extends BaseAdapter implements 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 static final int VIEW_GROUPING_ROUTE = 3;
+        private static final int VIEW_GROUPING_DONE = 4;
 
-        private int mSelectedItemPosition;
+        private int mSelectedItemPosition = -1;
         private final ArrayList<Object> mItems = new ArrayList<Object>();
         final MediaRouterCallback mCallback = new MediaRouterCallback();
 
+        private RouteCategory mCategoryEditingGroups;
+        private RouteGroup mEditingGroup;
+
+        // Temporary lists for manipulation
+        private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
+        private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
+
+        private boolean mIgnoreUpdates;
+
         RouteAdapter() {
             update();
         }
 
         void update() {
-            // TODO this is kind of naive, but our data sets are going to be
-            // fairly small on average.
+            /*
+             * This is kind of wacky, but our data sets are going to be
+             * fairly small on average. Ideally we should be able to do some of this stuff
+             * in-place instead.
+             *
+             * Basic idea: each entry in mItems represents an item in the list for quick access.
+             * Entries can be a RouteCategory (section header), a RouteInfo with a category of
+             * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
+             * the user to change the group),
+             */
+            if (mIgnoreUpdates) return;
+
             mItems.clear();
 
             final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
+            mSelectedItemPosition = -1;
 
-            final ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+            List<RouteInfo> routes;
             final int catCount = mRouter.getCategoryCount();
             for (int i = 0; i < catCount; i++) {
                 final RouteCategory cat = mRouter.getCategoryAt(i);
-                cat.getRoutes(routes);
+                routes = cat.getRoutes(mCatRouteList);
 
                 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);
+                if (cat == mCategoryEditingGroups) {
+                    addGroupEditingCategoryRoutes(routes);
+                } else {
+                    addSelectableRoutes(selectedRoute, routes);
                 }
+
+                routes.clear();
             }
 
             notifyDataSetChanged();
-            if (mListView != null) {
+            if (mListView != null && mSelectedItemPosition >= 0) {
                 mListView.setItemChecked(mSelectedItemPosition, true);
             }
         }
 
+        void scrollToEditingGroup() {
+            if (mCategoryEditingGroups == null || mListView == null) return;
+
+            int pos = 0;
+            int bound = 0;
+            final int itemCount = mItems.size();
+            for (int i = 0; i < itemCount; i++) {
+                final Object item = mItems.get(i);
+                if (item != null && item == mCategoryEditingGroups) {
+                    bound = i;
+                }
+                if (item == null) {
+                    pos = i;
+                    break; // this is always below the category header; we can stop here.
+                }
+            }
+
+            mListView.smoothScrollToPosition(pos, bound);
+        }
+
+        void scrollToSelectedItem() {
+            if (mListView == null || mSelectedItemPosition < 0) return;
+
+            mListView.smoothScrollToPosition(mSelectedItemPosition);
+        }
+
+        void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
+            final int routeCount = from.size();
+            for (int j = 0; j < routeCount; j++) {
+                final RouteInfo info = from.get(j);
+                if (info == selectedRoute) {
+                    mSelectedItemPosition = mItems.size();
+                }
+                mItems.add(info);
+            }
+        }
+
+        void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
+            // Unpack groups and flatten for presentation
+            // mSortRouteList will always be empty here.
+            final int topCount = from.size();
+            for (int i = 0; i < topCount; i++) {
+                final RouteInfo route = from.get(i);
+                final RouteGroup group = route.getGroup();
+                if (group == route) {
+                    // This is a group, unpack it.
+                    final int groupCount = group.getRouteCount();
+                    for (int j = 0; j < groupCount; j++) {
+                        final RouteInfo innerRoute = group.getRouteAt(j);
+                        mSortRouteList.add(innerRoute);
+                    }
+                } else {
+                    mSortRouteList.add(route);
+                }
+            }
+            // Sort by name. This will keep the route positions relatively stable even though they
+            // will be repeatedly added and removed.
+            Collections.sort(mSortRouteList, mComparator);
+
+            mItems.addAll(mSortRouteList);
+            mSortRouteList.clear();
+
+            mItems.add(null); // Sentinel reserving space for the "done" button.
+        }
+
         @Override
         public int getCount() {
             return mItems.size();
@@ -256,7 +302,7 @@
 
         @Override
         public int getViewTypeCount() {
-            return 3;
+            return 5;
         }
 
         @Override
@@ -264,7 +310,13 @@
             final Object item = getItem(position);
             if (item instanceof RouteCategory) {
                 return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
+            } else if (item == null) {
+                return VIEW_GROUPING_DONE;
             } else {
+                final RouteInfo info = (RouteInfo) item;
+                if (info.getCategory() == mCategoryEditingGroups) {
+                    return VIEW_GROUPING_ROUTE;
+                }
                 return VIEW_ROUTE;
             }
         }
@@ -276,7 +328,14 @@
 
         @Override
         public boolean isEnabled(int position) {
-            return getItemViewType(position) == VIEW_ROUTE;
+            switch (getItemViewType(position)) {
+                case VIEW_ROUTE:
+                case VIEW_GROUPING_ROUTE:
+                case VIEW_GROUPING_DONE:
+                    return true;
+                default:
+                    return false;
+            }
         }
 
         @Override
@@ -301,6 +360,7 @@
                 holder.text1 = (TextView) convertView.findViewById(R.id.text1);
                 holder.text2 = (TextView) convertView.findViewById(R.id.text2);
                 holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+                holder.check = (CheckBox) convertView.findViewById(R.id.check);
                 holder.expandGroupButton = (ImageButton) convertView.findViewById(
                         R.id.expand_button);
                 if (holder.expandGroupButton != null) {
@@ -322,12 +382,19 @@
                 holder.position = position;
             }
 
-            if (viewType == VIEW_ROUTE) {
-                bindItemView(position, holder);
-            } else {
-                bindHeaderView(position, holder);
+            switch (viewType) {
+                case VIEW_ROUTE:
+                case VIEW_GROUPING_ROUTE:
+                    bindItemView(position, holder);
+                    break;
+                case VIEW_SECTION_HEADER:
+                case VIEW_TOP_HEADER:
+                    bindHeaderView(position, holder);
+                    break;
             }
 
+            convertView.setActivated(position == mSelectedItemPosition);
+
             return convertView;
         }
 
@@ -351,14 +418,24 @@
 
             RouteCategory cat = info.getCategory();
             boolean canGroup = false;
-            if (cat.isGroupable()) {
-                final RouteGroup group = (RouteGroup) info;
-                canGroup = group.getRouteCount() > 1 ||
-                        getItemViewType(position - 1) == VIEW_ROUTE ||
-                        (position < getCount() - 1 && getItemViewType(position + 1) == VIEW_ROUTE);
+            if (cat == mCategoryEditingGroups) {
+                RouteGroup group = info.getGroup();
+                holder.check.setEnabled(group.getRouteCount() > 1);
+                holder.check.setChecked(group == mEditingGroup);
+            } else {
+                if (cat.isGroupable()) {
+                    final RouteGroup group = (RouteGroup) info;
+                    canGroup = group.getRouteCount() > 1 ||
+                            getItemViewType(position - 1) == VIEW_ROUTE ||
+                            (position < getCount() - 1 &&
+                                    getItemViewType(position + 1) == VIEW_ROUTE);
+                }
             }
-            holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
-            holder.expandGroupListener.position = position;
+
+            if (holder.expandGroupButton != null) {
+                holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
+                holder.expandGroupListener.position = position;
+            }
         }
 
         void bindHeaderView(int position, ViewHolder holder) {
@@ -372,14 +449,62 @@
 
         @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.
+            final int type = getItemViewType(position);
+            if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
                 return;
+            } else if (type == VIEW_GROUPING_DONE) {
+                finishGrouping();
+                return;
+            } else {
+                final Object item = getItem(position);
+                if (!(item instanceof RouteInfo)) {
+                    // Oops. Stale event running around? Skip it.
+                    return;
+                }
+
+                final RouteInfo route = (RouteInfo) item;
+                if (type == VIEW_ROUTE) {
+                    mRouter.selectRouteInt(mRouteTypes, route);
+                    dismiss();
+                } else if (type == VIEW_GROUPING_ROUTE) {
+                    final Checkable c = (Checkable) view;
+                    final boolean wasChecked = c.isChecked();
+
+                    mIgnoreUpdates = true;
+                    RouteGroup oldGroup = route.getGroup();
+                    if (!wasChecked && oldGroup != mEditingGroup) {
+                        // Assumption: in a groupable category oldGroup will never be null.
+                        if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
+                            // Old group was selected but is now empty. Select the group
+                            // we're manipulating since that's where the last route went.
+                            mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
+                        }
+                        oldGroup.removeRoute(route);
+                        mEditingGroup.addRoute(route);
+                        c.setChecked(true);
+                    } else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
+                        mEditingGroup.removeRoute(route);
+
+                        // In a groupable category this will add
+                        // the route into its own new group.
+                        mRouter.addRouteInt(route);
+                    }
+                    mIgnoreUpdates = false;
+                    update();
+                }
             }
-            mRouter.selectRouteInt(mRouteTypes, (RouteInfo) item);
-            dismiss();
+        }
+
+        boolean isGrouping() {
+            return mCategoryEditingGroups != null;
+        }
+
+        void finishGrouping() {
+            mCategoryEditingGroups = null;
+            mEditingGroup = null;
+            getDialog().setCanceledOnTouchOutside(true);
+            update();
+            scrollToSelectedItem();
         }
 
         class ExpandGroupListener implements View.OnClickListener {
@@ -389,7 +514,13 @@
             public void onClick(View v) {
                 // Assumption: this is only available for the user to click if we're presenting
                 // a groupable category, where every top-level route in the category is a group.
-                onExpandGroup((RouteGroup) getItem(position));
+                final RouteGroup group = (RouteGroup) getItem(position);
+                mEditingGroup = group;
+                mCategoryEditingGroups = group.getCategory();
+                getDialog().setCanceledOnTouchOutside(false);
+                mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
+                update();
+                scrollToEditingGroup();
             }
         }
 
@@ -411,6 +542,9 @@
 
             @Override
             public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+                if (info == mEditingGroup) {
+                    finishGrouping();
+                }
                 update();
             }
 
@@ -432,246 +566,6 @@
         }
     }
 
-    private class GroupAdapter extends BaseAdapter implements ListView.OnItemClickListener {
-        private static final int VIEW_HEADER = 0;
-        private static final int VIEW_ROUTE = 1;
-        private static final int VIEW_DONE = 2;
-
-        private RouteGroup mPrimary;
-        private RouteCategory mCategory;
-        private final ArrayList<RouteInfo> mTempList = new ArrayList<RouteInfo>();
-        private final ArrayList<RouteInfo> mFlatRoutes = new ArrayList<RouteInfo>();
-        private boolean mIgnoreUpdates;
-        
-        final MediaRouterCallback mCallback = new MediaRouterCallback();
-
-        public GroupAdapter(RouteGroup primary) {
-            mPrimary = primary;
-            mCategory = primary.getCategory();
-            update();
-        }
-
-        @Override
-        public int getCount() {
-            return mFlatRoutes.size() + 2;
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            return 3;
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            if (position == 0) {
-                return VIEW_HEADER;
-            } else if (position == getCount() - 1) {
-                return VIEW_DONE;
-            }
-            return VIEW_ROUTE;
-        }
-
-        void update() {
-            if (mIgnoreUpdates) return;
-            mFlatRoutes.clear();
-            mCategory.getRoutes(mTempList);
-
-            // Unpack groups and flatten for presentation
-            final int topCount = mTempList.size();
-            for (int i = 0; i < topCount; i++) {
-                final RouteInfo route = mTempList.get(i);
-                final RouteGroup group = route.getGroup();
-                if (group == route) {
-                    // This is a group, unpack it.
-                    final int groupCount = group.getRouteCount();
-                    for (int j = 0; j < groupCount; j++) {
-                        final RouteInfo innerRoute = group.getRouteAt(j);
-                        mFlatRoutes.add(innerRoute);
-                    }
-                } else {
-                    mFlatRoutes.add(route);
-                }
-            }
-            mTempList.clear();
-
-            // Sort by name. This will keep the route positions relatively stable even though they
-            // will be repeatedly added and removed.
-            Collections.sort(mFlatRoutes, mComparator);
-            notifyDataSetChanged();
-        }
-
-        void initCheckedItems() {
-            if (mIgnoreUpdates) return;
-            mListView.clearChoices();
-            int count = mFlatRoutes.size();
-            for (int i = 0; i < count; i++){
-                final RouteInfo route = mFlatRoutes.get(i);
-                if (route.getGroup() == mPrimary) {
-                    mListView.setItemChecked(i + 1, true);
-                }
-            }
-        }
-
-        @Override
-        public Object getItem(int position) {
-            if (position == 0) {
-                return mCategory;
-            } else if (position == getCount() - 1) {
-                return null; // Done
-            }
-            return mFlatRoutes.get(position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return position > 0;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final int viewType = getItemViewType(position);
-
-            ViewHolder holder;
-            if (convertView == null) {
-                convertView = mInflater.inflate(GROUP_ITEM_LAYOUTS[viewType], parent, false);
-                holder = new ViewHolder();
-                holder.position = position;
-                holder.text1 = (TextView) convertView.findViewById(R.id.text1);
-                holder.text2 = (TextView) convertView.findViewById(R.id.text2);
-                holder.icon = (ImageView) convertView.findViewById(R.id.icon);
-                convertView.setTag(holder);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
-                holder.position = position;
-            }
-
-            if (viewType == VIEW_ROUTE) {
-                bindItemView(position, holder);
-            } else if (viewType == VIEW_HEADER) {
-                bindHeaderView(position, holder);
-            }
-
-            return convertView;
-        }
-
-        void bindItemView(int position, ViewHolder holder) {
-            RouteInfo info = (RouteInfo) getItem(position);
-            holder.text1.setText(info.getName(getActivity()));
-            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) {
-            holder.text1.setText(mCategory.getName(getActivity()));
-        }
-
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            if (getItemViewType(position) == VIEW_DONE) {
-                onDoneGrouping();
-                return;
-            }
-
-            final ListView lv = (ListView) parent;
-            final RouteInfo route = mFlatRoutes.get(position - 1);
-            final boolean checked = lv.isItemChecked(position);
-
-            mIgnoreUpdates = true;
-            RouteGroup oldGroup = route.getGroup();
-            if (checked && oldGroup != mPrimary) {
-                // Assumption: in a groupable category oldGroup will never be null.
-                oldGroup.removeRoute(route);
-
-                // If the group is now empty, remove the group too.
-                if (oldGroup.getRouteCount() == 0) {
-                    if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
-                        // Old group was selected but is now empty. Select the group
-                        // we're manipulating since that's where the last route went.
-                        mRouter.selectRouteInt(mRouteTypes, mPrimary);
-                    }
-                    mRouter.removeRouteInt(oldGroup);
-                }
-
-                mPrimary.addRoute(route);
-            } else if (!checked) {
-                if (mPrimary.getRouteCount() > 1) {
-                    mPrimary.removeRoute(route);
-
-                    // In a groupable category this will add the route into its own new group.
-                    mRouter.addRouteInt(route);
-                } else {
-                    // We're about to remove the last route.
-                    // Don't let this happen, as it would be silly.
-                    // Turn the checkmark back on again. Silly user!
-                    lv.setItemChecked(position, true);
-                }
-            }
-            mIgnoreUpdates = false;
-            update();
-            initCheckedItems();
-        }
-
-        class MediaRouterCallback extends MediaRouter.Callback {
-            @Override
-            public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
-            }
-
-            @Override
-            public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
-            }
-
-            @Override
-            public void onRouteAdded(MediaRouter router, RouteInfo info) {
-                update();
-                initCheckedItems();
-            }
-
-            @Override
-            public void onRouteRemoved(MediaRouter router, RouteInfo info) {
-                if (info == mPrimary) {
-                    // Can't keep grouping, clean it up.
-                    onDoneGrouping();
-                } else {
-                    update();
-                    initCheckedItems();
-                }
-            }
-
-            @Override
-            public void onRouteChanged(MediaRouter router, RouteInfo info) {
-                update();
-            }
-
-            @Override
-            public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
-                    int index) {
-                update();
-                initCheckedItems();
-            }
-
-            @Override
-            public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
-                update();
-                initCheckedItems();
-            }
-        }
-    }
-
     class RouteComparator implements Comparator<RouteInfo> {
         @Override
         public int compare(RouteInfo lhs, RouteInfo rhs) {
@@ -687,8 +581,8 @@
 
         @Override
         public void onBackPressed() {
-            if (mGroupAdapter != null) {
-                onDoneGrouping();
+            if (mAdapter != null && mAdapter.isGrouping()) {
+                mAdapter.finishGrouping();
             } else {
                 super.onBackPressed();
             }
diff --git a/core/res/res/layout/media_route_list_item_checkable.xml b/core/res/res/layout/media_route_list_item_checkable.xml
index f6ba09e..d0bffb6 100644
--- a/core/res/res/layout/media_route_list_item_checkable.xml
+++ b/core/res/res/layout/media_route_list_item_checkable.xml
@@ -17,6 +17,7 @@
 <com.android.internal.view.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="?android:attr/listPreferredItemHeight"
+              android:background="?android:attr/selectableItemBackground"
               android:gravity="center_vertical">
 
     <ImageView android:layout_width="56dp"
diff --git a/core/res/res/layout/media_route_list_item_collapse_group.xml b/core/res/res/layout/media_route_list_item_collapse_group.xml
index 3f4b1c0..d605c18 100644
--- a/core/res/res/layout/media_route_list_item_collapse_group.xml
+++ b/core/res/res/layout/media_route_list_item_collapse_group.xml
@@ -14,26 +14,31 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="?android:attr/listPreferredItemHeightSmall"
-              android:background="#19ffffff"
-              android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-              android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-              android:gravity="center_vertical">
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:background="?android:attr/selectableItemBackground">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:background="#19ffffff"
+        android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+        android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+        android:gravity="center_vertical">
 
-    <TextView android:layout_width="0dp"
-              android:layout_height="wrap_content"
-              android:layout_weight="1"
-              android:singleLine="true"
-              android:ellipsize="marquee"
-              android:text="@string/media_route_chooser_grouping_done"
-              android:textAppearance="?android:attr/textAppearanceMedium" />
+        <TextView android:layout_width="0dp"
+                  android:layout_height="wrap_content"
+                  android:layout_weight="1"
+                  android:singleLine="true"
+                  android:ellipsize="marquee"
+                  android:text="@string/media_route_chooser_grouping_done"
+                  android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/ic_media_group_collapse"
-        android:scaleType="center" />
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_media_group_collapse"
+            android:scaleType="center" />
 
-</LinearLayout>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
index 6cb31d4..4d3b144 100644
--- a/core/tests/coretests/src/android/app/SearchablesTest.java
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -71,7 +71,7 @@
      */
     public void testNonSearchable() {
         // test basic array & hashmap
-        Searchables searchables = new Searchables(mContext);
+        Searchables searchables = new Searchables(mContext, 0);
         searchables.buildSearchableList();
 
         // confirm that we return null for non-searchy activities
@@ -103,7 +103,7 @@
 
         // build item list with real-world source data
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
-        Searchables searchables = new Searchables(mockContext);
+        Searchables searchables = new Searchables(mockContext, 0);
         searchables.buildSearchableList();
         // tests with "real" searchables (deprecate, this should be a unit test)
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
@@ -122,7 +122,7 @@
         MyMockContext mockContext = new MyMockContext(mContext, mockPM);
 
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
-        Searchables searchables = new Searchables(mockContext);
+        Searchables searchables = new Searchables(mockContext, 0);
         searchables.buildSearchableList();
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         assertNotNull(searchablesList);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b497f63..9e70b7f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -271,9 +271,9 @@
         if (cat.isGroupable() && !(info instanceof RouteGroup)) {
             // Enforce that any added route in a groupable category must be in a group.
             final RouteGroup group = new RouteGroup(info.getCategory());
-            group.addRoute(info);
             sStatic.mRoutes.add(group);
             dispatchRouteAdded(group);
+            group.addRoute(info);
 
             info = group;
         } else {
@@ -552,6 +552,8 @@
         final RouteCategory mCategory;
         Drawable mIcon;
 
+        private Object mTag;
+
         RouteInfo(RouteCategory category) {
             mCategory = category;
         }
@@ -621,6 +623,29 @@
             return mIcon;
         }
 
+        /**
+         * 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;
+        }
+
         void setStatusInt(CharSequence status) {
             if (!status.equals(mStatus)) {
                 mStatus = status;
@@ -652,7 +677,6 @@
      */
     public static class UserRouteInfo extends RouteInfo {
         RemoteControlClient mRcc;
-        private Object mTag;
 
         UserRouteInfo(RouteCategory category) {
             super(category);
@@ -720,29 +744,6 @@
         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;
-        }
     }
 
     /**
@@ -888,6 +889,12 @@
         void routeUpdated() {
             int types = 0;
             final int count = mRoutes.size();
+            if (count == 0) {
+                // Don't keep empty groups in the router.
+                MediaRouter.removeRoute(this);
+                return;
+            }
+
             for (int i = 0; i < count; i++) {
                 types |= mRoutes.get(i).mSupportedTypes;
             }
@@ -901,6 +908,7 @@
             final int count = mRoutes.size();
             for (int i = 0; i < count; i++) {
                 final RouteInfo info = mRoutes.get(i);
+                // TODO: There's probably a much more correct way to localize this.
                 if (i > 0) sb.append(", ");
                 sb.append(info.mName);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9c99653..3c19ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -462,11 +462,12 @@
         signalCluster.setNetworkController(mNetworkController);
 
         if (SHOW_CARRIER_LABEL) {
-            // for wifi-only devices, we show SSID; otherwise, we show PLMN
+            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
+            // for other devices, we show whatever network is connected
             if (mNetworkController.hasMobileDataFeature()) {
                 mNetworkController.addMobileLabelView(mCarrierLabel);
             } else {
-                mNetworkController.addWifiLabelView(mCarrierLabel);
+                mNetworkController.addCombinedLabelView(mCarrierLabel);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index b8f6054..1068267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -121,8 +121,12 @@
     private int mWimaxSignal = 0;
     private int mWimaxState = 0;
     private int mWimaxExtraState = 0;
+
     // data connectivity (regardless of state, can we access the internet?)
     // state of inet connection - 0 not connected, 100 connected
+    private boolean mConnected = false;
+    private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+    private String mConnectedNetworkTypeName;
     private int mInetCondition = 0;
     private static final int INET_CONDITION_THRESHOLD = 50;
 
@@ -868,6 +872,16 @@
                 .getSystemService(Context.CONNECTIVITY_SERVICE);
         final NetworkInfo info = connManager.getActiveNetworkInfo();
 
+        // Are we connected at all, by any interface?
+        mConnected = info != null && info.isConnected();
+        if (mConnected) {
+            mConnectedNetworkType = info.getType();
+            mConnectedNetworkTypeName = info.getTypeName();
+        } else {
+            mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+            mConnectedNetworkTypeName = null;
+        }
+
         int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
 
         if (CHATTY) {
@@ -912,12 +926,13 @@
             //   - We are connected to mobile data, or
             //   - We are not connected to mobile data, as long as the *reason* packets are not
             //     being routed over that link is that we have better connectivity via wifi.
-            // If data is disconnected for some other reason but wifi is connected, we show nothing.
+            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) 
+            // is connected, we show nothing.
             // Otherwise (nothing connected) we show "No internet connection".
 
             if (mDataConnected) {
                 mobileLabel = mNetworkName;
-            } else if (mWifiConnected) {
+            } else if (mConnected) {
                 if (hasService()) {
                     mobileLabel = mNetworkName;
                 } else {
@@ -997,6 +1012,12 @@
                     R.string.accessibility_bluetooth_tether);
         }
 
+        final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
+        if (ethernetConnected) {
+            // TODO: icons and strings for Ethernet connectivity
+            combinedLabel = mConnectedNetworkTypeName;
+        }
+
         if (mAirplaneMode &&
                 (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
             // Only display the flight-mode icon if not in "emergency calls only" mode.
@@ -1023,7 +1044,7 @@
                 combinedSignalIconId = mDataSignalIconId;
             }
         }
-        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected) {
+        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) {
             // pretty much totally disconnected
 
             combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
@@ -1224,6 +1245,9 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NetworkController state:");
+        pw.println(String.format("  %s network type %d (%s)", 
+                mConnected?"CONNECTED":"DISCONNECTED",
+                mConnectedNetworkType, mConnectedNetworkTypeName));
         pw.println("  - telephony ------");
         pw.print("  hasService()=");
         pw.println(hasService());
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 08a042a..7898342 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -9383,7 +9383,7 @@
 
     @Override
     public UserInfo getUser(int userId) {
-        enforceSystemOrRoot("Only the system can remove users");
+        enforceSystemOrRoot("Only the system can query user");
         return sUserManager.getUser(userId);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
index 8579535..3e7f722 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java
@@ -48,13 +48,14 @@
     }
 
     public CommandDetails(Parcel in) {
-        compRequired = true;
+        compRequired = in.readInt() != 0;
         commandNumber = in.readInt();
         typeOfCommand = in.readInt();
         commandQualifier = in.readInt();
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(compRequired ? 1 : 0);
         dest.writeInt(commandNumber);
         dest.writeInt(typeOfCommand);
         dest.writeInt(commandQualifier);
@@ -111,4 +112,4 @@
     ComprehensionTlvTag getTag() {
         return ComprehensionTlvTag.ITEM_ICON_ID_LIST;
     }
-}
\ No newline at end of file
+}