Work Profile Passphrase Setting

Create a new section in Security Settings which includes all
settings for the Work Challenge.
Only some settings apply to the Work Challenge, so we reuse
the security settings layouts for items and compare them against
a whitelist to remove unwanted items.

Additionally, remove all usages of ChooseLockGeneric.KEY_USER_ID
in favor of Intent.EXTRA_USER_ID.

Change-Id: I3d1ba953a2056f7c61a7b3feeb8b49f1a352dff6
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fd359bc..bb6bbc0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3015,6 +3015,12 @@
     <!-- Displayed when user launches a widget configuration activity that was uninstalled -->
     <string name="activity_not_found">Application is not installed on your phone.</string>
 
+    <!-- Profile Lock settings -->
+    <!-- Security & location settings screen, header for profile specific section -->
+    <string name="lock_settings_profile_title">Work profile</string>
+    <!-- Security & location settings screen, setting option name -->
+    <string name="lock_settings_profile_label">Work profile security</string>
+
     <!-- Applications Settings --> <skip />
     <!-- Applications settings screen, setting option name for the user to go to the screen to manage installed applications  -->
     <string name="manageapplications_settings_title">Manage apps</string>
@@ -5562,6 +5568,7 @@
     <string name="keywords_ignore_optimizations">ignore optimizations, doze, app standby</string>
     <string name="keywords_color_mode">vibrant, RGB, sRGB, color, natural, standard</string>
     <string name="keywords_lockscreen">slide to unlock, password, pattern, PIN</string>
+    <string name="keywords_profile_challenge">work challenge, work, profile</string>
 
     <!-- NFC Wi-Fi pairing/setup strings-->
 
diff --git a/res/xml/profile_challenge_settings.xml b/res/xml/profile_challenge_settings.xml
new file mode 100644
index 0000000..cd61119
--- /dev/null
+++ b/res/xml/profile_challenge_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/lock_settings_profile_label">
+
+</PreferenceScreen>
diff --git a/res/xml/security_settings_profile.xml b/res/xml/security_settings_profile.xml
new file mode 100644
index 0000000..1c10b79
--- /dev/null
+++ b/res/xml/security_settings_profile.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+                  android:title="@string/lock_settings_picker_title">
+
+    <PreferenceCategory
+        android:key="security_category_profile"
+        android:title="@string/lock_settings_profile_title">
+
+        <PreferenceScreen
+            android:key="profile_challenge"
+            android:title="@string/lock_settings_profile_label"
+            settings:keywords="@string/keywords_profile_challenge"
+            android:persistent="false"/>
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 73f8c46..2641ad2 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -48,7 +48,6 @@
 
 public class ChooseLockGeneric extends SettingsActivity {
     public static final String CONFIRM_CREDENTIALS = "confirm_credentials";
-    public static final String KEY_USER_ID = "user_id";
 
     @Override
     public Intent getIntent() {
@@ -369,6 +368,10 @@
                             visible = false;
                         }
                     } else if (KEY_UNLOCK_SET_NONE.equals(key)) {
+                        if (mUserId != UserHandle.myUserId()) {
+                            // Swipe doesn't make sense for profiles.
+                            visible = false;
+                        }
                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
                     } else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
                         enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index cf330ad..eacf1ab 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -87,7 +87,7 @@
             boolean confirmCredentials, int userId) {
         Intent intent = createIntent(context, quality, minLength, maxLength,
                 requirePasswordToDecrypt, confirmCredentials);
-        intent.putExtra(ChooseLockGeneric.KEY_USER_ID, userId);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         return intent;
     }
 
@@ -103,7 +103,7 @@
             int maxLength, boolean requirePasswordToDecrypt, String password, int userId) {
         Intent intent = createIntent(context, quality, minLength, maxLength,
                 requirePasswordToDecrypt, password);
-        intent.putExtra(ChooseLockGeneric.KEY_USER_ID, userId);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         return intent;
     }
 
@@ -120,7 +120,7 @@
             int maxLength, boolean requirePasswordToDecrypt, long challenge, int userId) {
         Intent intent = createIntent(context, quality, minLength, maxLength,
                 requirePasswordToDecrypt, challenge);
-        intent.putExtra(ChooseLockGeneric.KEY_USER_ID, userId);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         return intent;
     }
 
diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java
index 4a6008c..e20db93 100644
--- a/src/com/android/settings/ChooseLockPattern.java
+++ b/src/com/android/settings/ChooseLockPattern.java
@@ -78,7 +78,7 @@
         intent.putExtra("key_lock_method", "pattern");
         intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials);
         intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePassword);
