Merge "Fix Sim Setting OOB error." into lmp-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a800b4b..5e10171 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -692,17 +692,6 @@
                 android:value="true" />
         </activity>
 
-        <activity android:name="SettingsSafetyLegalActivity"
-                android:label="@string/settings_safetylegal_activity_title"
-                android:theme="@*android:style/Theme.Material.Light.Dialog.Alert">
-            <intent-filter>
-                <action android:name="android.settings.SAFETY" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                android:value="true" />
-        </activity>
-
         <activity android:name="Settings$ManageApplicationsActivity"
                 android:label="@string/applications_settings"
                 android:taskAffinity="">
diff --git a/res/drawable/ic_audio_alarm_24dp.xml b/res/drawable/ic_audio_alarm_24dp.xml
deleted file mode 100644
index ddfaa01..0000000
--- a/res/drawable/ic_audio_alarm_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_alarm from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_alarm"
-        android:inset="4dp"
-        />
diff --git a/res/drawable/ic_audio_vol_24dp.xml b/res/drawable/ic_audio_vol_24dp.xml
deleted file mode 100644
index ecb3a40..0000000
--- a/res/drawable/ic_audio_vol_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_vol from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_vol"
-        android:inset="4dp"
-        />
diff --git a/res/drawable/ring_notif.xml b/res/drawable/ring_notif.xml
deleted file mode 100644
index cc9da31..0000000
--- a/res/drawable/ring_notif.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_ring_notif"
-        android:inset="4dp"
-        />
-
diff --git a/res/drawable/ring_notif_mute.xml b/res/drawable/ring_notif_mute.xml
deleted file mode 100644
index fe9417d..0000000
--- a/res/drawable/ring_notif_mute.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif_mute from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_ring_notif_mute"
-        android:inset="4dp"
-        />
diff --git a/res/drawable/ring_notif_vibrate.xml b/res/drawable/ring_notif_vibrate.xml
deleted file mode 100644
index 37113d7..0000000
--- a/res/drawable/ring_notif_vibrate.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<!-- shrink ic_audio_ring_notif_vibrate from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_ring_notif_vibrate"
-        android:inset="4dp"
-        />
diff --git a/res/layout/preference_volume_slider.xml b/res/layout/preference_volume_slider.xml
new file mode 100644
index 0000000..878a710
--- /dev/null
+++ b/res/layout/preference_volume_slider.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+            android:textColor="?android:attr/textColorPrimary"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="6dp">
+
+            <ImageView
+                android:id="@android:id/icon"
+                android:layout_gravity="center_vertical|start"
+                android:layout_width="24dp"
+                android:layout_height="24dp" />
+
+            <SeekBar android:id="@*android:id/seekbar"
+                android:layout_marginStart="24dp"
+                android:layout_gravity="center_vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <TextView android:id="@+id/suppression_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|start"
+                android:textAlignment="viewStart"
+                android:layout_marginStart="24dp"
+                android:paddingStart="14dp"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAppearance="@android:style/TextAppearance.Material.Body1"
+                android:textColor="?android:attr/textColorSecondary" />
+
+        </FrameLayout>
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f6f4d5a..d8d442c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1451,10 +1451,12 @@
     <string name="wifi_scan_always_available_title">Always allow scanning</string>
     <!-- Checkbox summary for option to toggle scan always available setting -->
     <string name="wifi_scan_always_available_summary">Let Google\'s location service and other apps scan for networks, even when Wi\u2011Fi is off</string>
