diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml
index 3eb57d3ecda..e20eaff5206 100644
--- a/res/navigation/privatespace_main_context_nav.xml
+++ b/res/navigation/privatespace_main_context_nav.xml
@@ -35,6 +35,9 @@
+
+
Couldn\u2019t set up a private space
Try Again
+
+ Exit
+
+ Private space isn\u2019t available.\nView possible causes
+
+ View possible causes
Choose a new lock for private space?
diff --git a/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java
new file mode 100644
index 00000000000..d2bdb8cbe79
--- /dev/null
+++ b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java
@@ -0,0 +1,99 @@
+/*
+ * 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.privatespace;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.util.Linkify;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedFragment;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+
+import java.util.regex.Pattern;
+
+public class PrivateProfileCreationRestrictedError extends InstrumentedFragment {
+ private static final String TAG = "PrivateSpaceCreationErr";
+
+ @NonNull
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ GlifLayout rootView =
+ (GlifLayout)
+ inflater.inflate(R.layout.privatespace_creation_error, container, false);
+ final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
+ mixin.setPrimaryButton(
+ new FooterButton.Builder(getContext())
+ .setText(R.string.private_space_exit_label)
+ .setListener(onExit())
+ .setButtonType(FooterButton.ButtonType.NEXT)
+ .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+ .build());
+ OnBackPressedCallback callback =
+ new OnBackPressedCallback(true /* enabled by default */) {
+ @Override
+ public void handleOnBackPressed() {
+ // Handle the back button event. We intentionally don't want to allow back
+ // button to work in this screen during the setup flow.
+ }
+ };
+ requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
+ rootView.setDescriptionText(R.string.private_space_error_description);
+ TextView textView = rootView.getDescriptionTextView();
+ Pattern pattern = Pattern.compile(getString(R.string.private_space_error_causes_text));
+ Linkify.addLinks(
+ textView,
+ pattern,
+ getString(R.string.private_space_learn_more_url));
+
+ return rootView;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.PRIVATE_SPACE_SETUP_SPACE_CREATION_ERROR;
+ }
+
+ private View.OnClickListener onExit() {
+ return v -> {
+ Activity activity = getActivity();
+ if (activity != null) {
+ mMetricsFeatureProvider.action(
+ getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_CANCEL_CREATE_SPACE);
+ Log.i(TAG, "private space setup exited");
+ activity.finish();
+ }
+ };
+ }
+}
+
diff --git a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
index ce85d7238cc..0bfedbd394e 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
@@ -29,6 +29,7 @@ import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -49,6 +50,7 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
private static final String TAG = "PrivateSpaceCreateFrag";
private static final int PRIVATE_SPACE_CREATE_POST_DELAY_MS = 1000;
private static final int PRIVATE_SPACE_ACCOUNT_LOGIN_POST_DELAY_MS = 5000;
+ private static final int PRIVATE_SPACE_SETUP_NO_ERROR = 0;
private static final Handler sHandler = new Handler(Looper.getMainLooper());
private Runnable mRunnable =
() -> {
@@ -122,6 +124,11 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
Log.i(TAG, "Private Space created");
mMetricsFeatureProvider.action(
getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, true);
+ if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+ mMetricsFeatureProvider.action(
+ getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS,
+ PRIVATE_SPACE_SETUP_NO_ERROR);
+ }
if (isConnectedToInternet()) {
registerReceiver();
sHandler.postDelayed(
@@ -132,8 +139,18 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
}
} else {
mMetricsFeatureProvider.action(
- getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, false);
- showPrivateSpaceErrorScreen();
+ getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED,
+ false);
+ if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+ int errorCode = PrivateSpaceMaintainer.getInstance(
+ getActivity()).getPrivateSpaceCreateError();
+ mMetricsFeatureProvider.action(
+ getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS,
+ errorCode);
+ showPrivateSpaceErrorScreen(errorCode);
+ } else {
+ showPrivateSpaceErrorScreen();
+ }
}
}
@@ -147,6 +164,16 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
.navigate(R.id.action_create_profile_error);
}
+ private void showPrivateSpaceErrorScreen(int errorCode) {
+ if (errorCode == UserManager.USER_OPERATION_ERROR_USER_RESTRICTED
+ || errorCode == UserManager.USER_OPERATION_ERROR_PRIVATE_PROFILE) {
+ NavHostFragment.findNavController(PrivateSpaceCreationFragment.this)
+ .navigate(R.id.action_create_profile_error_restrict);
+ } else {
+ showPrivateSpaceErrorScreen();
+ }
+ }
+
/** Returns true if device has an active internet connection, false otherwise. */
private boolean isConnectedToInternet() {
ConnectivityManager cm =
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index ec044daacbf..dd6a4bb6ab4 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -60,6 +60,7 @@ public class PrivateSpaceMaintainer {
private final Context mContext;
private final UserManager mUserManager;
private final ActivityManager mActivityManager;
+ private int mErrorCode;
@GuardedBy("this")
private UserHandle mUserHandle;
private final KeyguardManager mKeyguardManager;
@@ -111,6 +112,9 @@ public class PrivateSpaceMaintainer {
userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
} catch (Exception e) {
Log.e(TAG, "Error creating private space", e);
+ if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+ mErrorCode = ((UserManager.UserOperationException) e).getUserOperationResult();
+ }
return false;
}
@@ -312,6 +316,11 @@ public class PrivateSpaceMaintainer {
return mUserManager.canAddPrivateProfile() || doesPrivateSpaceExist();
}
+ /** Returns the error code for private space creation failure*/
+ public int getPrivateSpaceCreateError() {
+ return mErrorCode;
+ }
+
/** Returns true if private space exists and is running, otherwise returns false */
@VisibleForTesting
synchronized boolean isPrivateProfileRunning() {