Merge "Bluetooth: remove unused SINK_STATE_CHANGED action" am: fb0ab547c2 am: e10af2add7
am: 5741187f31

Change-Id: I7ecebb4aebb9755571baebf00718156d28415f3e
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ddbe730..f92149e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,6 +6,9 @@
 
     <original-package android:name="com.android.settings" />
 
+    <protected-broadcast android:name="com.android.settings.CARRIER_PROVISIONING" />
+    <protected-broadcast android:name="com.android.settings.TRIGGER_CARRIER_PROVISIONING" />
+
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
index e11e997..e2447fe 100644
--- a/res/layout/radio_info.xml
+++ b/res/layout/radio_info.xml
@@ -302,5 +302,23 @@
                       android:textSize="12sp" />
         </LinearLayout>
 
+        <!-- Carrier Provisioning -->
+        <LinearLayout style="@style/entry_layout">
+            <Button android:id="@+id/carrier_provisioning"
+                    android:textSize="14sp"
+                    android:layout_marginTop="8dip"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/carrier_provisioning"
+            />
+            <Button android:id="@+id/trigger_carrier_provisioning"
+                    android:textSize="14sp"
+                    android:layout_marginTop="8dip"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/trigger_carrier_provisioning"
+            />
+        </LinearLayout>
+
     </LinearLayout>
 </ScrollView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8789aef..dd2188b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -81,7 +81,23 @@
     <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="radioInfo_menu_viewSDN">View Service Dialing Numbers</string>
     <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
-    <string name="radioInfo_menu_getPDP">Get PDP List</string>
+    <string name="radioInfo_menu_getIMS">IMS Service Status</string>
+
+    <!-- Phone Info screen. IMS Registration Title.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_title">IMS Status</string>
+
+    <!-- Phone Info screen. IMS Status - Registered.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_registered">Registered</string>
+    <!-- Phone Info screen. Ims Status - Unregistered.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_not_registered">Not Registered</string>
+
+    <!-- Phone Info screen. Ims Feature Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_feature_status_available">Available</string>
+    <!-- Phone Info screen. Ims Feature status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_feature_status_unavailable">Unavailable</string>
+
+    <!-- Phone Info screen. IMS Registration.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status">IMS Registration: <xliff:g id="status" example="registered">%1$s</xliff:g>\u000AVoice over LTE: <xliff:g id="availability" example="available">%2$s</xliff:g>\u000AVoice over WiFi: <xliff:g id="availability" example="available">%3$s</xliff:g>\u000AVideo Calling: <xliff:g id="availability" example="available">%4$s</xliff:g>\u000AUT Interface: <xliff:g id="availability" example="available">%5$s</xliff:g></string>
 
     <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="radioInfo_service_in">In Service</string>
@@ -6884,6 +6900,9 @@
     <!-- Label for list to control apps that ignore battery saving restrictions [CHAR LIMIT=27]-->
     <string name="high_power_apps">Battery optimization</string>
 
+    <!-- Label for menu to launch a screen showing usage alerts for battery [CHAR LIMIT=30] -->
+    <string name="additional_battery_info">Usage alerts</string>
+
     <!-- Filter for apps allowed to use a lot of power [CHAR LIMIT=25] -->
     <string name="high_power_filter_on">Not optimized</string>
 
@@ -7864,4 +7883,10 @@
 
     <!-- Warning when activating the automatic storage manager on legacy devices. [CHAR LIMIT=NONE] -->
     <string name="automatic_storage_manager_activation_warning">Your storage is now being managed by the storage manager</string>
+
+    <!-- Carrier Provisioning Info [CHAR LIMIT=NONE] -->
+    <string name="carrier_provisioning">Carrier Provisioning Info</string>
+    <!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
+    <string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
+
 </resources>
diff --git a/res/xml/suggestion_ordering.xml b/res/xml/suggestion_ordering.xml
index 1eeafba..1da4067 100644
--- a/res/xml/suggestion_ordering.xml
+++ b/res/xml/suggestion_ordering.xml
@@ -15,6 +15,7 @@
 -->
 
 <optional-steps>
+    <step category="com.android.settings.suggested.category.RESTORE_BACKUP" />
     <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
     <step category="com.android.settings.suggested.category.EMAIL" />
     <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index d109eb1..e059041 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -186,22 +186,15 @@
                         ENCRYPT_REQUESTED_DISABLED);
             }
 
-            int targetUser = Utils.getSecureTargetUser(
+            // a) If this is started from other user, use that user id.
+            // b) If this is started from the same user, read the extra if this is launched
+            //    from Settings app itself.
+            // c) Otherwise, use UserHandle.myUserId().
+            mUserId = Utils.getSecureTargetUser(
                     getActivity().getActivityToken(),
                     UserManager.get(getActivity()),
-                    null,
+                    getArguments(),
                     getActivity().getIntent().getExtras()).getIdentifier();
-            if (ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(chooseLockAction)
-                    || !mLockPatternUtils.isSeparateProfileChallengeAllowed(targetUser)) {
-                // Always use parent if explicitely requested or if profile challenge is not
-                // supported
-                Bundle arguments = getArguments();
-                mUserId = Utils.getUserIdFromBundle(getContext(), arguments != null ? arguments
-                        : getActivity().getIntent().getExtras());
-            } else {
-                mUserId = targetUser;
-            }
-
             if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
                     && Utils.isManagedProfile(UserManager.get(getActivity()), mUserId)
                     && mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -256,6 +249,8 @@
             } else if (KEY_SKIP_FINGERPRINT.equals(key)) {
                 Intent chooseLockGenericIntent = new Intent(getActivity(), ChooseLockGeneric.class);
                 chooseLockGenericIntent.setAction(getIntent().getAction());
+                // Forward the target user id to  ChooseLockGeneric.
+                chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
                 chooseLockGenericIntent.putExtra(PASSWORD_CONFIRMED, mPasswordConfirmed);
                 startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST);
                 return true;
@@ -343,6 +338,8 @@
                 if (data != null) {
                     intent.putExtras(data.getExtras());
                 }