-    <!-- Checkbox title for option to Automatically manage Wi\u2011Fi  [CHAR LIMIT=40] -->
-    <string name="wifi_automatically_manage_title">Automatically manage Wi\u2011Fi</string>
-    <!-- Checkbox summary for option to Automatically manage Wi\u2011Fi  [CHAR LIMIT=100] -->
-    <string name="wifi_automatically_manage_summary">Let <xliff:g id="wifi_assistant">%1$s</xliff:g> manage your Wi\u2011Fi connection</string>
+    <!-- Checkbox title for option to connect to open Wi-Fi automatically [CHAR LIMIT=40] -->
+    <string name="wifi_automatically_connect_title">Connect to open Wi\u2011Fi automatically</string>
+    <!-- Checkbox summary for option to connect to open Wi-Fi automatically  [CHAR LIMIT=100] -->
+    <string name="wifi_automatically_connect_summary">Allow a Wi\u2011Fi assistant to automatically rate and connect to open Wi\u2011Fi networks determined to be high quality</string>
+    <!-- Dialog title for option to select an app which connects to open Wi-Fi automatically [CHAR LIMIT=40] -->
+    <string name="wifi_select_assistant_dialog_title">Select a Wi\u2011Fi assistant</string>
     <!-- Preference title for option to install certificates -->
     <string name="wifi_install_credentials">Install certificates</string>
     <string name="wifi_scan_notify_text_location_on">To improve location accuracy and for other purposes, Google and other apps may scan for nearby networks, even when Wi-Fi is off. If you don\'t want this to happen, go to Advanced &gt; Scanning always available.</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0e83d90..162dced 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -312,6 +312,10 @@
         <item name="android:layout">@layout/apn_preference_layout</item>
     </style>
 
+    <style name="AppListSwitchPreference" parent="@*android:style/Preference.Material.DialogPreference">
+        <item name="android:widgetLayout">@*android:layout/preference_widget_switch</item>
+    </style>
+
     <style name="TextAppearance.Medium" parent="@android:style/TextAppearance.Material.Medium">
     </style>
 
@@ -341,5 +345,4 @@
     <style name="Widget.TimePicker" parent="@*android:style/Widget.Material.Light.TimePicker">
         <item name="@android:numbersBackgroundColor">@android:color/white</item>
     </style>
-
 </resources>
diff --git a/res/xml/device_info_settings.xml b/res/xml/device_info_settings.xml
index 2c47c40..45eda60 100644
--- a/res/xml/device_info_settings.xml
+++ b/res/xml/device_info_settings.xml
@@ -81,12 +81,6 @@
         </PreferenceScreen>
 
         <PreferenceScreen
-                android:key="safetylegal"
-                android:title="@string/settings_safetylegal_title">
-            <intent android:action="android.settings.SAFETY" />
-        </PreferenceScreen>
-
-        <PreferenceScreen
                 android:key="regulatory_info"
                 android:title="@string/regulatory_information">
             <intent android:action="android.settings.SHOW_REGULATORY_INFO" />
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index 3f9b40c..e5dda19 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -27,25 +27,25 @@
         <!-- Media volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="media_volume"
-                android:icon="@drawable/ic_audio_vol_24dp"
+                android:icon="@*android:drawable/ic_audio_vol"
                 android:title="@string/media_volume_option_title" />
 
         <!-- Alarm volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="alarm_volume"
-                android:icon="@drawable/ic_audio_alarm_24dp"
+                android:icon="@*android:drawable/ic_audio_alarm"
                 android:title="@string/alarm_volume_option_title" />
 
         <!-- Ring volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="ring_volume"
-                android:icon="@drawable/ring_notif"
+                android:icon="@*android:drawable/ic_audio_ring_notif"
                 android:title="@string/ring_volume_option_title" />
 
         <!-- Notification volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="notification_volume"
-                android:icon="@drawable/ring_notif"
+                android:icon="@*android:drawable/ic_audio_ring_notif"
                 android:title="@string/notification_volume_option_title" />
 
         <!-- Also vibrate for calls -->
diff --git a/res/xml/wifi_advanced_settings.xml b/res/xml/wifi_advanced_settings.xml
index 5b81d25..a7f47b1 100644
--- a/res/xml/wifi_advanced_settings.xml
+++ b/res/xml/wifi_advanced_settings.xml
@@ -30,11 +30,6 @@
             android:summary="@string/wifi_scan_always_available_summary"
             android:persistent="false" />
 
