Fix enterprise settings bug

This fixes a bug where the picker
is not using the correct user id
and also ensures that the settings
activity uses the correct user when
launched.

Test: flash & test w/ work profile
Bug 322071206

Change-Id: I846593ff9ae320d9bb774e3e79ed9ef41f101ff5
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 195c44e..b728063 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4403,9 +4403,12 @@
         <activity android:name=".applications.credentials.CredentialsPickerActivity"
                 android:excludeFromRecents="true"
                 android:launchMode="singleInstance"
-                android:exported="false">
-            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.applications.credentials.DefaultCombinedPicker" />
+                android:exported="true">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
         </activity>
 
         <activity
@@ -4550,19 +4553,6 @@
                 android:exported="false">
         </activity>
 
-        <activity android:name=".applications.autofill.AutofillPickerTrampolineActivity"
-                android:theme="@android:style/Theme.NoDisplay"
-                android:excludeFromRecents="true"
-                android:launchMode="singleInstance"
-                android:exported="true"
-                android:label="@string/autofill_app">
-            <intent-filter android:priority="1">
-                <action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="package" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="Settings$AdvancedConnectedDeviceActivity"
                   android:exported="true"
                   android:label="@string/connected_device_connections_title">
diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
index f8a3b0f..4f27870 100644
--- a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
+++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.applications.credentials;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -24,7 +25,6 @@
 import android.credentials.CredentialProviderInfo;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.service.autofill.AutofillServiceInfo;
 import android.text.TextUtils;
 import android.util.IconDrawableFactory;
@@ -49,7 +49,7 @@
     private static final String TAG = "CombinedProviderInfo";
     private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
     private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
-            "android.intent.category.LAUNCHER";
+            "android.intent.category.DEFAULT";
 
     private final List<CredentialProviderInfo> mCredentialProviderInfos;
     private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
@@ -327,10 +327,8 @@
     }
 
     public static @Nullable Intent createSettingsActivityIntent(
-            @NonNull Context context,
             @Nullable CharSequence packageName,
-            @Nullable CharSequence settingsActivity,
-            int currentUserId) {
+            @Nullable CharSequence settingsActivity) {
         if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
             return null;
         }
@@ -350,19 +348,25 @@
         Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
         intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
         intent.setComponent(cn);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
 
-        int contextUserId = context.getUser().getIdentifier();
-        if (currentUserId != contextUserId && UserManager.isHeadlessSystemUserMode()) {
-            Log.w(
-                    TAG,
-                    "onLeftSideClicked(): using context for current user ("
-                            + currentUserId
-                            + ") instead of user "
-                            + contextUserId
-                            + " on headless system user mode");
-            context = context.createContextAsUser(UserHandle.of(currentUserId), /* flags= */ 0);
+    /** Launches the settings activity intent. */
+    public static void launchSettingsActivityIntent(
+            @NonNull Context context,
+            @Nullable CharSequence packageName,
+            @Nullable CharSequence settingsActivity,
+            int userId) {
+        Intent settingsIntent = createSettingsActivityIntent(packageName, settingsActivity);
+        if (settingsIntent == null) {
+            return;
         }
 
-        return intent;
+        try {
+            context.startActivityAsUser(settingsIntent, UserHandle.of(userId));
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to open settings activity", e);
+        }
     }
 }
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 2f04b62..8ea7a9c1 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -20,7 +20,6 @@
 
 import android.app.Activity;
 import android.app.Dialog;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -653,16 +652,8 @@
 
                     @Override
                     public void onLeftSideClicked() {
-                        Intent settingsIntent =
-                                CombinedProviderInfo.createSettingsActivityIntent(
-                                        mContext, packageName, settingsActivity, getUser());
-                        if (settingsIntent != null) {
-                            try {
-                                mContext.startActivity(settingsIntent);
-                            } catch (ActivityNotFoundException e) {
-                                Log.e(TAG, "Failed to open settings activity", e);
-                            }
-                        }
+                        CombinedProviderInfo.launchSettingsActivityIntent(
+                                mContext, packageName, settingsActivity, getUser());
                     }
                 });
 
diff --git a/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java b/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
index 495c104..479a184 100644
--- a/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
+++ b/src/com/android/settings/applications/credentials/CredentialsPickerActivity.java
@@ -16,15 +16,53 @@
 
 package com.android.settings.applications.credentials;
 
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.SettingsActivity;
 