+                // Forward the target user id to fingerprint setup page.
+                intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
                 startActivity(intent);
                 finish();
             } else if (requestCode == SKIP_FINGERPRINT_REQUEST) {
diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java
index 8bab2cf..2966800 100644
--- a/src/com/android/settings/InstrumentedFragment.java
+++ b/src/com/android/settings/InstrumentedFragment.java
@@ -16,10 +16,14 @@
 
 package com.android.settings;
 
+import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.os.Bundle;
 import android.support.v14.preference.PreferenceFragment;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.overlay.SurveyFeatureProvider;
 
 /**
  * Instrumented fragment that logs visibility state.
@@ -39,6 +43,7 @@
      * {@link com.android.settings.InstrumentedFragment}.
      */
     protected abstract int getMetricsCategory();
+    private BroadcastReceiver mReceiver;
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -48,10 +53,34 @@
     public void onResume() {
         super.onResume();
         MetricsLogger.visible(getActivity(), getMetricsCategory());
+
+        Activity activity = getActivity();
+        // guard against the activity not existing yet or the feature being disabled
+        if (activity != null) {
+            SurveyFeatureProvider provider =
+                    FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity);
+            if (provider != null) {
+                // Try to download a survey if there is none available, show the survey otherwise
+                String id = provider.getSurveyId(activity, getClass().getSimpleName());
+                if (provider.getSurveyExpirationDate(activity, id) <= -1) {
+                    // register the receiver to show the survey on completion.
+                    mReceiver = provider.createAndRegisterReceiver(activity);
+                    provider.downloadSurvey(activity, id, "fakeData");
+                } else {
+                    provider.showSurveyIfAvailable(activity, id);
+                }
+            }
+        }
     }
 
     @Override
     public void onPause() {
+        Activity activity = getActivity();
+        if (mReceiver != null && activity != null) {
+            SurveyFeatureProvider.unregisterReceiver(activity, mReceiver);
+            mReceiver = null;
+        }
+
         super.onPause();
         MetricsLogger.hidden(getActivity(), getMetricsCategory());
     }
diff --git a/src/com/android/settings/InstrumentedPreferenceFragment.java b/src/com/android/settings/InstrumentedPreferenceFragment.java
index 243e0bc..ba6cb79 100644
--- a/src/com/android/settings/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/InstrumentedPreferenceFragment.java
@@ -16,9 +16,13 @@
 
 package com.android.settings;
 
+import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.support.v14.preference.PreferenceFragment;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.overlay.SurveyFeatureProvider;
 
 /**
  * Instrumented preference fragment that logs visibility state.
@@ -32,15 +36,40 @@
      * {@link com.android.settings.InstrumentedFragment}.
      */
     protected abstract int getMetricsCategory();
+    private BroadcastReceiver mReceiver;
 
     @Override
     public void onResume() {
         super.onResume();
         MetricsLogger.visible(getActivity(), getMetricsCategory());
+
+        Activity activity = getActivity();
+        // guard against the activity not existing yet or the feature being disabled
+        if (activity != null) {
+            SurveyFeatureProvider provider =
+                    FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity);
+            if (provider != null) {
+                // Try to download a survey if there is none available, show the survey otherwise
+                String id = provider.getSurveyId(activity, getClass().getSimpleName());
+                if (provider.getSurveyExpirationDate(activity, id) <= -1) {
+                    // register the receiver to show the survey on completion.
+                    mReceiver = provider.createAndRegisterReceiver(activity);
+                    provider.downloadSurvey(activity, id, null /* data */);
+                } else {
+                    provider.showSurveyIfAvailable(activity, id);
+                }
+            }
+        }
     }
 
     @Override
     public void onPause() {
+        Activity activity = getActivity();
+        if (mReceiver != null && activity != null) {
+            SurveyFeatureProvider.unregisterReceiver(activity, mReceiver);
+            mReceiver = null;
+        }
+
         super.onPause();
         MetricsLogger.hidden(getActivity(), getMetricsCategory());
     }
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index 54ff4db..65a784a 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -17,6 +17,8 @@
 package com.android.settings;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.app.QueuedWork;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -160,7 +162,7 @@
     private static final int MENU_ITEM_VIEW_ADN     = 1;
     private static final int MENU_ITEM_VIEW_FDN     = 2;
     private static final int MENU_ITEM_VIEW_SDN     = 3;
-    private static final int MENU_ITEM_GET_PDP_LIST = 4;
+    private static final int MENU_ITEM_GET_IMS_STATUS = 4;
     private static final int MENU_ITEM_TOGGLE_DATA  = 5;
 
     private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
@@ -193,6 +195,8 @@
     private Button updateSmscButton;
     private Button refreshSmscButton;
     private Button oemInfoButton;
+    private Button carrierProvisioningButton;
+    private Button triggercarrierProvisioningButton;
     private Switch imsVolteProvisionedSwitch;
     private Switch imsVtProvisionedSwitch;
     private Switch imsWfcProvisionedSwitch;
@@ -412,6 +416,11 @@
         refreshSmscButton.setOnClickListener(mRefreshSmscButtonHandler);
         dnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
         dnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
+        carrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
+        carrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        triggercarrierProvisioningButton = (Button) findViewById(R.id.trigger_carrier_provisioning);
+        triggercarrierProvisioningButton.setOnClickListener(
+                mTriggerCarrierProvisioningButtonHandler);
 
         oemInfoButton = (Button) findViewById(R.id.oem_info);
         oemInfoButton.setOnClickListener(mOemInfoButtonHandler);
