AR/FR: Keyboard challenge takes precedence.
Tested manually by: (1) Setting up a gmail account. (2) Setting up a screen pattern. (3) Going to Settings > System > Reset Options > Factory Wipe (4) Hit the Erase button. (5) Enter pattern into the Screenlock prompt. (6) Notice the Google credential confirmation screen. Prior to this change we would skip (5) if there was a Google account on the device. Test: make RunSettingsRoboTests ROBOTEST_FILTER=andorid.settings.MasterClear Bug: 72694056 Change-Id: Ie07c678ecbc8361e8e1792f82efdfb1261db49fb
This commit is contained in:
@@ -123,24 +123,34 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
return !((requestCode != KEYGUARD_REQUEST) && (requestCode != CREDENTIAL_CONFIRM_REQUEST));
|
return !((requestCode != KEYGUARD_REQUEST) && (requestCode != CREDENTIAL_CONFIRM_REQUEST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isShowFinalConfirmation(int requestCode, int resultCode) {
|
|
||||||
return (resultCode == Activity.RESULT_OK) || (requestCode == CREDENTIAL_CONFIRM_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
onActivityResultInternal(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal method that allows easy testing without dealing with super references.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void onActivityResultInternal(int requestCode, int resultCode, Intent data) {
|
||||||
if (!isValidRequestCode(requestCode)) {
|
if (!isValidRequestCode(requestCode)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user entered a valid keyguard trace, present the final
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
// confirmation prompt; otherwise, go back to the initial state.
|
|
||||||
if (isShowFinalConfirmation(requestCode, resultCode)) {
|
|
||||||
showFinalConfirmation();
|
|
||||||
} else {
|
|
||||||
establishInitialState();
|
establishInitialState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = null;
|
||||||
|
// If returning from a Keyguard request, try to show an account confirmation request if
|
||||||
|
// applciable.
|
||||||
|
if (CREDENTIAL_CONFIRM_REQUEST != requestCode
|
||||||
|
&& (intent = getAccountConfirmationIntent()) != null) {
|
||||||
|
showAccountCredentialConfirmation(intent);
|
||||||
|
} else {
|
||||||
|
showFinalConfirmation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +165,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean tryShowAccountConfirmation() {
|
void showAccountCredentialConfirmation(Intent intent) {
|
||||||
|
startActivityForResult(intent, CREDENTIAL_CONFIRM_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Intent getAccountConfirmationIntent() {
|
||||||
final Context context = getActivity();
|
final Context context = getActivity();
|
||||||
final String accountType = context.getString(R.string.account_type);
|
final String accountType = context.getString(R.string.account_type);
|
||||||
final String packageName = context.getString(R.string.account_confirmation_package);
|
final String packageName = context.getString(R.string.account_confirmation_package);
|
||||||
@@ -163,7 +178,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
if (TextUtils.isEmpty(accountType)
|
if (TextUtils.isEmpty(accountType)
|
||||||
|| TextUtils.isEmpty(packageName)
|
|| TextUtils.isEmpty(packageName)
|
||||||
|| TextUtils.isEmpty(className)) {
|
|| TextUtils.isEmpty(className)) {
|
||||||
return false;
|
Log.i(TAG, "Resources not set for account confirmation.");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
final AccountManager am = AccountManager.get(context);
|
final AccountManager am = AccountManager.get(context);
|
||||||
Account[] accounts = am.getAccountsByType(accountType);
|
Account[] accounts = am.getAccountsByType(accountType);
|
||||||
@@ -179,12 +195,14 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
&& packageName.equals(resolution.activityInfo.packageName)) {
|
&& packageName.equals(resolution.activityInfo.packageName)) {
|
||||||
// Note that we need to check the packagename to make sure that an Activity resolver
|
// Note that we need to check the packagename to make sure that an Activity resolver
|
||||||
// wasn't returned.
|
// wasn't returned.
|
||||||
startActivityForResult(
|
return requestAccountConfirmation;
|
||||||
requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST);
|
} else {
|
||||||
return true;
|
Log.i(TAG, "Unable to resolve Activity: " + packageName + "/" + className);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "No " + accountType + " accounts installed!");
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,7 +228,14 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tryShowAccountConfirmation() && !runKeyguardConfirmation(KEYGUARD_REQUEST)) {
|
if (runKeyguardConfirmation(KEYGUARD_REQUEST)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = getAccountConfirmationIntent();
|
||||||
|
if (intent != null) {
|
||||||
|
showAccountCredentialConfirmation(intent);
|
||||||
|
} else {
|
||||||
showFinalConfirmation();
|
showFinalConfirmation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,7 +253,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
|||||||
* time, then simply reuse the inflated views directly whenever we need
|
* time, then simply reuse the inflated views directly whenever we need
|
||||||
* to change contents.
|
* to change contents.
|
||||||
*/
|
*/
|
||||||
private void establishInitialState() {
|
@VisibleForTesting
|
||||||
|
void establishInitialState() {
|
||||||
mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_master_clear);
|
mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_master_clear);
|
||||||
mInitiateButton.setOnClickListener(mInitiateListener);
|
mInitiateButton.setOnClickListener(mInitiateListener);
|
||||||
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
|
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
|
||||||
|
@@ -18,11 +18,14 @@ package com.android.settings;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.robolectric.Shadows.shadowOf;
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
@@ -86,6 +89,9 @@ public class MasterClearTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private Activity mMockActivity;
|
private Activity mMockActivity;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Intent mMockIntent;
|
||||||
|
|
||||||
private ShadowActivity mShadowActivity;
|
private ShadowActivity mShadowActivity;
|
||||||
private ShadowAccountManager mShadowAccountManager;
|
private ShadowAccountManager mShadowAccountManager;
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
@@ -110,7 +116,7 @@ public class MasterClearTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mMasterClear = spy(new MasterClear());
|
mMasterClear = spy(new MasterClear());
|
||||||
mActivity = Robolectric.setupActivity(Activity.class);
|
mActivity = Robolectric.setupActivity(Activity.class);
|
||||||
mShadowActivity = shadowOf(mActivity);
|
mShadowActivity = shadowOf(mActivity);https://stackoverflow.com/questions/14889951/how-to-verify-a-method-is-called-two-times-with-mockito-verify
|
||||||
// mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
|
// mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
|
||||||
mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
|
mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
|
||||||
|
|
||||||
@@ -213,38 +219,115 @@ public class MasterClearTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTryShowAccountConfirmation_unsupported() {
|
public void testOnActivityResultInternal_invalideRequest() {
|
||||||
when(mMasterClear.getActivity()).thenReturn(mActivity);
|
int invalidRequestCode = -1;
|
||||||
/* Using the default resources, account confirmation shouldn't trigger */
|
doReturn(false).when(mMasterClear).isValidRequestCode(eq(invalidRequestCode));
|
||||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
|
||||||
|
mMasterClear.onActivityResultInternal(invalidRequestCode, Activity.RESULT_OK, null);
|
||||||
|
|
||||||
|
verify(mMasterClear, times(1)).isValidRequestCode(eq(invalidRequestCode));
|
||||||
|
verify(mMasterClear, times(0)).establishInitialState();
|
||||||
|
verify(mMasterClear, times(0)).getAccountConfirmationIntent();
|
||||||
|
verify(mMasterClear, times(0)).showFinalConfirmation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTryShowAccountConfirmation_no_relevant_accounts() {
|
public void testOnActivityResultInternal_resultCanceled() {
|
||||||
|
doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
doNothing().when(mMasterClear).establishInitialState();
|
||||||
|
|
||||||
|
mMasterClear.onActivityResultInternal(
|
||||||
|
MasterClear.KEYGUARD_REQUEST, Activity.RESULT_CANCELED, null);
|
||||||
|
|
||||||
|
verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
verify(mMasterClear, times(1)).establishInitialState();
|
||||||
|
verify(mMasterClear, times(0)).getAccountConfirmationIntent();
|
||||||
|
verify(mMasterClear, times(0)).showFinalConfirmation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnActivityResultInternal_keyguardRequestTriggeringConfirmAccount() {
|
||||||
|
doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
doReturn(mMockIntent).when(mMasterClear).getAccountConfirmationIntent();
|
||||||
|
doNothing().when(mMasterClear).showAccountCredentialConfirmation(eq(mMockIntent));
|
||||||
|
|
||||||
|
mMasterClear.onActivityResultInternal(
|
||||||
|
MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null);
|
||||||
|
|
||||||
|
verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
verify(mMasterClear, times(0)).establishInitialState();
|
||||||
|
verify(mMasterClear, times(1)).getAccountConfirmationIntent();
|
||||||
|
verify(mMasterClear, times(1)).showAccountCredentialConfirmation(eq(mMockIntent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnActivityResultInternal_keyguardRequestTriggeringShowFinal() {
|
||||||
|
doReturn(true).when(mMasterClear).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
doReturn(null).when(mMasterClear).getAccountConfirmationIntent();
|
||||||
|
doNothing().when(mMasterClear).showFinalConfirmation();
|
||||||
|
|
||||||
|
mMasterClear.onActivityResultInternal(
|
||||||
|
MasterClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null);
|
||||||
|
|
||||||
|
verify(mMasterClear, times(1)).isValidRequestCode(eq(MasterClear.KEYGUARD_REQUEST));
|
||||||
|
verify(mMasterClear, times(0)).establishInitialState();
|
||||||
|
verify(mMasterClear, times(1)).getAccountConfirmationIntent();
|
||||||
|
verify(mMasterClear, times(1)).showFinalConfirmation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnActivityResultInternal_confirmRequestTriggeringShowFinal() {
|
||||||
|
doReturn(true).when(mMasterClear)
|
||||||
|
.isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
||||||
|
doNothing().when(mMasterClear).showFinalConfirmation();
|
||||||
|
|
||||||
|
mMasterClear.onActivityResultInternal(
|
||||||
|
MasterClear.CREDENTIAL_CONFIRM_REQUEST, Activity.RESULT_OK, null);
|
||||||
|
|
||||||
|
verify(mMasterClear, times(1))
|
||||||
|
.isValidRequestCode(eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
||||||
|
verify(mMasterClear, times(0)).establishInitialState();
|
||||||
|
verify(mMasterClear, times(0)).getAccountConfirmationIntent();
|
||||||
|
verify(mMasterClear, times(1)).showFinalConfirmation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAccountConfirmationIntent_unsupported() {
|
||||||
|
when(mMasterClear.getActivity()).thenReturn(mActivity);
|
||||||
|
/* Using the default resources, account confirmation shouldn't trigger */
|
||||||
|
assertThat(mMasterClear.getAccountConfirmationIntent()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAccountConfirmationIntent_no_relevant_accounts() {
|
||||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
when(mMockActivity.getString(R.string.account_confirmation_package))
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_class)).thenReturn(TEST_CONFIRMATION_CLASS);
|
.thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||||
|
when(mMockActivity.getString(R.string.account_confirmation_class))
|
||||||
|
.thenReturn(TEST_CONFIRMATION_CLASS);
|
||||||
|
|
||||||
Account[] accounts = new Account[0];
|
Account[] accounts = new Account[0];
|
||||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||||
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
||||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
assertThat(mMasterClear.getAccountConfirmationIntent()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTryShowAccountConfirmation_unresolved() {
|
public void testGetAccountConfirmationIntent_unresolved() {
|
||||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
when(mMockActivity.getString(R.string.account_confirmation_package))
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_class)).thenReturn(TEST_CONFIRMATION_CLASS);
|
.thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||||
|
when(mMockActivity.getString(R.string.account_confirmation_class))
|
||||||
|
.thenReturn(TEST_CONFIRMATION_CLASS);
|
||||||
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
||||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||||
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
||||||
// The package manager should not resolve the confirmation intent targeting the non-existent
|
// The package manager should not resolve the confirmation intent targeting the non-existent
|
||||||
// confirmation package.
|
// confirmation package.
|
||||||
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
|
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
|
||||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
assertThat(mMasterClear.getAccountConfirmationIntent()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -252,8 +335,10 @@ public class MasterClearTest {
|
|||||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||||
// Only try to show account confirmation if the appropriate resource overlays are available.
|
// Only try to show account confirmation if the appropriate resource overlays are available.
|
||||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
when(mMockActivity.getString(R.string.account_confirmation_package))
|
||||||
when(mMockActivity.getString(R.string.account_confirmation_class)).thenReturn(TEST_CONFIRMATION_CLASS);
|
.thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||||
|
when(mMockActivity.getString(R.string.account_confirmation_class))
|
||||||
|
.thenReturn(TEST_CONFIRMATION_CLASS);
|
||||||
// Add accounts to trigger the search for a resolving intent.
|
// Add accounts to trigger the search for a resolving intent.
|
||||||
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
||||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||||
@@ -268,10 +353,18 @@ public class MasterClearTest {
|
|||||||
resolveInfo.activityInfo = activityInfo;
|
resolveInfo.activityInfo = activityInfo;
|
||||||
when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
|
when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
|
||||||
|
|
||||||
// Finally mock out the startActivityForResultCall
|
Intent actualIntent = mMasterClear.getAccountConfirmationIntent();
|
||||||
doNothing().when(mMasterClear).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
assertEquals(TEST_CONFIRMATION_PACKAGE, actualIntent.getComponent().getPackageName());
|
||||||
|
assertEquals(TEST_CONFIRMATION_CLASS, actualIntent.getComponent().getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue();
|
public void testShowAccountCredentialConfirmation() {
|
||||||
|
// Finally mock out the startActivityForResultCall
|
||||||
|
doNothing().when(mMasterClear)
|
||||||
|
.startActivityForResult(eq(mMockIntent), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
||||||
|
mMasterClear.showAccountCredentialConfirmation(mMockIntent);
|
||||||
|
verify(mMasterClear, times(1))
|
||||||
|
.startActivityForResult(eq(mMockIntent), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Reference in New Issue
Block a user