-/** Standalone activity used to launch a {@link DefaultCombinedPicker} fragment. */
+/**
+ * Standalone activity used to launch a {@link DefaultCombinedPicker} fragment if the user is a
+ * normal user, a {@link DefaultCombinedPickerWork} fragment if the user is a work profile or {@link
+ * DefaultCombinedPickerPrivate} fragment if the user is a private profile.
+ */
 public class CredentialsPickerActivity extends SettingsActivity {
+    private static final String TAG = "CredentialsPickerActivity";
+
+    /** Injects the fragment name into the intent so the correct fragment is opened. */
+    @VisibleForTesting
+    public static void injectFragmentIntoIntent(Context context, Intent intent) {
+        final int userId = UserHandle.myUserId();
+        final UserManager userManager = UserManager.get(context);
+
+        if (DefaultCombinedPickerWork.isUserHandledByFragment(userManager, userId)) {
+            Slog.d(TAG, "Creating picker fragment using work profile");
+            intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerWork.class.getName());
+        } else if (DefaultCombinedPickerPrivate.isUserHandledByFragment(userManager)) {
+            Slog.d(TAG, "Creating picker fragment using private profile");
+            intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerPrivate.class.getName());
+        } else {
+            Slog.d(TAG, "Creating picker fragment using normal profile");
+            intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPicker.class.getName());
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        injectFragmentIntoIntent(this, getIntent());
+        super.onCreate(savedInstanceState);
+    }
 
     @Override
     protected boolean isValidFragment(String fragmentName) {
         return super.isValidFragment(fragmentName)
-                || DefaultCombinedPicker.class.getName().equals(fragmentName);
+                || DefaultCombinedPicker.class.getName().equals(fragmentName)
+                || DefaultCombinedPickerWork.class.getName().equals(fragmentName)
+                || DefaultCombinedPickerPrivate.class.getName().equals(fragmentName);
     }
 }
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
index 722cb1a..8d8af0e 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
@@ -17,14 +17,29 @@
 package com.android.settings.applications.credentials;
 
 import android.os.UserManager;
+import android.util.Slog;
 
 import com.android.settings.Utils;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
 
 public class DefaultCombinedPickerPrivate extends DefaultCombinedPicker {
+    private static final String TAG = "DefaultCombinedPickerPrivate";
+
     @Override
     protected int getUser() {
         UserManager userManager = getContext().getSystemService(UserManager.class);
         return Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
     }
+
+    /** Returns whether the user is handled by this fragment. */
+    public static boolean isUserHandledByFragment(UserManager userManager) {
+        try {
+            // If there is no private profile then this will throw an exception.
+            Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
+            return true;
+        } catch (IllegalStateException e) {
+            Slog.e(TAG, "Failed to get private profile user id", e);
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java b/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
index 9808502..945d6b8 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPickerWork.java
@@ -19,13 +19,16 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
-import com.android.settings.Utils;
-
 public class DefaultCombinedPickerWork extends DefaultCombinedPicker {
+    private static final String TAG = "DefaultCombinedPickerWork";
 
     @Override
     protected int getUser() {
-        UserHandle workProfile = Utils.getManagedProfile(UserManager.get(getContext()));
-        return workProfile.getIdentifier();
+        return UserHandle.myUserId();
+    }
+
+    /** Returns whether the user is handled by this fragment. */
+    public static boolean isUserHandledByFragment(UserManager userManager, int userId) {
+        return userManager.isManagedProfile(userId);
     }
 }
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
index 0fb1769..49dd7cd 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.applications.credentials;
 
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.credentials.CredentialManager;
@@ -26,11 +25,11 @@
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
 import android.view.autofill.AutofillManager;
-import android.util.Slog;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +82,7 @@
         // hand side presses to align the UX.
         if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
             // We need to return an empty intent here since the class we inherit
-            // from will throw an NPE if we return null and we don't want it to 
+            // from will throw an NPE if we return null and we don't want it to
             // open anything since we added the buttons.
             return new Intent();
         }
@@ -99,10 +98,10 @@
                     topProvider.getAppName(mContext),
                     topProvider.getSettingsSubtitle(),
                     topProvider.getAppIcon(mContext, getUser()),
-                    createSettingsActivityIntent(
-                            topProvider.getPackageName(), topProvider.getSettingsActivity()));
+                    topProvider.getPackageName(),
+                    topProvider.getSettingsActivity());
         } else {
-            updatePreferenceForProvider(preference, null, null, null, null);
+            updatePreferenceForProvider(preference, null, null, null, null, null);
         }
     }
 
@@ -112,7 +111,8 @@
             @Nullable CharSequence appName,
             @Nullable String appSubtitle,
             @Nullable Drawable appIcon,