-    <SwitchPreference
-            android:key="wifi_assistant"
-            android:title="@string/wifi_automatically_manage_title"
-            android:persistent="false" />
-
     <ListPreference
             android:key="sleep_policy"
             android:title="@string/wifi_setting_sleep_policy_title"
@@ -42,6 +37,13 @@
             android:entries="@array/wifi_sleep_policy_entries"
             android:entryValues="@array/wifi_sleep_policy_values" />
 
+    <com.android.settings.AppListSwitchPreference
+            android:key="wifi_assistant"
+            android:title="@string/wifi_automatically_connect_title"
+            android:summary="@string/wifi_automatically_connect_summary"
+            android:dialogTitle="@string/wifi_select_assistant_dialog_title"
+            android:persistent="false" />
+
     <ListPreference
             android:key="frequency_band"
             android:title="@string/wifi_setting_frequency_band_title"
diff --git a/src/com/android/settings/AppListPreference.java b/src/com/android/settings/AppListPreference.java
index 2180983..96897ae 100644
--- a/src/com/android/settings/AppListPreference.java
+++ b/src/com/android/settings/AppListPreference.java
@@ -67,6 +67,11 @@
         }
     }
 
+    public AppListPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
     public AppListPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -105,6 +110,8 @@
         setEntryValues(packageNames);
         if (selectedIndex != -1) {
             setValueIndex(selectedIndex);
+        } else {
+            setValue(null);
         }
     }
 
diff --git a/src/com/android/settings/AppListSwitchPreference.java b/src/com/android/settings/AppListSwitchPreference.java
new file mode 100644
index 0000000..f9f1ba0
--- /dev/null
+++ b/src/com/android/settings/AppListSwitchPreference.java
@@ -0,0 +1,59 @@
+package com.android.settings;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Checkable;
+
+/**
+ * A hybrid of AppListPreference and SwitchPreference, representing a preference which can be on or
+ * off but must have a selected value when turned on.
+ *
+ * It is invalid to show this preference when zero valid apps are present.
+ */
+public class AppListSwitchPreference extends AppListPreference {
+    private static final String TAG = "AppListSwitchPref";
+
+    private Checkable mSwitch;
+
+    public AppListSwitchPreference(Context context, AttributeSet attrs) {
+        super(context, attrs, 0, R.style.AppListSwitchPreference);
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+        mSwitch = (Checkable) view.findViewById(com.android.internal.R.id.switchWidget);
+        mSwitch.setChecked(getValue() != null);
+    }
+
+    @Override
+    protected void showDialog(Bundle state) {
+        if (getValue() != null) {
+            // Turning off the current value.
+            if (callChangeListener(null)) {
+                setValue(null);
+            }
+        } else if (getEntryValues() == null || getEntryValues().length == 0) {
+            Log.e(TAG, "Attempting to show dialog with zero entries: " + getKey());
+        } else if (getEntryValues().length == 1) {
+            // Suppress the dialog and just toggle the preference with the only choice.
+            String value = getEntryValues()[0].toString();
+            if (callChangeListener(value)) {
+                setValue(value);
+            }
+        } else {
+            super.showDialog(state);
+        }
+    }
+
+    @Override
+    public void setValue(String value) {
+        super.setValue(value);
+        if (mSwitch != null) {
+            mSwitch.setChecked(value != null);
+        }
+    }
+}
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
index 3d66b77..52b48bb 100644
--- a/src/com/android/settings/SettingsLicenseActivity.java
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -16,205 +16,64 @@
 
 package com.android.settings;
 
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Log;
-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;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.zip.GZIPInputStream;
+import java.io.File;
 
 /**
  * The "dialog" that shows from "License" in the Settings app.
  */
 public class SettingsLicenseActivity extends Activity {
-
     private static final String TAG = "SettingsLicenseActivity";
-    private static final boolean LOGV = false || false;
 
     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);
 
