Show different error screen for PS creation based on error

This contains the change to show a different fragment on private space
creation error based on the error code returned by UserManager.
On private space create error checks for the returned error code and if
PS is not supported on the device showns error screen containing a link to Help
Center atricle to find out more about the reason for profile creation
failure.

ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS metric is logged with the error
code on create error else 0 on sucessful space creation.

Recording: b/340130375#comment17

Bug: 340130375
Test: Manual
Flag: android.multiuser.show_different_creation_error_for_unsupported_devices
Change-Id: Ifa0345fb6aad64599009f8aa79d168f57fd35c03
This commit is contained in:
josephpv
2024-07-24 14:20:11 +00:00
committed by Joseph Vincent
parent b6e0cd99c1
commit af87233e1e
5 changed files with 149 additions and 2 deletions

View File

@@ -35,6 +35,9 @@
<action
android:id="@+id/action_set_lock_fragment"
app:destination="@id/ps_profile_lock_fragment"/>
<action
android:id="@+id/action_create_profile_error_restrict"
app:destination="@id/ps_profile_error_restricted_fragment"/>
</fragment>
<fragment android:id="@+id/ps_profile_error_fragment"
android:name="com.android.settings.privatespace.PrivateProfileCreationError"
@@ -67,6 +70,9 @@
android:id="@+id/action_lock_success_fragment"
app:destination="@id/ps_pre_finish_delay_fragment"/>
</fragment>
<fragment android:id="@+id/ps_profile_error_restricted_fragment"
android:name="com.android.settings.privatespace.PrivateProfileCreationRestrictedError"
android:label="fragment_ps_error_exit"/>
<action android:id="@+id/action_pre_finish_delay_fragment"
app:destination="@id/ps_pre_finish_delay_fragment"/>
<action android:id="@+id/action_advance_login_error"

View File

@@ -1374,6 +1374,12 @@
<string name="private_space_error_screen_title">Couldn\u2019t set up a private space</string>
<!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
<string name="private_space_tryagain_label">Try Again</string>
<!-- Label for button to exit private space setup on creation error. [CHAR LIMIT=30] -->
<string name="private_space_exit_label">Exit</string>
<!-- Description in Private space error page with a link to the Help Center page[CHAR LIMIT=NONE] -->
<string name="private_space_error_description">Private space isn\u2019t available.\nView possible causes</string>
<!-- Text in Private space error page that points to view possible error causes [CHAR LIMIT=40] -->
<string name="private_space_error_causes_text">View possible causes</string>
<!-- Title for private space lock setup screen. [CHAR LIMIT=90] -->
<string name="private_space_lockscreen_title">Choose a new lock for private space?</string>
<!-- Summary for the private space lock setup screen. [CHAR LIMIT=NONE] -->

View File

@@ -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();
}
};
}
}

View File

@@ -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 =

View File

@@ -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() {