am 7ab66973: Merge "DO NOT MERGE Show user dialog for BT." into gingerbread

* commit '7ab66973a973b9d23c411bf1c6631fa93c4903bf':
  DO NOT MERGE Show user dialog for BT.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4a1bfc3..48f2a74 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -207,6 +207,7 @@
         </activity>
 
         <activity android:name=".vpn.VpnSettings"
+                android:label="@string/vpn_settings_title"
                 android:configChanges="orientation|keyboardHidden"
                 android:clearTaskOnLaunch="true">
             <intent-filter>
@@ -214,6 +215,7 @@
                 <action android:name="android.net.vpn.SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
+                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
         </activity>
 
@@ -343,7 +345,8 @@
 
         <activity android:name="SettingsLicenseActivity"
                 android:label="@string/settings_license_activity_title"
-                android:theme="@*android:style/Theme.Dialog.Alert">
+                android:theme="@*android:style/Theme.Dialog.Alert"
+                android:configChanges="orientation">
             <intent-filter>
                 <action android:name="android.settings.LICENSE" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/res/layout/bluetooth_pin_entry.xml b/res/layout/bluetooth_pin_entry.xml
index 16b75fc..b0c1216 100644
--- a/res/layout/bluetooth_pin_entry.xml
+++ b/res/layout/bluetooth_pin_entry.xml
@@ -44,8 +44,9 @@
             android:layout_marginTop="20dip"
             android:layout_marginLeft="20dip"
             android:layout_marginRight="20dip"
+            android:inputType="textPassword"
             android:singleLine="true" />
 
     </LinearLayout>
 
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4a7aa66..44246dc 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -177,6 +177,8 @@
     <string name="battery_info_health_over_voltage">Over voltage</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_health_unspecified_failure">Unknown error</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_health_cold">Cold</string>
 
     <!-- Used as setting title (for checkbox) on second screen after selecting Bluetooth settings -->
     <string name="bluetooth">Bluetooth</string>
diff --git a/res/xml/apn_editor.xml b/res/xml/apn_editor.xml
index 287a6f8..f000dd0 100644
--- a/res/xml/apn_editor.xml
+++ b/res/xml/apn_editor.xml
@@ -22,7 +22,7 @@
         android:dialogTitle="@string/apn_name"
         android:key="apn_name"
         android:singleLine="true"
-        android:inputType="textEmailAddress"
+        android:inputType="text"
         />
     <EditTextPreference
         android:title="@string/apn_apn"
diff --git a/res/xml/settings.xml b/res/xml/settings.xml
index 8c92870..6ddbf7c 100644
--- a/res/xml/settings.xml
+++ b/res/xml/settings.xml
@@ -20,6 +20,20 @@
     android:title="@string/settings_label"
     android:key="parent">
 
+        <!-- Operator hook -->
+
+        <com.android.settings.IconPreferenceScreen
+            android:key="operator_settings">
+            <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" />
+        </com.android.settings.IconPreferenceScreen>
+
+        <!-- Manufacturer hook -->
+
+        <com.android.settings.IconPreferenceScreen
+            android:key="manufacturer_settings">
+            <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
+        </com.android.settings.IconPreferenceScreen>
+
         <com.android.settings.IconPreferenceScreen
             android:title="@string/radio_controls_title"
             settings:icon="@drawable/ic_settings_wireless">
diff --git a/res/xml/sim_lock_settings.xml b/res/xml/sim_lock_settings.xml
index b2ecb11..8c3a0a0 100644
--- a/res/xml/sim_lock_settings.xml
+++ b/res/xml/sim_lock_settings.xml
@@ -25,6 +25,7 @@
             android:summaryOff="@string/sim_lock_off"/>
     <com.android.settings.EditPinPreference
             android:key="sim_pin"
+            android:dependency="sim_toggle"
             android:title="@string/sim_pin_change"
             android:dialogTitle="@string/sim_change_pin"
             android:dialogMessage="@string/sim_enter_old"
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index 4ac7401..0dbc99f 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -17,6 +17,7 @@
 package com.android.settings;
 
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Intent;
@@ -53,6 +54,7 @@
     private static final int MENU_DELETE = Menu.FIRST;
     private static final int MENU_SAVE = Menu.FIRST + 1;
     private static final int MENU_CANCEL = Menu.FIRST + 2;
+    private static final int ERROR_DIALOG_ID = 0;
 
     private static String sNotSet;
     private EditTextPreference mName;
@@ -240,6 +242,8 @@
             int authVal = mCursor.getInt(AUTH_TYPE_INDEX);
             if (authVal != -1) {
                 mAuthType.setValueIndex(authVal);
+            } else {
+                mAuthType.setValue(null);
             }
 
             mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
@@ -388,19 +392,8 @@
         String mcc = checkNotSet(mMcc.getText());
         String mnc = checkNotSet(mMnc.getText());
 
-        String errorMsg = null;
-        if (name.length() < 1) {
-            errorMsg = mRes.getString(R.string.error_name_empty);
-        } else if (apn.length() < 1) {
-            errorMsg = mRes.getString(R.string.error_apn_empty);
-        } else if (mcc.length() != 3) {
-            errorMsg = mRes.getString(R.string.error_mcc_not3);
-        } else if ((mnc.length() & 0xFFFE) != 2) {
-            errorMsg = mRes.getString(R.string.error_mnc_not23);
-        }
-
-        if (errorMsg != null && !force) {
-            showErrorMessage(errorMsg);
+        if (getErrorMsg() != null && !force) {
+            showDialog(ERROR_DIALOG_ID);
             return false;
         }
 
@@ -462,12 +455,54 @@
         return true;
     }
 