-        String fileName = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
-        if (TextUtils.isEmpty(fileName)) {
-            Log.e(TAG, "The system property for the license file is empty.");
+        final String path = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
+        if (TextUtils.isEmpty(path)) {
+            Log.e(TAG, "The system property for the license file is empty");
             showErrorAndFinish();
             return;
         }
 
-        // The activity does not have any view itself,
-        // so set it invisible to avoid displaying the title text in the background.
-        setVisible(false);
-
-        mWebView = new WebView(this);
-
-        mHandler = new Handler() {
-
-            @Override
-            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() {
-        if (mTextDlg != null && mTextDlg.isShowing()) {
-            mTextDlg.dismiss();
+        final File file = new File(path);
+        if (!file.exists() || file.length() == 0) {
+            Log.e(TAG, "License file " + path + " does not exist");
+            showErrorAndFinish();
+            return;
         }
-        if (mSpinnerDlg != null && mSpinnerDlg.isShowing()) {
-            mSpinnerDlg.dismiss();
+
+        // Kick off external viewer due to WebView security restrictions; we
+        // carefully point it at HTMLViewer, since it offers to decompress
+        // before viewing.
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setDataAndType(Uri.fromFile(file), "text/html");
+        intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.settings_license_activity_title));
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setPackage("com.android.htmlviewer");
+
+        try {
+            startActivity(intent);
+            finish();
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to find viewer", e);
+            showErrorAndFinish();
         }
-        super.onDestroy();
-    }
-
-    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();
-            }
-        });
-
-        // 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();
-                if (SettingsLicenseActivity.this.isResumed()) {
-                    mTextDlg.show();
-                }
-            }
-        });
-
-        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
deleted file mode 100644
index 368ee1d..0000000
--- a/src/com/android/settings/SettingsSafetyLegalActivity.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.settings;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-import android.content.DialogInterface;
-
-/**
- * The "dialog" that shows from "Safety information" in the Settings app.
- */
-public class SettingsSafetyLegalActivity extends AlertActivity 
-        implements DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
-    private static final String PROPERTY_LSAFETYLEGAL_URL = "ro.url.safetylegal";
-
-    private WebView mWebView;
-
-    private AlertDialog mErrorDialog = null;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        String userSafetylegalUrl = SystemProperties.get(PROPERTY_LSAFETYLEGAL_URL);
-
-        final Configuration configuration = getResources().getConfiguration();
-        final String language = configuration.locale.getLanguage();
-        final String country = configuration.locale.getCountry();
-
-        String loc = String.format("locale=%s-%s", language, country);
-
-        userSafetylegalUrl = String.format("%s&%s", userSafetylegalUrl, loc);
-
-        mWebView = new WebView(this);
-
-        // Begin accessing
-        mWebView.getSettings().setJavaScriptEnabled(true);
-        if (savedInstanceState == null) {
-            mWebView.loadUrl(userSafetylegalUrl);
-        } else {
-            mWebView.restoreState(savedInstanceState);
-        }
-        mWebView.setWebViewClient(new WebViewClient() {
-            @Override
-            public void onPageFinished(WebView view, String url) {
-                // Change from 'Loading...' to the real title
-                mAlert.setTitle(getString(R.string.settings_safetylegal_activity_title));
-            }
-
-            @Override
-            public void onReceivedError(WebView view, int errorCode,
-                    String description, String failingUrl) {
-                showErrorAndFinish(failingUrl);
-            }
-        });
-
-        final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.settings_safetylegal_activity_loading);
-        p.mView = mWebView;
-        p.mForceInverseBackground = true;
-        setupAlert();
-    }
-
-    private void showErrorAndFinish(String url) {
-        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
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 
-                && event.getAction() == KeyEvent.ACTION_DOWN) {
-            if (mWebView.canGoBack()) {
-                mWebView.goBack();
-                return true;
-            }
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    public void onClick(DialogInterface dialog, int whichButton) {
-        finish();
-    }
-
-    public void onCancel(DialogInterface dialog) {
-        finish();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle icicle) {
-        mWebView.saveState(icicle);
-        super.onSaveInstanceState(icicle);
-    }
-}
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index c611772..230bbb2 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -16,11 +16,7 @@
 
 package com.android.settings;
 
