Merge "Fix crash when rotating HighPowerDetail dialog" am: 783ff43769 am: 7598c6201d
am: 8711f4055d

Change-Id: Idb2d601fa627dcb8268228435192392c760787d0
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f98a799..58d4642 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>
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/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index 54ff4db..2be1737 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
@@ -497,7 +499,7 @@
     }
 
     private void restoreFromBundle(Bundle b) {
-        if( b == null) {
+        if (b == null) {
             return;
         }
 
@@ -537,8 +539,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 +634,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 +862,7 @@
     }
 
     private final void updateNetworkType() {
-        if( phone != null ) {
+        if (phone != null) {
             ServiceState ss = phone.getServiceState();
             dataNetwork.setText(ServiceState.rilRadioTechnologyToString(
                     phone.getServiceState().getRilDataRadioTechnology()));
@@ -1069,10 +1071,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 +1147,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 +1198,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsVolteProvisionedState(isChecked);
-       }
+        }
     };
 
     private boolean isImsVtProvisioned() {
@@ -1185,7 +1213,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsVtProvisionedState(isChecked);
-       }
+        }
     };
 
     private boolean isImsWfcProvisioned() {
@@ -1200,7 +1228,7 @@
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             setImsWfcProvisionedState(isChecked);
-       }
+        }
     };
 
     private void updateImsProvisionedState() {
@@ -1210,14 +1238,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() {
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/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/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