Merge change 1110 into donut

* changes:
  Track install/removal/update of packages that provide backup services
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index cd44277..8fc2447 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -601,11 +601,11 @@
         CharSequence text = null;
         
         // optionally show one or the other.
-        if (mSearchable.mBadgeIcon) {
+        if (mSearchable.useBadgeIcon()) {
             icon = mActivityContext.getResources().getDrawable(mSearchable.getIconId());
             visibility = View.VISIBLE;
             if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
-        } else if (mSearchable.mBadgeLabel) {
+        } else if (mSearchable.useBadgeLabel()) {
             text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
             visibility = View.VISIBLE;
             if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
@@ -714,8 +714,8 @@
         // if it's an action specified by the searchable activity, launch the
         // entered query with the action key
         SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
-        if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
-            launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
+        if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+            launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
             return true;
         }
         
@@ -830,7 +830,7 @@
         // in the voice search system.   We have to keep the bundle separate,
         // because it becomes immutable once it enters the PendingIntent
         Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
-        queryIntent.setComponent(mSearchable.mSearchActivity);
+        queryIntent.setComponent(mSearchable.getSearchActivity());
         PendingIntent pending = PendingIntent.getActivity(
                 getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
         
@@ -913,8 +913,8 @@
                 }
                 if (event.getAction() == KeyEvent.ACTION_DOWN) {
                     SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
-                    if ((actionKey != null) && (actionKey.mQueryActionMsg != null)) {
-                        launchQuerySearch(keyCode, actionKey.mQueryActionMsg);
+                    if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+                        launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
                         return true;
                     }
                 }
@@ -1010,8 +1010,8 @@
             // Next, check for an "action key"
             SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
             if ((actionKey != null) && 
-                    ((actionKey.mSuggestActionMsg != null) || 
-                     (actionKey.mSuggestActionMsgColumn != null))) {
+                    ((actionKey.getSuggestActionMsg() != null) || 
+                     (actionKey.getSuggestActionMsgColumn() != null))) {
                 // launch suggestion using action key column
                 int position = mSearchAutoComplete.getListSelection();
                 if (position != ListView.INVALID_POSITION) {
@@ -1292,7 +1292,7 @@
             intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
         }
         // attempt to enforce security requirement (no 3rd-party intents)
-        intent.setComponent(mSearchable.mSearchActivity);
+        intent.setComponent(mSearchable.getSearchActivity());
         return intent;
     }
     
@@ -1308,14 +1308,14 @@
     private static String getActionKeyMessage(Cursor c, SearchableInfo.ActionKeyInfo actionKey) {
         String result = null;
         // check first in the cursor data, for a suggestion-specific message
-        final String column = actionKey.mSuggestActionMsgColumn;
+        final String column = actionKey.getSuggestActionMsgColumn();
         if (column != null) {
             result = SuggestionsAdapter.getColumnString(c, column);
         }
         // If the cursor didn't give us a message, see if there's a single message defined
         // for the actionkey (for all suggestions)
         if (result == null) {
-            result = actionKey.mSuggestActionMsg;
+            result = actionKey.getSuggestActionMsg();
         }
         return result;
     }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 39f3bcd1..2d2f98a 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1602,7 +1602,7 @@
     public static boolean isDefaultSearchable(SearchableInfo searchable) {
         SearchableInfo defaultSearchable = SearchManager.getSearchableInfo(null, true);
         return defaultSearchable != null 
-                && defaultSearchable.mSearchActivity.equals(searchable.mSearchActivity);
+                && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity());
     }
     
     /**
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index af2a321..6a02fc9 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -231,14 +231,14 @@
             return query;
         }
         
-        if (mSearchable.mQueryRewriteFromData) {
+        if (mSearchable.shouldRewriteQueryFromData()) {
             String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
             if (data != null) {
                 return data;
             }
         }
         
-        if (mSearchable.mQueryRewriteFromText) {
+        if (mSearchable.shouldRewriteQueryFromText()) {
             String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
             if (text1 != null) {
                 return text1;
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index aad7ae2..842fc75 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -35,14 +35,13 @@
 import android.view.inputmethod.EditorInfo;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 public final class SearchableInfo implements Parcelable {
 
     // general debugging support
-    final static String LOG_TAG = "SearchableInfo";
-    
-    // set this flag to 1 to prevent any apps from providing suggestions
-    final static int DBG_INHIBIT_SUGGESTIONS = 0;
+    private static final boolean DBG = true;
+    private static final String LOG_TAG = "SearchableInfo";
 
     // static strings used for XML lookups.
     // TODO how should these be documented for the developer, in a more structured way than 
@@ -51,40 +50,44 @@
     private static final String MD_XML_ELEMENT_SEARCHABLE = "searchable";
     private static final String MD_XML_ELEMENT_SEARCHABLE_ACTION_KEY = "actionkey";
     
+    // flags in the searchMode attribute
+    private static final int SEARCH_MODE_BADGE_LABEL = 0x04;
+    private static final int SEARCH_MODE_BADGE_ICON = 0x08;
+    private static final int SEARCH_MODE_QUERY_REWRITE_FROM_DATA = 0x10;
+    private static final int SEARCH_MODE_QUERY_REWRITE_FROM_TEXT = 0x20;
+    
     // true member variables - what we know about the searchability
-    // TO-DO replace public with getters
-    public boolean mSearchable = false;
-    private int mLabelId = 0;
-    public ComponentName mSearchActivity = null;
-    private int mHintId = 0;
-    private int mSearchMode = 0;
-    public boolean mBadgeLabel = false;
-    public boolean mBadgeIcon = false;
-    public boolean mQueryRewriteFromData = false;
-    public boolean mQueryRewriteFromText = false;
-    private int mIconId = 0;
-    private int mSearchButtonText = 0;
-    private int mSearchInputType = 0;
-    private int mSearchImeOptions = 0;
-    private boolean mIncludeInGlobalSearch = false;
-    private String mSuggestAuthority = null;
-    private String mSuggestPath = null;
-    private String mSuggestSelection = null;
-    private String mSuggestIntentAction = null;
-    private String mSuggestIntentData = null;
-    private int mSuggestThreshold = 0;
-    private ActionKeyInfo mActionKeyList = null;
-    private String mSuggestProviderPackage = null;
+    private final int mLabelId;
+    private final ComponentName mSearchActivity;
+    private final int mHintId;
+    private final int mSearchMode;
+    private final int mIconId;
+    private final int mSearchButtonText;
+    private final int mSearchInputType;
+    private final int mSearchImeOptions;
+    private final boolean mIncludeInGlobalSearch;
+    private final String mSuggestAuthority;
+    private final String mSuggestPath;
+    private final String mSuggestSelection;
+    private final String mSuggestIntentAction;
+    private final String mSuggestIntentData;
+    private final int mSuggestThreshold;
+    // Maps key codes to action key information. auto-boxing is not so bad here,
+    // since keycodes for the hard keys are < 127. For such values, Integer.valueOf()
+    // uses shared Integer objects.
+    // This is not final, to allow lazy initialization.
+    private HashMap<Integer,ActionKeyInfo> mActionKeys = null;
+    private final String mSuggestProviderPackage;
     
     // Flag values for Searchable_voiceSearchMode
     private static int VOICE_SEARCH_SHOW_BUTTON = 1;
     private static int VOICE_SEARCH_LAUNCH_WEB_SEARCH = 2;
     private static int VOICE_SEARCH_LAUNCH_RECOGNIZER = 4;
-    private int mVoiceSearchMode = 0;
-    private int mVoiceLanguageModeId;       // voiceLanguageModel
-    private int mVoicePromptTextId;         // voicePromptText
-    private int mVoiceLanguageId;           // voiceLanguage
-    private int mVoiceMaxResults;           // voiceMaxResults
+    private final int mVoiceSearchMode;
+    private final int mVoiceLanguageModeId;       // voiceLanguageModel
+    private final int mVoicePromptTextId;         // voicePromptText
+    private final int mVoiceLanguageId;           // voiceLanguage
+    private final int mVoiceMaxResults;           // voiceMaxResults
 
     
     /**
@@ -95,7 +98,42 @@
     public String getSuggestAuthority() {
         return mSuggestAuthority;
     }
-    
+
+    /**
+     * Gets the component name of the searchable activity.
+     */
+    public ComponentName getSearchActivity() {
+        return mSearchActivity;
+    }
+
+    /**
+     * Checks whether the badge should be a text label.
+     */
+    public boolean useBadgeLabel() {
+        return 0 != (mSearchMode & SEARCH_MODE_BADGE_LABEL);
+    }
+
+    /**
+     * Checks whether the badge should be an icon.
+     */
+    public boolean useBadgeIcon() {
+        return (0 != (mSearchMode & SEARCH_MODE_BADGE_ICON)) && (mIconId != 0);
+    }
+
+    /**
+     * Checks whether the text in the query field should come from the suggestion intent data.
+     */
+    public boolean shouldRewriteQueryFromData() {
+        return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_DATA);
+    }
+
+    /**
+     * Checks whether the text in the query field should come from the suggestion title.
+     */
+    public boolean shouldRewriteQueryFromText() {
+        return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT);
+    }
+
     /**
      * Retrieve the path for obtaining search suggestions.
      * 
@@ -218,10 +256,9 @@
      * @param attr The attribute set we found in the XML file, contains the values that are used to
      * construct the object.
      * @param cName The component name of the searchable activity
+     * @throws IllegalArgumentException if the searchability info is invalid or insufficient
      */
     private SearchableInfo(Context activityContext, AttributeSet attr, final ComponentName cName) {
-        // initialize as an "unsearchable" object
-        mSearchable = false;
         mSearchActivity = cName;
         
         TypedArray a = activityContext.obtainStyledAttributes(attr,
@@ -240,21 +277,19 @@
         mIncludeInGlobalSearch = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false);
 
-        setSearchModeFlags();
-        if (DBG_INHIBIT_SUGGESTIONS == 0) {
-            mSuggestAuthority = a.getString(
-                    com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
-            mSuggestPath = a.getString(
-                    com.android.internal.R.styleable.Searchable_searchSuggestPath);
-            mSuggestSelection = a.getString(
-                    com.android.internal.R.styleable.Searchable_searchSuggestSelection);
-            mSuggestIntentAction = a.getString(
-                    com.android.internal.R.styleable.Searchable_searchSuggestIntentAction);
-            mSuggestIntentData = a.getString(
-                    com.android.internal.R.styleable.Searchable_searchSuggestIntentData);
-            mSuggestThreshold = a.getInt(
-                    com.android.internal.R.styleable.Searchable_searchSuggestThreshold, 0);
-        }
+        mSuggestAuthority = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
+        mSuggestPath = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSuggestPath);
+        mSuggestSelection = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSuggestSelection);
+        mSuggestIntentAction = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSuggestIntentAction);
+        mSuggestIntentData = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSuggestIntentData);
+        mSuggestThreshold = a.getInt(
+                com.android.internal.R.styleable.Searchable_searchSuggestThreshold, 0);
+
         mVoiceSearchMode = 
             a.getInt(com.android.internal.R.styleable.Searchable_voiceSearchMode, 0);
         // TODO this didn't work - came back zero from YouTube