-import com.android.settings.wifi.WifiApEnabler;
-import com.android.settings.wifi.WifiApDialog;
-
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.app.Dialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothPan;
@@ -31,7 +27,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiConfiguration;
@@ -44,16 +39,13 @@
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
-import android.text.TextUtils;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.webkit.WebView;
 import android.widget.TextView;
 
-import java.io.InputStream;
+import com.android.settings.wifi.WifiApDialog;
+import com.android.settings.wifi.WifiApEnabler;
+
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.Locale;
 
 /*
  * Displays preferences for Tethering.
@@ -69,7 +61,6 @@
 
     private static final int DIALOG_AP_SETTINGS = 1;
 
-    private WebView mView;
     private SwitchPreference mUsbTether;
 
     private WifiApEnabler mWifiApEnabler;
@@ -182,8 +173,6 @@
 
         mProvisionApp = getResources().getStringArray(
                 com.android.internal.R.array.config_mobile_hotspot_provision_app);
-
-        mView = new WebView(activity);
     }
 
     @Override
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index 4f92cf6..e9c39d9 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -260,7 +260,9 @@
 
         addPreferencesFromResource(R.xml.wireless_settings);
 
-        final boolean isSecondaryUser = UserHandle.myUserId() != UserHandle.USER_OWNER;
+        final int myUserId = UserHandle.myUserId();
+        final boolean isSecondaryUser = myUserId != UserHandle.USER_OWNER;
+        final boolean isRestrictedUser = mUm.getUserInfo(myUserId).isRestricted();
 
         final Activity activity = getActivity();
         mAirplaneModePreference = (SwitchPreference) findPreference(KEY_TOGGLE_AIRPLANE);
@@ -272,8 +274,13 @@
         mNfcEnabler = new NfcEnabler(activity, nfc, androidBeam);
 
         mSmsApplicationPreference = (AppListPreference) findPreference(KEY_SMS_APPLICATION);
-        mSmsApplicationPreference.setOnPreferenceChangeListener(this);
-        initSmsApplicationSetting();
+        // Restricted users cannot currently read/write SMS.
+        if (isRestrictedUser) {
+            removePreference(KEY_SMS_APPLICATION);
+        } else {
+            mSmsApplicationPreference.setOnPreferenceChangeListener(this);
+            initSmsApplicationSetting();
+        }
 
         // Remove NSD checkbox by default
         getPreferenceScreen().removePreference(nsd);
@@ -485,8 +492,11 @@
                 result.add(KEY_TOGGLE_NSD);
 
                 final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-                final boolean isSecondaryUser = UserHandle.myUserId() != UserHandle.USER_OWNER;
-                final boolean isWimaxEnabled = !isSecondaryUser && context.getResources().getBoolean(
+                final int myUserId = UserHandle.myUserId();
+                final boolean isSecondaryUser = myUserId != UserHandle.USER_OWNER;
+                final boolean isRestrictedUser = um.getUserInfo(myUserId).isRestricted();
+                final boolean isWimaxEnabled = !isSecondaryUser
+                        && context.getResources().getBoolean(
                         com.android.internal.R.bool.config_wimaxEnabled);
                 if (!isWimaxEnabled
                         || um.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
@@ -498,7 +508,8 @@
                 }
 
                 // Remove NFC if not available
-                final NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+                final NfcManager manager = (NfcManager)
+                        context.getSystemService(Context.NFC_SERVICE);
                 if (manager != null) {
                     NfcAdapter adapter = manager.getDefaultAdapter();
                     if (adapter == null) {
@@ -524,7 +535,7 @@
                 // Remove SMS Application if the device does not support SMS
                 TelephonyManager tm =
                         (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-                if (!tm.isSmsCapable()) {
+                if (!tm.isSmsCapable() || isRestrictedUser) {
                     result.add(KEY_SMS_APPLICATION);
                 }
 
@@ -539,8 +550,8 @@
                 result.add(KEY_PROXY_SETTINGS);
 
                 // Disable Tethering if it's not allowed or if it's a wifi-only device
-                ConnectivityManager cm =
-                        (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+                ConnectivityManager cm = (ConnectivityManager)
+                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
                 if (isSecondaryUser || !cm.isTetheringSupported()) {
                     result.add(KEY_TETHER_SETTINGS);
                 }
diff --git a/src/com/android/settings/notification/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java
index 6899440..bd08d8f 100644
--- a/src/com/android/settings/notification/NotificationSettings.java
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -16,9 +16,15 @@
 
 package com.android.settings.notification;
 
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
@@ -42,7 +48,6 @@
 import android.provider.Settings;
 import android.util.Log;
 
-import android.widget.SeekBar;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
@@ -53,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 public class NotificationSettings extends SettingsPreferenceFragment implements Indexable {
     private static final String TAG = "NotificationSettings";
@@ -75,6 +81,7 @@
     private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
     private final H mHandler = new H();
     private final SettingsObserver mSettingsObserver = new SettingsObserver();
+    private final Receiver mReceiver = new Receiver();
 
     private Context mContext;
     private PackageManager mPM;
@@ -90,6 +97,8 @@
     private Preference mNotificationAccess;
     private boolean mSecure;
     private int mLockscreenSelectedValue;
+    private ComponentName mSuppressor;
+    private int mRingOrNotificationProgress;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -128,6 +137,7 @@
 
         mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
         refreshNotificationListeners();
+        updateEffectsSuppressor();
     }
 
     @Override
@@ -136,6 +146,8 @@
         refreshNotificationListeners();
         lookupRingtoneNames();
         mSettingsObserver.register(true);
+        mReceiver.register(true);
+        updateEffectsSuppressor();
     }
 
     @Override
@@ -143,6 +155,7 @@
         super.onPause();
         mVolumeCallback.stopSample();
         mSettingsObserver.register(false);
+        mReceiver.register(false);
     }
 
     // === Volumes ===
@@ -154,12 +167,46 @@
         return volumePref;
     }
 
-    private void updateRingOrNotificationIcon(int progress) {
-        mRingOrNotificationPreference.showIcon(progress > 0
-                    ? R.drawable.ring_notif
-                    : (mVibrator == null
-                            ? R.drawable.ring_notif_mute
-                            : R.drawable.ring_notif_vibrate));
+    private void updateRingOrNotificationIcon() {
+        mRingOrNotificationPreference.showIcon(mSuppressor != null
+                ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+                : mRingOrNotificationProgress > 0
+                        ? com.android.internal.R.drawable.ic_audio_ring_notif
+                        : (mVibrator == null
+                                ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+                                : com.android.internal.R.drawable.ic_audio_ring_notif_vibrate));
+    }
+
+    private void updateEffectsSuppressor() {
+        final ComponentName suppressor = NotificationManager.from(mContext).getEffectsSuppressor();
+        if (Objects.equals(suppressor, mSuppressor)) return;
+        mSuppressor = suppressor;
+        if (mRingOrNotificationPreference != null) {
+            final String text = suppressor != null ?
+                    mContext.getString(com.android.internal.R.string.muted_by,
+                            getSuppressorCaption(suppressor)) : null;
+            mRingOrNotificationPreference.setSuppressionText(text);
+        }
+        updateRingOrNotificationIcon();
+    }
+
+    private String getSuppressorCaption(ComponentName suppressor) {
+        final PackageManager pm = mContext.getPackageManager();
+        try {
+            final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
+            if (info != null) {
+                final CharSequence seq = info.loadLabel(pm);
+                if (seq != null) {
+                    final String str = seq.toString().trim();
+                    if (str.length() > 0) {
+                        return str;
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            Log.w(TAG, "Error loading suppressor caption", e);
+        }
+        return suppressor.getPackageName();
     }
 
     private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
@@ -464,6 +511,7 @@
         private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
         private static final int STOP_SAMPLE = 3;
         private static final int UPDATE_RINGER_ICON = 4;
+        private static final int UPDATE_EFFECTS_SUPPRESSOR = 5;
 
         private H() {
             super(Looper.getMainLooper());
@@ -482,12 +530,36 @@
                     mVolumeCallback.stopSample();
                     break;
                 case UPDATE_RINGER_ICON:
-                    updateRingOrNotificationIcon(msg.arg1);
+                    mRingOrNotificationProgress = msg.arg1;
+                    updateRingOrNotificationIcon();
+                    break;
+                case UPDATE_EFFECTS_SUPPRESSOR:
+                    updateEffectsSuppressor();
                     break;
             }
         }
     }
 
+    private class Receiver extends BroadcastReceiver {
+        private boolean mRegistered;
+
+        public void register(boolean register) {
+            if (mRegistered == register) return;
+            if (register) {
+                mContext.registerReceiver(this,
+                        new IntentFilter(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+            } else {
+                mContext.unregisterReceiver(this);
+            }
+            mRegistered = register;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
+        }
+    }
+
     // === Indexing ===
 
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index f391f86..0fdcc19 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -23,14 +23,18 @@
 import android.preference.PreferenceManager;
 import android.preference.SeekBarPreference;
 import android.preference.SeekBarVolumizer;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.SeekBar;
+import android.widget.TextView;
 
 import com.android.settings.R;
 
+import java.util.Objects;
+
 /** A slider preference that directly controls an audio stream volume (no dialog) **/
 public class VolumeSeekBarPreference extends SeekBarPreference
         implements PreferenceManager.OnActivityStopListener {
@@ -41,10 +45,13 @@
     private SeekBarVolumizer mVolumizer;
     private Callback mCallback;
     private ImageView mIconView;
+    private TextView mSuppressionTextView;
+    private String mSuppressionText;
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        setLayoutResource(R.layout.preference_volume_slider);
     }
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -108,13 +115,14 @@
         mVolumizer.start();
         mVolumizer.setSeekBar(mSeekBar);
         mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+        mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
         mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