-            @Nullable Intent settingsActivityIntent) {
+            @Nullable CharSequence packageName,
+            @Nullable CharSequence settingsActivity) {
         if (appName == null) {
             preference.setTitle(R.string.app_list_preference_none);
         } else {
@@ -133,13 +133,8 @@
             primaryPref.setDelegate(
                     new PrimaryProviderPreference.Delegate() {
                         public void onOpenButtonClicked() {
-                            if (settingsActivityIntent != null) {
-                                try {
-                                    startActivity(settingsActivityIntent);
-                                } catch (ActivityNotFoundException e) {
-                                    Slog.e(TAG, "Failed to open settings activity", e);
-                                }
-                            }
+                            CombinedProviderInfo.launchSettingsActivityIntent(
+                                    mContext, packageName, settingsActivity, getUser());
                         }
 
                         public void onChangeButtonClicked() {
@@ -148,7 +143,7 @@
                     });
 
             // Hide the open button if there is no defined settings activity.
-            primaryPref.setOpenButtonVisible(settingsActivityIntent != null);
+            primaryPref.setOpenButtonVisible(!TextUtils.isEmpty(settingsActivity));
             primaryPref.setButtonsVisible(appName != null);
         }
     }
@@ -198,13 +193,8 @@
 
     /** Creates an intent to open the credential picker. */
     private Intent createIntentToOpenPicker() {
-        return new Intent(mContext, CredentialsPickerActivity.class);
-    }
-
-    /** Creates an intent to open the settings activity of the primary provider (if available). */
-    public @Nullable Intent createSettingsActivityIntent(
-            @Nullable String packageName, @Nullable String settingsActivity) {
-        return CombinedProviderInfo.createSettingsActivityIntent(
-                mContext, packageName, settingsActivity, getUser());
+        final Context context =
+                mContext.createContextAsUser(UserHandle.of(getUser()), /* flags= */ 0);
+        return new Intent(context, CredentialsPickerActivity.class);
     }
 }
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java
new file mode 100644
index 0000000..044c23d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialsPickerActivityTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 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.applications.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class CredentialsPickerActivityTest {
+
+    @Mock private UserManager mUserManager;
+
+    private Context mMockContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMockContext = spy(ApplicationProvider.getApplicationContext());
+        when(mMockContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+    }
+
+    @Test
+    public void testInjectFragmentIntoIntent_normalProfile() {
+        Intent intent = new Intent();
+        CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+        assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(DefaultCombinedPicker.class.getName());
+    }
+
+    @Test
+    public void testInjectFragmentIntoIntent_workProfile() {
+        Intent intent = new Intent();
+
+        // Simulate managed / work profile.
+        when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
+        assertThat(DefaultCombinedPickerWork.isUserHandledByFragment(mUserManager, 10)).isTrue();
+
+        CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+        assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(DefaultCombinedPickerWork.class.getName());
+    }
+
+    @Test
+    public void testInjectFragmentIntoIntent_privateProfile() {
+        Intent intent = new Intent();
+
+        // Simulate private profile.
+        UserHandle privateUser = new UserHandle(100);
+        when(mUserManager.getUserInfo(100))
+                .thenReturn(new UserInfo(100, "", "", 0, UserManager.USER_TYPE_PROFILE_PRIVATE));
+        when(mUserManager.getUserProfiles()).thenReturn(Lists.newArrayList(privateUser));
+        assertThat(DefaultCombinedPickerPrivate.isUserHandledByFragment(mUserManager)).isTrue();
+
+        CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
+        assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(DefaultCombinedPickerPrivate.class.getName());
+    }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
index 301fcfa..d02240e 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
@@ -87,16 +87,14 @@
 
     @Test
     public void ensureSettingsActivityIntentCreatedSuccessfully() {
-        DefaultCombinedPreferenceController dcpc =
-                new DefaultCombinedPreferenceController(mContext);
-
         // Ensure that the settings activity is only created if we haved the right combination
         // of package and class name.
-        assertThat(dcpc.createSettingsActivityIntent(null, null)).isNull();
-        assertThat(dcpc.createSettingsActivityIntent("", null)).isNull();
-        assertThat(dcpc.createSettingsActivityIntent("", "")).isNull();
-        assertThat(dcpc.createSettingsActivityIntent("com.test", "")).isNull();
-        assertThat(dcpc.createSettingsActivityIntent("com.test", "ClassName")).isNotNull();
+        assertThat(CombinedProviderInfo.createSettingsActivityIntent(null, null)).isNull();
+        assertThat(CombinedProviderInfo.createSettingsActivityIntent("", null)).isNull();
+        assertThat(CombinedProviderInfo.createSettingsActivityIntent("", "")).isNull();
+        assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "")).isNull();
+        assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "ClassName"))
+                .isNotNull();
     }
 
     @Test
@@ -112,13 +110,13 @@
 
         // Update the preference to use the provider and make sure the view
         // was updated.
-        dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null);
+        dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null, null);
         assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
         assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
         assertThat(ppp.getIcon()).isEqualTo(appIcon);
 
         // Set the preference back to none and make sure the view was updated.
-        dcpc.updatePreferenceForProvider(ppp, null, null, null, null);
+        dcpc.updatePreferenceForProvider(ppp, null, null, null, null, null);
         assertThat(ppp.getTitle().toString()).isEqualTo("None");
         assertThat(ppp.getSummary()).isNull();
         assertThat(ppp.getIcon()).isNull();