From 9260c67352e9d7ef85617bddcd6f572210ed040f Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Fri, 19 May 2023 23:17:47 +0000 Subject: [PATCH 1/7] DO NOT MERGE: Prevent non-system IME from becoming device admin Currently selected IME can inject KeyEvent on DeviceAdminAdd screen to activate itself as device admin and cause various DoS attacks. This CL ensures KeyEvent on "Activate" button can only come from system apps. Bug: 280793427 Test: atest DeviceAdminActivationTest Change-Id: I6470d1684d707f4b1e86f8b456be0b4e0af5f188 (cherry picked from commit 70a501d02e0a6aefd874767a15378ba998759373) --- .../deviceadmin/DeviceAdminAdd.java | 131 +++++++++--------- 1 file changed, 69 insertions(+), 62 deletions(-) diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java index 1d45c518a6f..c43bde64a35 100644 --- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java +++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java @@ -66,6 +66,7 @@ import android.text.TextUtils.TruncateAt; import android.util.EventLog; import android.util.Log; import android.view.Display; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -155,12 +156,12 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { mHandler = new Handler(getMainLooper()); - mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); - mAppOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); + mDPM = getSystemService(DevicePolicyManager.class); + mAppOps = getSystemService(AppOpsManager.class); mLayoutInflaternflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); PackageManager packageManager = getPackageManager(); - if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { Log.w(TAG, "Cannot start ADD_DEVICE_ADMIN as a new task"); finish(); return; @@ -170,7 +171,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { EXTRA_CALLED_FROM_SUPPORT_DIALOG, false); String action = getIntent().getAction(); - ComponentName who = (ComponentName)getIntent().getParcelableExtra( + ComponentName who = (ComponentName) getIntent().getParcelableExtra( DevicePolicyManager.EXTRA_DEVICE_ADMIN); if (who == null) { String packageName = getIntent().getStringExtra(EXTRA_DEVICE_ADMIN_PACKAGE_NAME); @@ -226,7 +227,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); int count = avail == null ? 0 : avail.size(); boolean found = false; - for (int i=0; i getString(R.string.device_admin_warning_simplified, - profileOwnerName), profileOwnerName)); + profileOwnerName), profileOwnerName)); return; } setContentView(R.layout.device_admin_add); - mAdminIcon = (ImageView)findViewById(R.id.admin_icon); - mAdminName = (TextView)findViewById(R.id.admin_name); - mAdminDescription = (TextView)findViewById(R.id.admin_description); + mAdminIcon = (ImageView) findViewById(R.id.admin_icon); + mAdminName = (TextView) findViewById(R.id.admin_name); + mAdminDescription = (TextView) findViewById(R.id.admin_description); mProfileOwnerWarning = (TextView) findViewById(R.id.profile_owner_warning); mProfileOwnerWarning.setText( mDPM.getResources().getString(SET_PROFILE_OWNER_POSTSETUP_WARNING, () -> getString(R.string.adding_profile_owner_warning))); - mAddMsg = (TextView)findViewById(R.id.add_msg); + mAddMsg = (TextView) findViewById(R.id.add_msg); mAddMsgExpander = (ImageView) findViewById(R.id.add_msg_expander); final View.OnClickListener onClickListener = new View.OnClickListener() { @Override @@ -380,7 +381,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { boolean hideMsgExpander = mAddMsg.getLineCount() <= maxLines; mAddMsgExpander.setVisibility(hideMsgExpander ? View.GONE : View.VISIBLE); if (hideMsgExpander) { - ((View)mAddMsgExpander.getParent()).invalidate(); + ((View) mAddMsgExpander.getParent()).invalidate(); } mAddMsg.getViewTreeObserver().removeOnGlobalLayoutListener(this); } @@ -398,7 +399,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { mCancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_DECLINED_BY_USER, - mDeviceAdmin.getActivityInfo().applicationInfo.uid); + mDeviceAdmin.getActivityInfo().applicationInfo.uid); finish(); } }); @@ -420,58 +421,64 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { final View restrictedAction = findViewById(R.id.restricted_action); restrictedAction.setFilterTouchesWhenObscured(true); - restrictedAction.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (!mActionButton.isEnabled()) { - showPolicyTransparencyDialogIfRequired(); - return; - } - if (mAdding) { - addAndFinish(); - } else if (isManagedProfile(mDeviceAdmin) - && mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner())) { - final int userId = UserHandle.myUserId(); - UserDialogs.createRemoveDialog(DeviceAdminAdd.this, userId, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UserManager um = UserManager.get(DeviceAdminAdd.this); - um.removeUser(userId); - finish(); - } - } - ).show(); - } else if (mUninstalling) { - mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName()); - finish(); - } else if (!mWaitingForRemoveMsg) { - try { - // Don't allow the admin to put a dialog up in front - // of us while we interact with the user. - ActivityManager.getService().stopAppSwitches(); - } catch (RemoteException e) { - } - mWaitingForRemoveMsg = true; - mDPM.getRemoveWarning(mDeviceAdmin.getComponent(), - new RemoteCallback(new RemoteCallback.OnResultListener() { - @Override - public void onResult(Bundle result) { - CharSequence msg = result != null - ? result.getCharSequence( - DeviceAdminReceiver.EXTRA_DISABLE_WARNING) - : null; - continueRemoveAction(msg); - } - }, mHandler)); - // Don't want to wait too long. - getWindow().getDecorView().getHandler().postDelayed(new Runnable() { - @Override public void run() { - continueRemoveAction(null); - } - }, 2*1000); - } + + final View.OnClickListener restrictedActionClickListener = v -> { + if (!mActionButton.isEnabled()) { + showPolicyTransparencyDialogIfRequired(); + return; } + if (mAdding) { + addAndFinish(); + } else if (isManagedProfile(mDeviceAdmin) + && mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner())) { + final int userId = UserHandle.myUserId(); + UserDialogs.createRemoveDialog(DeviceAdminAdd.this, userId, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + UserManager um = UserManager.get(DeviceAdminAdd.this); + um.removeUser(userId); + finish(); + } + } + ).show(); + } else if (mUninstalling) { + mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName()); + finish(); + } else if (!mWaitingForRemoveMsg) { + try { + // Don't allow the admin to put a dialog up in front + // of us while we interact with the user. + ActivityManager.getService().stopAppSwitches(); + } catch (RemoteException e) { + } + mWaitingForRemoveMsg = true; + mDPM.getRemoveWarning(mDeviceAdmin.getComponent(), + new RemoteCallback(new RemoteCallback.OnResultListener() { + @Override + public void onResult(Bundle result) { + CharSequence msg = result != null + ? result.getCharSequence( + DeviceAdminReceiver.EXTRA_DISABLE_WARNING) + : null; + continueRemoveAction(msg); + } + }, mHandler)); + // Don't want to wait too long. + getWindow().getDecorView().getHandler().postDelayed( + () -> continueRemoveAction(null), 2 * 1000); + } + }; + restrictedAction.setOnKeyListener((view, keyCode, keyEvent) -> { + if ((keyEvent.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) == 0) { + Log.e(TAG, "Can not activate device-admin with KeyEvent from non-system app."); + // Consume event to suppress click. + return true; + } + // Fallback to view click handler. + return false; }); + restrictedAction.setOnClickListener(restrictedActionClickListener); } /** From ac04ce9091230921a93807089e550ed50178ee15 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Mon, 5 Jun 2023 17:01:03 +0800 Subject: [PATCH 2/7] Change the summary string to exclude 'Audio output' feature 'Audio output' plan to punt into U-QPR or V. Change the summary to not mention it. Bug: 286174797 Test: flash rom and check UI not exist Change-Id: I66ffb26c9b9ed612f41be7025a474cc63f256d87 --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index a72794dc4b5..66518049439 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -131,7 +131,7 @@ Hearing device settings - Audio output, shortcut, hearing aid compatibility + Shortcut, hearing aid compatibility For this device From 98744f1a8efbf4a5c4440b0b9b595ef8e8d242af Mon Sep 17 00:00:00 2001 From: Wenhui Yang Date: Mon, 5 Jun 2023 23:49:28 +0000 Subject: [PATCH 3/7] Fix fingerprint unlock stuck after renaming FingerprintAuthenticationClient wasn't cancelled successfully because cancelletion signal was set to null. Test: Manual - rename an existing fingerprint and observe fingerprint unlock can be entered again Test: atest FingerprintSettingsFragmentTest Fixes: 283926104 Change-Id: Id33cc3d3e8052f5cc39eddac26a75047d3139633 --- .../FingerprintAuthenticateSidecar.java | 13 ++++-- .../FingerprintSettingsFragmentTest.java | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java index 426405627ab..f3c8aba4095 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java @@ -21,6 +21,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.os.CancellationSignal; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.InstrumentedFragment; /** @@ -80,7 +81,6 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { - mCancellationSignal = null; if (mListener != null) { mListener.onAuthenticationError(errMsgId, errString); } else { @@ -108,10 +108,12 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { } public void stopAuthentication() { - if (mCancellationSignal != null && !mCancellationSignal.isCanceled()) { + if (mCancellationSignal != null) { + // This will automatically check if the cancel has been sent and if so + // it won't send it again. mCancellationSignal.cancel(); + mCancellationSignal = null; } - mCancellationSignal = null; } public void setListener(Listener listener) { @@ -129,4 +131,9 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { } mListener = listener; } + + @VisibleForTesting + boolean isCancelled() { + return mCancellationSignal == null || mCancellationSignal.isCanceled(); + } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java index 18b05add7ad..1998d56b1ca 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -44,6 +44,7 @@ import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; +import android.os.CancellationSignal; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -68,6 +69,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -92,6 +94,16 @@ public class FingerprintSettingsFragmentTest { @Mock private FragmentTransaction mFragmentTransaction; + @Captor + private ArgumentCaptor mCancellationSignalArgumentCaptor = + ArgumentCaptor.forClass(CancellationSignal.class); + @Captor + private ArgumentCaptor + mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass( + FingerprintManager.AuthenticationCallback.class); + + private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar; + @Before public void setUp() { doReturn(true).when(mFingerprintManager).isHardwareDetected(); @@ -146,6 +158,34 @@ public class FingerprintSettingsFragmentTest { false)).isTrue(); } + // Test the case when FingerprintAuthenticateSidecar receives an error callback from the + // framework or from another authentication client. The cancellation signal should not be set + // to null because there may exist a running authentication client. + // The signal can only be cancelled from the caller in FingerprintSettings. + @Test + public void testCancellationSignalLifeCycle() { + setUpFragment(false); + + mFingerprintAuthenticateSidecar.setFingerprintManager(mFingerprintManager); + + doNothing().when(mFingerprintManager).authenticate(any(), + mCancellationSignalArgumentCaptor.capture(), + mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt()); + + mFingerprintAuthenticateSidecar.startAuthentication(1); + + assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull(); + assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull(); + + // Authentication error callback should not cancel the signal. + mAuthenticationCallbackArgumentCaptor.getValue().onAuthenticationError(0, ""); + assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isFalse(); + + // The signal should be cancelled when caller stops the authentication. + mFingerprintAuthenticateSidecar.stopAuthentication(); + assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isTrue(); + } + private void setUpFragment(boolean showChooseLock) { Intent intent = new Intent(); if (!showChooseLock) { @@ -166,6 +206,10 @@ public class FingerprintSettingsFragmentTest { doReturn(fragmentManager).when(mFragment).getFragmentManager(); doReturn(fragmentManager).when(mActivity).getSupportFragmentManager(); + mFingerprintAuthenticateSidecar = new FingerprintAuthenticateSidecar(); + doReturn(mFingerprintAuthenticateSidecar).when(fragmentManager).findFragmentByTag( + "authenticate_sidecar"); + doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt()); setSensor(); From f01b99e725ee630683c274c8b60481cfcc7a9423 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Thu, 18 May 2023 11:16:22 +0000 Subject: [PATCH 4/7] Passing the writing repair mode password flag to verify credential api Handles the ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL intent to prompt the user for device credentials. Passing the writing repair mode password flag to the verify credential api when the user is authenticating. Bug: 277561275 Test: am start -a android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL Change-Id: Id018586b0ed535555c157b7516c9571b049978ad --- AndroidManifest.xml | 1 + .../password/ChooseLockSettingsHelper.java | 24 +++++++++++++++---- .../ConfirmDeviceCredentialActivity.java | 14 +++++++++++ .../ConfirmDeviceCredentialBaseFragment.java | 3 +++ .../password/ConfirmLockPassword.java | 4 +++- .../settings/password/ConfirmLockPattern.java | 4 +++- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2c3e7f3d12a..185d21ec7b2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2397,6 +2397,7 @@ + diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java index 216f7db1ac1..943a937a32a 100644 --- a/src/com/android/settings/password/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java @@ -71,6 +71,8 @@ public final class ChooseLockSettingsHelper { // Gatekeeper password handle, which can subsequently be used to generate Gatekeeper // HardwareAuthToken(s) via LockSettingsService#verifyGatekeeperPasswordHandle public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"; + public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW = + "request_write_repair_mode_pw"; /** * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are @@ -152,6 +154,7 @@ public final class ChooseLockSettingsHelper { @Nullable private RemoteLockscreenValidationSession mRemoteLockscreenValidationSession; @Nullable private ComponentName mRemoteLockscreenValidationServiceComponent; private boolean mRequestGatekeeperPasswordHandle; + private boolean mRequestWriteRepairModePassword; private boolean mTaskOverlay; public Builder(@NonNull Activity activity) { @@ -335,6 +338,17 @@ public final class ChooseLockSettingsHelper { return this; } + /** + * @param requestWriteRepairModePassword Set {@code true} to request that + * LockSettingsService writes the password data to the repair mode file after the user + * credential is verified successfully. + */ + @NonNull public Builder setRequestWriteRepairModePassword( + boolean requestWriteRepairModePassword) { + mRequestWriteRepairModePassword = requestWriteRepairModePassword; + return this; + } + /** * Support of ActivityResultLauncher. * @@ -385,7 +399,7 @@ public final class ChooseLockSettingsHelper { mBuilder.mRemoteLockscreenValidationSession, mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId, mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle, - mBuilder.mTaskOverlay); + mBuilder.mRequestWriteRepairModePassword, mBuilder.mTaskOverlay); } private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, @@ -396,7 +410,7 @@ public final class ChooseLockSettingsHelper { @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession, @Nullable ComponentName remoteLockscreenValidationServiceComponent, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle, - boolean taskOverlay) { + boolean requestWriteRepairModePassword, boolean taskOverlay) { Optional> activityClass = determineAppropriateActivityClass( returnCredentials, forceVerifyPath, userId, remoteLockscreenValidationSession); if (activityClass.isEmpty()) { @@ -407,7 +421,7 @@ public final class ChooseLockSettingsHelper { returnCredentials, external, forceVerifyPath, userId, alternateButton, checkboxLabel, remoteLockscreenValidation, remoteLockscreenValidationSession, remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly, - requestGatekeeperPasswordHandle, taskOverlay); + requestGatekeeperPasswordHandle, requestWriteRepairModePassword, taskOverlay); } private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header, @@ -418,7 +432,7 @@ public final class ChooseLockSettingsHelper { @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession, @Nullable ComponentName remoteLockscreenValidationServiceComponent, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle, - boolean taskOverlay) { + boolean requestWriteRepairModePassword, boolean taskOverlay) { final Intent intent = new Intent(); intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title); intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header); @@ -442,6 +456,8 @@ public final class ChooseLockSettingsHelper { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, requestGatekeeperPasswordHandle); + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, + requestWriteRepairModePassword); intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName()); intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index fabca6bb983..d409c0f9dfb 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -171,6 +171,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction()); mTaskOverlay = isInternalActivity() && intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false); + final boolean prepareRepairMode = + KeyguardManager.ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL.equals( + intent.getAction()); mUserId = UserHandle.myUserId(); if (isInternalActivity()) { @@ -244,6 +247,17 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { .setExternal(true) .show(); return; + } else if (prepareRepairMode) { + final ChooseLockSettingsHelper.Builder builder = + new ChooseLockSettingsHelper.Builder(this); + launchedCDC = builder.setHeader(mTitle) + .setDescription(mDetails) + .setExternal(true) + .setUserId(mUserId) + .setTaskOverlay(mTaskOverlay) + .setRequestWriteRepairModePassword(true) + .setForceVerifyPath(true) + .show(); } else if (isEffectiveUserManagedProfile && isInternalActivity()) { mCredentialMode = CREDENTIAL_MANAGED; if (isBiometricAllowed(effectiveUserId, mUserId)) { diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java index f4cfabc754c..5a123b89315 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java @@ -105,6 +105,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr protected final Handler mHandler = new Handler(); protected boolean mFrp; protected boolean mRemoteValidation; + protected boolean mRequestWriteRepairModePassword; protected CharSequence mAlternateButtonText; protected BiometricManager mBiometricManager; @Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession; @@ -130,6 +131,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false); mForceVerifyPath = intent.getBooleanExtra( ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, false); + mRequestWriteRepairModePassword = intent.getBooleanExtra( + ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false); if (intent.getBooleanExtra(IS_REMOTE_LOCKSCREEN_VALIDATION, false)) { if (FeatureFlagUtils.isEnabled(getContext(), diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java index 03b89f25051..1b535069f3c 100644 --- a/src/com/android/settings/password/ConfirmLockPassword.java +++ b/src/com/android/settings/password/ConfirmLockPassword.java @@ -496,7 +496,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { } } else if (mForceVerifyPath) { if (isInternalActivity()) { - startVerifyPassword(credential, intent, 0 /* flags */); + final int flags = mRequestWriteRepairModePassword + ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0; + startVerifyPassword(credential, intent, flags); return; } } else { diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java index 7db25fd830f..c10f865343f 100644 --- a/src/com/android/settings/password/ConfirmLockPattern.java +++ b/src/com/android/settings/password/ConfirmLockPattern.java @@ -523,7 +523,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity { } } else if (mForceVerifyPath) { if (isInternalActivity()) { - startVerifyPattern(credential, intent, 0 /* flags */); + final int flags = mRequestWriteRepairModePassword + ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0; + startVerifyPattern(credential, intent, flags); return; } } else { From f148baa3728b5f7ae33d2c8a2a0c5d7c57092d23 Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Sat, 10 Jun 2023 00:13:58 +0800 Subject: [PATCH 5/7] Remove redundant methods and refactor Remove the summary method and write the code in AOSP directly instead to force string consistency. Also refactor a bit after the modification. Fixes: 276399056 Test: robotests Change-Id: I76ad740b694363a3cdfb3748e41c840fb678b93d --- ...tteryFirstUseDatePreferenceController.java | 27 ++++++-- ...ryManufactureDatePreferenceController.java | 28 ++++++-- .../BatterySettingsFeatureProvider.java | 11 +--- .../BatterySettingsFeatureProviderImpl.java | 65 +------------------ .../settings/fuelgauge/BatteryUtils.java | 12 +++- .../settings/overlay/FeatureFactory.java | 3 +- .../settings/overlay/FeatureFactoryImpl.java | 4 +- ...yFirstUseDatePreferenceControllerTest.java | 36 +++++++++- ...nufactureDatePreferenceControllerTest.java | 35 +++++++++- ...atterySettingsFeatureProviderImplTest.java | 62 ++---------------- .../testutils/FakeFeatureFactory.java | 2 +- .../settings/testutils/FakeFeatureFactory.kt | 4 +- .../testutils/FakeFeatureFactory.java | 2 +- 13 files changed, 139 insertions(+), 152 deletions(-) diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java index 8ad2ecab894..7d10b5b8a7f 100644 --- a/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java +++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java @@ -17,32 +17,51 @@ package com.android.settings.deviceinfo.batteryinfo; import android.content.Context; +import android.os.BatteryManager; import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; +import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.overlay.FeatureFactory; +import java.util.concurrent.TimeUnit; + /** * A controller that manages the information about battery first use date. */ public class BatteryFirstUseDatePreferenceController extends BasePreferenceController { - private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider; + private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider; + private final BatteryManager mBatteryManager; + + private long mFirstUseDateInMs; public BatteryFirstUseDatePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mBatterySettingsFeatureProvider = FeatureFactory.getFactory( - context).getBatterySettingsFeatureProvider(context); + context).getBatterySettingsFeatureProvider(); + mBatteryManager = mContext.getSystemService(BatteryManager.class); } @Override public int getAvailabilityStatus() { - return mBatterySettingsFeatureProvider.isFirstUseDateAvailable() + return mBatterySettingsFeatureProvider.isFirstUseDateAvailable(getFirstUseDate()) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override public CharSequence getSummary() { - return mBatterySettingsFeatureProvider.getFirstUseDateSummary(); + return isAvailable() + ? BatteryUtils.getBatteryInfoFormattedDate(mFirstUseDateInMs) + : null; + } + + private long getFirstUseDate() { + if (mFirstUseDateInMs == 0L) { + final long firstUseDateInSec = mBatteryManager.getLongProperty( + BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE); + mFirstUseDateInMs = TimeUnit.MILLISECONDS.convert(firstUseDateInSec, TimeUnit.SECONDS); + } + return mFirstUseDateInMs; } } diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java index 45b299ab5a7..d1b9df03a82 100644 --- a/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java +++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java @@ -17,32 +17,52 @@ package com.android.settings.deviceinfo.batteryinfo; import android.content.Context; +import android.os.BatteryManager; import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; +import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.overlay.FeatureFactory; +import java.util.concurrent.TimeUnit; + /** * A controller that manages the information about battery manufacture date. */ public class BatteryManufactureDatePreferenceController extends BasePreferenceController { - private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider; + private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider; + private final BatteryManager mBatteryManager; + + private long mManufactureDateInMs; public BatteryManufactureDatePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mBatterySettingsFeatureProvider = FeatureFactory.getFactory( - context).getBatterySettingsFeatureProvider(context); + context).getBatterySettingsFeatureProvider(); + mBatteryManager = mContext.getSystemService(BatteryManager.class); } @Override public int getAvailabilityStatus() { - return mBatterySettingsFeatureProvider.isManufactureDateAvailable() + return mBatterySettingsFeatureProvider.isManufactureDateAvailable(getManufactureDate()) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override public CharSequence getSummary() { - return mBatterySettingsFeatureProvider.getManufactureDateSummary(); + return isAvailable() + ? BatteryUtils.getBatteryInfoFormattedDate(mManufactureDateInMs) + : null; + } + + private long getManufactureDate() { + if (mManufactureDateInMs == 0L) { + final long manufactureDateInSec = mBatteryManager.getLongProperty( + BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE); + mManufactureDateInMs = TimeUnit.MILLISECONDS.convert(manufactureDateInSec, + TimeUnit.SECONDS); + } + return mManufactureDateInMs; } } diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java index 18fef1a374f..dd13f1c4842 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java @@ -20,15 +20,8 @@ package com.android.settings.fuelgauge; public interface BatterySettingsFeatureProvider { /** Returns true if manufacture date should be shown */ - boolean isManufactureDateAvailable(); + boolean isManufactureDateAvailable(long manufactureDateMs); /** Returns true if first use date should be shown */ - boolean isFirstUseDateAvailable(); - - /** Returns the summary of battery manufacture date */ - CharSequence getManufactureDateSummary(); - - /** Returns the summary of battery first use date */ - CharSequence getFirstUseDateSummary(); - + boolean isFirstUseDateAvailable(long firstUseDateMs); } diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java index ed4795ab785..2dcb1b12004 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java @@ -16,77 +16,16 @@ package com.android.settings.fuelgauge; -import android.content.Context; -import android.os.BatteryManager; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.concurrent.TimeUnit; - /** Feature provider implementation for battery settings usage. */ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider { - protected Context mContext; - - private BatteryManager mBatteryManager; - private long mManufactureDateInMs; - private long mFirstUseDateInMs; - - public BatterySettingsFeatureProviderImpl(Context context) { - mContext = context.getApplicationContext(); - mBatteryManager = mContext.getSystemService(BatteryManager.class); - } - @Override - public boolean isManufactureDateAvailable() { + public boolean isManufactureDateAvailable(long manufactureDateMs) { return false; } @Override - public boolean isFirstUseDateAvailable() { + public boolean isFirstUseDateAvailable(long firstUseDateMs) { return false; } - - @Override - public CharSequence getManufactureDateSummary() { - return isManufactureDateAvailable() - ? getFormattedDate(getManufactureDate()) - : null; - } - - @Override - public CharSequence getFirstUseDateSummary() { - return isFirstUseDateAvailable() - ? getFormattedDate(getFirstUseDate()) - : null; - } - - protected long getManufactureDate() { - if (mManufactureDateInMs == 0L) { - final long manufactureDateInSec = mBatteryManager.getLongProperty( - BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE); - mManufactureDateInMs = TimeUnit.MILLISECONDS.convert(manufactureDateInSec, - TimeUnit.SECONDS); - } - return mManufactureDateInMs; - } - - protected long getFirstUseDate() { - if (mFirstUseDateInMs == 0L) { - final long firstUseDateInSec = mBatteryManager.getLongProperty( - BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE); - mFirstUseDateInMs = TimeUnit.MILLISECONDS.convert(firstUseDateInSec, TimeUnit.SECONDS); - } - return mFirstUseDateInMs; - } - - private CharSequence getFormattedDate(long dateInMs) { - final Instant instant = Instant.ofEpochMilli(dateInMs); - final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format( - DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); - - return localDate; - } } diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index f5d3950fc2f..29c7591863c 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -64,8 +64,10 @@ import com.google.protobuf.MessageLite; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.List; /** @@ -671,6 +673,14 @@ public class BatteryUtils { } return summary.toString(); } + /** Format the date of battery related info */ + public static CharSequence getBatteryInfoFormattedDate(long dateInMs) { + final Instant instant = Instant.ofEpochMilli(dateInMs); + final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format( + DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); + + return localDate; + } /** Builds the battery usage time information for one timestamp. */ private static String buildBatteryUsageTimeInfo(final Context context, long timeInMs, diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 9d81baa9bca..5cbdb25a4ef 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -125,8 +125,7 @@ public abstract class FeatureFactory { /** * Gets implementation for Battery Settings provider. */ - public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider( - Context context); + public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(); public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index d4f18782c05..ba163774e5f 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -150,9 +150,9 @@ public class FeatureFactoryImpl extends FeatureFactory { } @Override - public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) { + public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() { if (mBatterySettingsFeatureProvider == null) { - mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl(context); + mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl(); } return mBatterySettingsFeatureProvider; } diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java index a7f6a2e4a2b..9ac69af4462 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java @@ -21,9 +21,12 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; import android.content.Context; +import android.os.BatteryManager; import androidx.test.core.app.ApplicationProvider; @@ -33,16 +36,23 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowBatteryManager; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBatteryManager.class}) public class BatteryFirstUseDatePreferenceControllerTest { private BatteryFirstUseDatePreferenceController mController; private Context mContext; + private BatteryManager mBatteryManager; + private ShadowBatteryManager mShadowBatteryManager; private FakeFeatureFactory mFactory; @Before public void setUp() { mContext = ApplicationProvider.getApplicationContext(); + mBatteryManager = mContext.getSystemService(BatteryManager.class); + mShadowBatteryManager = shadowOf(mBatteryManager); mFactory = FakeFeatureFactory.setupForTest(); mController = new BatteryFirstUseDatePreferenceController(mContext, "battery_info_first_use_date"); @@ -50,15 +60,37 @@ public class BatteryFirstUseDatePreferenceControllerTest { @Test public void getAvailabilityStatus_dateAvailable_returnAvailable() { - when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable()).thenReturn(true); + when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong())) + .thenReturn(true); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } @Test public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() { - when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable()).thenReturn(false); + when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong())) + .thenReturn(false); assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); } + + @Test + public void getSummary_available_returnExpectedDate() { + when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong())) + .thenReturn(true); + mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE, + 1669680000L); + + final CharSequence result = mController.getSummary(); + + assertThat(result.toString()).isEqualTo("November 29, 2022"); + } + + @Test + public void getSummary_unavailable_returnNull() { + when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(anyLong())) + .thenReturn(false); + + assertThat(mController.getSummary()).isNull(); + } } diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java index c4a5486b259..fed74f3a8f9 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java @@ -21,9 +21,12 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; import android.content.Context; +import android.os.BatteryManager; import androidx.test.core.app.ApplicationProvider; @@ -33,17 +36,24 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowBatteryManager; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBatteryManager.class}) public class BatteryManufactureDatePreferenceControllerTest { private BatteryManufactureDatePreferenceController mController; private Context mContext; + private BatteryManager mBatteryManager; + private ShadowBatteryManager mShadowBatteryManager; private FakeFeatureFactory mFactory; @Before public void setUp() { mContext = ApplicationProvider.getApplicationContext(); + mBatteryManager = mContext.getSystemService(BatteryManager.class); + mShadowBatteryManager = shadowOf(mBatteryManager); mFactory = FakeFeatureFactory.setupForTest(); mController = new BatteryManufactureDatePreferenceController(mContext, "battery_info_manufacture_date"); @@ -51,16 +61,37 @@ public class BatteryManufactureDatePreferenceControllerTest { @Test public void getAvailabilityStatus_dateAvailable_returnAvailable() { - when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable()).thenReturn(true); + when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable( + anyLong())).thenReturn(true); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } @Test public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() { - when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable()) + when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(anyLong())) .thenReturn(false); assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); } + + @Test + public void getSummary_available_returnExpectedDate() { + when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable( + anyLong())).thenReturn(true); + mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE, + 1669680000L); + + final CharSequence result = mController.getSummary(); + + assertThat(result.toString()).isEqualTo("November 29, 2022"); + } + + @Test + public void getSummary_unavailable_returnNull() { + when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(anyLong())) + .thenReturn(false); + + assertThat(mController.getSummary()).isNull(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java index dfef3c7f0b8..6c0912561b2 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java @@ -18,83 +18,29 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.robolectric.Shadows.shadowOf; - -import android.content.Context; -import android.os.BatteryManager; - -import androidx.test.core.app.ApplicationProvider; +import static org.mockito.ArgumentMatchers.anyLong; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowBatteryManager; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBatteryManager.class}) public class BatterySettingsFeatureProviderImplTest { - private BatteryManager mBatteryManager; - private ShadowBatteryManager mShadowBatteryManager; - private Context mContext; private BatterySettingsFeatureProviderImpl mImpl; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(ApplicationProvider.getApplicationContext()); - mBatteryManager = mContext.getSystemService(BatteryManager.class); - mShadowBatteryManager = shadowOf(mBatteryManager); - mImpl = spy(new BatterySettingsFeatureProviderImpl(mContext)); + mImpl = new BatterySettingsFeatureProviderImpl(); } @Test public void isManufactureDateAvailable_returnFalse() { - assertThat(mImpl.isManufactureDateAvailable()).isFalse(); + assertThat(mImpl.isManufactureDateAvailable(anyLong())).isFalse(); } @Test public void isFirstUseDateAvailable_returnFalse() { - assertThat(mImpl.isFirstUseDateAvailable()).isFalse(); - } - - @Test - public void getManufactureDateSummary_available_returnExpectedDate() { - doReturn(true).when(mImpl).isManufactureDateAvailable(); - mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE, - 1669680000L); - - final CharSequence result = mImpl.getManufactureDateSummary(); - - assertThat(result.toString()).isEqualTo("November 29, 2022"); - } - - @Test - public void getManufactureDateSummary_unavailable_returnNull() { - doReturn(false).when(mImpl).isManufactureDateAvailable(); - - assertThat(mImpl.getManufactureDateSummary()).isNull(); - } - - @Test - public void getFirstUseDateSummary_available_returnExpectedDate() { - doReturn(true).when(mImpl).isFirstUseDateAvailable(); - mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE, - 1669680000L); - - final CharSequence result = mImpl.getFirstUseDateSummary(); - - assertThat(result.toString()).isEqualTo("November 29, 2022"); - } - - @Test - public void getFirstUseDateSummary_unavailable_returnNull() { - doReturn(false).when(mImpl).isFirstUseDateAvailable(); - - assertThat(mImpl.getFirstUseDateSummary()).isNull(); + assertThat(mImpl.isFirstUseDateAvailable(anyLong())).isFalse(); } } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index 47053802e39..a6c4cd28c11 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -171,7 +171,7 @@ public class FakeFeatureFactory extends FeatureFactory { } @Override - public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) { + public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() { return batterySettingsFeatureProvider; } diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt index 50589ac67e0..bdaf805db06 100644 --- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt +++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt @@ -84,9 +84,7 @@ class FakeFeatureFactory : FeatureFactory() { TODO("Not yet implemented") } - override fun getBatterySettingsFeatureProvider( - context: Context?, - ): BatterySettingsFeatureProvider { + override fun getBatterySettingsFeatureProvider(): BatterySettingsFeatureProvider { TODO("Not yet implemented") } diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index d7490100990..db2339f9e7a 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -157,7 +157,7 @@ public class FakeFeatureFactory extends FeatureFactory { } @Override - public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) { + public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() { return batterySettingsFeatureProvider; } From b452bc9f9d9a23b8f3402326aa05fbd101930ab1 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Fri, 9 Jun 2023 15:36:00 +0800 Subject: [PATCH 6/7] Add manufactured year into hardware info page Bug: 285471557 Test: manual visually Change-Id: Iaab254ac6ee77217e29d89524d782577f44119cb --- res/values/strings.xml | 2 ++ res/xml/hardware_info.xml | 9 ++++++ .../HardwareInfoFeatureProvider.kt | 26 +++++++++++++++ .../HardwareInfoFeatureProviderImpl.kt | 24 ++++++++++++++ .../ManufacturedYearPreferenceController.kt | 32 +++++++++++++++++++ .../settings/overlay/FeatureFactory.java | 6 ++++ .../settings/overlay/FeatureFactoryImpl.java | 7 ++++ .../testutils/FakeFeatureFactory.java | 7 ++++ .../settings/testutils/FakeFeatureFactory.kt | 5 +++ .../testutils/FakeFeatureFactory.java | 7 ++++ 10 files changed, 125 insertions(+) create mode 100644 src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProvider.kt create mode 100644 src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProviderImpl.kt create mode 100644 src/com/android/settings/deviceinfo/hardwareinfo/ManufacturedYearPreferenceController.kt diff --git a/res/values/strings.xml b/res/values/strings.xml index 1ba43d261f6..45b60265548 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2642,6 +2642,8 @@ Model Hardware version + + Manufactured year Equipment ID diff --git a/res/xml/hardware_info.xml b/res/xml/hardware_info.xml index 641f707a65e..e086a486ee0 100644 --- a/res/xml/hardware_info.xml +++ b/res/xml/hardware_info.xml @@ -48,4 +48,13 @@ settings:controller="com.android.settings.deviceinfo.hardwareinfo.HardwareRevisionPreferenceController" settings:enableCopying="true"/> + + + diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProvider.kt b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProvider.kt new file mode 100644 index 00000000000..400ece9a217 --- /dev/null +++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProvider.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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.deviceinfo.hardwareinfo + +/** + * Feature provider for hardware info + */ +interface HardwareInfoFeatureProvider { + /** + * Returns the manufactured year + */ + val manufacturedYear: String? +} \ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProviderImpl.kt b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProviderImpl.kt new file mode 100644 index 00000000000..54a112bec35 --- /dev/null +++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFeatureProviderImpl.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.deviceinfo.hardwareinfo + +/** + * Feature provider for hardware info + */ +object HardwareInfoFeatureProviderImpl : HardwareInfoFeatureProvider { + override val manufacturedYear: String? + get() = null +} \ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/ManufacturedYearPreferenceController.kt b/src/com/android/settings/deviceinfo/hardwareinfo/ManufacturedYearPreferenceController.kt new file mode 100644 index 00000000000..0b0eeb750ec --- /dev/null +++ b/src/com/android/settings/deviceinfo/hardwareinfo/ManufacturedYearPreferenceController.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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.deviceinfo.hardwareinfo + +import android.content.Context +import com.android.settings.core.BasePreferenceController +import com.android.settings.overlay.FeatureFactory + +/** Preference controller for Manufactured Year. */ +class ManufacturedYearPreferenceController(context: Context, preferenceKey: String) : + BasePreferenceController(context, preferenceKey) { + private val year: String? = + FeatureFactory.getFactory(context).hardwareInfoFeatureProvider.manufacturedYear + + override fun getAvailabilityStatus(): Int = + if (!year.isNullOrEmpty()) AVAILABLE else UNSUPPORTED_ON_DEVICE + + override fun getSummary(): CharSequence = year ?: "" +} \ No newline at end of file diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index a6d595810b3..c536a382b55 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -33,6 +33,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -109,6 +110,11 @@ public abstract class FeatureFactory { */ public abstract SuggestionFeatureProvider getSuggestionFeatureProvider(); + /** + * Retrieves implementation for Hardware Info feature. + */ + public abstract HardwareInfoFeatureProvider getHardwareInfoFeatureProvider(); + public abstract SupportFeatureProvider getSupportFeatureProvider(Context context); public abstract MetricsFeatureProvider getMetricsFeatureProvider(); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 584e72a08c5..3ddda474b08 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -47,6 +47,8 @@ import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProviderImpl; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; @@ -115,6 +117,11 @@ public class FeatureFactoryImpl extends FeatureFactory { private AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider; private WifiFeatureProvider mWifiFeatureProvider; + @Override + public HardwareInfoFeatureProvider getHardwareInfoFeatureProvider() { + return HardwareInfoFeatureProviderImpl.INSTANCE; + } + @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { return null; diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index a17ce4f8608..29a6da372a2 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -31,6 +31,8 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProviderImpl; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -287,6 +289,11 @@ public class FakeFeatureFactory extends FeatureFactory { return mAccessibilityMetricsFeatureProvider; } + @Override + public HardwareInfoFeatureProvider getHardwareInfoFeatureProvider() { + return HardwareInfoFeatureProviderImpl.INSTANCE; + } + @Override public AdvancedVpnFeatureProvider getAdvancedVpnFeatureProvider() { return mAdvancedVpnFeatureProvider; diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt index da6e8231536..99d4f32b253 100644 --- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt +++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt @@ -27,6 +27,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider import com.android.settings.bluetooth.BluetoothFeatureProvider import com.android.settings.dashboard.DashboardFeatureProvider import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider import com.android.settings.fuelgauge.BatterySettingsFeatureProvider import com.android.settings.fuelgauge.BatteryStatusFeatureProvider @@ -175,6 +176,10 @@ class FakeFeatureFactory : FeatureFactory() { TODO("Not yet implemented") } + override fun getHardwareInfoFeatureProvider(): HardwareInfoFeatureProvider { + TODO("Not yet implemented") + } + override fun getAdvancedVpnFeatureProvider(): AdvancedVpnFeatureProvider { TODO("Not yet implemented") } diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index 0150b726544..697217bd332 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -29,6 +29,8 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider; +import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProviderImpl; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -273,6 +275,11 @@ public class FakeFeatureFactory extends FeatureFactory { return mAccessibilityMetricsFeatureProvider; } + @Override + public HardwareInfoFeatureProvider getHardwareInfoFeatureProvider() { + return HardwareInfoFeatureProviderImpl.INSTANCE; + } + @Override public AdvancedVpnFeatureProvider getAdvancedVpnFeatureProvider() { return mAdvancedVpnFeatureProvider; From 7e6443e025ca49a9e8030712e2959cdaa286f1c4 Mon Sep 17 00:00:00 2001 From: Wenhui Yang Date: Tue, 6 Jun 2023 22:02:52 +0000 Subject: [PATCH 7/7] Fix face re-enroll stuck for work profile Call super.onCreate(savedInstanceState) first to pass the correct userId in maxFacesEnrolled. Test: Manual - delete face unlock for work and re-enroll and observe face unlock can be enrolled successfully Test: atest FaceEnrollIntroductionTest Fixes: 284819031 Change-Id: Ic1620c0ca3ca9adc61f5281abd34471f0c1b3f97 --- .../face/FaceEnrollIntroduction.java | 4 +- .../face/FaceEnrollIntroductionTest.java | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java index bff998a4a3e..bea0c3389ec 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java @@ -120,6 +120,8 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { protected void onCreate(Bundle savedInstanceState) { mFaceManager = getFaceManager(); + super.onCreate(savedInstanceState); + if (savedInstanceState == null && !WizardManagerHelper.isAnySetupWizard(getIntent()) && !getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false) @@ -130,8 +132,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { finish(); } - super.onCreate(savedInstanceState); - // Wait super::onCreated() then return because SuperNotCalledExceptio will be thrown // if we don't wait for it. if (isFinishing()) { diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java index c4da1335509..df15e5c32f8 100644 --- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java @@ -40,6 +40,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.Resources; import android.hardware.face.Face; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; @@ -116,6 +117,7 @@ public class FaceEnrollIntroductionTest { private FaceEnrollIntroduction mSpyActivity; private FakeFeatureFactory mFakeFeatureFactory; private ShadowUserManager mUserManager; + private Resources mResources; enum GateKeeperAction {CALL_SUPER, RETURN_BYTE_ARRAY, THROW_CREDENTIAL_NOT_MATCH} @@ -245,6 +247,14 @@ public class FaceEnrollIntroductionTest { when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(faces); } + private void setFaceManagerToHaveWithUserId(int numEnrollments, int userId) { + List faces = new ArrayList<>(); + for (int i = 0; i < numEnrollments; i++) { + faces.add(new Face("Face " + i /* name */, 1 /*faceId */, 1 /* deviceId */)); + } + when(mFaceManager.getEnrolledFaces(userId)).thenReturn(faces); + } + @Test public void intro_CheckCanEnroll() { setFaceManagerToHave(0 /* numEnrollments */); @@ -546,4 +556,40 @@ public class FaceEnrollIntroductionTest { assertThat(mActivity.getPostureCallback()).isNull(); } + @Test + public void testFaceEnrollIntroduction_maxFacesNotEnrolled_addUserProfile() { + // Enroll a face for one user + setFaceManagerToHaveWithUserId(1, 0); + + mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = spy(mContext.getResources()); + when(mResources.getInteger(R.integer.suw_max_faces_enrollable)).thenReturn(1); + + mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, new Intent()); + mActivity = (TestFaceEnrollIntroduction) mController.get(); + + mController.create(); + + // The maximum number of faces is already enrolled + int result = mActivity.checkMaxEnrolled(); + assertThat(result).isEqualTo(R.string.face_intro_error_max); + + // Add another user profile + mUserManager.addUser(10, "", 0); + final Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_USER_ID, 10); + + when(mResources.getInteger(R.integer.suw_max_faces_enrollable)).thenReturn(2); + + mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); + mActivity = (TestFaceEnrollIntroduction) mController.get(); + + mController.create(); + + // The maximum number of faces hasn't been enrolled, so a new face + // can be enrolled for the added user profile + result = mActivity.checkMaxEnrolled(); + assertThat(result).isEqualTo(0); + } + }