+        updateSuppressionText();
     }
 
     // during initialization, this preference is the SeekBar listener
     @Override
-    public void onProgressChanged(SeekBar seekBar, int progress,
-            boolean fromTouch) {
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
         super.onProgressChanged(seekBar, progress, fromTouch);
         mCallback.onStreamValueChanged(mStream, progress);
     }
@@ -133,6 +141,21 @@
                 + "/" + R.raw.media_volume);
     }
 
+    public void setSuppressionText(String text) {
+        if (Objects.equals(text, mSuppressionText)) return;
+        mSuppressionText = text;
+        updateSuppressionText();
+    }
+
+    private void updateSuppressionText() {
+        if (mSuppressionTextView != null && mSeekBar != null) {
+            mSuppressionTextView.setText(mSuppressionText);
+            final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText);
+            mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.INVISIBLE);
+            mSeekBar.setVisibility(showSuppression ? View.INVISIBLE : View.VISIBLE);
+        }
+    }
+
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
         void onStreamValueChanged(int stream, int progress);
diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java
index 7d06fdb..ac93c2e 100644
--- a/src/com/android/settings/wifi/AdvancedWifiSettings.java
+++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.settings.AppListSwitchPreference;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
@@ -66,6 +67,7 @@
 
     private WifiManager mWifiManager;
     private NetworkScoreManager mNetworkScoreManager;