@@ -497,7 +506,7 @@
     }
 
     private void restoreFromBundle(Bundle b) {
-        if( b == null) {
+        if (b == null) {
             return;
         }
 
@@ -537,8 +546,8 @@
                 R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
         menu.add(1, MENU_ITEM_VIEW_SDN, 0,
                 R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
-        menu.add(1, MENU_ITEM_GET_PDP_LIST,
-                0, R.string.radioInfo_menu_getPDP).setOnMenuItemClickListener(mGetPdpList);
+        menu.add(1, MENU_ITEM_GET_IMS_STATUS,
+                0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
         menu.add(1, MENU_ITEM_TOGGLE_DATA,
                 0, R.string.radio_info_data_connection_disable).setOnMenuItemClickListener(mToggleData);
         return true;
@@ -632,7 +641,7 @@
         StringBuilder sb = new StringBuilder();
 
         if (cids != null) {
-            if ( cids.isEmpty() ) {
+            if (cids.isEmpty()) {
                 sb.append("no neighboring cells");
             } else {
                 for (NeighboringCellInfo cell : cids) {
@@ -860,7 +869,7 @@
     }
 
     private final void updateNetworkType() {
-        if( phone != null ) {
+        if (phone != null) {
             ServiceState ss = phone.getServiceState();
             dataNetwork.setText(ServiceState.rilRadioTechnologyToString(
                     phone.getServiceState().getRilDataRadioTechnology()));
@@ -1069,10 +1078,36 @@
         }
     };
 
-    private MenuItem.OnMenuItemClickListener mGetPdpList = new MenuItem.OnMenuItemClickListener() {
+    private MenuItem.OnMenuItemClickListener mGetImsStatus = new MenuItem.OnMenuItemClickListener() {
         public boolean onMenuItemClick(MenuItem item) {
-            //FIXME: Replace with a TelephonyManager call
-            phone.getDataCallList(null);
+            boolean isImsRegistered = phone.isImsRegistered();
+            boolean availableVolte = phone.isVolteEnabled();
+            boolean availableWfc = phone.isWifiCallingEnabled();
+            boolean availableVt = phone.isVideoEnabled();
+            boolean availableUt = phone.isUtEnabled();
+
+            final String imsRegString = isImsRegistered ?
+                getString(R.string.radio_info_ims_reg_status_registered) :
+                getString(R.string.radio_info_ims_reg_status_not_registered);
+
+            final String available = getString(R.string.radio_info_ims_feature_status_available);
+            final String unavailable = getString(
+                    R.string.radio_info_ims_feature_status_unavailable);
+
+            String imsStatus = getString(R.string.radio_info_ims_reg_status,
+                    imsRegString,
+                    availableVolte ? available : unavailable,
+                    availableWfc ? available : unavailable,
+                    availableVt ? available : unavailable,
+                    availableUt ? available : unavailable);
+
+            AlertDialog imsDialog = new AlertDialog.Builder(RadioInfo.this)
+                .setMessage(imsStatus)
+                .setTitle(getString(R.string.radio_info_ims_reg_status_title))
+                .create();
+
+            imsDialog.show();
+
             return true;
         }
     };
@@ -1119,22 +1154,22 @@
         radioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
     }
 
-    void setImsVolteProvisionedState( boolean state ) {
+    void setImsVolteProvisionedState(boolean state) {
         Log.d(TAG, "setImsVolteProvisioned state: " + ((state)? "on":"off"));
-        setImsConfigProvisionedState( IMS_VOLTE_PROVISIONED_CONFIG_ID, state );
+        setImsConfigProvisionedState(IMS_VOLTE_PROVISIONED_CONFIG_ID, state);
     }
 
-    void setImsVtProvisionedState( boolean state ) {
+    void setImsVtProvisionedState(boolean state) {
         Log.d(TAG, "setImsVtProvisioned() state: " + ((state)? "on":"off"));
-        setImsConfigProvisionedState( IMS_VT_PROVISIONED_CONFIG_ID, state );
+        setImsConfigProvisionedState(IMS_VT_PROVISIONED_CONFIG_ID, state);
     }
 
-    void setImsWfcProvisionedState( boolean state ) {
+    void setImsWfcProvisionedState(boolean state) {
         Log.d(TAG, "setImsWfcProvisioned() state: " + ((state)? "on":"off"));
-        setImsConfigProvisionedState( IMS_WFC_PROVISIONED_CONFIG_ID, state );
+        setImsConfigProvisionedState(IMS_WFC_PROVISIONED_CONFIG_ID, state);
     }
 
-    void setImsConfigProvisionedState( int configItem, boolean state ) {
+    void setImsConfigProvisionedState(int configItem, boolean state) {
         if (phone != null && mImsManager != null) {
             QueuedWork.singleThreadExecutor().submit(new Runnable() {
                 public void run() {
@@ -1170,7 +1205,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsVolteProvisionedState(isChecked);
-       }
+        }
     };
 
     private boolean isImsVtProvisioned() {
@@ -1185,7 +1220,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsVtProvisionedState(isChecked);
-       }
+        }
     };
 
     private boolean isImsWfcProvisioned() {
@@ -1200,7 +1235,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsWfcProvisionedState(isChecked);
-       }
+        }
     };
 
     private void updateImsProvisionedState() {
@@ -1210,14 +1245,20 @@
         imsVolteProvisionedSwitch.setOnCheckedChangeListener(null);
         imsVolteProvisionedSwitch.setChecked(isImsVolteProvisioned());
         imsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
+        imsVolteProvisionedSwitch.setEnabled(
+                mImsManager.isVolteEnabledByPlatform(phone.getContext()));
 
         imsVtProvisionedSwitch.setOnCheckedChangeListener(null);
         imsVtProvisionedSwitch.setChecked(isImsVtProvisioned());
         imsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
+        imsVtProvisionedSwitch.setEnabled(
+            mImsManager.isVtEnabledByPlatform(phone.getContext()));
 
         imsWfcProvisionedSwitch.setOnCheckedChangeListener(null);
         imsWfcProvisionedSwitch.setChecked(isImsWfcProvisioned());
         imsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
+        imsWfcProvisionedSwitch.setEnabled(
+            mImsManager.isWfcEnabledByPlatform(phone.getContext()));
     }
 
     OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
@@ -1261,6 +1302,22 @@
         }
     };
 
+    OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
+            getApplicationContext().sendBroadcast(
+                    intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        }
+    };
+
+    OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
+            getApplicationContext().sendBroadcast(
+                    intent, android.Manifest.permission.MODIFY_PHONE_STATE);
+        }
+    };
+
     AdapterView.OnItemSelectedListener mPreferredNetworkHandler =
             new AdapterView.OnItemSelectedListener() {
 
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index dd62c6f..eec269f 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -102,6 +102,7 @@
 import java.util.Locale;
 
 import static android.content.Intent.EXTRA_USER;
+import static android.content.Intent.EXTRA_USER_ID;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
@@ -651,16 +652,21 @@
 
     /**
      * Returns the target user for a Settings activity.
-     *
-     * The target user can be either the current user, the user that launched this activity or
-     * the user contained as an extra in the arguments or intent extras.
-     *
+     * <p>
+     * User would be retrieved in this order:
+     * <ul>
+     * <li> If this activity is launched from other user, return that user id.
+     * <li> If this is launched from the Settings app in same user, return the user contained as an
+     *      extra in the arguments or intent extras.
+     * <li> Otherwise, return UserHandle.myUserId().
+     * </ul>
+     * <p>
      * Note: This is secure in the sense that it only returns a target user different to the current
      * one if the app launching this activity is the Settings app itself, running in the same user
      * or in one that is in the same profile group, or if the user id is provided by the system.
      */
     public static UserHandle getSecureTargetUser(IBinder activityToken,
-           UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
+            UserManager um, @Nullable Bundle arguments, @Nullable Bundle intentExtras) {
         UserHandle currentUser = new UserHandle(UserHandle.myUserId());
         IActivityManager am = ActivityManagerNative.getDefault();
         try {
@@ -675,16 +681,14 @@
                     return launchedFromUser;
                 }
             }
-            UserHandle extrasUser = intentExtras != null
-                    ? (UserHandle) intentExtras.getParcelable(EXTRA_USER) : null;
+            UserHandle extrasUser = getUserHandleFromBundle(intentExtras);
             if (extrasUser != null && !extrasUser.equals(currentUser)) {
                 // Check it's secure
                 if (launchedFromSettingsApp && isProfileOf(um, extrasUser)) {
                     return extrasUser;
                 }
             }
-            UserHandle argumentsUser = arguments != null
-                    ? (UserHandle) arguments.getParcelable(EXTRA_USER) : null;
+            UserHandle argumentsUser = getUserHandleFromBundle(arguments);
             if (argumentsUser != null && !argumentsUser.equals(currentUser)) {
                 // Check it's secure
                 if (launchedFromSettingsApp && isProfileOf(um, argumentsUser)) {
@@ -696,7 +700,26 @@
             Log.v(TAG, "Could not talk to activity manager.", e);
         }
         return currentUser;
-   }
+    }
+
+    /**
+     * Lookup both {@link Intent#EXTRA_USER} and {@link Intent#EXTRA_USER_ID} in the bundle
+     * and return the {@link UserHandle} object. Return {@code null} if nothing is found.
+     */
+    private static @Nullable UserHandle getUserHandleFromBundle(Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final UserHandle user = bundle.getParcelable(EXTRA_USER);
+        if (user != null) {
+            return user;
+        }
+        final int userId = bundle.getInt(EXTRA_USER_ID, -1);
+        if (userId != -1) {
+            return UserHandle.of(userId);
+        }
+        return null;
+    }
 
     /**
      * Returns the target user for a Settings activity.
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
index b176efd..300451d 100755
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -96,6 +96,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mReceiverRegistered = false;
 
         Intent intent = getIntent();
         if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
@@ -129,6 +130,8 @@
                     intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
                 if (passkey == BluetoothDevice.ERROR) {
                     Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
+                    mDevice.setPairingConfirmation(false);
+                    finish();
                     return;
                 }
                 mPairingKey = String.format(Locale.US, "%06d", passkey);
@@ -146,6 +149,7 @@
                     intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
                 if (pairingKey == BluetoothDevice.ERROR) {
                     Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
+                    finish();
                     return;
                 }
                 if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
@@ -158,6 +162,8 @@
 
             default:
                 Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
+                finish();
+                return;
         }
 
         /*
diff --git a/src/com/android/settings/dashboard/SuggestionFeatureProvider.java b/src/com/android/settings/dashboard/SuggestionFeatureProvider.java
new file mode 100644
index 0000000..4b331fa
--- /dev/null
+++ b/src/com/android/settings/dashboard/SuggestionFeatureProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.dashboard;
+
+import android.content.Context;
+
+/** Interface should be implemented if you have added new suggestions */
+public interface SuggestionFeatureProvider {
+
+    /** Return true if className is the name of a class of one of your newly added suggestion. */
+    boolean isPresent(String className);
+
+    /** Return true if the suggestion has already been completed and does not need to be shown */
+    boolean isSuggestionCompleted(Context context);
+
+}
\ No newline at end of file
diff --git a/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java
new file mode 100644
index 0000000..1b1db89
--- /dev/null
+++ b/src/com/android/settings/dashboard/SuggestionFeatureProviderImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.dashboard;
+
+import android.content.Context;
+
+public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider {
+
+    @Override
+    public boolean isPresent(String className) {
+        return false;
+    }
+
+    @Override
+    public boolean isSuggestionCompleted(Context context) {
+        return false;
+    }
+
+}
diff --git a/src/com/android/settings/dashboard/SuggestionsChecks.java b/src/com/android/settings/dashboard/SuggestionsChecks.java
index b816a79..02fbe55 100644
--- a/src/com/android/settings/dashboard/SuggestionsChecks.java
+++ b/src/com/android/settings/dashboard/SuggestionsChecks.java
@@ -36,6 +36,7 @@
 import com.android.settings.Settings.WifiCallingSuggestionActivity;
 import com.android.settings.Settings.ZenModeAutomationSuggestionActivity;
 import com.android.settings.WallpaperSuggestionActivity;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.drawer.Tile;
 
 import java.util.Collection;
@@ -66,6 +67,13 @@
         } else if (className.equals(FingerprintEnrollSuggestionActivity.class.getName())) {
             return isDeviceSecured() || !isFingerprintEnabled();
         }
+
+        SuggestionFeatureProvider provider =
+            FeatureFactory.getFactory(mContext).getSuggestionFeatureProvider();
+        if (provider != null && provider.isPresent(className)) {
+            return provider.isSuggestionCompleted(mContext);
+        }
+
         return false;
     }
 
diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
index 7757efc..1cba34a 100644
--- a/src/com/android/settings/deviceinfo/StorageSettings.java
+++ b/src/com/android/settings/deviceinfo/StorageSettings.java
@@ -53,6 +53,8 @@
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
 import com.android.settingslib.drawer.SettingsDrawerActivity;
 
 import java.io.File;
@@ -167,7 +169,8 @@
 
         for (VolumeInfo vol : volumes) {
             if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
-                final long volumeTotalBytes = getTotalSize(vol);
+                final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol,
+                        sTotalInternalStorage);
                 final int color = COLOR_PRIVATE[privateCount++ % COLOR_PRIVATE.length];
                 mInternalCategory.addPreference(
                         new StorageVolumePreference(context, vol, color, volumeTotalBytes));
@@ -276,7 +279,8 @@
             if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
                 final Bundle args = new Bundle();
                 args.putString(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
-                PrivateVolumeSettings.setVolumeSize(args, getTotalSize(vol));
+                PrivateVolumeSettings.setVolumeSize(args, PrivateStorageInfo.getTotalSize(vol,
+                        sTotalInternalStorage));
                 startFragment(this, PrivateVolumeSettings.class.getCanonicalName(),
                         -1, 0, args);
                 return true;
@@ -506,47 +510,15 @@
         private void updateSummary() {
             // TODO: Register listener.
             final StorageManager storageManager = mContext.getSystemService(StorageManager.class);
-            if (sTotalInternalStorage <= 0) {
-                sTotalInternalStorage = storageManager.getPrimaryStorageSize();
-            }
-            final List<VolumeInfo> volumes = storageManager.getVolumes();
-            long privateFreeBytes = 0;
-            long privateTotalBytes = 0;
-            for (VolumeInfo info : volumes) {
-                final File path = info.getPath();
-                if (info.getType() != VolumeInfo.TYPE_PRIVATE || path == null) {
-                    continue;
-                }
-                privateTotalBytes += getTotalSize(info);
-                privateFreeBytes += path.getFreeSpace();
-            }
-            long privateUsedBytes = privateTotalBytes - privateFreeBytes;
+            PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
+                    new StorageManagerVolumeProvider(storageManager));
+            long privateUsedBytes = info.totalBytes - info.freeBytes;
             mLoader.setSummary(this, mContext.getString(R.string.storage_summary,
                     Formatter.formatFileSize(mContext, privateUsedBytes),
-                    Formatter.formatFileSize(mContext, privateTotalBytes)));
+                    Formatter.formatFileSize(mContext, info.totalBytes)));
         }
     }
 
