Merge "Follow up for I8050df3177b6d540829c3f0989a4e4b1381c87ec (2/2)" into mnc-dev
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e4e9255..646c385 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -207,7 +207,9 @@
     <integer name="bluetooth_name_length">32</integer>
     <dimen name="bluetooth_pairing_padding">20dp</dimen>
 
+    <!-- WiFi Preferences -->
     <dimen name="wifi_divider_height">1px</dimen>
+    <dimen name="wifi_preference_badge_padding">8dip</dimen>
 
     <!-- Color picker -->
     <dimen name="color_swatch_size">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e6c9bce..c00d904 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1552,7 +1552,7 @@
     <!-- Label for the check box to show password -->
     <string name="wifi_show_password">Show password</string>
     <!-- Label for the RadioGroup to choose wifi ap band -->
-    <string name="wifi_ap_band_config">Config AP Band</string>
+    <string name="wifi_ap_band_config">Select AP Band</string>
     <!-- Label for the radio button to choose wifi ap 2.4 GHz band -->
     <string name="wifi_ap_choose_2G">2.4 GHz Band</string>
     <!-- Label for the radio button to choose wifi ap 5GHz band -->
@@ -2451,6 +2451,12 @@
     <!-- Title of wizard button offering to cancel move [CHAR LIMIT=32] -->
     <string name="storage_wizard_move_progress_cancel">Cancel move</string>
 
+    <!-- Title of wizard step prompting user to start data migration [CHAR LIMIT=32] -->
+    <string name="storage_wizard_slow_body">This <xliff:g id="name" example="SD card">^1</xliff:g> appears to be slow.
+\n\nYou can continue, but apps moved to this location may stutter and data transfers may take a long time.
+\n\nConsider using a faster <xliff:g id="name" example="SD card">^1</xliff:g> for better performance.
+</string>
+
     <!-- Phone info screen, section titles: -->
     <string name="battery_status_title">Battery status</string>
     <!-- Phone info screen, section titles: -->
@@ -4595,6 +4601,8 @@
     <string name="backup_erase_dialog_message">Stop backing up your Wi\u2011Fi passwords, bookmarks, other settings, and app data, plus erase all copies on Google servers?</string>
     <!-- Dialog title for confirmation to erase full backup data from server -->
     <string name="fullbackup_erase_dialog_message">Stop backing up device data (such as Wi-Fi passwords and call history) and app data (such as settings and files stored by apps), plus erase all copies on Google Drive?</string>
+    <!-- Summary for explanation of what full app data backup means. Manufacturers may wish to overlay this resource with their own text  -->
+    <string name="fullbackup_data_summary">Automatically back up device data (such as Wi-Fi passwords and call history) and app data (such as settings and files stored by apps) remotely.\n\nWhen you turn on automatic backup, device and app data is periodically saved remotely. App data can be any data that an app has saved (based on developer settings), including potentially sensitive data such as contacts, messages, and photos.</string>
     <!-- Device admin settings screen --><skip />
     <!-- Device admin settings activity title -->
     <string name="device_admin_settings_title">Device administration settings</string>
diff --git a/res/xml/privacy_settings.xml b/res/xml/privacy_settings.xml
index 4607254..f9b4fbe 100644
--- a/res/xml/privacy_settings.xml
+++ b/res/xml/privacy_settings.xml
@@ -19,11 +19,11 @@
     android:title="@string/privacy_settings_title">
 
     <!-- Backup settings -->
-    <SwitchPreference
+    <PreferenceScreen
         android:key="backup_data"
         android:title="@string/backup_data_title"
-        android:summary="@string/backup_data_summary"
-        android:persistent="false" />
+        android:persistent="false"
+        android:fragment="com.android.settings.backup.ToggleBackupSettingFragment"/>
 
     <PreferenceScreen
         android:key="configure_account"
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index 1338ac7..6a2ff5e 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -21,6 +21,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.PasswordEntryKeyboardHelper;
 import com.android.internal.widget.PasswordEntryKeyboardView;
+import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.notification.RedactionInterstitial;
 
 import android.app.Activity;
@@ -127,6 +128,7 @@
         private boolean mHasChallenge;
         private long mChallenge;
         private TextView mPasswordEntry;
+        private TextViewInputDisabler mPasswordEntryInputDisabler;
         private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
         private int mPasswordMaxLength = 16;
         private int mPasswordMinLetters = 0;
@@ -255,6 +257,7 @@
             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
             mPasswordEntry.addTextChangedListener(this);
+            mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
 
             final Activity activity = getActivity();
             mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
@@ -315,7 +318,7 @@
         public void onResume() {
             super.onResume();
             updateStage(mUiStage);
-            mPasswordEntry.setEnabled(true);
+            mPasswordEntryInputDisabler.setInputEnabled(true);
             mKeyboardView.requestFocus();
         }
 