-    private void showErrorMessage(String message) {
-        new AlertDialog.Builder(this)
-            .setTitle(R.string.error_title)
-            .setMessage(message)
-            .setPositiveButton(android.R.string.ok, null)
-            .show();
+    private String getErrorMsg() {
+        String errorMsg = null;
+
+        String name = checkNotSet(mName.getText());
+        String apn = checkNotSet(mApn.getText());
+        String mcc = checkNotSet(mMcc.getText());
+        String mnc = checkNotSet(mMnc.getText());
+
+        if (name.length() < 1) {
+            errorMsg = mRes.getString(R.string.error_name_empty);
+        } else if (apn.length() < 1) {
+            errorMsg = mRes.getString(R.string.error_apn_empty);
+        } else if (mcc.length() != 3) {
+            errorMsg = mRes.getString(R.string.error_mcc_not3);
+        } else if ((mnc.length() & 0xFFFE) != 2) {
+            errorMsg = mRes.getString(R.string.error_mnc_not23);
+        }
+
+        return errorMsg;
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+
+        if (id == ERROR_DIALOG_ID) {
+            String msg = getErrorMsg();
+
+            return new AlertDialog.Builder(this)
+                    .setTitle(R.string.error_title)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .setMessage(msg)
+                    .create();
+        }
+
+        return super.onCreateDialog(id);
+    }
+
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog) {
+        super.onPrepareDialog(id, dialog);
+
+        if (id == ERROR_DIALOG_ID) {
+            String msg = getErrorMsg();
+
+            if (msg != null) {
+                ((AlertDialog)dialog).setMessage(msg);
+            }
+        }
     }
 
     private void deleteApn() {
diff --git a/src/com/android/settings/BatteryInfo.java b/src/com/android/settings/BatteryInfo.java
index 1cbe47f..2f9d50e 100644
--- a/src/com/android/settings/BatteryInfo.java
+++ b/src/com/android/settings/BatteryInfo.java
@@ -142,6 +142,8 @@
                     healthString = getString(R.string.battery_info_health_over_voltage);
                 } else if (health == BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
                     healthString = getString(R.string.battery_info_health_unspecified_failure);
+                } else if (health == BatteryManager.BATTERY_HEALTH_COLD) {
+                    healthString = getString(R.string.battery_info_health_cold);
                 } else {
                     healthString = getString(R.string.battery_info_health_unknown);
                 }
diff --git a/src/com/android/settings/DateTimeSettingsSetupWizard.java b/src/com/android/settings/DateTimeSettingsSetupWizard.java
index 8dd970b..a6a60c1 100644
--- a/src/com/android/settings/DateTimeSettingsSetupWizard.java
+++ b/src/com/android/settings/DateTimeSettingsSetupWizard.java
@@ -20,7 +20,6 @@
 import android.view.View;
 import android.view.Window;
 import android.view.View.OnClickListener;
-import android.widget.LinearLayout;
 
 public class DateTimeSettingsSetupWizard extends DateTimeSettings implements OnClickListener {
     private View mNextButton;
diff --git a/src/com/android/settings/IconPreferenceScreen.java b/src/com/android/settings/IconPreferenceScreen.java
index c7c5303..31abf0a 100644
--- a/src/com/android/settings/IconPreferenceScreen.java
+++ b/src/com/android/settings/IconPreferenceScreen.java
@@ -22,6 +22,7 @@
 import android.preference.Preference;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 
 public class IconPreferenceScreen extends Preference {
@@ -48,4 +49,26 @@
             imageView.setImageDrawable(mIcon);
         }
     }
+
+    /**
+     * Sets the icon for this Preference with a Drawable.
+     *
+     * @param icon The icon for this Preference
+     */
+    public void setIcon(Drawable icon) {
+        if ((icon == null && mIcon != null) || (icon != null && !icon.equals(mIcon))) {
+            mIcon = icon;
+            notifyChanged();
+        }
+    }
+
+    /**
+     * Returns the icon of this Preference.
+     *
+     * @return The icon.
+     * @see #setIcon(Drawable)
+     */
+    public Drawable getIcon() {
+        return mIcon;
+    }
 }
diff --git a/src/com/android/settings/LanguageSettings.java b/src/com/android/settings/LanguageSettings.java
index 8ab8a0c..50c78b6 100644
--- a/src/com/android/settings/LanguageSettings.java
+++ b/src/com/android/settings/LanguageSettings.java
@@ -57,6 +57,8 @@
     
     private String mLastInputMethodId;
     private String mLastTickedInputMethodId;
+
+    private AlertDialog mDialog = null;
     
     static public String getInputMethodIdFromKey(String key) {
         return key;
@@ -258,29 +260,35 @@
                 if (selImi == null) {
                     return super.onPreferenceTreeClick(preferenceScreen, preference);
                 }
-                AlertDialog d = (new AlertDialog.Builder(this))
-                        .setTitle(android.R.string.dialog_alert_title)
-                        .setIcon(android.R.drawable.ic_dialog_alert)
-                        .setMessage(getString(R.string.ime_security_warning,
-                                selImi.getServiceInfo().applicationInfo.loadLabel(
-                                        getPackageManager())))
-                        .setCancelable(true)
-                        .setPositiveButton(android.R.string.ok,
-                                new DialogInterface.OnClickListener() {
-                                    public void onClick(DialogInterface dialog, int which) {
-                                        chkPref.setChecked(true);
-                                        mLastTickedInputMethodId = id;
-                                    }
-                            
-                        })
-                        .setNegativeButton(android.R.string.cancel,
-                                new DialogInterface.OnClickListener() {
-                                    public void onClick(DialogInterface dialog, int which) {
-                                    }
-                            
-                        })
-                        .create();
-                d.show();
+                if (mDialog == null) {
+                    mDialog = (new AlertDialog.Builder(this))
+                            .setTitle(android.R.string.dialog_alert_title)
+                            .setIcon(android.R.drawable.ic_dialog_alert)
+                            .setCancelable(true)
+                            .setPositiveButton(android.R.string.ok,
+                                    new DialogInterface.OnClickListener() {
+                                        public void onClick(DialogInterface dialog, int which) {
+                                            chkPref.setChecked(true);
+                                            mLastTickedInputMethodId = id;
+                                        }
+
+                            })
+                            .setNegativeButton(android.R.string.cancel,
+                                    new DialogInterface.OnClickListener() {
+                                        public void onClick(DialogInterface dialog, int which) {
+                                        }
+
+                            })
+                            .create();
+                } else {
+                    if (mDialog.isShowing()) {
+                        mDialog.dismiss();
+                    }
+                }
+                mDialog.setMessage(getString(R.string.ime_security_warning,
+                        selImi.getServiceInfo().applicationInfo.loadLabel(
+                                getPackageManager())));
+                mDialog.show();
             } else if (id.equals(mLastTickedInputMethodId)) {
                 mLastTickedInputMethodId = null;
             }