-    private static long getTotalSize(VolumeInfo info) {
-        // Device could have more than one primary storage, which could be located in the
-        // internal flash (UUID_PRIVATE_INTERNAL) or in an external disk.
-        // If it's internal, try to get its total size from StorageManager first
-        // (sTotalInternalStorage), since that size is more precise because it accounts for
-        // the system partition.
-        if (info.getType() == VolumeInfo.TYPE_PRIVATE
-                && Objects.equals(info.getFsUuid(), StorageManager.UUID_PRIVATE_INTERNAL)
-                && sTotalInternalStorage > 0) {
-            return sTotalInternalStorage;
-        } else {
-            final File path = info.getPath();
-            if (path == null) {
-                // Should not happen, caller should have checked.
-                Log.e(TAG, "info's path is null on getTotalSize(): " + info);
-                return 0;
-            }
-            return path.getTotalSpace();
-        }
-    }
 
     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
             = new SummaryLoader.SummaryProviderFactory() {
diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java
index 269249a..1af9df1 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageBase.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java
@@ -25,6 +25,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -39,7 +40,8 @@
 public abstract class PowerUsageBase extends SettingsPreferenceFragment {
 
     // +1 to allow ordering for PowerUsageSummary.
-    private static final int MENU_STATS_REFRESH = Menu.FIRST + 1;
+    @VisibleForTesting
+    static final int MENU_STATS_REFRESH = Menu.FIRST + 1;
 
     protected BatteryStatsHelper mStatsHelper;
     protected UserManager mUm;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
new file mode 100644
index 0000000..25743d1
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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.fuelgauge;
+
+import android.content.Intent;
+
+/**
+ * Feature Provider used in power usage
+ */
+public interface PowerUsageFeatureProvider {
+
+  /**
+   * Check whether additional battery info feature is enabled.
+   */
+  boolean isAdditionalBatteryInfoEnabled();
+
+  /**
+   * Gets an {@link Intent} to show additional battery info.
+   */
+  Intent getAdditionalBatteryInfoIntent();
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 443b480..be0a7cc 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -26,6 +26,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.UserHandle;
+import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
 import android.text.TextUtils;
@@ -43,6 +44,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.applications.ManageApplications;
 import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.BatteryInfo;
 
 import java.util.ArrayList;
@@ -67,7 +69,9 @@
 
     private static final int MENU_STATS_TYPE = Menu.FIRST;
     private static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
-    private static final int MENU_HELP = Menu.FIRST + 4;
+    @VisibleForTesting
+    static final int MENU_ADDITIONAL_BATTERY_INFO = Menu.FIRST + 4;
+    private static final int MENU_HELP = Menu.FIRST + 5;
 
     private BatteryHistoryPreference mHistPref;
     private PreferenceGroup mAppListGroup;
@@ -130,12 +134,20 @@
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         if (DEBUG) {
-            menu.add(0, MENU_STATS_TYPE, 0, R.string.menu_stats_total)
+            menu.add(Menu.NONE, MENU_STATS_TYPE, Menu.NONE, R.string.menu_stats_total)
                     .setIcon(com.android.internal.R.drawable.ic_menu_info_details)
                     .setAlphabeticShortcut('t');
         }
 
-        menu.add(0, MENU_HIGH_POWER_APPS, 0, R.string.high_power_apps);
+        menu.add(Menu.NONE, MENU_HIGH_POWER_APPS, Menu.NONE, R.string.high_power_apps);
+
+        PowerUsageFeatureProvider powerUsageFeatureProvider =
+                FeatureFactory.getFactory(getContext()).getPowerUsageFeatureProvider(getContext());
+        if (powerUsageFeatureProvider != null &&
+                powerUsageFeatureProvider.isAdditionalBatteryInfoEnabled()) {
+            menu.add(Menu.NONE, MENU_ADDITIONAL_BATTERY_INFO,
+                    Menu.NONE, R.string.additional_battery_info);
+        }
         super.onCreateOptionsMenu(menu, inflater);
     }
 
@@ -163,6 +175,11 @@
                 sa.startPreferencePanel(ManageApplications.class.getName(), args,
                         R.string.high_power_apps, null, null, 0);
                 return true;
+            case MENU_ADDITIONAL_BATTERY_INFO:
+                startActivity(FeatureFactory.getFactory(getContext())
+                        .getPowerUsageFeatureProvider(getContext())
+                        .getAdditionalBatteryInfoIntent());
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 0e637a7..c7dbfdd 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -155,7 +155,7 @@
     }
 
     protected void setupImportancePrefs(boolean notBlockable, boolean notSilenceable,
-                                        int importance, boolean banned) {
+            int importance, boolean banned) {
         if (mShowSlider && !notSilenceable) {
             setVisible(mBlock, false);
             setVisible(mSilent, false);
@@ -176,10 +176,11 @@
             });
         } else {
             setVisible(mImportance, false);
-            if (notBlockable) {
+            // Hide controls that are not settable, unless they are already switched on.
+            final boolean blocked = (importance == Ranking.IMPORTANCE_NONE || banned);
+            if (notBlockable && !blocked) {
                 setVisible(mBlock, false);
             } else {
-                boolean blocked = importance == Ranking.IMPORTANCE_NONE || banned;
                 mBlock.setChecked(blocked);
                 mBlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                     @Override
@@ -193,10 +194,11 @@
                     }
                 });
             }