@@ -270,44 +305,31 @@
         a.recycle();
 
         // get package info for suggestions provider (if any)
+        String suggestProviderPackage = null;
         if (mSuggestAuthority != null) {
             PackageManager pm = activityContext.getPackageManager();
             ProviderInfo pi = pm.resolveContentProvider(mSuggestAuthority, 0);
             if (pi != null) {
-                mSuggestProviderPackage = pi.packageName;
+                suggestProviderPackage = pi.packageName;
             }
         }
+        mSuggestProviderPackage = suggestProviderPackage;
 
         // for now, implement some form of rules - minimal data
-        if (mLabelId != 0) {
-            mSearchable = true;
-        } else {
-            // Provide some help for developers instead of just silently discarding
-            Log.w(LOG_TAG, "Insufficient metadata to configure searchability for " + 
-                    cName.flattenToShortString());
+        if (mLabelId == 0) {
+            throw new IllegalArgumentException("No label.");
         }
     }
-
-    /**
-     * Convert searchmode to flags.
-     */
-    private void setSearchModeFlags() {
-        mBadgeLabel = (0 != (mSearchMode & 4));
-        mBadgeIcon = (0 != (mSearchMode & 8)) && (mIconId != 0);
-        mQueryRewriteFromData = (0 != (mSearchMode & 0x10));
-        mQueryRewriteFromText = (0 != (mSearchMode & 0x20));
-    }
     
     /**
      * Private class used to hold the "action key" configuration
      */
     public static class ActionKeyInfo implements Parcelable {
         
-        public int mKeyCode = 0;
-        public String mQueryActionMsg;
-        public String mSuggestActionMsg;
-        public String mSuggestActionMsgColumn;
-        private ActionKeyInfo mNext;
+        private final int mKeyCode;
+        private final String mQueryActionMsg;
+        private final String mSuggestActionMsg;
+        private final String mSuggestActionMsgColumn;
         
         /**
          * Create one object using attributeset as input data.
@@ -315,10 +337,9 @@
          *        is about.
          * @param attr The attribute set we found in the XML file, contains the values that are used to
          * construct the object.
-         * @param next We'll build these up using a simple linked list (since there are usually
-         * just zero or one).
+         * @throws IllegalArgumentException if the action key configuration is invalid
          */
-        public ActionKeyInfo(Context activityContext, AttributeSet attr, ActionKeyInfo next) {
+        public ActionKeyInfo(Context activityContext, AttributeSet attr) {
             TypedArray a = activityContext.obtainStyledAttributes(attr,
                     com.android.internal.R.styleable.SearchableActionKey);
 
@@ -326,23 +347,20 @@
                     com.android.internal.R.styleable.SearchableActionKey_keycode, 0);
             mQueryActionMsg = a.getString(
                     com.android.internal.R.styleable.SearchableActionKey_queryActionMsg);
-            if (DBG_INHIBIT_SUGGESTIONS == 0) {
-                mSuggestActionMsg = a.getString(
-                        com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg);
-                mSuggestActionMsgColumn = a.getString(
-                        com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn);
-            }
+            mSuggestActionMsg = a.getString(
+                    com.android.internal.R.styleable.SearchableActionKey_suggestActionMsg);
+            mSuggestActionMsgColumn = a.getString(
+                    com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn);
             a.recycle();
 
-            // initialize any other fields
-            mNext = next;
-
-            // sanity check.  must have at least one action message, or invalidate the object.
-            if ((mQueryActionMsg == null) && 
+            // sanity check.
+            if (mKeyCode == 0) {
+                throw new IllegalArgumentException("No keycode.");
+            } else if ((mQueryActionMsg == null) && 
                     (mSuggestActionMsg == null) && 
                     (mSuggestActionMsgColumn == null)) {
-                mKeyCode = 0;
-            }           
+                throw new IllegalArgumentException("No message information.");
+            }
         }
 
         /**
@@ -351,14 +369,28 @@
          *
          * @param in The Parcel containing the previously written ActionKeyInfo,
          * positioned at the location in the buffer where it was written.
-         * @param next The value to place in mNext, creating a linked list
          */
-        public ActionKeyInfo(Parcel in, ActionKeyInfo next) {
+        public ActionKeyInfo(Parcel in) {
             mKeyCode = in.readInt();
             mQueryActionMsg = in.readString();
             mSuggestActionMsg = in.readString();
             mSuggestActionMsgColumn = in.readString();
-            mNext = next;
+        }
+
+        public int getKeyCode() {
+            return mKeyCode;
+        }
+
+        public String getQueryActionMsg() {
+            return mQueryActionMsg;
+        }
+
+        public String getSuggestActionMsg() {
+            return mSuggestActionMsg;
+        }
+
+        public String getSuggestActionMsgColumn() {
+            return mSuggestActionMsgColumn;
         }
 
         public int describeContents() {
@@ -380,16 +412,19 @@
      * @return Returns the ActionKeyInfo record, or null if none defined
      */
     public ActionKeyInfo findActionKey(int keyCode) {
-        ActionKeyInfo info = mActionKeyList;
-        while (info != null) {
-            if (info.mKeyCode == keyCode) {
-                return info;
-            }
-            info = info.mNext;
+        if (mActionKeys == null) {
+            return null;
         }
-        return null;
+        return mActionKeys.get(keyCode);
     }
-    
+
+    private void addActionKey(ActionKeyInfo keyInfo) {
+        if (mActionKeys == null) {
+            mActionKeys = new HashMap<Integer,ActionKeyInfo>();
+        }
+        mActionKeys.put(keyInfo.getKeyCode(), keyInfo);
+    }
+
     public static SearchableInfo getActivityMetaData(Context context, ActivityInfo activityInfo) {
         // for each component, try to find metadata
         XmlResourceParser xml = 
@@ -401,14 +436,22 @@
         
         SearchableInfo searchable = getActivityMetaData(context, xml, cName);
         xml.close();
+        
+        if (DBG) {
+            Log.d(LOG_TAG, "Checked " + activityInfo.name
+                    + ",label=" + searchable.getLabelId()
+                    + ",icon=" + searchable.getIconId()
+                    + ",suggestAuthority=" + searchable.getSuggestAuthority()
+                    + ",target=" + searchable.getSearchActivity().getClassName()
+                    + ",global=" + searchable.shouldIncludeInGlobalSearch()
+                    + ",threshold=" + searchable.getSuggestThreshold());
+        }
         return searchable;
     }
     
     /**
      * Get the metadata for a given activity
      * 
-     * TODO: clean up where we return null vs. where we throw exceptions.
-     * 
      * @param context runtime context
      * @param xml XML parser for reading attributes
      * @param cName The component name of the searchable activity
@@ -429,9 +472,11 @@
                     if (xml.getName().equals(MD_XML_ELEMENT_SEARCHABLE)) {
                         AttributeSet attr = Xml.asAttributeSet(xml);
                         if (attr != null) {
-                            result = new SearchableInfo(activityContext, attr, cName);
-                            // if the constructor returned a bad object, exit now.
-                            if (! result.mSearchable) {
+                            try {
+                                result = new SearchableInfo(activityContext, attr, cName);
+                            } catch (IllegalArgumentException ex) {
+                                Log.w(LOG_TAG, "Invalid searchable metadata for " +
+                                        cName.flattenToShortString() + ": " + ex.getMessage());
                                 return null;
                             }
                         }
@@ -442,11 +487,12 @@
                         }
                         AttributeSet attr = Xml.asAttributeSet(xml);
                         if (attr != null) {
-                            ActionKeyInfo keyInfo = new ActionKeyInfo(activityContext, attr, 
-                                    result.mActionKeyList);
-                            // only add to list if it is was useable
-                            if (keyInfo.mKeyCode != 0) {
-                                result.mActionKeyList = keyInfo;
+                            try {
+                                result.addActionKey(new ActionKeyInfo(activityContext, attr));
+                            } catch (IllegalArgumentException ex) {
+                                Log.w(LOG_TAG, "Invalid action key for " +
+                                        cName.flattenToShortString() + ": " + ex.getMessage());
+                                return null;
                             }
                         }
                     }
@@ -454,14 +500,16 @@
                 tagType = xml.next();
             }
         } catch (XmlPullParserException e) {
-            throw new RuntimeException(e);
+            Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
+            return null;
         } catch (IOException e) {
-            throw new RuntimeException(e);
+            Log.w(LOG_TAG, "Reading searchable metadata for " + cName.flattenToShortString(), e);
+            return null;
         }
         
         return result;
     }
-    
+
     /**
      * Return the "label" (user-visible name) of this searchable context.  This must be 
      * accessed using the target (searchable) Activity's resources, not simply the context of the
@@ -610,7 +658,6 @@
      * positioned at the location in the buffer where it was written.
      */
     public SearchableInfo(Parcel in) {
-        mSearchable = in.readInt() != 0;
         mLabelId = in.readInt();
         mSearchActivity = ComponentName.readFromParcel(in);
         mHintId = in.readInt();
@@ -620,7 +667,6 @@
         mSearchInputType = in.readInt();
         mSearchImeOptions = in.readInt();
         mIncludeInGlobalSearch = in.readInt() != 0;
-        setSearchModeFlags();
 
         mSuggestAuthority = in.readString();
         mSuggestPath = in.readString();
@@ -629,12 +675,10 @@
         mSuggestIntentData = in.readString();
         mSuggestThreshold = in.readInt();
 
-        mActionKeyList = null;
-        int count = in.readInt();
-        while (count-- > 0) {
-            mActionKeyList = new ActionKeyInfo(in, mActionKeyList);
+        for (int count = in.readInt(); count > 0; count--) {
+            addActionKey(new ActionKeyInfo(in));
         }
-        
+
         mSuggestProviderPackage = in.readString();
         
         mVoiceSearchMode = in.readInt();
@@ -649,7 +693,6 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSearchable ? 1 : 0);
         dest.writeInt(mLabelId);
         mSearchActivity.writeToParcel(dest, flags);
         dest.writeInt(mHintId);
@@ -667,20 +710,15 @@
         dest.writeString(mSuggestIntentData);
         dest.writeInt(mSuggestThreshold);
 
-        // This is usually a very short linked list so we'll just pre-count it
-        ActionKeyInfo nextKeyInfo = mActionKeyList;
-        int count = 0;
-        while (nextKeyInfo != null) {
-            ++count;
-            nextKeyInfo = nextKeyInfo.mNext;
+        if (mActionKeys == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(mActionKeys.size());
+            for (ActionKeyInfo actionKey : mActionKeys.values()) {
+                actionKey.writeToParcel(dest, flags);
+            }
         }
-        dest.writeInt(count);
-        // Now write count of 'em
-        nextKeyInfo = mActionKeyList;
-        while (count-- > 0) {
-            nextKeyInfo.writeToParcel(dest, flags);
-        }
-        
+
         dest.writeString(mSuggestProviderPackage);
 
         dest.writeInt(mVoiceSearchMode);
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index 40d7449..9586d56 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -210,7 +210,7 @@
                 SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
                 if (searchable != null) {
                     newSearchablesList.add(searchable);
-                    newSearchablesMap.put(searchable.mSearchActivity, searchable);
+                    newSearchablesMap.put(searchable.getSearchActivity(), searchable);
                     if (searchable.shouldIncludeInGlobalSearch()) {
                         newSearchablesInGlobalSearchList.add(searchable);
                     }
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 20c54fb..1ff0f7e 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -490,7 +490,7 @@
         if (null == language) {
             return; // no match possible
         }
-        language.toLowerCase();
+        language = language.toLowerCase();
         if (null == country) {
             country = "";
         }
@@ -503,13 +503,12 @@
                 final int N = locales.length;
                 String bestMatch = null;
                 for(int i = 0; i < N; i++) {
-                    if (locales[i]!=null && locales[i].length() >= 2 &&
+                    // only match full (lang + country) locales
+                    if (locales[i]!=null && locales[i].length() >= 5 &&
                             locales[i].substring(0,2).equals(language)) {
-                        if (locales[i].length() >= 5) {
-                            if (locales[i].substring(3,5).equals(country)) {
-                                bestMatch = locales[i];
-                                break;
-                            }
+                        if (locales[i].substring(3,5).equals(country)) {
+                            bestMatch = locales[i];
+                            break;
                         } else if (null == bestMatch) {
                             bestMatch = locales[i];
                         }
@@ -518,12 +517,8 @@
                 if (null != bestMatch) {
                     IActivityManager am = ActivityManagerNative.getDefault();
                     Configuration config = am.getConfiguration();
-                    if (bestMatch.length() >= 5) {
-                        config.locale = new Locale(bestMatch.substring(0,2),
-                                                   bestMatch.substring(3,5));
-                    } else {
-                        config.locale = new Locale(bestMatch.substring(0,2));
-                    }
+                    config.locale = new Locale(bestMatch.substring(0,2),
+                                               bestMatch.substring(3,5));
                     config.userSetLocale = true;
                     am.updateConfiguration(config);
                 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
index 743c979..bdf67ba 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
@@ -87,8 +87,7 @@
 
         SearchableInfo si = searchables.getSearchableInfo(thisActivity);
         assertNotNull(si);
-        assertTrue(si.mSearchable);
-        assertEquals(thisActivity, si.mSearchActivity);
+        assertEquals(thisActivity, si.getSearchActivity());
         
         Context appContext = si.getActivityContext(mContext);
         assertNotNull(appContext);
@@ -194,10 +193,9 @@
     
     private void checkSearchable(SearchableInfo si) {
         assertNotNull(si);
-        assertTrue(si.mSearchable);
         assertTrue(si.getLabelId() != 0);        // This must be a useable string
-        assertNotEmpty(si.mSearchActivity.getClassName());
-        assertNotEmpty(si.mSearchActivity.getPackageName());
+        assertNotEmpty(si.getSearchActivity().getClassName());
+        assertNotEmpty(si.getSearchActivity().getPackageName());
         if (si.getSuggestAuthority() != null) {
             // The suggestion fields are largely optional, so we'll just confirm basic health
             assertNotEmpty(si.getSuggestAuthority());
@@ -210,12 +208,12 @@
         /* For now, test the most common action key (CALL) */
         ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
         if (ai != null) {
-            assertEquals(ai.mKeyCode, KeyEvent.KEYCODE_CALL);
+            assertEquals(ai.getKeyCode(), KeyEvent.KEYCODE_CALL);
             // one of these three fields must be non-null & non-empty
-            boolean m1 = (ai.mQueryActionMsg != null) && (ai.mQueryActionMsg.length() > 0);
-            boolean m2 = (ai.mSuggestActionMsg != null) && (ai.mSuggestActionMsg.length() > 0);
-            boolean m3 = (ai.mSuggestActionMsgColumn != null) && 
-                            (ai.mSuggestActionMsgColumn.length() > 0);
+            boolean m1 = (ai.getQueryActionMsg() != null) && (ai.getQueryActionMsg().length() > 0);
+            boolean m2 = (ai.getSuggestActionMsg() != null) && (ai.getSuggestActionMsg().length() > 0);
+            boolean m3 = (ai.getSuggestActionMsgColumn() != null) && 
+                            (ai.getSuggestActionMsgColumn().length() > 0);
             assertTrue(m1 || m2 || m3);
         }