-        intent.putExtra(ChooseLockGeneric.KEY_USER_ID, userId);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         return intent;
     }
 
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index 53fbb7f..b88dea2 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -192,7 +192,7 @@
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
-        intent.putExtra(ChooseLockGeneric.KEY_USER_ID, userId);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
         if (external) {
             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
diff --git a/src/com/android/settings/ProfileChallengePreferenceFragment.java b/src/com/android/settings/ProfileChallengePreferenceFragment.java
new file mode 100644
index 0000000..34c38f0
--- /dev/null
+++ b/src/com/android/settings/ProfileChallengePreferenceFragment.java
@@ -0,0 +1,193 @@
+/*
+ * 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;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+import android.support.v7.preference.Preference.OnPreferenceClickListener;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Settings for the Profile Challenge.
+ */
+public class ProfileChallengePreferenceFragment extends SettingsPreferenceFragment
+        implements OnPreferenceChangeListener {
+    private static final String TAG = "WorkChallengePreferenceFragment";
+
+    private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
+    private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
+
+    private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
+
+    // Not all preferences make sense for the Work Challenge, this is a whitelist.
+    private static final Set<String> ALLOWED_PREFERENCE_KEYS = new HashSet<>();
+    {
+        ALLOWED_PREFERENCE_KEYS.add(KEY_UNLOCK_SET_OR_CHANGE);
+        ALLOWED_PREFERENCE_KEYS.add(KEY_VISIBLE_PATTERN);
+    }
+    // These switch preferences need special handling since they're not all stored in Settings.
+    private static final Set<String> SWITCH_PREFERENCE_KEYS = new HashSet<>();
+    {
+        SWITCH_PREFERENCE_KEYS.add(KEY_VISIBLE_PATTERN);
+    }
+
+    private LockPatternUtils mLockPatternUtils;
+    private int mProfileUserId;
+
+    private SwitchPreference mVisiblePattern;
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.PROFILE_CHALLENGE;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLockPatternUtils = new LockPatternUtils(getActivity());
+
+        mProfileUserId = getArguments().getInt(Intent.EXTRA_USER_ID, -1);
+        if (mProfileUserId == -1) {
+            finish();
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        final String key = preference.getKey();
+        if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
+            Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
+            startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
+                    R.string.lock_settings_picker_title, SET_OR_CHANGE_LOCK_METHOD_REQUEST, extras);
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object value) {
+        final String key = preference.getKey();
+        if (KEY_VISIBLE_PATTERN.equals(key)) {
+            mLockPatternUtils.setVisiblePatternEnabled((Boolean) value, mProfileUserId);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        // Make sure we reload the preference hierarchy since some of these settings
+        // depend on others...
+        createPreferenceHierarchy();
+
+        if (mVisiblePattern != null) {
+            mVisiblePattern.setChecked(mLockPatternUtils.isVisiblePatternEnabled(
+                    mProfileUserId));
+        }
+    }
+
+    private void createPreferenceHierarchy() {
+        PreferenceScreen root = getPreferenceScreen();
+        if (root != null) {
+            root.removeAll();
+        }
+        addPreferencesFromResource(R.xml.profile_challenge_settings);
+        root = getPreferenceScreen();
+
+        // Add options for lock/unlock screen
+        final int resid = getResIdForLockUnlockScreen(getActivity(), mLockPatternUtils);
+        addPreferencesFromResource(resid);
+
+        mVisiblePattern = (SwitchPreference) root.findPreference(KEY_VISIBLE_PATTERN);
+
+        removeNonWhitelistedItems(root);
+    }
+
+    private void removeNonWhitelistedItems(PreferenceGroup prefScreen) {
+        int numPreferences = prefScreen.getPreferenceCount();
+        int i = 0;
+        while (i < numPreferences) {
+            final Preference pref = prefScreen.getPreference(i);
+            // Recursively look into categories and remove them if they are empty.
+            if (pref instanceof PreferenceCategory) {
+                PreferenceCategory category = (PreferenceCategory) pref;
+                removeNonWhitelistedItems(category);
+                if (category.getPreferenceCount() == 0) {
+                    prefScreen.removePreference(category);
+                    --i;
+                    --numPreferences;
+                }
+            } else if (ALLOWED_PREFERENCE_KEYS.contains(pref.getKey())) {
+                if (SWITCH_PREFERENCE_KEYS.contains(pref.getKey())) {
+                    pref.setOnPreferenceChangeListener(this);
+                }
+            } else {
+                prefScreen.removePreference(pref);
+                --i;
+                --numPreferences;
+            }
+            ++i;
+        }
+    }
+
+    private int getResIdForLockUnlockScreen(Context context,
+            LockPatternUtils lockPatternUtils) {
+        int resid = 0;
+        if (!lockPatternUtils.isSecure(mProfileUserId)) {
+            resid = R.xml.security_settings_lockscreen;
+        } else {
+            switch (lockPatternUtils.getKeyguardStoredPasswordQuality(mProfileUserId)) {
+                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+                    resid = R.xml.security_settings_pattern;
+                    break;
+                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+                    resid = R.xml.security_settings_pin;
+                    break;
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+                    resid = R.xml.security_settings_password;
+                    break;
+            }
+        }
+        return resid;
+    }
+}
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 4ce76ce..4e9447e 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
@@ -79,6 +80,7 @@
 
     // Lock Settings
     private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
+    private static final String KEY_UNLOCK_SET_OR_CHANGE_PROFILE = "profile_challenge";
     private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
     private static final String KEY_SECURITY_CATEGORY = "security_category";
     private static final String KEY_DEVICE_ADMIN_CATEGORY = "device_admin_category";
@@ -138,6 +140,8 @@
     private Intent mTrustAgentClickIntent;
     private Preference mOwnerInfoPref;
 
+    private int mProfileChallengeUserId;
+
     @Override
     protected int getMetricsCategory() {
         return MetricsLogger.SECURITY;
@@ -209,6 +213,20 @@
         final int resid = getResIdForLockUnlockScreen(getActivity(), mLockPatternUtils);
         addPreferencesFromResource(resid);
 
+        List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId());
+        int numProfiles = profiles.size();
+        if (numProfiles > 1) {
+            for (int i = 0; i < numProfiles; ++i) {
+                UserInfo profile = profiles.get(i);
+                if (profile.id != UserHandle.myUserId()) {
+                    mProfileChallengeUserId = profile.id;
+                }
+            }
+            if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+                addPreferencesFromResource(R.xml.security_settings_profile);
+            }
+        }
+
         // Add options for device encryption
         mIsAdmin = mUm.isAdminUser();
 