-            if (notSilenceable) {
+            final boolean silenced = (importance == Ranking.IMPORTANCE_LOW);
+            if (notSilenceable && !silenced) {
                 setVisible(mSilent, false);
             } else {
-                mSilent.setChecked(importance == Ranking.IMPORTANCE_LOW);
+                mSilent.setChecked(silenced);
                 mSilent.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                     @Override
                     public boolean onPreferenceChange(Preference preference, Object newValue) {
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 1bffc2b..277642d 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -21,6 +21,8 @@
 import android.util.Log;
 
 import com.android.settings.R;
+import com.android.settings.dashboard.SuggestionFeatureProvider;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 
 /**
  * Abstract class for creating feature controllers. Allows OEM implementations to define their own
@@ -59,8 +61,14 @@
         return sFactory;
     }
 
+    public abstract SuggestionFeatureProvider getSuggestionFeatureProvider();
+
     public abstract SupportFeatureProvider getSupportFeatureProvider(Context context);
 
+    public abstract PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context);
+
+    public abstract SurveyFeatureProvider getSurveyFeatureProvider(Context context);
+
     public static final class FactoryNotFoundException extends RuntimeException {
         public FactoryNotFoundException(Throwable throwable) {
             super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index ce561f3..a31ce72 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -17,15 +17,40 @@
 package com.android.settings.overlay;
 
 import android.content.Context;
+import android.support.annotation.Keep;
+import com.android.settings.dashboard.SuggestionFeatureProvider;
+import com.android.settings.dashboard.SuggestionFeatureProviderImpl;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 
 /**
  * {@link FeatureFactory} implementation for AOSP Settings.
  */
-public final class FeatureFactoryImpl extends FeatureFactory {
+@Keep
+public class FeatureFactoryImpl extends FeatureFactory {
+
+    private SuggestionFeatureProvider mSuggestionFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
         return null;
     }
 
+    @Override
+    public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
+        return null;
+    }
+
+    @Override
+    public SurveyFeatureProvider getSurveyFeatureProvider(Context context) {
+        return null;
+    }
+
+    @Override
+    public SuggestionFeatureProvider getSuggestionFeatureProvider() {
+        if (mSuggestionFeatureProvider == null) {
+            mSuggestionFeatureProvider = new SuggestionFeatureProviderImpl();
+        }
+        return mSuggestionFeatureProvider;
+    }
+
 }
diff --git a/src/com/android/settings/overlay/SurveyFeatureProvider.java b/src/com/android/settings/overlay/SurveyFeatureProvider.java
new file mode 100644
index 0000000..416a602
--- /dev/null
+++ b/src/com/android/settings/overlay/SurveyFeatureProvider.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 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.overlay;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.support.annotation.Nullable;
+import android.support.v4.content.LocalBroadcastManager;
+
+/**
+ * An interface for classes wishing to provide the ability to serve surveys to implement.
+ */
+public interface SurveyFeatureProvider {
+
+    /**
+     * Downloads a survey asynchronously to shared preferences to be served at a later date.
+     *
+     * @param activity A valid context.
+     * @param surveyId A unique Id representing a survey to download.
+     * @param data a text blob to be attached to the survey results.
+     */
+    void downloadSurvey(Activity activity, String surveyId, @Nullable String data);
+
+    /**
+     * Shows a previously downloaded survey/prompt if possible in the activity provided.
+     *
+     * @param activity The host activity to show the survey in.
+     * @param surveyId A unique Id representing a survey to download.
+     * @return A boolean indicating if a survey was shown or not.
+     */
+    boolean showSurveyIfAvailable(Activity activity, String surveyId);
+
+    /**
+     * A helper method to get the surveyId. Implementers should create a mapping of
+     * keys to surveyIds and provide them via this function.
+     *
+     * @param context A valid context.
+     * @param simpleKey The simple name of the key to get the surveyId for.
+     * @return The unique Id as a string or null on error.
+     */
+    String getSurveyId(Context context, String simpleKey);
+
+    /**
+     * Removes the survey for {@code siteId} if it expired, then returns the expiration date (as a
+     * unix timestamp) for the remaining survey should it exist and be ready to show. Returns -1 if
+     * no valid survey exists after removing the potentially expired one.
+     *
+     * @param context the calling context.
+     * @param surveyId the site ID.
+     * @return the unix timestamp for the available survey for the given {@coe siteId} or -1 if
+     * there is none available.
+     */
+    long getSurveyExpirationDate(Context context, String surveyId);
+
+    /**
+     * Registers an activity to show surveys/prompts as soon as they are downloaded. The receiver
+     * should be unregistered prior to destroying the activity to avoid undefined behavior by
+     * calling {@link #unregisterReceiver(Activity, BroadcastReceiver)}.
+     * @param activity The activity that should show surveys once they are downloaded.
+     * @return the broadcast receiver listening for survey downloads. Must be unregistered before
+     * leaving the activity.
+     */
+    BroadcastReceiver createAndRegisterReceiver(Activity activity);
+
+    /**
+     * Unregisters the broadcast receiver for this activity. Should only be called once per activity
+     * after a call to {@link #createAndRegisterReceiver(Activity)}.
+     * @param activity The activity that was used to register the BroadcastReceiver.
+     */
+    static void unregisterReceiver(Activity activity, BroadcastReceiver receiver) {
+        if (activity == null) {
+            throw new IllegalStateException("Cannot unregister receiver if activity is null");
+        }
+
+        LocalBroadcastManager.getInstance(activity).unregisterReceiver(receiver);
+    }
+}
diff --git a/src/com/android/settings/password/FingerprintManagerWrapper.java b/src/com/android/settings/password/FingerprintManagerWrapper.java
new file mode 100644
index 0000000..b00f786
--- /dev/null
+++ b/src/com/android/settings/password/FingerprintManagerWrapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.password;
+
+import android.annotation.NonNull;
+import android.hardware.fingerprint.FingerprintManager;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Wrapper of {@link FingerprintManager}. Workaround for roboelectic testing. See
+ * {@link IFingerprintManager} for details.
+ */
+public class FingerprintManagerWrapper implements IFingerprintManager {
+    private @NonNull FingerprintManager mFingerprintManager;
+
+    public FingerprintManagerWrapper(@NonNull FingerprintManager fingerprintManager) {
+        Preconditions.checkNotNull(fingerprintManager);
+        mFingerprintManager = fingerprintManager;
+    }
+
+    public boolean isHardwareDetected() {
+        return mFingerprintManager.isHardwareDetected();
+    }
+
+    public boolean hasEnrolledFingerprints(int userId) {
+        return mFingerprintManager.hasEnrolledFingerprints(userId);
+    }
+
+    public long preEnroll() {
+        return mFingerprintManager.preEnroll();
+    }
+}
diff --git a/src/com/android/settings/password/IFingerprintManager.java b/src/com/android/settings/password/IFingerprintManager.java
new file mode 100644
index 0000000..15a9242
--- /dev/null
+++ b/src/com/android/settings/password/IFingerprintManager.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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.password;
+
+/**
+ * This is the workaround to allow us test {@link SetNewPasswordController} which uses a new hidden
+ * API {@link android.hardware.fingerprint.FingerprintManager#hasEnrolledFingerprints(int)} that
+ * roboelectric does not support yet. Having roboelectic to support latest platform API is tracked
+ * in b/30995831.
+ */
+public interface IFingerprintManager {
+    boolean isHardwareDetected();
+
+    boolean hasEnrolledFingerprints(int userId);
+
+    long preEnroll();
+}
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index 7cdf006..b06da02 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -16,13 +16,18 @@
 
 package com.android.settings.password;
 
-import android.annotation.Nullable;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 
 import com.android.settings.ChooseLockGeneric;
+import com.android.settings.SetupChooseLockGeneric;
+import com.android.settings.Utils;
 
 /**
  * Trampolines {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and
@@ -30,6 +35,7 @@
  * activity for handling set new password.
  */
 public class SetNewPasswordActivity extends Activity implements SetNewPasswordController.Ui {
+    private static final String TAG = "SetNewPasswordActivity";
     private String mNewPasswordAction;
     private SetNewPasswordController mSetNewPasswordController;
 
@@ -38,17 +44,22 @@
         super.onCreate(savedState);
 
         mNewPasswordAction = getIntent().getAction();
-        mSetNewPasswordController = new SetNewPasswordController(this, this);
+        if (!ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
+                && !ACTION_SET_NEW_PARENT_PROFILE_PASSWORD.equals(mNewPasswordAction)) {
+            Log.e(TAG, "Unexpected action to launch this activity");
+            finish();
+            return;
+        }
+        mSetNewPasswordController = SetNewPasswordController.create(
+                this, this, getIntent(), getActivityToken());
         mSetNewPasswordController.dispatchSetNewPasswordIntent();
     }
 
     @Override
-    public void launchChooseLock(@Nullable Bundle chooseLockFingerprintExtras) {
+    public void launchChooseLock(Bundle chooseLockFingerprintExtras) {
         Intent intent = new Intent(this, ChooseLockGeneric.class)
                 .setAction(mNewPasswordAction);
-        if (chooseLockFingerprintExtras != null) {
-            intent.putExtras(chooseLockFingerprintExtras);
-        }
+        intent.putExtras(chooseLockFingerprintExtras);
         startActivity(intent);
         finish();
     }
diff --git a/src/com/android/settings/password/SetNewPasswordController.java b/src/com/android/settings/password/SetNewPasswordController.java
index 470723b..b2b20d9 100644
--- a/src/com/android/settings/password/SetNewPasswordController.java
+++ b/src/com/android/settings/password/SetNewPasswordController.java
@@ -16,20 +16,27 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
-import android.os.UserHandle;
+import android.os.IBinder;
+import android.os.UserManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.ChooseLockGeneric;
 import com.android.settings.ChooseLockSettingsHelper;
+import com.android.settings.Utils;
 
 /**
  * Business logic for {@link SetNewPasswordActivity}.
@@ -42,31 +49,54 @@
 
     interface Ui {
         /** Starts the {@link ChooseLockGeneric} activity with the given extras. */
-        void launchChooseLock(@Nullable Bundle chooseLockFingerprintExtras);
+        void launchChooseLock(Bundle chooseLockFingerprintExtras);
     }
 
-    private final int mCurrentUserId;
+    /**
+     * Which user is setting new password.
+     */
+    private final int mTargetUserId;
     private final PackageManager mPackageManager;
-    @Nullable private final FingerprintManager mFingerprintManager;
+    @Nullable private final IFingerprintManager mFingerprintManager;
     private final DevicePolicyManager mDevicePolicyManager;
     private final Ui mUi;
 
-    public SetNewPasswordController(Context context, Ui ui) {
-        this(context.getUserId(),
+    public static SetNewPasswordController create(Context context, Ui ui, Intent intent,
+            IBinder activityToken) {
+        // Trying to figure out which user is setting new password. If it is
+        // ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or the calling user is not allowed to set
+        // separate profile challenge, it is the current user to set new password. Otherwise,
+        // it is the user who starts this activity setting new password.
+        int userId = ActivityManager.getCurrentUser();
+        if (ACTION_SET_NEW_PASSWORD.equals(intent.getAction())) {
+            final int callingUserId = Utils.getSecureTargetUser(activityToken,
+                    UserManager.get(context), null, intent.getExtras()).getIdentifier();
+            final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+            if (lockPatternUtils.isSeparateProfileChallengeAllowed(callingUserId)) {
+                userId = callingUserId;
+            }
+        }
+        // Create a wrapper of FingerprintManager for testing, see IFingerPrintManager for details.
+        final FingerprintManager fingerprintManager =
+                (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
+        final IFingerprintManager fingerprintManagerWrapper =
+                fingerprintManager == null
+                        ? null
+                        : new FingerprintManagerWrapper(fingerprintManager);
+        return new SetNewPasswordController(userId,
                 context.getPackageManager(),
-                (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE),
-                (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE),
-                ui);
+                fingerprintManagerWrapper,
+                (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE), ui);
     }
 
     @VisibleForTesting
     SetNewPasswordController(
-            int currentUserId,
+            int targetUserId,
             PackageManager packageManager,
-            FingerprintManager fingerprintManager,
+            IFingerprintManager fingerprintManager,
             DevicePolicyManager devicePolicyManager,
             Ui ui) {
-        mCurrentUserId = currentUserId;
+        mTargetUserId = targetUserId;
         mPackageManager = checkNotNull(packageManager);
         mFingerprintManager = fingerprintManager;
         mDevicePolicyManager = checkNotNull(devicePolicyManager);
@@ -77,38 +107,38 @@
      * Dispatches the set new password intent to the correct activity that handles it.
      */
     public void dispatchSetNewPasswordIntent() {
+        final Bundle extras;
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
                 && mFingerprintManager != null
                 && mFingerprintManager.isHardwareDetected()
-                && !mFingerprintManager.hasEnrolledFingerprints()
+                && !mFingerprintManager.hasEnrolledFingerprints(mTargetUserId)
                 && !isFingerprintDisabledByAdmin()) {
-            mUi.launchChooseLock(getFingerprintChooseLockExtras());
+            extras = getFingerprintChooseLockExtras();
         } else {
-            mUi.launchChooseLock(null);
+            extras = new Bundle();
         }
+        // No matter we show fingerprint options or not, we should tell the next activity which
+        // user is setting new password.
+        extras.putInt(Intent.EXTRA_USER_ID, mTargetUserId);
+        mUi.launchChooseLock(extras);
     }
 
     private Bundle getFingerprintChooseLockExtras() {
         Bundle chooseLockExtras = new Bundle();
-        if (mFingerprintManager != null) {
-            long challenge = mFingerprintManager.preEnroll();
-            chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
-                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-            chooseLockExtras.putBoolean(
-                    ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
-            chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
-            chooseLockExtras.putLong(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
-            chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
-            if (mCurrentUserId != UserHandle.USER_NULL) {
-                chooseLockExtras.putInt(Intent.EXTRA_USER_ID, mCurrentUserId);
-            }
-        }
+        long challenge = mFingerprintManager.preEnroll();
+        chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
+                PASSWORD_QUALITY_SOMETHING);
+        chooseLockExtras.putBoolean(
+                ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
+        chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
+        chooseLockExtras.putLong(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+        chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
         return chooseLockExtras;
     }
 
     private boolean isFingerprintDisabledByAdmin() {
-        int disabledFeatures = mDevicePolicyManager.getKeyguardDisabledFeatures(
-                null, mCurrentUserId);
-        return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
+        int disabledFeatures =
+                mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
+        return (disabledFeatures & KEYGUARD_DISABLE_FINGERPRINT) != 0;
     }
 }
diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java
index f064050..15be224 100644
--- a/src/com/android/settings/wifi/WifiEnabler.java
+++ b/src/com/android/settings/wifi/WifiEnabler.java
@@ -226,8 +226,13 @@
         if (mayDisableTethering(isChecked)) {
             mWifiManager.setWifiApEnabled(null, false);
         }
-        MetricsLogger.action(mContext,
-                isChecked ? MetricsEvent.ACTION_WIFI_ON : MetricsEvent.ACTION_WIFI_OFF);
+        if (isChecked) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WIFI_ON);
+        } else {
+            // Log if user was connected at the time of switching off.
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WIFI_OFF,
+                    mConnected.get());
+        }
         if (!mWifiManager.setWifiEnabled(isChecked)) {
             // Error
             mSwitchBar.setEnabled(true);
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 7007d19..1d07f6c 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -481,12 +481,13 @@
         }
         switch (item.getItemId()) {
             case MENU_ID_CONNECT: {
-                if (mSelectedAccessPoint.isSaved()) {
-                    connect(mSelectedAccessPoint.getConfig());
+                boolean isSavedNetwork = mSelectedAccessPoint.isSaved();
+                if (isSavedNetwork) {
+                    connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
                 } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
                     /** Bypass dialog for unsecured networks */
                     mSelectedAccessPoint.generateOpenNetworkConfig();
-                    connect(mSelectedAccessPoint.getConfig());
+                    connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
                 } else {
                     showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
                 }
@@ -519,7 +520,7 @@
             if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
                     !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
                 mSelectedAccessPoint.generateOpenNetworkConfig();
-                connect(mSelectedAccessPoint.getConfig());
+                connect(mSelectedAccessPoint.getConfig(), false /* isSavedNetwork */);
             } else if (mSelectedAccessPoint.isSaved()) {
                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_VIEW);
             } else {
@@ -813,14 +814,14 @@
         if (config == null) {
             if (mSelectedAccessPoint != null
                     && mSelectedAccessPoint.isSaved()) {
-                connect(mSelectedAccessPoint.getConfig());
+                connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
             }
         } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
             mWifiManager.save(config, mSaveListener);
         } else {
             mWifiManager.save(config, mSaveListener);
             if (mSelectedAccessPoint != null) { // Not an "Add network"
-                connect(config);
+                connect(config, false /* isSavedNetwork */);
             }
         }
 
@@ -850,13 +851,17 @@
         changeNextButtonState(false);
     }
 
-    protected void connect(final WifiConfiguration config) {
-        MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT);
+    protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
+        // Log subtype if configuration is a saved network.
+        MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT,
+                isSavedNetwork);
         mWifiManager.connect(config, mConnectListener);
     }
 
-    protected void connect(final int networkId) {
-        MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT);
+    protected void connect(final int networkId, boolean isSavedNetwork) {
+        // Log subtype if configuration is a saved network.
+        MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT,
+                isSavedNetwork);
         mWifiManager.connect(networkId, mConnectListener);
     }
 
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
index 2f35478..68f28ee 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
@@ -130,17 +130,17 @@
     }
 
     @Override
-    protected void connect(final WifiConfiguration config) {
+    protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
         WifiSetupActivity activity = (WifiSetupActivity) getActivity();
         activity.networkSelected();
-        super.connect(config);
+        super.connect(config, isSavedNetwork);
     }
 
     @Override
-    protected void connect(final int networkId) {
+    protected void connect(final int networkId, boolean isSavedNetwork) {
         WifiSetupActivity activity = (WifiSetupActivity) getActivity();
         activity.networkSelected();
-        super.connect(networkId);
+        super.connect(networkId, isSavedNetwork);
     }
 
     @Override