@@ -305,4 +313,13 @@
         return super.onPreferenceTreeClick(preferenceScreen, preference);
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+    }
+
 }
diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java
index 15810b3..f611137 100644
--- a/src/com/android/settings/ProgressCategory.java
+++ b/src/com/android/settings/ProgressCategory.java
@@ -26,7 +26,8 @@
 public class ProgressCategory extends PreferenceCategory {
 
     private boolean mProgress = false;
-    
+    private View oldView = null;
+
     public ProgressCategory(Context context, AttributeSet attrs) {
         super(context, attrs);
         setLayoutResource(R.layout.preference_progress_category);
@@ -41,6 +42,13 @@
         int visibility = mProgress ? View.VISIBLE : View.INVISIBLE;
         textView.setVisibility(visibility);
         progressBar.setVisibility(visibility);
+
+        if (oldView != null) {
+            oldView.findViewById(R.id.scanning_progress).setVisibility(View.GONE);
+            oldView.findViewById(R.id.scanning_text).setVisibility(View.GONE);
+            oldView.setVisibility(View.GONE);
+        }
+        oldView = view;
     }
     
     /**
diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java
index 80fe3c9..66c81c6 100644
--- a/src/com/android/settings/ProxySelector.java
+++ b/src/com/android/settings/ProxySelector.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.net.Proxy;
@@ -73,6 +74,7 @@
         HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
     }
 
+    private static final int ERROR_DIALOG_ID = 0;
 
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -84,13 +86,32 @@
         populateFields(false);
     }
 
-    protected void showError(int error) {
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        if (id == ERROR_DIALOG_ID) {
+            String hostname = mHostnameField.getText().toString().trim();
+            String portStr = mPortField.getText().toString().trim();
+            String msg = getString(validate(hostname, portStr));
 
-        new AlertDialog.Builder(this)
-                .setTitle(R.string.proxy_error)
-                .setMessage(error)
-                .setPositiveButton(R.string.proxy_error_dismiss, null)
-                .show();
+            return new AlertDialog.Builder(this)
+                    .setTitle(R.string.proxy_error)
+                    .setPositiveButton(R.string.proxy_error_dismiss, null)
+                    .setMessage(msg)
+                    .create();
+        }
+        return super.onCreateDialog(id);
+    }
+
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog) {
+        super.onPrepareDialog(id, dialog);
+
+        if (id == ERROR_DIALOG_ID) {
+            String hostname = mHostnameField.getText().toString().trim();
+            String portStr = mPortField.getText().toString().trim();
+            String msg = getString(validate(hostname, portStr));
+            ((AlertDialog)dialog).setMessage(msg);
+        }
     }
 
     void initView() {
@@ -188,7 +209,7 @@
 
         int result = validate(hostname, portStr);
         if (result > 0) {
-            showError(result);
+            showDialog(ERROR_DIALOG_ID);
             return false;
         }
 
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index f0fcdd7..f1c30d0 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -774,15 +774,18 @@
                     sb.append("\n    to ")
                       .append(pdp.getApn().toString());
                 }
-                sb.append("\ninterface: ")
-                  .append(phone.getInterfaceName(phone.getActiveApnTypes()[0]))
-                  .append("\naddress: ")
-                  .append(phone.getIpAddress(phone.getActiveApnTypes()[0]))
-                  .append("\ngateway: ")
-                  .append(phone.getGateway(phone.getActiveApnTypes()[0]));
-                String[] dns = phone.getDnsServers(phone.getActiveApnTypes()[0]);
-                if (dns != null) {
-                    sb.append("\ndns: ").append(dns[0]).append(", ").append(dns[1]);
+                String[] activeApnTypes = phone.getActiveApnTypes();
+                if (activeApnTypes.length > 0) {
+                    sb.append("\ninterface: ")
+                      .append(phone.getInterfaceName(activeApnTypes[0]))
+                      .append("\naddress: ")
+                      .append(phone.getIpAddress(activeApnTypes[0]))
+                      .append("\ngateway: ")
+                      .append(phone.getGateway(activeApnTypes[0]));
+                    String[] dns = phone.getDnsServers(activeApnTypes[0]);
+                    if (dns != null) {
+                        sb.append("\ndns: ").append(dns[0]).append(", ").append(dns[1]);
+                    }
                 }
             } else if (dc.isInactive()) {
                 sb.append("    disconnected with last try at ")
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 05a655a..454ea60 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -20,7 +20,6 @@
 import java.util.Observable;
 import java.util.Observer;
 
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
@@ -34,18 +33,15 @@
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
-import android.preference.Preference.OnPreferenceChangeListener;
 import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
-import android.util.Log;
 import android.view.View;
 import android.widget.TextView;
 import android.widget.Toast;
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index f07892e..d924834 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -21,7 +21,6 @@
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceGroup;
-import android.telephony.TelephonyManager;
 
 public class Settings extends PreferenceActivity {
 
@@ -30,13 +29,14 @@
     private static final String KEY_SYNC_SETTINGS = "sync_settings";
     private static final String KEY_DOCK_SETTINGS = "dock_settings";
     
+    private static final String KEY_OPERATOR_SETTINGS = "operator_settings";
+    private static final String KEY_MANUFACTURER_SETTINGS = "manufacturer_settings";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         
         addPreferencesFromResource(R.xml.settings);
-        
-        int activePhoneType = TelephonyManager.getDefault().getPhoneType();
 
         PreferenceGroup parent = (PreferenceGroup) findPreference(KEY_PARENT);
         Utils.updatePreferenceToSpecificActivityOrRemove(this, parent, KEY_SYNC_SETTINGS, 0);
@@ -45,6 +45,11 @@
         if (getResources().getBoolean(R.bool.has_dock_settings) == false && dockSettings != null) {
             parent.removePreference(dockSettings);
         }
+
+        Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent,
+                KEY_OPERATOR_SETTINGS);
+        Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent,
+                KEY_MANUFACTURER_SETTINGS);
     }
     
     @Override
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
index 0b809e1..99828ce 100644
--- a/src/com/android/settings/SettingsLicenseActivity.java
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -17,6 +17,8 @@
 package com.android.settings;
 
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Config;
@@ -24,6 +26,12 @@
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.Toast;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.res.Configuration;
 
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -32,13 +40,10 @@
 import java.io.InputStreamReader;
 import java.util.zip.GZIPInputStream;
 
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-
 /**
  * The "dialog" that shows from "License" in the Settings app.
  */
-public class SettingsLicenseActivity extends AlertActivity {
+public class SettingsLicenseActivity extends Activity {
 
     private static final String TAG = "SettingsLicenseActivity";
     private static final boolean LOGV = false || Config.LOGV;
@@ -46,6 +51,83 @@
     private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
     private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
 
+    private Handler mHandler;
+    private WebView mWebView;
+    private ProgressDialog mSpinnerDlg;
+    private AlertDialog mTextDlg;
+
+    private class LicenseFileLoader implements Runnable {
+
+        private static final String INNER_TAG = "SettingsLicenseActivity.LicenseFileLoader";
+        public static final int STATUS_OK = 0;
+        public static final int STATUS_NOT_FOUND = 1;
+        public static final int STATUS_READ_ERROR = 2;
+        public static final int STATUS_EMPTY_FILE = 3;
+
+        private String mFileName;
+        private Handler mHandler;
+
+        public LicenseFileLoader(String fileName, Handler handler) {
+            mFileName = fileName;
+            mHandler = handler;
+        }
+
+        public void run() {
+
+            int status = STATUS_OK;
+
+            InputStreamReader inputReader = null;
+            StringBuilder data = new StringBuilder(2048);
+            try {
+                char[] tmp = new char[2048];
+                int numRead;
+                if (mFileName.endsWith(".gz")) {
+                    inputReader = new InputStreamReader(
+                        new GZIPInputStream(new FileInputStream(mFileName)));
+                } else {
+                    inputReader = new FileReader(mFileName);
+                }
+
+                while ((numRead = inputReader.read(tmp)) >= 0) {
+                    data.append(tmp, 0, numRead);
+                }
+            } catch (FileNotFoundException e) {
+                Log.e(INNER_TAG, "License HTML file not found at " + mFileName, e);
+                status = STATUS_NOT_FOUND;
+            } catch (IOException e) {
+                Log.e(INNER_TAG, "Error reading license HTML file at " + mFileName, e);
+                status = STATUS_READ_ERROR;
+            } finally {
+                try {
+                    if (inputReader != null) {
+                        inputReader.close();
+                    }
+                } catch (IOException e) {
+                }
+            }
+
+            if ((status == STATUS_OK) && TextUtils.isEmpty(data)) {
+                Log.e(INNER_TAG, "License HTML is empty (from " + mFileName + ")");
+                status = STATUS_EMPTY_FILE;
+            }
+
+            // Tell the UI thread that we are finished.
+            Message msg = mHandler.obtainMessage(status, null);
+            if (status == STATUS_OK) {
+                msg.obj = data.toString();
+            }
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    public SettingsLicenseActivity() {
+        super();
+        mHandler = null;
+        mWebView = null;
+        mSpinnerDlg = null;
+        mTextDlg = null;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -57,67 +139,82 @@
             return;
         }
 
-        InputStreamReader inputReader = null;
-        StringBuilder data = null;
-        try {
-            data = new StringBuilder(2048);
-            char tmp[] = new char[2048];
-            int numRead;
-            if (fileName.endsWith(".gz")) {
-                inputReader = new InputStreamReader(
-                    new GZIPInputStream(new FileInputStream(fileName)));
-            } else {
-                inputReader = new FileReader(fileName);
-            }
-            while ((numRead = inputReader.read(tmp)) >= 0) {
-                data.append(tmp, 0, numRead);
-            }
-        } catch (FileNotFoundException e) {
-            Log.e(TAG, "License HTML file not found at " + fileName, e);
-            showErrorAndFinish();
-            return;
-        } catch (IOException e) {
-            Log.e(TAG, "Error reading license HTML file at " + fileName, e);
-            showErrorAndFinish();
-            return;
-        } finally {
-            try {
-                if (inputReader != null) {
-                    inputReader.close();
-                }
-            } catch (IOException e) {
-            }
-        }
+        // The activity does not have any view itself,
+        // so set it invisible to avoid displaying the title text in the background.
+        setVisible(false);
 
-        if (TextUtils.isEmpty(data)) {
-            Log.e(TAG, "License HTML is empty (from " + fileName + ")");
-            showErrorAndFinish();
-            return;
-        }
+        mWebView = new WebView(this);
 
-        WebView webView = new WebView(this);
+        mHandler = new Handler() {
 
-        // Begin the loading.  This will be done in a separate thread in WebView.
-        webView.loadDataWithBaseURL(null, data.toString(), "text/html", "utf-8", null);
-        webView.setWebViewClient(new WebViewClient() {
             @Override
-            public void onPageFinished(WebView view, String url) {
-                // Change from 'Loading...' to the real title
-                mAlert.setTitle(getString(R.string.settings_license_activity_title));
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+
+                if (msg.what == LicenseFileLoader.STATUS_OK) {
+                    String text = (String) msg.obj;
+                    showPageOfText(text);
+                } else {
+                    showErrorAndFinish();
+                }
+            }
+        };
+
+        CharSequence title = getText(R.string.settings_license_activity_title);
+        CharSequence msg = getText(R.string.settings_license_activity_loading);
+
+        ProgressDialog pd = ProgressDialog.show(this, title, msg, true, false);
+        pd.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+        mSpinnerDlg = pd;
+
+        // Start separate thread to do the actual loading.
+        Thread thread = new Thread(new LicenseFileLoader(fileName, mHandler));
+        thread.start();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mTextDlg != null) {
+            mTextDlg.dismiss();
+        }
+    }
+
+    private void showPageOfText(String text) {
+        // Create an AlertDialog to display the WebView in.
+        AlertDialog.Builder builder = new AlertDialog.Builder(SettingsLicenseActivity.this);
+        builder.setCancelable(true)
+               .setView(mWebView)
+               .setTitle(R.string.settings_license_activity_title);
+
+        mTextDlg = builder.create();
+        mTextDlg.setOnDismissListener(new OnDismissListener() {
+
+            public void onDismiss(DialogInterface dlgi) {
+                SettingsLicenseActivity.this.finish();
             }
         });
 
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.settings_license_activity_loading);
-        p.mView = webView;
-        p.mForceInverseBackground = true;
-        setupAlert();
+        // Begin the loading.  This will be done in a separate thread in WebView.
+        mWebView.loadDataWithBaseURL(null, text, "text/html", "utf-8", null);
+        mWebView.setWebViewClient(new WebViewClient() {
+            @Override
+            public void onPageFinished(WebView view, String url) {
+                mSpinnerDlg.dismiss();
+                mSpinnerDlg = null;
+                mTextDlg.show();
+                mTextDlg = null;
+            }
+        });
+
+        mWebView = null;
     }
 
     private void showErrorAndFinish() {
+        mSpinnerDlg.dismiss();
+        mSpinnerDlg = null;
         Toast.makeText(this, R.string.settings_license_activity_unavailable, Toast.LENGTH_LONG)
                 .show();
         finish();
     }
-
 }
diff --git a/src/com/android/settings/SettingsSafetyLegalActivity.java b/src/com/android/settings/SettingsSafetyLegalActivity.java
index 0c51928..368ee1d 100644
--- a/src/com/android/settings/SettingsSafetyLegalActivity.java
+++ b/src/com/android/settings/SettingsSafetyLegalActivity.java
@@ -40,6 +40,8 @@
 
     private WebView mWebView;
 
+    private AlertDialog mErrorDialog = null;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -85,14 +87,31 @@
     }
 
     private void showErrorAndFinish(String url) {
-        new AlertDialog.Builder(this)
-                .setMessage(getResources()
-                        .getString(R.string.settings_safetylegal_activity_unreachable, url))
-                .setTitle(R.string.settings_safetylegal_activity_title)
-                .setPositiveButton(android.R.string.ok, this)
-                .setOnCancelListener(this)
-                .setCancelable(true)
-                .show();
+        if (mErrorDialog == null) {
+            mErrorDialog = new AlertDialog.Builder(this)
+                    .setTitle(R.string.settings_safetylegal_activity_title)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setOnCancelListener(this)
+                    .setCancelable(true)
+                    .create();
+        } else {
+            if (mErrorDialog.isShowing()) {
+                mErrorDialog.dismiss();
+            }
+        }
+        mErrorDialog.setMessage(getResources()
+                .getString(R.string.settings_safetylegal_activity_unreachable, url));
+        mErrorDialog.show();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (mErrorDialog != null) {
+            mErrorDialog.dismiss();
+            mErrorDialog = null;
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/SoundSettings.java b/src/com/android/settings/SoundSettings.java
index bfb5566..2984684 100644
--- a/src/com/android/settings/SoundSettings.java
+++ b/src/com/android/settings/SoundSettings.java
@@ -16,8 +16,6 @@
 
 package com.android.settings;
 
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -320,8 +318,8 @@
     public boolean onPreferenceChange(Preference preference, Object objValue) {
         final String key = preference.getKey();
         if (KEY_EMERGENCY_TONE.equals(key)) {
-            int value = Integer.parseInt((String) objValue);
             try {
+                int value = Integer.parseInt((String) objValue);
                 Settings.System.putInt(getContentResolver(),
                         Settings.System.EMERGENCY_TONE, value);
             } catch (NumberFormatException e) {
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 6764c4c..22377ab 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -21,7 +21,6 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.os.Bundle;
-import android.os.SystemProperties;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -33,8 +32,6 @@
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.util.Log;
 import android.webkit.WebView;
 
 import java.io.InputStream;
@@ -67,7 +64,6 @@
     private BroadcastReceiver mTetherChangeReceiver;
 
     private String[] mUsbRegexs;
-    private ArrayList mUsbIfaces;
 
     private String[] mWifiRegexs;
 
@@ -161,7 +157,8 @@
                         ConnectivityManager.EXTRA_ACTIVE_TETHER);
                 ArrayList<String> errored = intent.getStringArrayListExtra(
                         ConnectivityManager.EXTRA_ERRORED_TETHER);
-                updateState(available.toArray(), active.toArray(), errored.toArray());
+                updateState((String[]) available.toArray(), (String[]) active.toArray(),
+                        (String[]) errored.toArray());
             } else if (intent.getAction().equals(Intent.ACTION_MEDIA_SHARED) ||
                        intent.getAction().equals(Intent.ACTION_MEDIA_UNSHARED)) {
                 updateState();
@@ -205,8 +202,8 @@
         updateState(available, tethered, errored);
     }
 
-    private void updateState(Object[] available, Object[] tethered,
-            Object[] errored) {
+    private void updateState(String[] available, String[] tethered,
+            String[] errored) {
         ConnectivityManager cm =
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
         boolean usbTethered = false;
@@ -215,8 +212,7 @@
         boolean usbErrored = false;
         boolean massStorageActive =
                 Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
-        for (Object o : available) {
-            String s = (String)o;
+        for (String s : available) {
             for (String regex : mUsbRegexs) {
                 if (s.matches(regex)) {
                     usbAvailable = true;
@@ -226,14 +222,12 @@
                 }
             }
         }
-        for (Object o : tethered) {
-            String s = (String)o;
+        for (String s : tethered) {
             for (String regex : mUsbRegexs) {
                 if (s.matches(regex)) usbTethered = true;
             }
         }
-        for (Object o: errored) {
-            String s = (String)o;
+        for (String s: errored) {
             for (String regex : mUsbRegexs) {
                 if (s.matches(regex)) usbErrored = true;
             }
diff --git a/src/com/android/settings/UsageStats.java b/src/com/android/settings/UsageStats.java
index fcb6990..f67eeec 100755
--- a/src/com/android/settings/UsageStats.java
+++ b/src/com/android/settings/UsageStats.java
@@ -52,7 +52,7 @@
  */
 public class UsageStats extends Activity implements OnItemSelectedListener {
     private static final String TAG="UsageStatsActivity";
-    private static final boolean localLOGV = true;
+    private static final boolean localLOGV = false;
     private Spinner mTypeSpinner;
     private ListView mListView;
     private IUsageStats mUsageStatsService;
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d4f1f11..b29ec06f 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -22,8 +22,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.SystemProperties;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceGroup;
+import android.text.TextUtils;
 
 import java.util.List;
 
@@ -35,6 +41,24 @@
     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
 
     /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the icon that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the title that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the summary text that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
+
+    /**
      * Finds a matching activity for a preference's intent. If a matching
      * activity is not found, it will remove the preference.
      *
@@ -90,10 +114,97 @@
     }
 
     /**
+     * Finds a matching activity for a preference's intent. If a matching
+     * activity is not found, it will remove the preference. The icon, title and
+     * summary of the preference will also be updated with the values retrieved
+     * from the activity's meta-data elements. If no meta-data elements are
+     * specified then the preference title will be set to match the label of the
+     * activity, an icon and summary text will not be displayed.
+     *
+     * @param context The context.
+     * @param parentPreferenceGroup The preference group that contains the
+     *            preference whose intent is being resolved.
+     * @param preferenceKey The key of the preference whose intent is being
+     *            resolved.
+     *
+     * @return Whether an activity was found. If false, the preference was
+     *         removed.
+     *
+     * @see {@link #META_DATA_PREFERENCE_ICON}
+     *      {@link #META_DATA_PREFERENCE_TITLE}
+     *      {@link #META_DATA_PREFERENCE_SUMMARY}
+     */
+    public static boolean updatePreferenceToSpecificActivityFromMetaDataOrRemove(Context context,
+            PreferenceGroup parentPreferenceGroup, String preferenceKey) {
+
+        IconPreferenceScreen preference = (IconPreferenceScreen)parentPreferenceGroup
+                .findPreference(preferenceKey);
+        if (preference == null) {
+            return false;
+        }
+
+        Intent intent = preference.getIntent();
+        if (intent != null) {
+            // Find the activity that is in the system image
+            PackageManager pm = context.getPackageManager();
+            List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            int listSize = list.size();
+            for (int i = 0; i < listSize; i++) {
+                ResolveInfo resolveInfo = list.get(i);
+                if ((resolveInfo.activityInfo.applicationInfo.flags
+                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    Drawable icon = null;
+                    String title = null;
+                    String summary = null;
+
+                    // Get the activity's meta-data
+                    try {
+                        Resources res = pm
+                                .getResourcesForApplication(resolveInfo.activityInfo.packageName);
+                        Bundle metaData = resolveInfo.activityInfo.metaData;
+
+                        if (res != null && metaData != null) {
+                            icon = res.getDrawable(metaData.getInt(META_DATA_PREFERENCE_ICON));
+                            title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
+                            summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
+                        }
+                    } catch (NameNotFoundException e) {
+                        // Ignore
+                    } catch (NotFoundException e) {
+                        // Ignore
+                    }
+
+                    // Set the preference title to the activity's label if no
+                    // meta-data is found
+                    if (TextUtils.isEmpty(title)) {
+                        title = resolveInfo.loadLabel(pm).toString();
+                    }
+
+                    // Set icon, title and summary for the preference
+                    preference.setIcon(icon);
+                    preference.setTitle(title);
+                    preference.setSummary(summary);
+
+                    // Replace the intent with this specific activity
+                    preference.setIntent(new Intent().setClassName(
+                            resolveInfo.activityInfo.packageName,
+                            resolveInfo.activityInfo.name));
+
+                   return true;
+                }
+            }
+        }
+
+        // Did not find a matching activity, so remove the preference
+        parentPreferenceGroup.removePreference(preference);
+
+        return false;
+    }
+
+    /**
      * Returns true if Monkey is running.
      */
     public static boolean isMonkeyRunning() {
         return SystemProperties.getBoolean("ro.monkey", false);
     }
-
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index e897673..2eb3b17 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -166,8 +166,6 @@
 
         // Repopulate (which isn't too bad since it's cached in the settings
         // bluetooth manager
-        mDevicePreferenceMap.clear();
-        mDeviceList.removeAll();
         addDevices();
 
         if (mScreenType == SCREEN_TYPE_SETTINGS) {
@@ -192,8 +190,12 @@
     protected void onPause() {
         super.onPause();
 
-        mLocalManager.setForegroundActivity(null);
+        mDevicePreferenceMap.clear();
+        mDeviceList.removeAll();
 
+        mLocalManager.setForegroundActivity(null);
+        mDevicePreferenceMap.clear();
+        mDeviceList.removeAll();
         unregisterReceiver(mReceiver);
 
         mLocalManager.unregisterCallback(this);
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index f8d25ac..26bb4e8 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -67,6 +67,8 @@
 
     private final LocalBluetoothManager mLocalManager;
 
+    private AlertDialog mDialog = null;
+
     private List<Callback> mCallbacks = new ArrayList<Callback>();
 
     /**
@@ -194,12 +196,29 @@
             }
         };
 
-        new AlertDialog.Builder(context)
-                .setTitle(getName())
-                .setMessage(message)
-                .setPositiveButton(android.R.string.ok, disconnectListener)
-                .setNegativeButton(android.R.string.cancel, null)
-                .show();
+        if (mDialog == null) {
+            mDialog = new AlertDialog.Builder(context)
+                    .setPositiveButton(android.R.string.ok, disconnectListener)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .create();
+        } else {
+            if (mDialog.isShowing()) {
+                mDialog.dismiss();
+            }
+        }
+        mDialog.setTitle(getName());
+        mDialog.setMessage(message);
+        mDialog.show();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+
+        super.finalize();
     }
 
     public void connect() {
diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
index ca5d3ab..2d2b01c 100644
--- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
+++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
@@ -204,7 +204,7 @@
         return true;
     }
 
-    public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
+    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         
         // Open the clear shortcut dialog
         Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
@@ -314,7 +314,7 @@
             String intentUri = c.getString(intentColumn);
             PackageManager packageManager = getPackageManager();
             try {
-                Intent intent = Intent.getIntent(intentUri);
+                Intent intent = Intent.parseUri(intentUri, 0);
                 ResolveInfo info = packageManager.resolveActivity(intent, 0);
                 if (info != null) {
                     title = info.loadLabel(packageManager);
diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java
index 497f4bf..349befb 100644
--- a/src/com/android/settings/vpn/VpnEditor.java
+++ b/src/com/android/settings/vpn/VpnEditor.java
@@ -19,6 +19,7 @@
 import com.android.settings.R;
 
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.net.vpn.L2tpIpsecProfile;
@@ -44,6 +45,7 @@
 public class VpnEditor extends PreferenceActivity {
     private static final int MENU_SAVE = Menu.FIRST;
     private static final int MENU_CANCEL = Menu.FIRST + 1;
+    private static final int CONFIRM_DIALOG_ID = 0;
     private static final String KEY_PROFILE = "profile";
     private static final String KEY_ORIGINAL_PROFILE_NAME = "orig_profile_name";
 
@@ -98,7 +100,7 @@
 
             case MENU_CANCEL:
                 if (profileChanged()) {
-                    showCancellationConfirmDialog();
+                    showDialog(CONFIRM_DIALOG_ID);
                 } else {
                     finish();
                 }
@@ -171,21 +173,39 @@
         }
     }
 
-    private void showCancellationConfirmDialog() {
-        new AlertDialog.Builder(this)
-                .setTitle(android.R.string.dialog_alert_title)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(mAddingProfile
-                        ? R.string.vpn_confirm_add_profile_cancellation
-                        : R.string.vpn_confirm_edit_profile_cancellation)
-                .setPositiveButton(R.string.vpn_yes_button,
-                        new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int w) {
-                                finish();
-                            }
-                        })
-                .setNegativeButton(R.string.vpn_mistake_button, null)
-                .show();
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+
+        if (id == CONFIRM_DIALOG_ID) {
+            return new AlertDialog.Builder(this)
+                    .setTitle(android.R.string.dialog_alert_title)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setMessage(mAddingProfile
+                            ? R.string.vpn_confirm_add_profile_cancellation
+                            : R.string.vpn_confirm_edit_profile_cancellation)
+                    .setPositiveButton(R.string.vpn_yes_button,
+                            new DialogInterface.OnClickListener() {
+                                public void onClick(DialogInterface dialog, int w) {
+                                    finish();
+                                }
+                            })
+                    .setNegativeButton(R.string.vpn_mistake_button, null)
+                    .create();
+        }
+
+        return super.onCreateDialog(id);
+    }
+
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog) {
+        super.onPrepareDialog(id, dialog);
+
+        if (id == CONFIRM_DIALOG_ID) {
+            ((AlertDialog)dialog).setMessage(mAddingProfile
+                    ? getString(R.string.vpn_confirm_add_profile_cancellation)
+                    : getString(R.string.vpn_confirm_edit_profile_cancellation));
+        }
     }
 
     private VpnProfile getProfile() {
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6f0b5ef..53bf40f 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -30,8 +30,29 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name="Operator" android:label="Operator Hook Test" >
+            <intent-filter>
+                <action android:name="com.android.settings.OPERATOR_APPLICATION_SETTING" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.title" android:resource="@string/operator_settings_title" />
+            <meta-data android:name="com.android.settings.summary" android:resource="@string/operator_settings_summary" />
+            <meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
+        </activity>
+        <activity android:name="Manufacturer" android:label="Manufacturer Hook Test" >
+            <intent-filter>
+                <action android:name="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.title" android:resource="@string/manufacturer_settings_title" />
+            <meta-data android:name="com.android.settings.summary" android:resource="@string/manufacturer_settings_summary" />
+            <meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
+        </activity>
     </application>
 
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.settings"
+        android:label="Settings App Tests">
+    </instrumentation>
+
     <instrumentation android:name="SettingsLaunchPerformance"
         android:targetPackage="com.android.settings"
         android:label="Settings Launch Performance">
diff --git a/tests/res/drawable/ic_settings_applications.png b/tests/res/drawable/ic_settings_applications.png
new file mode 100755
index 0000000..5cea33f
--- /dev/null
+++ b/tests/res/drawable/ic_settings_applications.png
Binary files differ
diff --git a/tests/res/layout/manufacturer_main.xml b/tests/res/layout/manufacturer_main.xml
new file mode 100644
index 0000000..8f8c48f
--- /dev/null
+++ b/tests/res/layout/manufacturer_main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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:orientation="vertical" android:layout_width="fill_parent"
+  android:layout_height="fill_parent">
+  <TextView android:layout_width="fill_parent"
+    android:layout_height="wrap_content" android:text="@string/manufacturer_hello" />
+</LinearLayout>
diff --git a/tests/res/layout/operator_main.xml b/tests/res/layout/operator_main.xml
new file mode 100644
index 0000000..3cf8e00
--- /dev/null
+++ b/tests/res/layout/operator_main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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:orientation="vertical" android:layout_width="fill_parent"
+  android:layout_height="fill_parent">
+  <TextView android:layout_width="fill_parent"
+    android:layout_height="wrap_content" android:text="@string/operator_hello" />
+</LinearLayout>
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index 00de425..9fb98f9 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -22,4 +22,10 @@
     <string name="discoverable">Discoverable</string>
     <string name="start_scan">Start scan</string>
     <string name="stop_scan">Stop scan</string>
+    <string name="operator_hello">Hello Operator!</string>
+    <string name="operator_settings_title">Operator</string>
+    <string name="operator_settings_summary">Operator hook that can be used to start activity of choice</string>
+    <string name="manufacturer_hello">Hello Manufacturer!</string>
+    <string name="manufacturer_settings_title">Manufacturer</string>
+    <string name="manufacturer_settings_summary">Manufacturer hook that can be used to start activity of choice</string>
 </resources>
diff --git a/tests/src/com/android/settings/SettingsHookTests.java b/tests/src/com/android/settings/SettingsHookTests.java
new file mode 100644
index 0000000..b14e5bc
--- /dev/null
+++ b/tests/src/com/android/settings/SettingsHookTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import com.android.settings.tests.Manufacturer;
+import com.android.settings.tests.Operator;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.List;
+
+/**
+ * Tests for the Settings operator/manufacturer hook.
+ *
+ * Running all tests:
+ *
+ *   make SettingsTests
+ *   adb push SettingsTests.apk /system/app/SettingsTests.apk
+ *   adb shell am instrument \
+ *    -w com.android.settings.tests/android.test.InstrumentationTestRunner
+ */
+public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings> {
+
+    private static final String PACKAGE_NAME = "com.android.settings.tests";
+
+    private static final String KEY_SETTINGS_ROOT = "parent";
+    private static final String KEY_SETTINGS_OPERATOR = "operator_settings";
+    private static final String KEY_SETTINGS_MANUFACTURER = "manufacturer_settings";
+
+    private static final String INTENT_OPERATOR_HOOK = "com.android.settings.OPERATOR_APPLICATION_SETTING";
+    private static final String INTENT_MANUFACTURER_HOOK = "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
+
+    private Settings mSettings;
+
+    public SettingsHookTests() {
+        super("com.android.settings", Settings.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSettings = getActivity();
+    }
+
+    /**
+     * Test that the operator/manufacturer settings hook test application is
+     * available and that it's installed in the device's system image.
+     */
+    public void testSettingsHookTestAppAvailable() throws Exception {
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        ApplicationInfo applicationInfo = pm.getApplicationInfo(PACKAGE_NAME, 0);
+        assertTrue((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+    }
+
+    /**
+     * Test that the operator test activity has registered an intent-filter for
+     * an action named 'android.settings.OPERATOR_APPLICATION_SETTING'.
+     */
+    public void testOperatorIntentFilter() {
+        boolean result = false;
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        Intent intent = new Intent(INTENT_OPERATOR_HOOK);
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list) {
+            if (resolveInfo.activityInfo.packageName.equals(PACKAGE_NAME)) {
+                result = true;
+            }
+        }
+        assertTrue("Intent-filer not found", result);
+    }
+
+    /**
+     * Test that the manufacturer test activity has registered an intent-filter
+     * for an action named 'android.settings.MANUFACTURER_APPLICATION_SETTING'.
+     */
+    public void testManufacturerIntentFilter() {
+        boolean result = false;
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        Intent intent = new Intent(INTENT_MANUFACTURER_HOOK);
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list) {
+            if (resolveInfo.activityInfo.packageName.equals(PACKAGE_NAME)) {
+                result = true;
+            }
+        }
+        assertTrue("Intent-filer not found", result);
+    }
+
+    /**
+     * Test that the operator preference is available in the Settings
+     * application.
+     */
+    public void testOperatorPreferenceAvailable() {
+        PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
+        Preference operatorPreference = root.findPreference(KEY_SETTINGS_OPERATOR);
+        assertNotNull(operatorPreference);
+    }
+
+    /**
+     * Test that the manufacturer preference is available in the Settings
+     * application.
+     */
+    public void testManufacturerPreferenceAvailable() {
+        PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
+        Preference manufacturerHook = root.findPreference(KEY_SETTINGS_MANUFACTURER);
+        assertNotNull(manufacturerHook);
+    }
+
+}
diff --git a/tests/src/com/android/settings/tests/Manufacturer.java b/tests/src/com/android/settings/tests/Manufacturer.java
new file mode 100644
index 0000000..692e6a8
--- /dev/null
+++ b/tests/src/com/android/settings/tests/Manufacturer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Manufacturer extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.manufacturer_main);
+    }
+}
diff --git a/tests/src/com/android/settings/tests/Operator.java b/tests/src/com/android/settings/tests/Operator.java
new file mode 100644
index 0000000..8a34363
--- /dev/null
+++ b/tests/src/com/android/settings/tests/Operator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Operator extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.operator_main);
+    }
+
+}