@@ -517,7 +520,7 @@
         }
 
         private void startVerifyPassword(final String pin, final boolean wasSecureBefore) {
-            mPasswordEntry.setEnabled(false);
+            mPasswordEntryInputDisabler.setInputEnabled(false);
             setNextEnabled(false);
             if (mPendingLockCheck != null) {
                 mPendingLockCheck.cancel(false);
@@ -531,7 +534,7 @@
                     new LockPatternChecker.OnVerifyCallback() {
                         @Override
                         public void onVerified(byte[] token) {
-                            mPasswordEntry.setEnabled(true);
+                            mPasswordEntryInputDisabler.setInputEnabled(true);
                             setNextEnabled(true);
                             mPendingLockCheck = null;
 
@@ -598,7 +601,7 @@
         private void updateUi() {
             String password = mPasswordEntry.getText().toString();
             final int length = password.length();
-            if (mUiStage == Stage.Introduction && length > 0) {
+            if (mUiStage == Stage.Introduction) {
                 if (length < mPasswordMinLength) {
                     String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
                             : R.string.lockpassword_pin_too_short, mPasswordMinLength);
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index 388fde7..c1d8adb 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -22,6 +22,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.TextViewInputDisabler;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
@@ -66,6 +67,7 @@
                 = "confirm_lock_password_fragment.key_num_wrong_confirm_attempts";
         private static final long ERROR_MESSAGE_TIMEOUT = 3000;
         private TextView mPasswordEntry;
+        private TextViewInputDisabler mPasswordEntryInputDisabler;
         private LockPatternUtils mLockPatternUtils;
         private AsyncTask<?, ?, ?> mPendingLockCheck;
         private TextView mHeaderTextView;
@@ -100,6 +102,7 @@
 
             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
+            mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
 
             mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
             mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
@@ -169,7 +172,7 @@
             if (deadline != 0) {
                 handleAttemptLockout(deadline);
             } else {
-                mPasswordEntry.setEnabled(true);
+                mPasswordEntryInputDisabler.setInputEnabled(true);
             }
         }
 
@@ -187,7 +190,7 @@
         }
 
         private void handleNext() {
-            mPasswordEntry.setEnabled(false);
+            mPasswordEntryInputDisabler.setInputEnabled(false);
             if (mPendingLockCheck != null) {
                 mPendingLockCheck.cancel(false);
             }
@@ -259,7 +262,7 @@
         }
 
         private void onPasswordChecked(boolean matched, Intent intent) {
-            mPasswordEntry.setEnabled(true);
+            mPasswordEntryInputDisabler.setInputEnabled(true);
             if (matched) {
                 getActivity().setResult(RESULT_OK, intent);
                 getActivity().finish();
@@ -276,7 +279,7 @@
 
         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
             long elapsedRealtime = SystemClock.elapsedRealtime();
-            mPasswordEntry.setEnabled(false);
+            mPasswordEntryInputDisabler.setInputEnabled(false);
             mCountdownTimer = new CountDownTimer(
                     elapsedRealtimeDeadline - elapsedRealtime,
                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
@@ -291,7 +294,7 @@
 
                 @Override
                 public void onFinish() {
-                    mPasswordEntry.setEnabled(true);
+                    mPasswordEntryInputDisabler.setInputEnabled(true);
                     mErrorTextView.setText("");
                     mNumWrongConfirmAttempts = 0;
                 }
diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java
index 6eb22fb..7c911cb 100644
--- a/src/com/android/settings/PrivacySettings.java
+++ b/src/com/android/settings/PrivacySettings.java
@@ -16,12 +16,9 @@
 
 package com.android.settings;
 
-import android.app.AlertDialog;
-import android.app.Dialog;
 import android.app.backup.IBackupManager;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Process;
@@ -49,8 +46,7 @@
 /**
  * Gesture lock pattern settings.
  */
-public class PrivacySettings extends SettingsPreferenceFragment implements
-        DialogInterface.OnClickListener, Indexable {
+public class PrivacySettings extends SettingsPreferenceFragment implements Indexable {
 
     // Vendor specific
     private static final String GSETTINGS_PROVIDER = "com.google.settings";
@@ -61,15 +57,11 @@
     private static final String FACTORY_RESET = "factory_reset";
     private static final String TAG = "PrivacySettings";
     private IBackupManager mBackupManager;
-    private SwitchPreference mBackup;
+    private PreferenceScreen mBackup;
     private SwitchPreference mAutoRestore;
-    private Dialog mConfirmDialog;
     private PreferenceScreen mConfigure;
     private boolean mEnabled;
 
-    private static final int DIALOG_ERASE_BACKUP = 2;
-    private int mDialogType;
-
     @Override
     protected int getMetricsCategory() {
         return MetricsLogger.PRIVACY;
@@ -89,8 +81,7 @@
         mBackupManager = IBackupManager.Stub.asInterface(
                 ServiceManager.getService(Context.BACKUP_SERVICE));
 
-        mBackup = (SwitchPreference) screen.findPreference(BACKUP_DATA);
-        mBackup.setOnPreferenceChangeListener(preferenceChangeListener);
+        mBackup = (PreferenceScreen) screen.findPreference(BACKUP_DATA);
 
         mAutoRestore = (SwitchPreference) screen.findPreference(AUTO_RESTORE);
         mAutoRestore.setOnPreferenceChangeListener(preferenceChangeListener);
@@ -120,16 +111,6 @@
         }
     }
 
-    @Override
-    public void onStop() {
-        if (mConfirmDialog != null && mConfirmDialog.isShowing()) {
-            mConfirmDialog.dismiss();
-        }
-        mConfirmDialog = null;
-        mDialogType = 0;
-        super.onStop();
-    }
-
     private OnPreferenceChangeListener preferenceChangeListener = new OnPreferenceChangeListener() {
         @Override
         public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -138,16 +119,7 @@
             }
             boolean nextValue = (Boolean) newValue;
             boolean result = false;
-            if (preference == mBackup) {
-                if (nextValue == false) {
-                    // Don't change Switch status until user makes choice in dialog
-                    // so return false here.
-                    showEraseBackupDialog();
-                } else {
-                    setBackupEnabled(true);
-                    result = true;
-                }
-            } else if (preference == mAutoRestore) {
+            if (preference == mAutoRestore) {
                 try {
                     mBackupManager.setAutoRestore(nextValue);
                     result = true;
@@ -159,19 +131,9 @@
         }
     };
 
-    private void showEraseBackupDialog() {
-        mDialogType = DIALOG_ERASE_BACKUP;
-        CharSequence msg = getResources().getText(R.string.backup_erase_dialog_message);
-        // TODO: DialogFragment?
-        mConfirmDialog = new AlertDialog.Builder(getActivity()).setMessage(msg)
-                .setTitle(R.string.backup_erase_dialog_title)
-                .setPositiveButton(android.R.string.ok, this)
-                .setNegativeButton(android.R.string.cancel, this)
-                .show();
-    }
 
     /*
-     * Creates toggles for each available location provider
+     * Creates toggles for each backup/reset preference.
      */
     private void updateToggles() {
         ContentResolver res = getContentResolver();
@@ -184,11 +146,14 @@
             String transport = mBackupManager.getCurrentTransport();
             configIntent = mBackupManager.getConfigurationIntent(transport);
             configSummary = mBackupManager.getDestinationString(transport);
+
+            mBackup.setSummary(backupEnabled
+                    ? R.string.accessibility_feature_state_on
+                    : R.string.accessibility_feature_state_off);
         } catch (RemoteException e) {
             // leave it 'false' and disable the UI; there's no backup manager
             mBackup.setEnabled(false);
         }
-        mBackup.setChecked(backupEnabled);
 
         mAutoRestore.setChecked(Settings.Secure.getInt(res,
                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) == 1);
@@ -208,54 +173,6 @@
         }
     }
 
-    private void updateConfigureSummary() {
-        try {
-            String transport = mBackupManager.getCurrentTransport();
-            String summary = mBackupManager.getDestinationString(transport);
-            setConfigureSummary(summary);
-        } catch (RemoteException e) {
-            // Not much we can do here
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        // Dialog is triggered before Switch status change, that means marking the Switch to
-        // true in showEraseBackupDialog() method will be override by following status change.
-        // So we do manual switching here due to users' response.
-        if (mDialogType == DIALOG_ERASE_BACKUP) {
-            // Accept turning off backup
-            if (which == DialogInterface.BUTTON_POSITIVE) {
-                setBackupEnabled(false);
-            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
-                // Reject turning off backup
-                setBackupEnabled(true);
-            }
-            updateConfigureSummary();
-        }
-        mDialogType = 0;
-    }
-
-    /**
-     * Informs the BackupManager of a change in backup state - if backup is disabled,
-     * the data on the server will be erased.
-     * @param enable whether to enable backup
-     */
-    private void setBackupEnabled(boolean enable) {
-        if (mBackupManager != null) {
-            try {
-                mBackupManager.setBackupEnabled(enable);
-            } catch (RemoteException e) {
-                mBackup.setChecked(!enable);
-                mAutoRestore.setEnabled(!enable);
-                return;
-            }
-        }
-        mBackup.setChecked(enable);
-        mAutoRestore.setEnabled(enable);
-        mConfigure.setEnabled(enable);
-    }
-
     @Override
     protected int getHelpResource() {
         return R.string.help_url_backup_reset;
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 7ce581f..c256f5c 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -1272,8 +1272,7 @@
                             ((UserManager) getSystemService(Context.USER_SERVICE))
                                     .getUserCount() > 1;
                     if (!UserHandle.MU_ENABLED
-                            || (!UserManager.supportsMultipleUsers()
-                                    && !hasMultipleUsers)
+                            || !UserManager.supportsMultipleUsers()
                             || Utils.isMonkeyRunning()) {
                         removeTile = true;
                     }
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 16234ba..124f9da 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -237,6 +237,8 @@
             getActivity().getActionBar().setTitle(R.string.usage_access_title);
         } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
             mListType = LIST_TYPE_HIGH_POWER;
+            // Default to showing system.
+            mShowSystem = true;
         } else {
             mListType = LIST_TYPE_MAIN;
         }
diff --git a/src/com/android/settings/backup/ToggleBackupSettingFragment.java b/src/com/android/settings/backup/ToggleBackupSettingFragment.java
new file mode 100644
index 0000000..e30266f
--- /dev/null
+++ b/src/com/android/settings/backup/ToggleBackupSettingFragment.java
@@ -0,0 +1,174 @@
+package com.android.settings.backup;
+
+import android.app.AlertDialog;
+import android.app.backup.IBackupManager;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.widget.SwitchBar;
+import com.android.settings.widget.ToggleSwitch;
+
+/**
+ * Fragment to display a bunch of text about backup and restore, and allow the user to enable/
+ * disable it.
+ */
+public class ToggleBackupSettingFragment extends SettingsPreferenceFragment
+        implements DialogInterface.OnClickListener {
+    private static final String TAG = "ToggleBackupSettingFragment";
+
+    private static final String BACKUP_TOGGLE = "toggle_backup";
+
+    private IBackupManager mBackupManager;
+
+    protected SwitchBar mSwitchBar;
+    protected ToggleSwitch mToggleSwitch;
+
+    private Preference mSummaryPreference;
+
+    private Dialog mConfirmDialog;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mBackupManager = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+
+        PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
+                getActivity());
+        setPreferenceScreen(preferenceScreen);
+        mSummaryPreference = new Preference(getActivity()) {
+            @Override
+            protected void onBindView(View view) {
+                super.onBindView(view);
+                final TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
+                summaryView.setText(getSummary());
+            }
+        };
+        mSummaryPreference.setPersistent(false);
+        mSummaryPreference.setLayoutResource(R.layout.text_description_preference);
+        preferenceScreen.addPreference(mSummaryPreference);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        SettingsActivity activity = (SettingsActivity) getActivity();
+        mSwitchBar = activity.getSwitchBar();
+        mToggleSwitch = mSwitchBar.getSwitch();
+
+        // Set up UI.
+        mSummaryPreference.setSummary(R.string.fullbackup_data_summary);
+        try {
+            boolean backupEnabled = mBackupManager == null ?
+                    false : mBackupManager.isBackupEnabled();
+            mSwitchBar.setCheckedInternal(backupEnabled);
+        } catch (RemoteException e) {
+            // The world is aflame, turn it off.
+            mSwitchBar.setEnabled(false);
+        }
+        getActivity().setTitle(R.string.backup_data_title);
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+
+        mToggleSwitch.setOnBeforeCheckedChangeListener(null);
+        mSwitchBar.hide();
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Set up toggle listener. We need this b/c we have to intercept the toggle event in order
+        // to pop up the dialogue.
+        mToggleSwitch.setOnBeforeCheckedChangeListener(
+                new ToggleSwitch.OnBeforeCheckedChangeListener() {
+                    @Override
+                    public boolean onBeforeCheckedChanged(
+                            ToggleSwitch toggleSwitch, boolean checked) {
+                        if (!checked) {
+                            // Don't change Switch status until user makes choice in dialog
+                            // so return false here.
+                            showEraseBackupDialog();
+                            return false;
+                        } else {
+                            setBackupEnabled(true);
+                            mSwitchBar.setCheckedInternal(true);
+                            return true;
+                        }
+                    }
+                });
+        mSwitchBar.show();
+    }
+
+    /** Get rid of the dialog if it's still showing. */
+    @Override
+    public void onStop() {
+        if (mConfirmDialog != null && mConfirmDialog.isShowing()) {
+            mConfirmDialog.dismiss();
+        }
+        mConfirmDialog = null;
+        super.onStop();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        // Accept turning off backup
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            setBackupEnabled(false);
+            mSwitchBar.setCheckedInternal(false);
+        } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+            // Reject turning off backup
+            setBackupEnabled(true);
+            mSwitchBar.setCheckedInternal(true);
+        }
+    }
+
+    private void showEraseBackupDialog() {
+        CharSequence msg = getResources().getText(R.string.fullbackup_erase_dialog_message);
+        // TODO: DialogFragment?
+        mConfirmDialog = new AlertDialog.Builder(getActivity()).setMessage(msg)
+                .setTitle(R.string.backup_erase_dialog_title)
+                .setPositiveButton(android.R.string.ok, this)
+                .setNegativeButton(android.R.string.cancel, this)
+                .show();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.PRIVACY;
+    }
+
+    /**
+     * Informs the BackupManager of a change in backup state - if backup is disabled,
+     * the data on the server will be erased.
+     * @param enable whether to enable backup
+     */
+    private void setBackupEnabled(boolean enable) {
+        if (mBackupManager != null) {
+            try {
+                mBackupManager.setBackupEnabled(enable);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error communicating with BackupManager", e);
+                return;
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
index e2e6dbe..f1f4510 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
@@ -18,7 +18,11 @@
 
 import static com.android.settings.deviceinfo.StorageSettings.TAG;
 
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -33,6 +37,8 @@
 import com.android.settings.R;
 
 public class StorageWizardFormatProgress extends StorageWizardBase {
+    private static final String TAG_SLOW_WARNING = "slow_warning";
+
     private boolean mFormatPrivate;
 
     @Override
@@ -56,6 +62,9 @@
     }
 
     public class PartitionTask extends AsyncTask<Void, Integer, Exception> {
+        private volatile long mInternalBench;
+        private volatile long mPrivateBench;
+
         @Override
         protected Exception doInBackground(Void... params) {
             try {
@@ -63,15 +72,12 @@
                     mStorage.partitionPrivate(mDisk.getId());
                     publishProgress(40);
 
-                    final long internalBench = mStorage.benchmark(null);
+                    mInternalBench = mStorage.benchmark(null);
                     publishProgress(60);
 
                     final VolumeInfo privateVol = findFirstVolume(VolumeInfo.TYPE_PRIVATE);
-                    final long privateBench = mStorage.benchmark(privateVol.id);
+                    mPrivateBench = mStorage.benchmark(privateVol.id);
 
-                    // TODO: plumb through to user when below threshold
-                    final float pct = (float) internalBench / (float) privateBench;
-                    Log.d(TAG, "New volume is " + pct + "x the speed of internal");
                 } else {
                     mStorage.partitionPublic(mDisk.getId());
                 }
@@ -89,40 +95,89 @@
         @Override
         protected void onPostExecute(Exception e) {
             final Context context = StorageWizardFormatProgress.this;
-            if (e == null) {
-                final String forgetUuid = getIntent().getStringExtra(
-                        StorageWizardFormatConfirm.EXTRA_FORGET_UUID);
-                if (!TextUtils.isEmpty(forgetUuid)) {
-                    mStorage.forgetVolume(forgetUuid);
-                }
-
-                final boolean offerMigrate;
-                if (mFormatPrivate) {
-                    // Offer to migrate only if storage is currently internal
-                    final VolumeInfo privateVol = getPackageManager()
-                            .getPrimaryStorageCurrentVolume();
-                    offerMigrate = (privateVol != null
-                            && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.getId()));
-                } else {
-                    offerMigrate = false;
-                }
-
-                if (offerMigrate) {
-                    final Intent intent = new Intent(context, StorageWizardMigrate.class);
-                    intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
-                    startActivity(intent);
-                } else {
-                    final Intent intent = new Intent(context, StorageWizardReady.class);
-                    intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
-                    startActivity(intent);
-                }
-                finishAffinity();
-
-            } else {
+            if (e != null) {
                 Log.e(TAG, "Failed to partition", e);
                 Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
                 finishAffinity();
+                return;
+            }
+
+            final float pct = (float) mInternalBench / (float) mPrivateBench;
+            Log.d(TAG, "New volume is " + pct + "x the speed of internal");
+
+            // TODO: refine this warning threshold
+            if (mPrivateBench > 2000000000) {
+                final SlowWarningFragment dialog = new SlowWarningFragment();
+                dialog.show(getFragmentManager(), TAG_SLOW_WARNING);
+            } else {
+                onFormatFinished();
             }
         }
     }
+
+    public class SlowWarningFragment extends DialogFragment {
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+
+            final String descrip = mDisk.getDescription();
+            final String genericDescip = getGenericDescription(mDisk);
+            builder.setMessage(TextUtils.expandTemplate(getText(R.string.storage_wizard_slow_body),
+                    descrip, genericDescip));
+
+            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    final StorageWizardFormatProgress target =
+                            (StorageWizardFormatProgress) getActivity();
+                    target.onFormatFinished();
+                }
+            });
+
+            return builder.create();
+        }
+    }
+
+    private String getGenericDescription(DiskInfo disk) {
+        // TODO: move this directly to DiskInfo
+        if (disk.isSd()) {
+            return getString(com.android.internal.R.string.storage_sd_card);
+        } else if (disk.isUsb()) {
+            return getString(com.android.internal.R.string.storage_usb_drive);
+        } else {
+            return null;
+        }
+    }
+
+    private void onFormatFinished() {
+        final String forgetUuid = getIntent().getStringExtra(
+                StorageWizardFormatConfirm.EXTRA_FORGET_UUID);
+        if (!TextUtils.isEmpty(forgetUuid)) {
+            mStorage.forgetVolume(forgetUuid);
+        }
+
+        final boolean offerMigrate;
+        if (mFormatPrivate) {
+            // Offer to migrate only if storage is currently internal
+            final VolumeInfo privateVol = getPackageManager()
+                    .getPrimaryStorageCurrentVolume();
+            offerMigrate = (privateVol != null
+                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.getId()));
+        } else {
+            offerMigrate = false;
+        }
+
+        if (offerMigrate) {
+            final Intent intent = new Intent(this, StorageWizardMigrate.class);
+            intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
+            startActivity(intent);
+        } else {
+            final Intent intent = new Intent(this, StorageWizardReady.class);
+            intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
+            startActivity(intent);
+        }
+        finishAffinity();
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java b/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
index 76b2fd1..e0369d6 100644
--- a/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
+++ b/src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
@@ -18,12 +18,14 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.util.Log;
+
 import com.android.settings.R;
 
 /**
@@ -46,7 +48,9 @@
                 new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        setCurrentFunction(which);
+                        if (!ActivityManager.isUserAMonkey()) {
+                            setCurrentFunction(which);
+                        }
                         dialog.dismiss();
                         UsbModeChooserActivity.this.finish();
                     }
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index d0dde32..2628c7f 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -33,7 +33,6 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.widget.Switch;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
@@ -45,7 +44,29 @@
 import java.util.List;
 
 /**
- * Location access settings.
+ * System location settings (Settings &gt; Location). The screen has three parts:
+ * <ul>
+ *     <li>Platform location controls</li>
+ *     <ul>
+ *         <li>In switch bar: location master switch. Used to toggle
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE} between
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE_OFF} and another location mode.
+ *         </li>
+ *         <li>Mode preference: only available if the master switch is on, selects between
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE} of
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE_HIGH_ACCURACY},
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE_BATTERY_SAVING}, or
+ *         {@link android.provider.Settings.Secure#LOCATION_MODE_SENSORS_ONLY}.</li>
+ *     </ul>
+ *     <li>Recent location requests: automatically populated by {@link RecentLocationApps}</li>
+ *     <li>Location services: multi-app settings provided from outside the Android framework. Each
+ *     is injected by a system-partition app via the {@link SettingInjectorService} API.</li>
+ * </ul>
+ * <p>
+ * Note that as of KitKat, the {@link SettingInjectorService} is the preferred method for OEMs to
+ * add their own settings to this page, rather than directly modifying the framework code. Among
+ * other things, this simplifies integration with future changes to the default (AOSP)
+ * implementation.
  */
 public class LocationSettings extends LocationSettingsBase
         implements SwitchBar.OnSwitchChangeListener {
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 4d28d4a..8aba203 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -208,7 +208,7 @@
         sResMap.put(UserSettings.class.getName(),
                 new SearchIndexableResource(
                         Ranking.getRankForClassName(UserSettings.class.getName()),
-                        R.xml.user_settings,
+                        NO_DATA_RES_ID,
                         UserSettings.class.getName(),
                         R.drawable.ic_settings_multiuser));
 
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index cef0833..d5244d7 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.app.Fragment;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -66,6 +65,9 @@
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
 import com.android.settings.drawable.CircleFramedDrawable;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -83,7 +85,7 @@
 public class UserSettings extends SettingsPreferenceFragment
         implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener,
         Preference.OnPreferenceChangeListener,
-        EditUserInfoController.OnContentChangedCallback {
+        EditUserInfoController.OnContentChangedCallback, Indexable {
 
     private static final String TAG = "UserSettings";
 
@@ -131,15 +133,11 @@
     private int mRemovingUserId = -1;
     private int mAddedUserId = 0;
     private boolean mAddingUser;
-    private boolean mEnabled = true;
-    private boolean mCanAddUser = true;
-    private boolean mCanAddRestrictedProfile = true;
+    private UserCapabilities mUserCaps;
 
     private final Object mUserLock = new Object();
     private UserManager mUserManager;
     private SparseArray<Bitmap> mUserIcons = new SparseArray<Bitmap>();
-    private boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER;
-    private boolean mIsGuest;
 
     private EditUserInfoController mEditUserInfoController =
             new EditUserInfoController();
@@ -198,16 +196,13 @@
             mEditUserInfoController.onRestoreInstanceState(icicle);
         }
         final Context context = getActivity();
+        mUserCaps = UserCapabilities.create(context);
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        boolean hasMultipleUsers = mUserManager.getUserCount() > 1;
-        if ((!UserManager.supportsMultipleUsers() && !hasMultipleUsers)
-                || Utils.isMonkeyRunning()) {
-            mEnabled = false;
+        if (!mUserCaps.mEnabled) {
             return;
         }
 
         final int myUserId = UserHandle.myUserId();
-        mIsGuest = mUserManager.getUserInfo(myUserId).isGuest();
 
         addPreferencesFromResource(R.xml.user_settings);
         mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
@@ -216,25 +211,15 @@
                 null /* delete icon handler */);
         mMePreference.setKey(KEY_USER_ME);
         mMePreference.setOnPreferenceClickListener(this);
-        if (mIsOwner) {
+        if (mUserCaps.mIsOwner) {
             mMePreference.setSummary(R.string.user_owner);
         }
         mAddUser = findPreference(KEY_ADD_USER);
-        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
-                Context.DEVICE_POLICY_SERVICE);
-        // No restricted profiles for tablets with a device owner, or phones.
-        if (dpm.getDeviceOwner() != null || Utils.isVoiceCapable(context)) {
-            mCanAddRestrictedProfile = false;
-        }
         // Determine if add user/profile button should be visible
-        if (!mIsOwner || UserManager.getMaxSupportedUsers() < 2
-                || !UserManager.supportsMultipleUsers()
-                || mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
-            mCanAddUser = false;
-        } else {
+        if (mUserCaps.mCanAddUser) {
             mAddUser.setOnPreferenceClickListener(this);
             // change label to only mention user, if restricted profiles are not supported
-            if (!mCanAddRestrictedProfile) {
+            if (!mUserCaps.mCanAddRestrictedProfile) {
                 mAddUser.setTitle(R.string.user_add_user_menu);
             }
         }
@@ -250,7 +235,7 @@
     public void onResume() {
         super.onResume();
 
-        if (!mEnabled) return;
+        if (!mUserCaps.mEnabled) return;
 
         loadProfile();
         updateUserList();
@@ -260,7 +245,7 @@
     public void onDestroy() {
         super.onDestroy();
 
-        if (!mEnabled) return;
+        if (!mUserCaps.mEnabled) return;
 
         getActivity().unregisterReceiver(mUserChangeReceiver);
     }
@@ -283,13 +268,13 @@
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         int pos = 0;
         UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
-        if (!mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)) {
+        if (!mUserCaps.mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)) {
             String nickname = mUserManager.getUserName();
             MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
                     getResources().getString(R.string.user_remove_user_menu, nickname));
             removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         }
-        if (mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
+        if (mUserCaps.mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
             MenuItem allowAddOnLockscreen = menu.add(0, MENU_ADD_ON_LOCKSCREEN, pos++,
                     R.string.user_add_on_lockscreen_menu);
             allowAddOnLockscreen.setCheckable(true);
@@ -320,7 +305,7 @@
      * Loads profile information for the current user.
      */
     private void loadProfile() {
-        if (mIsGuest) {
+        if (mUserCaps.mIsGuest) {
             // No need to load profile information
             mMePreference.setIcon(getEncircledDefaultIcon());
             mMePreference.setTitle(R.string.user_exit_guest_title);
@@ -451,7 +436,7 @@
             return;
         }
         UserInfo info = mUserManager.getUserInfo(userId);
-        if (info.isRestricted() && mIsOwner) {
+        if (info.isRestricted() && mUserCaps.mIsOwner) {
             Bundle extras = new Bundle();
             extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId);
             extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser);
@@ -462,7 +447,7 @@
         } else if (info.id == UserHandle.myUserId()) {
             // Jump to owner info panel
             OwnerInfoSettings.show(this);
-        } else if (mIsOwner) {
+        } else if (mUserCaps.mIsOwner) {
             Bundle extras = new Bundle();
             extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId);
             ((SettingsActivity) getActivity()).startPreferencePanel(
@@ -624,7 +609,7 @@
             }
             case DIALOG_USER_PROFILE_EDITOR: {
                 Dialog dlg = mEditUserInfoController.createDialog(
-                        (Fragment) this,
+                        this,
                         mMePreference.getIcon(),
                         mMePreference.getTitle(),
                         R.string.profile_info_settings_title,
@@ -668,7 +653,7 @@
             //updateUserList();
             new Thread() {
                 public void run() {
-                    UserInfo user = null;
+                    UserInfo user;
                     // Could take a few seconds
                     if (userType == USER_TYPE_USER) {
                         user = createTrustedUser();
@@ -704,7 +689,7 @@
      */
     private void exitGuest() {
         // Just to be safe
-        if (!mIsGuest) {
+        if (!mUserCaps.mIsGuest) {
             return;
         }
         removeThisUser();
@@ -740,8 +725,9 @@
                 //   Secondary user: Delete
                 //   Guest: Nothing
                 //   Restricted Profile: Settings
-                final boolean showSettings = mIsOwner && (voiceCapable || user.isRestricted());
-                final boolean showDelete = mIsOwner
+                final boolean showSettings = mUserCaps.mIsOwner
+                        && (voiceCapable || user.isRestricted());
+                final boolean showDelete = mUserCaps.mIsOwner
                         && (!voiceCapable && !user.isRestricted() && !user.isGuest());
                 pref = new UserPreference(context, null, user.id,
                         showSettings ? this : null,
@@ -791,7 +777,7 @@
             // Add a virtual Guest user for guest defaults
             UserPreference pref = new UserPreference(getActivity(), null,
                     UserPreference.USERID_GUEST_DEFAULTS,
-                    mIsOwner && voiceCapable? this : null /* settings icon handler */,
+                    mUserCaps.mIsOwner && voiceCapable? this : null /* settings icon handler */,
                     null /* delete icon handler */);
             pref.setTitle(R.string.user_guest);
             pref.setIcon(getEncircledDefaultIcon());
@@ -816,7 +802,7 @@
         // "User & Profiles", otherwise the category is skipped and elements are added directly
         // to preferenceScreen
         PreferenceGroup groupToAddUsers;
-        if (mCanAddRestrictedProfile) {
+        if (mUserCaps.mCanAddRestrictedProfile) {
             mUserListCategory.removeAll();
             mUserListCategory.setOrder(Preference.DEFAULT_ORDER);
             preferenceScreen.addPreference(mUserListCategory);
@@ -830,7 +816,7 @@
         }
 
         // Append Add user to the end of the list
-        if (mCanAddUser) {
+        if (mUserCaps.mCanAddUser) {
             boolean moreUsers = mUserManager.canAddMoreUsers();
             mAddUser.setEnabled(moreUsers);
             mAddUser.setOrder(Preference.DEFAULT_ORDER);
@@ -839,7 +825,7 @@
     }
 
     private boolean shouldShowGuestUserPreference(List<UserInfo> users) {
-        boolean showGuestPreference = !mIsGuest;
+        boolean showGuestPreference = !mUserCaps.mIsGuest;
         // If user has DISALLOW_ADD_USER don't allow creating a guest either.
         if (showGuestPreference && mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
             showGuestPreference = false;
@@ -856,7 +842,6 @@
 
 
     private void loadIconsAsync(List<Integer> missingIcons) {
-        final Resources resources = getResources();
         new AsyncTask<List<Integer>, Void, Void>() {
             @Override
             protected void onPostExecute(Void result) {
@@ -911,7 +896,7 @@
     @Override
     public boolean onPreferenceClick(Preference pref) {
         if (pref == mMePreference) {
-            if (mIsGuest) {
+            if (mUserCaps.mIsGuest) {
                 showDialog(DIALOG_CONFIRM_EXIT_GUEST);
                 return true;
             }
@@ -938,7 +923,7 @@
         } else if (pref == mAddUser) {
             // If we allow both types, show a picker, otherwise directly go to
             // flow for full user.
-            if (mCanAddRestrictedProfile) {
+            if (mUserCaps.mCanAddRestrictedProfile) {
                 showDialog(DIALOG_CHOOSE_USER_TYPE);
             } else {
                 onAddUserClicked(USER_TYPE_USER);
@@ -958,7 +943,7 @@
         // No guest user. Create one, if there's no restriction.
         // If it is not the primary user, then adding users from lockscreen must be enabled
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)
-                || (!mIsOwner && Settings.Global.getInt(getContentResolver(),
+                || (!mUserCaps.mIsOwner && Settings.Global.getInt(getContentResolver(),
                         Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 1)) {
             Log.i(TAG, "Blocking guest creation because it is restricted");
             return;
@@ -1030,4 +1015,76 @@
     public void onLabelChanged(CharSequence label) {
         mMePreference.setTitle(label);
     }
+
+    private static class UserCapabilities {
+        boolean mEnabled = true;
+        boolean mCanAddUser = true;
+        boolean mCanAddRestrictedProfile = true;
+        boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER;
+        boolean mIsGuest;
+
+        public static UserCapabilities create(Context context) {
+            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            UserCapabilities caps = new UserCapabilities();
+            if (!UserManager.supportsMultipleUsers() || Utils.isMonkeyRunning()) {
+                caps.mEnabled = false;
+                return caps;
+            }
+
+            if (!caps.mIsOwner || UserManager.getMaxSupportedUsers() < 2
+                    || !UserManager.supportsMultipleUsers()
+                    || userManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
+                caps.mCanAddUser = false;
+            }
+            DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                    Context.DEVICE_POLICY_SERVICE);
+            // No restricted profiles for tablets with a device owner, or phones.
+            if (dpm.getDeviceOwner() != null || Utils.isVoiceCapable(context)) {
+                caps.mCanAddRestrictedProfile = false;
+            }
+            final int myUserId = UserHandle.myUserId();
+            caps.mIsGuest = userManager.getUserInfo(myUserId).isGuest();
+            return caps;
+        }
+
+        @Override
+        public String toString() {
+            return "UserCapabilities{" +
+                    "mEnabled=" + mEnabled +
+                    ", mCanAddUser=" + mCanAddUser +
+                    ", mCanAddRestrictedProfile=" + mCanAddRestrictedProfile +
+                    ", mIsOwner=" + mIsOwner +
+                    ", mIsGuest=" + mIsGuest +
+                    '}';
+        }
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableRaw> getRawDataToIndex(Context context,
+                        boolean enabled) {
+                    final List<SearchIndexableRaw> result = new ArrayList<>();
+                    final UserCapabilities userCaps = UserCapabilities.create(context);
+                    if (!userCaps.mEnabled) {
+                        return result;
+                    }
+                    final Resources res = context.getResources();
+                    SearchIndexableRaw data = new SearchIndexableRaw(context);
+                    data.title = res.getString(R.string.user_settings_title);
+                    data.screenTitle = res.getString(R.string.user_settings_title);
+                    result.add(data);
+
+                    if (userCaps.mCanAddUser) {
+                        data = new SearchIndexableRaw(context);
+                        data.title = res.getString(userCaps.mCanAddRestrictedProfile ?
+                                R.string.user_add_user_or_profile_menu
+                                : R.string.user_add_user_menu);
+                        data.screenTitle = res.getString(R.string.user_settings_title);
+                        result.add(data);
+                    }
+                    return result;
+                }
+            };
+
 }
diff --git a/src/com/android/settings/vpn2/AppDialog.java b/src/com/android/settings/vpn2/AppDialog.java
index cd6a16c..c06de14 100644
--- a/src/com/android/settings/vpn2/AppDialog.java
+++ b/src/com/android/settings/vpn2/AppDialog.java
@@ -41,28 +41,25 @@
  */
 class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
     private final Listener mListener;
-    private final PackageInfo mPkgInfo;
+    private final PackageInfo mPackageInfo;
     private final String mLabel;
-    private final boolean mConnected;
 
-    AppDialog(Context context, Listener listener, PackageInfo pkgInfo, String label,
-            boolean connected) {
+    AppDialog(Context context, Listener listener, PackageInfo pkgInfo, String label) {
         super(context);
 
         mListener = listener;
-        mPkgInfo = pkgInfo;
+        mPackageInfo = pkgInfo;
         mLabel = label;
-        mConnected = connected;
     }
 
     public final PackageInfo getPackageInfo() {
-        return mPkgInfo;
+        return mPackageInfo;
     }
 
     @Override
     protected void onCreate(Bundle savedState) {
         setTitle(mLabel);
-        setMessage(getContext().getString(R.string.vpn_version, mPkgInfo.versionName));
+        setMessage(getContext().getString(R.string.vpn_version, mPackageInfo.versionName));
 
         createButtons();
         super.onCreate(savedState);
@@ -71,11 +68,9 @@
     protected void createButtons() {
         Context context = getContext();
 
-        if (mConnected) {
-            // Forget the network
-            setButton(DialogInterface.BUTTON_NEGATIVE,
-                    context.getString(R.string.vpn_forget), this);
-        }
+        // Forget the network
+        setButton(DialogInterface.BUTTON_NEGATIVE,
+                context.getString(R.string.vpn_forget), this);
 
         // Dismiss
         setButton(DialogInterface.BUTTON_POSITIVE,
diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java
index 6ac4fb5..906f896 100644
--- a/src/com/android/settings/vpn2/AppDialogFragment.java
+++ b/src/com/android/settings/vpn2/AppDialogFragment.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.net.VpnConfig;
@@ -41,18 +42,20 @@
 
     private static final String ARG_MANAGING = "managing";
     private static final String ARG_LABEL = "label";
-    private static final String ARG_PACKAGE = "package";
     private static final String ARG_CONNECTED = "connected";
+    private static final String ARG_PACKAGE = "package";
+
+    private PackageInfo mPackageInfo;
 
     private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
             ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
 
-    public static void show(VpnSettings parent, PackageInfo pkgInfo, String label, boolean managing,
-            boolean connected) {
+    public static void show(VpnSettings parent, PackageInfo packageInfo, String label,
+            boolean managing, boolean connected) {
         if (!parent.isAdded()) return;
 
         Bundle args = new Bundle();
-        args.putParcelable(ARG_PACKAGE, pkgInfo);
+        args.putParcelable(ARG_PACKAGE, packageInfo);
         args.putString(ARG_LABEL, label);
         args.putBoolean(ARG_MANAGING, managing);
         args.putBoolean(ARG_CONNECTED, connected);
@@ -66,13 +69,13 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         Bundle args = getArguments();
-        PackageInfo pkgInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
-        String label = args.getString(ARG_LABEL);
+        final String label = args.getString(ARG_LABEL);
         boolean managing = args.getBoolean(ARG_MANAGING);
         boolean connected = args.getBoolean(ARG_CONNECTED);
+        mPackageInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
 
         if (managing) {
-            return new AppDialog(getActivity(), this, pkgInfo, label, connected);
+            return new AppDialog(getActivity(), this, mPackageInfo, label);
         } else {
             // Build an AlertDialog with an option to disconnect.
             AlertDialog.Builder dlog = new AlertDialog.Builder(getActivity())
@@ -94,12 +97,6 @@
     }
 
     @Override
-    public void dismiss() {
-        ((VpnSettings) getTargetFragment()).update();
-        super.dismiss();
-    }
-
-    @Override
     public void onCancel(DialogInterface dialog) {
         dismiss();
         super.onCancel(dialog);
@@ -107,25 +104,29 @@
 
     @Override
     public void onForget(final DialogInterface dialog) {
-        PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
-        final String pkg = pkgInfo.packageName;
+        final int userId = UserHandle.getUserId(mPackageInfo.applicationInfo.uid);
         try {
-            VpnConfig vpnConfig = mService.getVpnConfig();
-            if (vpnConfig != null && pkg.equals(vpnConfig.user) && !vpnConfig.legacy) {
-                mService.setVpnPackageAuthorization(false);
-                onDisconnect(dialog);
-            }
+            mService.setVpnPackageAuthorization(mPackageInfo.packageName, userId, false);
+            onDisconnect(dialog);
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to forget authorization for " + pkg, e);
+            Log.e(TAG, "Failed to forget authorization of " + mPackageInfo.packageName +
+                    " for user " + userId, e);
         }
     }
 
     private void onDisconnect(final DialogInterface dialog) {
-        PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
+        final int userId = UserHandle.getUserId(mPackageInfo.applicationInfo.uid);
         try {
-            mService.prepareVpn(pkgInfo.packageName, VpnConfig.LEGACY_VPN);
+            final VpnConfig vpnConfig = mService.getVpnConfig(userId);
+            if (vpnConfig == null || vpnConfig.legacy) {
+                return;
+            }
+            if (mPackageInfo.packageName.equals(vpnConfig.user)) {
+                mService.prepareVpn(mPackageInfo.packageName, VpnConfig.LEGACY_VPN, userId);
+            }
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to disconnect package " + pkgInfo.packageName, e);
+            Log.e(TAG, "Failed to disconnect package " + mPackageInfo.packageName +
+                    " for user " + userId, e);
         }
     }
 }
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
index 42e1614..26b7982 100644
--- a/src/com/android/settings/vpn2/ConfigDialogFragment.java
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.util.Log;
@@ -128,12 +129,6 @@
     }
 
     @Override
-    public void dismiss() {
-        ((VpnSettings) getTargetFragment()).update();
-        super.dismiss();
-    }
-
-    @Override
     public void onCancel(DialogInterface dialog) {
         dismiss();
         super.onCancel(dialog);
@@ -151,7 +146,8 @@
         try {
             LegacyVpnInfo connected = mService.getLegacyVpnInfo();
             if (connected != null && profile.key.equals(connected.key)) {
-                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+                mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN,
+                        UserHandle.myUserId());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to disconnect", e);
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index e60a262..a82585d 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -71,6 +71,9 @@
         Handler.Callback, Preference.OnPreferenceClickListener {
     private static final String LOG_TAG = "VpnSettings";
 
+    private static final int RESCAN_MESSAGE = 0;
+    private static final int RESCAN_INTERVAL_MS = 1000;
+
     private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
     private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
@@ -90,7 +93,6 @@
 
     private Handler mUpdater;
     private LegacyVpnInfo mConnectedLegacyVpn;
-    private HashSet<String> mConnectedVpns = new HashSet<>();
 
     private boolean mUnavailable;
 
@@ -111,7 +113,6 @@
         }
 
         mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-        mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
 
         setHasOptionsMenu(true);
         addPreferencesFromResource(R.xml.vpn_settings2);
@@ -179,10 +180,32 @@
             LockdownConfigFragment.show(this);
         }
 
-        update();
+        // Start monitoring
+        mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
+
+        // Trigger a refresh
+        if (mUpdater == null) {
+            mUpdater = new Handler(this);
+        }
+        mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
     }
 
-    public void update() {
+    @Override
+    public void onPause() {
+        // Pause monitoring
+        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+
+        if (mUpdater != null) {
+            mUpdater.removeCallbacksAndMessages(null);
+        }
+
+        super.onPause();
+    }
+
+    @Override
+    public boolean handleMessage(Message message) {
+        mUpdater.removeMessages(RESCAN_MESSAGE);
+
         // Pref group within which to list VPNs
         PreferenceGroup vpnGroup = getPreferenceScreen();
         vpnGroup.removeAll();
@@ -200,18 +223,43 @@
 
         // 3rd-party VPN apps can change elsewhere. Reload them every time.
         for (AppOpsManager.PackageOps pkg : getVpnApps()) {
+            String key = getVpnIdentifier(UserHandle.getUserId(pkg.getUid()), pkg.getPackageName());
             final AppPreference pref = new AppPreference(getActivity(), mManageListener,
                     pkg.getPackageName(), pkg.getUid());
             pref.setOnPreferenceClickListener(this);
-            mAppPreferences.put(pkg.getPackageName(), pref);
+            mAppPreferences.put(key, pref);
             vpnGroup.addPreference(pref);
         }
 
-        // Start monitoring.
-        if (mUpdater == null) {
-            mUpdater = new Handler(this);
+        // Mark out connections with a subtitle
+        try {
+            // Legacy VPNs
+            LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
+            if (info != null) {
+                ConfigPreference preference = mConfigPreferences.get(info.key);
+                if (preference != null) {
+                    preference.setState(info.state);
+                    mConnectedLegacyVpn = info;
+                }
+            }
+
+            // Third-party VPNs
+            for (UserHandle profile : mUserManager.getUserProfiles()) {
+                VpnConfig cfg = mConnectivityService.getVpnConfig(profile.getIdentifier());
+                if (cfg != null) {
+                    final String key = getVpnIdentifier(profile.getIdentifier(), cfg.user);
+                    final AppPreference preference = mAppPreferences.get(key);
+                    if (preference != null) {
+                        preference.setState(AppPreference.STATE_CONNECTED);
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
         }
-        mUpdater.sendEmptyMessage(0);
+
+        mUpdater.sendEmptyMessageDelayed(RESCAN_MESSAGE, RESCAN_INTERVAL_MS);
+        return true;
     }
 
     @Override
@@ -275,68 +323,22 @@
         }
     };
 
-    @Override
-    public boolean handleMessage(Message message) {
-        mUpdater.removeMessages(0);
-
-        if (isResumed()) {
-            try {
-                // Legacy VPNs
-                LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
-                if (mConnectedLegacyVpn != null) {
-                    ConfigPreference preference = mConfigPreferences.get(mConnectedLegacyVpn.key);
-                    if (preference != null) {
-                        preference.setState(-1);
-                    }
-                    mConnectedLegacyVpn = null;
-                }
-                if (info != null) {
-                    ConfigPreference preference = mConfigPreferences.get(info.key);
-                    if (preference != null) {
-                        preference.setState(info.state);
-                        mConnectedLegacyVpn = info;
-                    }
-                }
-
-                // VPN apps
-                for (String key : mConnectedVpns) {
-                    AppPreference preference = mAppPreferences.get(key);
-                    if (preference != null) {
-                        preference.setState(AppPreference.STATE_DISCONNECTED);
-                    }
-                }
-                mConnectedVpns.clear();
-                // TODO: also query VPN services in user profiles STOPSHIP
-                VpnConfig cfg = mConnectivityService.getVpnConfig();
-                if (cfg != null) {
-                    mConnectedVpns.add(cfg.user);
-                }
-                for (String key : mConnectedVpns) {
-                    AppPreference preference = mAppPreferences.get(key);
-                    if (preference != null) {
-                        preference.setState(AppPreference.STATE_CONNECTED);
-                    }
-                }
-            } catch (RemoteException e) {
-                // ignore
-            }
-            mUpdater.sendEmptyMessageDelayed(0, 1000);
-        }
-        return true;
+    private static String getVpnIdentifier(int userId, String packageName) {
+        return Integer.toString(userId)+ "_" + packageName;
     }
 
     private NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
         public void onAvailable(Network network) {
             if (mUpdater != null) {
-                mUpdater.sendEmptyMessage(0);
+                mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
             }
         }
 
         @Override
         public void onLost(Network network) {
             if (mUpdater != null) {
-                mUpdater.sendEmptyMessage(0);
+                mUpdater.sendEmptyMessage(RESCAN_MESSAGE);
             }
         }
     };
diff --git a/src/com/android/settings/wifi/AccessPointPreference.java b/src/com/android/settings/wifi/AccessPointPreference.java
index 511d88d..55d575e 100644
--- a/src/com/android/settings/wifi/AccessPointPreference.java
+++ b/src/com/android/settings/wifi/AccessPointPreference.java
@@ -18,9 +18,12 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
+import android.net.wifi.WifiConfiguration;
+import android.os.UserHandle;
 import android.preference.Preference;
 import android.view.View;
 import android.widget.TextView;
+
 import com.android.settings.R;
 import com.android.settingslib.wifi.AccessPoint;
 
@@ -33,6 +36,7 @@
 
     private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
 
+    private TextView mTitleView;
     private TextView mSummaryView;
     private boolean showSummary = true;
     private boolean mForSavedNetworks = false;
@@ -56,9 +60,12 @@
         super.onBindView(view);
         updateIcon(mAccessPoint.getLevel(), getContext());
 
+        mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
+
         mSummaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
         mSummaryView.setVisibility(showSummary ? View.VISIBLE : View.GONE);
 
+        updateBadge(getContext());
         notifyChanged();
     }
 
@@ -94,6 +101,27 @@
         }
     }
 
+    protected void updateBadge(Context context) {
+        if (mTitleView != null) {
+            WifiConfiguration config = mAccessPoint.getConfig();
+            if (config == null) {
+                return;
+            }
+            // Fetch badge (may be null)
+            UserHandle creatorUser = new UserHandle(UserHandle.getUserId(config.creatorUid));
+            Drawable badge =
+                    context.getPackageManager().getUserBadgeForDensity(creatorUser, 0 /* dpi */);
+
+            // Distance from the end of the title at which this AP's user badge should sit.
+            final int badgePadding = context.getResources()
+                    .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+
+            // Attach to the end of the title view
+            mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, badge, null);
+            mTitleView.setCompoundDrawablePadding(badgePadding);
+        }
+    }
+
     /**
      * Shows or Hides the Summary of an AccessPoint.
      *
@@ -117,6 +145,7 @@
 
         final Context context = getContext();
         updateIcon(mAccessPoint.getLevel(), context);
+        updateBadge(context);
 
         // Force new summary
         setSummary(null);
diff --git a/src/com/android/settings/wifi/WifiNoInternetDialog.java b/src/com/android/settings/wifi/WifiNoInternetDialog.java
index a45d6a2..f044114 100644
--- a/src/com/android/settings/wifi/WifiNoInternetDialog.java
+++ b/src/com/android/settings/wifi/WifiNoInternetDialog.java
@@ -36,6 +36,8 @@
 import com.android.internal.app.AlertController;
 import com.android.settings.R;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
 public final class WifiNoInternetDialog extends AlertActivity implements
         DialogInterface.OnClickListener {
     private static final String TAG = "WifiNoInternetDialog";
@@ -67,14 +69,9 @@
 
         // TODO: add a registerNetworkCallback(Network network, NetworkCallback networkCallback) and
         // simplify this.
-        final NetworkRequest.Builder builder = new NetworkRequest.Builder();
-        for (int i = 0; i < 256; i++) {
-            try {
-                builder.removeCapability(i);
-            } catch (IllegalArgumentException e) {}
-        }
-        final NetworkRequest request = builder.build();
+        final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         mNetworkCallback = new NetworkCallback() {
+            @Override
             public void onLost(Network network) {
                 // Close the dialog if the network disconnects.
                 if (mNetwork.equals(network)) {
@@ -82,8 +79,14 @@
                     finish();
                 }
             }
-            // TODO: implement onNetworkCapabilitiesChanged so we can close the dialog if the
-            // network is now validated.
+            @Override
+            public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+                // Close the dialog if the network validates.
+                if (mNetwork.equals(network) && nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                    Log.d(TAG, "Network " + mNetwork + " validated");
+                    finish();
+                }
+            }
         };
 
         mCM = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
index 72dca9e..81815ae 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
@@ -194,6 +194,19 @@
             public void onClick(DialogInterface dialog, int which) {
                 if (which == DialogInterface.BUTTON_POSITIVE) {
                     if (mWifiP2pManager != null) {
+                        String name = mDeviceNameText.getText().toString();
+                        if (name != null) {
+                            for (int i = 0; i < name.length(); i++) {
+                                char cur = name.charAt(i);
+                                if(!Character.isDigit(cur) && !Character.isLetter(cur)
+                                        && cur != '-' && cur != '_' && cur != ' ') {
+                                    Toast.makeText(getActivity(),
+                                            R.string.wifi_p2p_failed_rename_message,
+                                            Toast.LENGTH_LONG).show();
+                                    return;
+                                }
+                            }
+                        }
                         mWifiP2pManager.setDeviceName(mChannel,
                                 mDeviceNameText.getText().toString(),
                                 new WifiP2pManager.ActionListener() {
diff --git a/tests/src/com/android/settings/vpn2/VpnTests.java b/tests/src/com/android/settings/vpn2/VpnTests.java
index 8300534..26cfc9b 100644
--- a/tests/src/com/android/settings/vpn2/VpnTests.java
+++ b/tests/src/com/android/settings/vpn2/VpnTests.java
@@ -22,6 +22,7 @@
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.security.NetworkSecurityPolicy;
@@ -214,7 +215,7 @@
      */
     private void disconnect() throws Exception {
         try {
-            mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+            mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, UserHandle.myUserId());
         } catch (RemoteException e) {
             Log.e(TAG, String.format("disconnect VPN exception: %s", e.toString()));
         }