+    private AppListSwitchPreference mWifiAssistantPreference;
 
     private IntentFilter mFilter;
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -130,18 +132,14 @@
         pref.setIntent(intent);
 
         final Context context = getActivity();
-        SwitchPreference wifiAssistant = (SwitchPreference)findPreference(KEY_WIFI_ASSISTANT);
-        NetworkScorerAppData scorer = getWifiAssistantApp(context);
-        if (UserHandle.myUserId() == UserHandle.USER_OWNER && scorer != null) {
-            final boolean checked = NetworkScorerAppManager.getActiveScorer(context) != null;
-            wifiAssistant.setSummary(getResources().getString(
-                    R.string.wifi_automatically_manage_summary, scorer.mScorerName));
-            wifiAssistant.setOnPreferenceChangeListener(this);
-            wifiAssistant.setChecked(checked);
-        } else {
-            if (wifiAssistant != null) {
-                getPreferenceScreen().removePreference(wifiAssistant);
-            }
+        mWifiAssistantPreference = (AppListSwitchPreference) findPreference(KEY_WIFI_ASSISTANT);
+        Collection<NetworkScorerAppData> scorers =
+                NetworkScorerAppManager.getAllValidScorers(context);
+        if (UserHandle.myUserId() == UserHandle.USER_OWNER && !scorers.isEmpty()) {
+            mWifiAssistantPreference.setOnPreferenceChangeListener(this);
+            initWifiAssistantPreference(scorers);
+        } else if (mWifiAssistantPreference != null) {
+            getPreferenceScreen().removePreference(mWifiAssistantPreference);
         }
 
         Intent wifiDirectIntent = new Intent(context,
@@ -202,6 +200,18 @@
         }
     }
 