@@ -655,6 +673,11 @@
         if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
             startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
                     R.string.lock_settings_picker_title, SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
+        } else if (KEY_UNLOCK_SET_OR_CHANGE_PROFILE.equals(key)) {
+            Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_USER_ID, mProfileChallengeUserId);
+            startFragment(this, "com.android.settings.ProfileChallengePreferenceFragment",
+                    R.string.lock_settings_profile_label, SET_OR_CHANGE_LOCK_METHOD_REQUEST, extras);
         } else if (KEY_TRUST_AGENT.equals(key)) {
             ChooseLockSettingsHelper helper =
                     new ChooseLockSettingsHelper(this.getActivity(), this);
@@ -752,6 +775,13 @@
             result.add(sir);
 
             final UserManager um = UserManager.get(context);
+            boolean hasChildProfile = um.getProfiles(UserHandle.myUserId()).size() > 1;
+            if (hasChildProfile && LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+                sir = new SearchIndexableResource(context);
+                sir.xmlResId = R.xml.security_settings_profile;
+                result.add(sir);
+            }
+
             if (um.isAdminUser()) {
                 DevicePolicyManager dpm = (DevicePolicyManager)
                         context.getSystemService(Context.DEVICE_POLICY_SERVICE);
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index ba975a2..3014225 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -335,6 +335,17 @@
         }
     };
 
+    private final BroadcastReceiver mUserAddRemoveReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_USER_ADDED)
+                    || action.equals(Intent.ACTION_USER_REMOVED)) {
+                Index.getInstance(getApplicationContext()).update();
+            }
+        }
+    };
+
     private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
             new DynamicIndexableContentMonitor();
 
@@ -752,6 +763,8 @@
                 mDevelopmentPreferencesListener);
 
         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_ADDED));
+        registerReceiver(mUserAddRemoveReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
 
         mDynamicIndexableContentMonitor.register(this);
 
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index ddea92b..9aa0403 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1026,7 +1026,7 @@
         if (bundle == null) {
             return getEffectiveUserId(context);
         }
-        int userId = bundle.getInt(ChooseLockGeneric.KEY_USER_ID, UserHandle.myUserId());
+        int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
         return getSameOwnerUserId(context, userId);
     }