+    private void initWifiAssistantPreference(Collection<NetworkScorerAppData> scorers) {
+        int count = scorers.size();
+        String[] packageNames = new String[count];
+        int i = 0;
+        for (NetworkScorerAppData scorer : scorers) {
+            packageNames[i] = scorer.mPackageName;
+            i++;
+        }
+        mWifiAssistantPreference.setPackageNames(packageNames,
+                mNetworkScoreManager.getActiveScorerPackage());
+    }
+
     private void updateSleepPolicySummary(Preference sleepPolicyPref, String value) {
         if (value != null) {
             String[] values = getResources().getStringArray(R.array.wifi_sleep_policy_values);
@@ -261,12 +271,13 @@
                 return false;
             }
         } else if (KEY_WIFI_ASSISTANT.equals(key)) {
-            if (((Boolean)newValue).booleanValue() == false) {
+            NetworkScorerAppData wifiAssistant =
+                    NetworkScorerAppManager.getScorer(context, (String) newValue);
+            if (wifiAssistant == null) {
                 mNetworkScoreManager.setActiveScorer(null);
                 return true;
             }
 
-            NetworkScorerAppData wifiAssistant = getWifiAssistantApp(context);
             Intent intent = new Intent();
             if (wifiAssistant.mConfigurationActivityClassName != null) {
                 // App has a custom configuration activity; launch that.
@@ -282,6 +293,9 @@
             }
 
             startActivity(intent);
+            // Don't update the preference widget state until the child activity returns.
+            // It will be updated in onResume after the activity finishes.
+            return false;
         }
 
         if (KEY_SLEEP_POLICY.equals(key)) {
@@ -317,22 +331,6 @@
         wifiIpAddressPref.setSelectable(false);
     }
 
-    /**
-     * Returns the Network Scorer for the Wifi Assistant App.
-     */
-    public static NetworkScorerAppData getWifiAssistantApp(Context context) {
-        Collection<NetworkScorerAppData> scorers =
-                NetworkScorerAppManager.getAllValidScorers(context);
-
-        if (scorers.isEmpty()) {
-            return null;
-        }
-
-        // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first
-        // scorer on the system, we should allow the user to select one.
-        return scorers.iterator().next();
-    }
-
     /* Wrapper class for the WPS dialog to properly handle life cycle events like rotation. */
     public static class WpsFragment extends DialogFragment {
         private static int mWpsSetup;