diff options
46 files changed, 1298 insertions, 515 deletions
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 5efda98a1518..bf67187f4bad 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -75,10 +75,10 @@ fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: St fun getClassFlag(classItem: ClassItem): String? { var classFlag = getFlagAnnotation(classItem) var cur = classItem - // If a class is not an inner class, use its @FlaggedApi annotation value. + // If a class is not a nested class, use its @FlaggedApi annotation value. // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi. - while (cur.isInnerClass() && classFlag == null) { - cur = cur.parent() as ClassItem + while (classFlag == null) { + cur = cur.containingClass() ?: break classFlag = getFlagAnnotation(cur) } return classFlag diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5aa89b98353f..805cfb7b115d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3738,6 +3738,7 @@ package android.content { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[], @Nullable android.app.BroadcastOptions); + method @FlaggedApi("android.os.ordered_broadcast_multiple_permissions") public void sendOrderedBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[], @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @Nullable android.os.Bundle, @NonNull android.os.UserHandle); field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context"; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index af56cb4d55b2..d1bd88cfd1d8 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1524,6 +1524,17 @@ class ContextImpl extends Context { public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + String[] receiverPermissions = receiverPermission == null ? null + : new String[] {receiverPermission}; + sendOrderedBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions, appOp, + options, resultReceiver, scheduler, initialCode, initialData, initialExtras); + } + + @Override + public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, + String[] receiverPermissions, int appOp, Bundle options, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { IIntentReceiver rd = null; if (resultReceiver != null) { if (mPackageInfo != null) { @@ -1543,8 +1554,6 @@ class ContextImpl extends Context { } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); - String[] receiverPermissions = receiverPermission == null ? null - : new String[] {receiverPermission}; try { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( @@ -1571,6 +1580,20 @@ class ContextImpl extends Context { } @Override + public void sendOrderedBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, + int initialCode, String initialData, @Nullable Bundle initialExtras, + @Nullable Bundle options) { + int intAppOp = AppOpsManager.OP_NONE; + if (!TextUtils.isEmpty(receiverAppOp)) { + intAppOp = AppOpsManager.strOpToOp(receiverAppOp); + } + sendOrderedBroadcastAsUserMultiplePermissions(intent, getUser(), receiverPermissions, + intAppOp, options, resultReceiver, scheduler, initialCode, initialData, + initialExtras); + } + + @Override public void sendOrderedBroadcast(Intent intent, int initialCode, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, String initialData, @Nullable Bundle initialExtras, Bundle options) { diff --git a/core/java/android/app/admin/OWNERS b/core/java/android/app/admin/OWNERS index 308f1d622c25..4f3f5d9c3535 100644 --- a/core/java/android/app/admin/OWNERS +++ b/core/java/android/app/admin/OWNERS @@ -1,7 +1,6 @@ # Bug component: 142675 # Assign bugs to device-policy-manager-triage@google.com -file:WorkDeviceExperience_OWNERS file:EnterprisePlatformSecurity_OWNERS yamasani@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file diff --git a/core/java/android/app/assist/OWNERS b/core/java/android/app/assist/OWNERS index e4ffd7f41aa0..b53bdc202a89 100644 --- a/core/java/android/app/assist/OWNERS +++ b/core/java/android/app/assist/OWNERS @@ -1,2 +1 @@ -hackz@google.com -volnov@google.com
\ No newline at end of file +srazdan@google.com diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a6eed50a594a..b121da37fd40 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2916,6 +2916,23 @@ public abstract class Context { @Nullable String initialData, @Nullable Bundle initialExtras); /** + * Similar to above but takes array of names of permissions that a receiver must hold in order + * to receive your broadcast. If empty, no permissions are required. + * + * @see #sendOrderedBroadcastAsUser(Intent, UserHandle, String, + * BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, + UserHandle user, String[] receiverPermissions, int appOp, Bundle options, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Version of * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers @@ -2997,6 +3014,21 @@ public abstract class Context { } /** + * Like {@link #sendOrderedBroadcast(Intent, String, String, BroadcastReceiver, Handler, int, + * String, Bundle)}, but also allows specification of a list of multiple permissions. + * @hide + */ + @FlaggedApi(Flags.FLAG_ORDERED_BROADCAST_MULTIPLE_PERMISSIONS) + @SystemApi + public void sendOrderedBroadcastMultiplePermissions( + @NonNull Intent intent, @NonNull String[] receiverPermissions, + @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras, @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the * Intent you are sending stays around after the broadcast is complete, * so that others can quickly retrieve that data through the return diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index a475c2925881..79fa6ea4d157 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -652,6 +652,16 @@ public class ContextWrapper extends Context { resultReceiver, scheduler, initialCode, initialData, initialExtras); } + /** @hide */ + @Override + public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, + @Nullable String[] receiverPermission, int appOp, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { + mBase.sendOrderedBroadcastAsUserMultiplePermissions(intent, user, receiverPermission, appOp, + options, resultReceiver, scheduler, initialCode, initialData, initialExtras); + } + @Override public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, @Nullable String receiverPermission, @Nullable String receiverAppOp, @@ -661,6 +671,17 @@ public class ContextWrapper extends Context { scheduler, initialCode, initialData, initialExtras); } + /** @hide */ + @Override + public void sendOrderedBroadcastMultiplePermissions( + @NonNull Intent intent, @NonNull String[] receiverPermissions, + @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras, @Nullable Bundle options) { + mBase.sendOrderedBroadcastMultiplePermissions(intent, receiverPermissions, receiverAppOp, + resultReceiver, scheduler, initialCode, initialData, initialExtras, options); + } + @Override public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode, @Nullable String receiverPermission, @Nullable String receiverAppOp, diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS index 51ad1519941b..43d3f5466ccf 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -5,7 +5,7 @@ michaelwr@google.com sumir@google.com # Camera -per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,zhijunhe@google.com,jchowdhary@google.com +per-file *Camera*=file:platform/frameworks/av:/camera/OWNERS # Sensor Privacy per-file *SensorPrivacy* = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index ca694579e9d0..200c1d8edafa 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -68,6 +68,14 @@ flag { } flag { + name: "ordered_broadcast_multiple_permissions" + is_exported: true + namespace: "bluetooth" + description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API" + bug: "345802719" +} + +flag { name: "battery_saver_supported_check_api" namespace: "backstage_power" description: "Guards a new API in PowerManager to check if battery saver is supported or not." diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS index 898947adcd1b..7f3b4e5a21b3 100644 --- a/core/java/android/view/autofill/OWNERS +++ b/core/java/android/view/autofill/OWNERS @@ -1,10 +1,11 @@ # Bug component: 351486 -simranjit@google.com haoranzhang@google.com +jiewenlei@google.com +simranjit@google.com skxu@google.com +shuc@google.com yunicorn@google.com -reemabajwa@google.com # Bug component: 543785 = per-file *Augmented* per-file *Augmented* = wangqi@google.com diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 82367834f93d..511c6802677e 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -109,4 +109,5 @@ interface ILockSettings { boolean isWeakEscrowTokenActive(long handle, int userId); boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId); void unlockUserKeyIfUnsecured(int userId); + boolean writeRepairModeCredential(int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e46b8d7c5fae..f4ad487f48ac 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -449,6 +449,21 @@ public class LockPatternUtils { } /** + * Save the current password data to the repair mode file. + * + * @return true if success or false otherwise. + */ + public boolean writeRepairModeCredential(int userId) { + throwIfCalledOnMainThread(); + try { + return getLockSettings().writeRepairModeCredential(userId); + } catch (RemoteException re) { + Log.e(TAG, "Failed to write repair mode credential", re); + return false; + } + } + + /** * Check to see if a credential matches the saved one. * If credential matches, return an opaque attestation that the challenge was verified. * diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 0734e6827d4d..11c220b14bcc 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -261,6 +261,8 @@ public class LockPatternView extends View { public float lineEndY = Float.MIN_VALUE; @Nullable Animator activationAnimator; + @Nullable + Animator deactivationAnimator; } /** @@ -667,7 +669,7 @@ public class LockPatternView extends View { */ private void resetPattern() { if (mKeepDotActivated && !mPattern.isEmpty()) { - resetLastActivatedCellProgress(); + resetPatternCellSize(); } mPattern.clear(); mPatternPath.reset(); @@ -676,14 +678,20 @@ public class LockPatternView extends View { invalidate(); } - private void resetLastActivatedCellProgress() { - final ArrayList<Cell> pattern = mPattern; - final Cell lastCell = pattern.get(pattern.size() - 1); - final CellState cellState = mCellStates[lastCell.row][lastCell.column]; - if (cellState.activationAnimator != null) { - cellState.activationAnimator.cancel(); + private void resetPatternCellSize() { + for (int i = 0; i < mCellStates.length; i++) { + for (int j = 0; j < mCellStates[i].length; j++) { + CellState cellState = mCellStates[i][j]; + if (cellState.activationAnimator != null) { + cellState.activationAnimator.cancel(); + } + if (cellState.deactivationAnimator != null) { + cellState.deactivationAnimator.cancel(); + } + cellState.activationAnimationProgress = 0f; + cellState.radius = mDotSize / 2f; + } } - cellState.activationAnimationProgress = 0f; } /** @@ -819,12 +827,16 @@ public class LockPatternView extends View { !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { addCellToPattern(fillInGapCell); if (mKeepDotActivated) { - startCellDeactivatedAnimation(fillInGapCell); + if (mFadePattern) { + startCellDeactivatedAnimation(fillInGapCell, /* fillInGap= */ true); + } else { + startCellActivatedAnimation(fillInGapCell); + } } } if (mKeepDotActivated && lastCell != null) { - startCellDeactivatedAnimation(lastCell); + startCellDeactivatedAnimation(lastCell, /* fillInGap= */ false); } addCellToPattern(cell); @@ -872,17 +884,25 @@ public class LockPatternView extends View { } private void startCellActivatedAnimation(Cell cell) { - startCellActivationAnimation(cell, CELL_ACTIVATE); + startCellActivationAnimation(cell, CELL_ACTIVATE, /* fillInGap= */ false); } - private void startCellDeactivatedAnimation(Cell cell) { - startCellActivationAnimation(cell, CELL_DEACTIVATE); + private void startCellDeactivatedAnimation(Cell cell, boolean fillInGap) { + startCellActivationAnimation(cell, CELL_DEACTIVATE, /* fillInGap= */ fillInGap); } - private void startCellActivationAnimation(Cell cell, int activate) { + /** + * Start cell animation. + * @param cell The cell to be animated. + * @param activate Whether the cell is being activated or deactivated. + * @param fillInGap Whether the cell is a gap cell, i.e. filled in based on current pattern. + */ + private void startCellActivationAnimation(Cell cell, int activate, boolean fillInGap) { final CellState cellState = mCellStates[cell.row][cell.column]; - if (cellState.activationAnimator != null) { + // When mKeepDotActivated is true, don't cancel the previous animator since it would leave + // a dot in an in-between size if the next dot is reached before the animation is finished. + if (cellState.activationAnimator != null && !mKeepDotActivated) { cellState.activationAnimator.cancel(); } AnimatorSet animatorSet = new AnimatorSet(); @@ -898,24 +918,37 @@ public class LockPatternView extends View { .with(createLineEndAnimation(cellState, startX, startY, getCenterXForColumn(cell.column), getCenterYForRow(cell.row))); if (mDotSize != mDotSizeActivated) { - animatorSetBuilder.with(createDotRadiusAnimation(cellState)); + animatorSetBuilder.with(createDotRadiusAnimation(cellState, activate, fillInGap)); } if (mDotColor != mDotActivatedColor) { - animatorSetBuilder.with(createDotActivationColorAnimation(cellState, activate)); + animatorSetBuilder.with( + createDotActivationColorAnimation(cellState, activate, fillInGap)); } - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - cellState.activationAnimator = null; - invalidate(); - } - }); - cellState.activationAnimator = animatorSet; + if (activate == CELL_ACTIVATE) { + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + cellState.activationAnimator = null; + invalidate(); + } + }); + cellState.activationAnimator = animatorSet; + } else { + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + cellState.deactivationAnimator = null; + invalidate(); + } + }); + cellState.deactivationAnimator = animatorSet; + } animatorSet.start(); } - private Animator createDotActivationColorAnimation(CellState cellState, int activate) { + private Animator createDotActivationColorAnimation( + CellState cellState, int activate, boolean fillInGap) { ValueAnimator.AnimatorUpdateListener updateListener = valueAnimator -> { cellState.activationAnimationProgress = @@ -934,7 +967,7 @@ public class LockPatternView extends View { deactivateAnimator.setDuration(DOT_ACTIVATION_DURATION_MILLIS); AnimatorSet set = new AnimatorSet(); - if (mKeepDotActivated) { + if (mKeepDotActivated && !fillInGap) { set.play(activate == CELL_ACTIVATE ? activateAnimator : deactivateAnimator); } else { // 'activate' ignored in this case, do full deactivate -> activate cycle @@ -977,7 +1010,7 @@ public class LockPatternView extends View { return valueAnimator; } - private Animator createDotRadiusAnimation(CellState state) { + private Animator createDotRadiusAnimation(CellState state, int activate, boolean fillInGap) { float defaultRadius = mDotSize / 2f; float activatedRadius = mDotSizeActivated / 2f; @@ -998,7 +1031,19 @@ public class LockPatternView extends View { deactivationAnimator.setDuration(DOT_RADIUS_DECREASE_DURATION_MILLIS); AnimatorSet set = new AnimatorSet(); - set.playSequentially(activationAnimator, deactivationAnimator); + if (mKeepDotActivated) { + if (mFadePattern) { + if (fillInGap) { + set.playSequentially(activationAnimator, deactivationAnimator); + } else { + set.play(activate == CELL_ACTIVATE ? activationAnimator : deactivationAnimator); + } + } else if (activate == CELL_ACTIVATE) { + set.play(activationAnimator); + } + } else { + set.playSequentially(activationAnimator, deactivationAnimator); + } return set; } @@ -1176,9 +1221,15 @@ public class LockPatternView extends View { // report pattern detected if (!mPattern.isEmpty()) { setPatternInProgress(false); - cancelLineAnimations(); if (mKeepDotActivated) { + // When mKeepDotActivated is true, cancelling dot animations and resetting dot radii + // are handled in #resetPattern(), since we want to keep the dots activated until + // the pattern are reset. deactivateLastCell(); + } else { + // When mKeepDotActivated is false, cancelling animations and resetting dot radii + // are handled here. + cancelLineAnimations(); } notifyPatternDetected(); // Also clear pattern if fading is enabled @@ -1198,7 +1249,7 @@ public class LockPatternView extends View { private void deactivateLastCell() { Cell lastCell = mPattern.get(mPattern.size() - 1); - startCellDeactivatedAnimation(lastCell); + startCellDeactivatedAnimation(lastCell, /* fillInGap= */ false); } private void cancelLineAnimations() { diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index e2672f5b03ba..cf2f202a03ac 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -9,18 +9,18 @@ per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS # Notification related -per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS -per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS -per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS -per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS -per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS -per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS -per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS -per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS -per-file ImageFloatingTextView.java = file:/services/core/java/com/android/server/notification/OWNERS -per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS -per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS -per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS +per-file *Notification* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file *Messaging* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file *Message* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file *Conversation* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file *People* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file *ImageResolver* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file CallLayout.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file CachingIconView.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file ImageFloatingTextView.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file ObservableTextView.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file RemeasuringLinearLayout.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS +per-file ViewClippingUtil.java = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS # Appwidget related per-file *RemoteViews* = file:/services/appwidget/java/com/android/server/appwidget/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java index b90480a1e794..92a7d8ef890d 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -68,6 +68,7 @@ import org.mockito.ArgumentCaptor; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.CompletableFuture; @RunWith(AndroidJUnit4.class) @SmallTest @@ -316,6 +317,40 @@ public class LockPatternUtilsTest { assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE); } + @Test + public void testWriteRepairModeCredential_mainThread() { + createTestLockSettings(); + var context = InstrumentationRegistry.getTargetContext(); + + var future = new CompletableFuture<Exception>(); + context.getMainThreadHandler().post(() -> { + try { + mLockPatternUtils.writeRepairModeCredential(USER_ID); + future.complete(null); + } catch (Exception e) { + future.complete(e); + } + }); + + var e = future.join(); + assertThat(e).isNotNull(); + assertThat(e.getMessage()).contains("should not be called from the main thread"); + } + + @Test + public void testWriteRepairModeCredential() throws Exception { + var ils = createTestLockSettings(); + + when(ils.writeRepairModeCredential(USER_ID)).thenReturn(false); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse(); + + when(ils.writeRepairModeCredential(USER_ID)).thenReturn(true); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isTrue(); + + when(ils.writeRepairModeCredential(USER_ID)).thenThrow(new RemoteException()); + assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse(); + } + private TestStrongAuthTracker createStrongAuthTracker() { final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext()); return new TestStrongAuthTracker(context, Looper.getMainLooper()); diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 701d145fe805..85dae631cac3 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -1,6 +1,5 @@ include /PACKAGE_MANAGER_OWNERS -alanstokes@google.com cbrubaker@google.com hackbod@android.com hackbod@google.com diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS index ebebd8a52c9a..cb422eab372b 100644 --- a/libs/WindowManager/Shell/OWNERS +++ b/libs/WindowManager/Shell/OWNERS @@ -1,5 +1,5 @@ xutan@google.com # Give submodule owners in shell resource approval -per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com +per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, vaniadesmonda@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com per-file res*/*/tv_*.xml = bronger@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS new file mode 100644 index 000000000000..482aaab6bc74 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS @@ -0,0 +1 @@ +per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index 0f24bb549158..2a0a28ebcaa2 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -9,7 +9,7 @@ hwwang@google.com chenghsiuchang@google.com atsjenk@google.com jorgegil@google.com -nmusgrave@google.com +vaniadesmonda@google.com pbdr@google.com tkachenkoi@google.com mpodolian@google.com diff --git a/location/Android.bp b/location/Android.bp index 7f3442c47d1f..10ca97d74660 100644 --- a/location/Android.bp +++ b/location/Android.bp @@ -30,9 +30,6 @@ java_sdk_library { "app-compat-annotations", "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage ], - hidden_api_packages: [ - "com.android.internal.location", - ], aidl: { include_dirs: [ "frameworks/base/location/java", diff --git a/location/java/com/android/internal/location/package-info.java b/location/java/com/android/internal/location/package-info.java new file mode 100644 index 000000000000..25573c15ce59 --- /dev/null +++ b/location/java/com/android/internal/location/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Exclude from API surfaces + * + * @hide + */ +package com.android.internal.location; diff --git a/nfc/Android.bp b/nfc/Android.bp index 421f06d5cf2b..2a01b3fec088 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -59,9 +59,6 @@ java_sdk_library { "android.nfc", "com.android.nfc", ], - hidden_api_packages: [ - "com.android.nfc", - ], impl_library_visibility: [ "//frameworks/base:__subpackages__", "//cts/tests/tests/nfc", diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlocklister.java index e726c6abfac3..9e23f884f4ba 100644 --- a/services/core/java/com/android/server/CertBlacklister.java +++ b/services/core/java/com/android/server/CertBlocklister.java @@ -16,37 +16,39 @@ package com.android.server; -import android.content.Context; import android.content.ContentResolver; +import android.content.Context; import android.database.ContentObserver; import android.os.Binder; import android.os.FileUtils; import android.provider.Settings; import android.util.Slog; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import libcore.io.IoUtils; - /** - * <p>CertBlacklister provides a simple mechanism for updating the platform denylists for SSL + * <p>CertBlocklister provides a simple mechanism for updating the platform denylists for SSL * certificate public keys and serial numbers. */ -public class CertBlacklister extends Binder { +public class CertBlocklister extends Binder { - private static final String TAG = "CertBlacklister"; + private static final String TAG = "CertBlocklister"; private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; + /* For compatibility reasons, the name of these paths cannot be changed */ public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt"; public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt"; - public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist"; - public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist"; + /* For compatibility reasons, the name of these keys cannot be changed */ + public static final String PUBKEY_BLOCKLIST_KEY = "pubkey_blacklist"; + public static final String SERIAL_BLOCKLIST_KEY = "serial_blacklist"; - private static class BlacklistObserver extends ContentObserver { + private static class BlocklistObserver extends ContentObserver { private final String mKey; private final String mName; @@ -54,7 +56,7 @@ public class CertBlacklister extends Binder { private final File mTmpDir; private final ContentResolver mContentResolver; - public BlacklistObserver(String key, String name, String path, ContentResolver cr) { + BlocklistObserver(String key, String name, String path, ContentResolver cr) { super(null); mKey = key; mName = name; @@ -66,59 +68,61 @@ public class CertBlacklister extends Binder { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - writeDenylist(); + new Thread("BlocklistUpdater") { + public void run() { + writeDenylist(); + } + }.start(); } public String getValue() { - return Settings.Secure.getString(mContentResolver, mKey); + return Settings.Secure.getStringForUser( + mContentResolver, mKey, mContentResolver.getUserId()); } private void writeDenylist() { - new Thread("BlacklistUpdater") { - public void run() { - synchronized(mTmpDir) { - String blacklist = getValue(); - if (blacklist != null) { - Slog.i(TAG, "Certificate blacklist changed, updating..."); - FileOutputStream out = null; - try { - // create a temporary file - File tmp = File.createTempFile("journal", "", mTmpDir); - // mark it -rw-r--r-- - tmp.setReadable(true, false); - // write to it - out = new FileOutputStream(tmp); - out.write(blacklist.getBytes()); - // sync to disk - FileUtils.sync(out); - // atomic rename - tmp.renameTo(new File(mPath)); - Slog.i(TAG, "Certificate blacklist updated"); - } catch (IOException e) { - Slog.e(TAG, "Failed to write blacklist", e); - } finally { - IoUtils.closeQuietly(out); - } - } - } + synchronized (mTmpDir) { + String blocklist = getValue(); + if (blocklist == null) { + return; } - }.start(); + if (mPath.equals(SERIAL_PATH)) { + Slog.w(TAG, "The certificate blocklist based on serials is deprecated. " + + "Please use the pubkey blocklist instead."); + } + Slog.i(TAG, "Certificate blocklist changed, updating..."); + FileOutputStream out = null; + try { + // Create a temporary file and rename it atomically. + File tmp = File.createTempFile("journal", "", mTmpDir); + tmp.setReadable(true /* readable */, false /* ownerOnly */); + out = new FileOutputStream(tmp); + out.write(blocklist.getBytes()); + FileUtils.sync(out); + tmp.renameTo(new File(mPath)); + Slog.i(TAG, "Certificate blocklist updated"); + } catch (IOException e) { + Slog.e(TAG, "Failed to write blocklist", e); + } finally { + IoUtils.closeQuietly(out); + } + } } } - public CertBlacklister(Context context) { + public CertBlocklister(Context context) { registerObservers(context.getContentResolver()); } - private BlacklistObserver buildPubkeyObserver(ContentResolver cr) { - return new BlacklistObserver(PUBKEY_BLACKLIST_KEY, + private BlocklistObserver buildPubkeyObserver(ContentResolver cr) { + return new BlocklistObserver(PUBKEY_BLOCKLIST_KEY, "pubkey", PUBKEY_PATH, cr); } - private BlacklistObserver buildSerialObserver(ContentResolver cr) { - return new BlacklistObserver(SERIAL_BLACKLIST_KEY, + private BlocklistObserver buildSerialObserver(ContentResolver cr) { + return new BlocklistObserver(SERIAL_BLOCKLIST_KEY, "serial", SERIAL_PATH, cr); @@ -127,16 +131,16 @@ public class CertBlacklister extends Binder { private void registerObservers(ContentResolver cr) { // set up the public key denylist observer cr.registerContentObserver( - Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY), - true, - buildPubkeyObserver(cr) + Settings.Secure.getUriFor(PUBKEY_BLOCKLIST_KEY), + true, + buildPubkeyObserver(cr) ); // set up the serial number denylist observer cr.registerContentObserver( - Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY), - true, - buildSerialObserver(cr) + Settings.Secure.getUriFor(SERIAL_BLOCKLIST_KEY), + true, + buildSerialObserver(cr) ); } } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 2545620a2630..a627a3967802 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -48,3 +48,6 @@ per-file VcnManagementService.java = file:/services/core/java/com/android/server # SystemConfig per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS + +# CertBlocklister +per-file Cert*.java = tweek@google.com, brambonne@google.com, prb@google.com, miguelaranda@google.com diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index dfd148dfc0ec..c2cb5e90ca58 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -241,12 +241,14 @@ public class RescueParty { * opportunity to reset any settings depending on our rescue level. */ public static void onSettingsProviderPublished(Context context) { - handleNativeRescuePartyResets(); - ContentResolver contentResolver = context.getContentResolver(); - DeviceConfig.setMonitorCallback( - contentResolver, - Executors.newSingleThreadExecutor(), - new RescuePartyMonitorCallback(context)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + handleNativeRescuePartyResets(); + ContentResolver contentResolver = context.getContentResolver(); + DeviceConfig.setMonitorCallback( + contentResolver, + Executors.newSingleThreadExecutor(), + new RescuePartyMonitorCallback(context)); + } } @@ -256,75 +258,81 @@ public class RescueParty { * on modules of newer versions. */ public static void resetDeviceConfigForPackages(List<String> packageNames) { - if (packageNames == null) { - return; - } - Set<String> namespacesToReset = new ArraySet<String>(); - Iterator<String> it = packageNames.iterator(); - RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); - // Get runtime package to namespace mapping if created. - if (rescuePartyObserver != null) { - while (it.hasNext()) { - String packageName = it.next(); - Set<String> runtimeAffectedNamespaces = - rescuePartyObserver.getAffectedNamespaceSet(packageName); - if (runtimeAffectedNamespaces != null) { - namespacesToReset.addAll(runtimeAffectedNamespaces); + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (packageNames == null) { + return; + } + Set<String> namespacesToReset = new ArraySet<String>(); + Iterator<String> it = packageNames.iterator(); + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); + // Get runtime package to namespace mapping if created. + if (rescuePartyObserver != null) { + while (it.hasNext()) { + String packageName = it.next(); + Set<String> runtimeAffectedNamespaces = + rescuePartyObserver.getAffectedNamespaceSet(packageName); + if (runtimeAffectedNamespaces != null) { + namespacesToReset.addAll(runtimeAffectedNamespaces); + } } } - } - // Get preset package to namespace mapping if created. - Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( - packageNames); - if (presetAffectedNamespaces != null) { - namespacesToReset.addAll(presetAffectedNamespaces); - } + // Get preset package to namespace mapping if created. + Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( + packageNames); + if (presetAffectedNamespaces != null) { + namespacesToReset.addAll(presetAffectedNamespaces); + } - // Clear flags under the namespaces mapped to these packages. - // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. - Iterator<String> namespaceIt = namespacesToReset.iterator(); - while (namespaceIt.hasNext()) { - String namespaceToReset = namespaceIt.next(); - Properties properties = new Properties.Builder(namespaceToReset).build(); - try { - if (!DeviceConfig.setProperties(properties)) { - logCriticalInfo(Log.ERROR, "Failed to clear properties under " + // Clear flags under the namespaces mapped to these packages. + // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. + Iterator<String> namespaceIt = namespacesToReset.iterator(); + while (namespaceIt.hasNext()) { + String namespaceToReset = namespaceIt.next(); + Properties properties = new Properties.Builder(namespaceToReset).build(); + try { + if (!DeviceConfig.setProperties(properties)) { + logCriticalInfo(Log.ERROR, "Failed to clear properties under " + namespaceToReset + ". Running `device_config get_sync_disabled_for_tests` will confirm" + " if config-bulk-update is enabled."); + } + } catch (DeviceConfig.BadConfigException exception) { + logCriticalInfo(Log.WARN, "namespace " + namespaceToReset + + " is already banned, skip reset."); } - } catch (DeviceConfig.BadConfigException exception) { - logCriticalInfo(Log.WARN, "namespace " + namespaceToReset - + " is already banned, skip reset."); } } } private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { Set<String> resultSet = new ArraySet<String>(); - try { - String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, - NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); - String[] mappingEntries = flagVal.split(","); - for (int i = 0; i < mappingEntries.length; i++) { - if (TextUtils.isEmpty(mappingEntries[i])) { - continue; - } - String[] splittedEntry = mappingEntries[i].split(":"); - if (splittedEntry.length != 2) { - throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); - } - String namespace = splittedEntry[0]; - String packageName = splittedEntry[1]; + if (!Flags.deprecateFlagsAndSettingsResets()) { + try { + String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, + NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); + String[] mappingEntries = flagVal.split(","); + for (int i = 0; i < mappingEntries.length; i++) { + if (TextUtils.isEmpty(mappingEntries[i])) { + continue; + } + String[] splitEntry = mappingEntries[i].split(":"); + if (splitEntry.length != 2) { + throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); + } + String namespace = splitEntry[0]; + String packageName = splitEntry[1]; - if (packageNames.contains(packageName)) { - resultSet.add(namespace); + if (packageNames.contains(packageName)) { + resultSet.add(namespace); + } } + } catch (Exception e) { + resultSet.clear(); + Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); + } finally { + return resultSet; } - } catch (Exception e) { - resultSet.clear(); - Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); - } finally { + } else { return resultSet; } } @@ -342,43 +350,54 @@ public class RescueParty { } public void onNamespaceUpdate(@NonNull String updatedNamespace) { - startObservingPackages(mContext, updatedNamespace); + if (!Flags.deprecateFlagsAndSettingsResets()) { + startObservingPackages(mContext, updatedNamespace); + } } public void onDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { - RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess( - callingPackage, - namespace); + + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess( + callingPackage, + namespace); + } } } private static void startObservingPackages(Context context, @NonNull String updatedNamespace) { - RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); - Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace); - if (callingPackages == null) { - return; + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); + Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet( + updatedNamespace); + if (callingPackages == null) { + return; + } + List<String> callingPackageList = new ArrayList<>(); + callingPackageList.addAll(callingPackages); + Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " + + updatedNamespace); + PackageWatchdog.getInstance(context).startObservingHealth( + rescuePartyObserver, + callingPackageList, + DEFAULT_OBSERVING_DURATION_MS); } - List<String> callingPackageList = new ArrayList<>(); - callingPackageList.addAll(callingPackages); - Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " - + updatedNamespace); - PackageWatchdog.getInstance(context).startObservingHealth( - rescuePartyObserver, - callingPackageList, - DEFAULT_OBSERVING_DURATION_MS); } private static void handleNativeRescuePartyResets() { - if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { - String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); - for (int i = 0; i < resetNativeCategories.length; i++) { - // Don't let RescueParty reset the namespace for RescueParty switches. - if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { - continue; + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { + String[] resetNativeCategories = + SettingsToPropertiesMapper.getResetNativeCategories(); + for (int i = 0; i < resetNativeCategories.length; i++) { + // Don't let RescueParty reset the namespace for RescueParty switches. + if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { + continue; + } + DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, + resetNativeCategories[i]); } - DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, - resetNativeCategories[i]); } } } @@ -400,6 +419,13 @@ public class RescueParty { } } + private static int getMaxRescueLevel() { + if (!SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { + return Level.factoryReset(); + } + return Level.reboot(); + } + /** * Get the rescue level to perform if this is the n-th attempt at mitigating failure. * @@ -409,19 +435,30 @@ public class RescueParty { * @return the rescue level for the n-th mitigation attempt. */ private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) { - if (mitigationCount == 1) { - return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; - } else if (mitigationCount == 2) { - return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; - } else if (mitigationCount == 3) { - return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; - } else if (mitigationCount == 4) { - return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT); - } else if (mitigationCount >= 5) { - return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET); + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (mitigationCount == 1) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; + } else if (mitigationCount == 2) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; + } else if (mitigationCount == 3) { + return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; + } else if (mitigationCount == 4) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT); + } else if (mitigationCount >= 5) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } } else { - Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); - return LEVEL_NONE; + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } } } @@ -451,13 +488,13 @@ public class RescueParty { return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_WARM_REBOOT); } else if (mitigationCount == 4) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); } else if (mitigationCount == 5) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES); + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES); } else if (mitigationCount == 6) { return Math.min(getMaxRescueLevel(mayPerformReboot), - RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS); + RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS); } else if (mitigationCount >= 7) { return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_FACTORY_RESET); } else { @@ -465,6 +502,22 @@ public class RescueParty { } } + /** + * Get the rescue level to perform if this is the n-th attempt at mitigating failure. + * + * @param mitigationCount the mitigation attempt number (1 = first attempt etc.). + * @return the rescue level for the n-th mitigation attempt. + */ + private static @RescueLevels int getRescueLevel(int mitigationCount) { + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + return Level.none(); + } + } + private static void executeRescueLevel(Context context, @Nullable String failedPackage, int level) { Slog.w(TAG, "Attempting rescue level " + levelToString(level)); @@ -537,13 +590,22 @@ public class RescueParty { executeWarmReboot(context, level, failedPackage); break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, + level); + } break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, + level); + } break; case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: - resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, level); + if (!Flags.deprecateFlagsAndSettingsResets()) { + resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, + level); + } break; case RESCUE_LEVEL_FACTORY_RESET: // Before the completion of Reboot, if any crash happens then PackageWatchdog @@ -560,6 +622,12 @@ public class RescueParty { private static void executeWarmReboot(Context context, int level, @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } + // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. setRebootProperty(true); @@ -579,6 +647,11 @@ public class RescueParty { private static void executeFactoryReset(Context context, int level, @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } setFactoryResetProperty(true); long now = System.currentTimeMillis(); setLastFactoryResetTimeMs(now); @@ -655,30 +728,32 @@ public class RescueParty { private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { - // No need to reset Settings again if they are already reset in the current level once. - if (getMaxRescueLevelAttempted() >= level) { - return; - } - setMaxRescueLevelAttempted(level); - // Try our best to reset all settings possible, and once finished - // rethrow any exception that we encountered - Exception res = null; - final ContentResolver resolver = context.getContentResolver(); - try { - Settings.Global.resetToDefaultsAsUser(resolver, null, mode, - UserHandle.SYSTEM.getIdentifier()); - } catch (Exception e) { - res = new RuntimeException("Failed to reset global settings", e); - } - for (int userId : getAllUserIds()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + // No need to reset Settings again if they are already reset in the current level once. + if (getMaxRescueLevelAttempted() >= level) { + return; + } + setMaxRescueLevelAttempted(level); + // Try our best to reset all settings possible, and once finished + // rethrow any exception that we encountered + Exception res = null; + final ContentResolver resolver = context.getContentResolver(); try { - Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); + Settings.Global.resetToDefaultsAsUser(resolver, null, mode, + UserHandle.SYSTEM.getIdentifier()); } catch (Exception e) { - res = new RuntimeException("Failed to reset secure settings for " + userId, e); + res = new RuntimeException("Failed to reset global settings", e); + } + for (int userId : getAllUserIds()) { + try { + Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); + } catch (Exception e) { + res = new RuntimeException("Failed to reset secure settings for " + userId, e); + } + } + if (res != null) { + throw res; } - } - if (res != null) { - throw res; } } @@ -728,18 +803,28 @@ public class RescueParty { @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { if (Flags.recoverabilityDetection()) { - return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage), failedPackage)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + mayPerformReboot(failedPackage), failedPackage)); + } else { + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } } else { - return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage))); + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + mayPerformReboot(failedPackage))); } - } else { - return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } + + Slog.i(TAG, "Checking available remediations for health check failure." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " available impact: " + impact); + return impact; } @Override @@ -748,12 +833,24 @@ public class RescueParty { if (isDisabled()) { return false; } + Slog.i(TAG, "Executing remediation." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " mitigationCount: " + mitigationCount); if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { - final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage), failedPackage) - : getRescueLevel(mitigationCount, - mayPerformReboot(failedPackage)); + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage), + failedPackage); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage)); + } executeRescueLevel(mContext, failedPackage == null ? null : failedPackage.getPackageName(), level); return true; @@ -787,8 +884,12 @@ public class RescueParty { return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; } if (Flags.recoverabilityDetection()) { - return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, - true, /*failedPackage=*/ null)); + if (!Flags.deprecateFlagsAndSettingsResets()) { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + true, /*failedPackage=*/ null)); + } else { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } } else { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); } @@ -800,9 +901,17 @@ public class RescueParty { return false; } boolean mayPerformReboot = !shouldThrottleReboot(); - final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount, - mayPerformReboot, /*failedPackage=*/ null) - : getRescueLevel(mitigationCount, mayPerformReboot); + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot, + /*failedPackage=*/ null); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot); + } executeRescueLevel(mContext, /*failedPackage=*/ null, level); return true; } @@ -828,18 +937,6 @@ public class RescueParty { return isPersistentSystemApp(failingPackage.getPackageName()); } - /** - * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset. - * Will return {@code false} if a factory reset was already offered recently. - */ - private boolean shouldThrottleReboot() { - Long lastResetTime = getLastFactoryResetTimeMs(); - long now = System.currentTimeMillis(); - long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); - return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); - } - private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { @@ -852,20 +949,22 @@ public class RescueParty { private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { - // Record it in calling packages to namespace map - Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); - if (namespaceSet == null) { - namespaceSet = new ArraySet<>(); - mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); - } - namespaceSet.add(namespace); - // Record it in namespace to calling packages map - Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); - if (callingPackageSet == null) { - callingPackageSet = new ArraySet<>(); + if (!Flags.deprecateFlagsAndSettingsResets()) { + // Record it in calling packages to namespace map + Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); + if (namespaceSet == null) { + namespaceSet = new ArraySet<>(); + mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); + } + namespaceSet.add(namespace); + // Record it in namespace to calling packages map + Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); + if (callingPackageSet == null) { + callingPackageSet = new ArraySet<>(); + } + callingPackageSet.add(callingPackage); + mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); } - callingPackageSet.add(callingPackage); - mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); } private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) { @@ -881,6 +980,18 @@ public class RescueParty { } } + /** + * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset. + * Will return {@code false} if a factory reset was already offered recently. + */ + private static boolean shouldThrottleReboot() { + Long lastResetTime = getLastFactoryResetTimeMs(); + long now = System.currentTimeMillis(); + long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, + DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); + return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); + } + private static int[] getAllUserIds() { int systemUserId = UserHandle.SYSTEM.getIdentifier(); int[] userIds = { systemUserId }; @@ -919,6 +1030,22 @@ public class RescueParty { } } + private static class Level { + static int none() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_NONE : LEVEL_NONE; + } + + static int reboot() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_WARM_REBOOT : LEVEL_WARM_REBOOT; + } + + static int factoryReset() { + return Flags.recoverabilityDetection() + ? RESCUE_LEVEL_FACTORY_RESET + : LEVEL_FACTORY_RESET; + } + } + private static String levelToString(int level) { if (Flags.recoverabilityDetection()) { switch (level) { diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java index 598f086db94d..4faadcbe0cca 100644 --- a/services/core/java/com/android/server/am/LmkdConnection.java +++ b/services/core/java/com/android/server/am/LmkdConnection.java @@ -91,10 +91,18 @@ public class LmkdConnection { @GuardedBy("mLmkdSocketLock") private LocalSocket mLmkdSocket = null; - // socket I/O streams - @GuardedBy("mLmkdSocketLock") + // mutex to synchronize socket output stream with socket creation/destruction + private final Object mLmkdOutputStreamLock = new Object(); + + // socket output stream + @GuardedBy("mLmkdOutputStreamLock") private OutputStream mLmkdOutputStream = null; - @GuardedBy("mLmkdSocketLock") + + // mutex to synchronize socket input stream with socket creation/destruction + private final Object mLmkdInputStreamLock = new Object(); + + // socket input stream + @GuardedBy("mLmkdInputStreamLock") private InputStream mLmkdInputStream = null; // buffer to store incoming data @@ -148,9 +156,13 @@ public class LmkdConnection { return false; } // connection established - mLmkdSocket = socket; - mLmkdOutputStream = ostream; - mLmkdInputStream = istream; + synchronized(mLmkdOutputStreamLock) { + synchronized(mLmkdInputStreamLock) { + mLmkdSocket = socket; + mLmkdOutputStream = ostream; + mLmkdInputStream = istream; + } + } mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(), EVENT_INPUT | EVENT_ERROR, new MessageQueue.OnFileDescriptorEventListener() { @@ -177,7 +189,13 @@ public class LmkdConnection { mMsgQueue.removeOnFileDescriptorEventListener( mLmkdSocket.getFileDescriptor()); IoUtils.closeQuietly(mLmkdSocket); - mLmkdSocket = null; + synchronized(mLmkdOutputStreamLock) { + synchronized(mLmkdInputStreamLock) { + mLmkdOutputStream = null; + mLmkdInputStream = null; + mLmkdSocket = null; + } + } } // wake up reply waiters if any synchronized (mReplyBufLock) { @@ -262,24 +280,33 @@ public class LmkdConnection { } private boolean write(ByteBuffer buf) { - synchronized (mLmkdSocketLock) { - try { - mLmkdOutputStream.write(buf.array(), 0, buf.position()); - } catch (IOException ex) { - return false; + boolean result = false; + + synchronized(mLmkdOutputStreamLock) { + if (mLmkdOutputStream != null) { + try { + mLmkdOutputStream.write(buf.array(), 0, buf.position()); + result = true; + } catch (IOException ex) { + } } - return true; } + + return result; } private int read(ByteBuffer buf) { - synchronized (mLmkdSocketLock) { - try { - return mLmkdInputStream.read(buf.array(), 0, buf.array().length); - } catch (IOException ex) { + int result = -1; + + synchronized(mLmkdInputStreamLock) { + if (mLmkdInputStream != null) { + try { + result = mLmkdInputStream.read(buf.array(), 0, buf.array().length); + } catch (IOException ex) { + } } - return -1; } + return result; } /** diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 64e3e8ed29c1..97678aa922a4 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -5491,8 +5491,9 @@ public final class ProcessList { void noteAppKill(final ProcessRecord app, final @Reason int reason, final @SubReason int subReason, final String msg) { if (DEBUG_PROCESSES) { - Slog.i(TAG, "note: " + app + " is being killed, reason: " + reason - + ", sub-reason: " + subReason + ", message: " + msg); + Slog.i(TAG, "note: " + app + " is being killed, reason: " + + ApplicationExitInfo.reasonCodeToString(reason) + ", sub-reason: " + + ApplicationExitInfo.subreasonToString(subReason) + ", message: " + msg); } if (app.getPid() > 0 && !app.isolated && app.getDeathRecipient() != null) { // We are killing it, put it into the dying process list. diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 0f3f8073edcc..73ca6fbf8e5f 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -436,8 +436,13 @@ public class BtHelper { if (mBluetoothHeadset == null || mBluetoothHeadsetDevice == null) { return false; } - return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) - == BluetoothHeadset.STATE_AUDIO_CONNECTED; + try { + return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) + == BluetoothHeadset.STATE_AUDIO_CONNECTED; + } catch (Exception e) { + Log.e(TAG, "Exception while getting audio state of " + mBluetoothHeadsetDevice, e); + } + return false; } // @GuardedBy("mDeviceBroker.mSetModeLock") @@ -1051,12 +1056,16 @@ public class BtHelper { } private void checkScoAudioState() { - if (mBluetoothHeadset != null - && mBluetoothHeadsetDevice != null - && mScoAudioState == SCO_STATE_INACTIVE - && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) - != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + try { + if (mBluetoothHeadset != null + && mBluetoothHeadsetDevice != null + && mScoAudioState == SCO_STATE_INACTIVE + && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) + != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + } + } catch (Exception e) { + Log.e(TAG, "Exception while getting audio state of " + mBluetoothHeadsetDevice, e); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1564b2f86218..0f7904e4ae0b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2949,6 +2949,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub hideStatusBarIconLocked(); mInFullscreenMode = false; mWindowManagerInternal.setDismissImeOnBackKeyPressed(false); + scheduleResetStylusHandwriting(); } @BinderThread diff --git a/services/core/java/com/android/server/location/altitude/AltitudeService.java b/services/core/java/com/android/server/location/altitude/AltitudeService.java index 289d4a25f208..96540c225e23 100644 --- a/services/core/java/com/android/server/location/altitude/AltitudeService.java +++ b/services/core/java/com/android/server/location/altitude/AltitudeService.java @@ -25,6 +25,7 @@ import android.frameworks.location.altitude.IAltitudeService; import android.location.Location; import android.location.altitude.AltitudeConverter; import android.os.RemoteException; +import android.util.Log; import com.android.server.SystemService; @@ -38,6 +39,8 @@ import java.io.IOException; */ public class AltitudeService extends IAltitudeService.Stub { + private static final String TAG = "AltitudeService"; + private final AltitudeConverter mAltitudeConverter = new AltitudeConverter(); private final Context mContext; @@ -59,6 +62,7 @@ public class AltitudeService extends IAltitudeService.Stub { try { mAltitudeConverter.addMslAltitudeToLocation(mContext, location); } catch (IOException e) { + Log.e(TAG, "", e); response.success = false; return response; } @@ -74,6 +78,7 @@ public class AltitudeService extends IAltitudeService.Stub { try { return mAltitudeConverter.getGeoidHeight(mContext, request); } catch (IOException e) { + Log.e(TAG, "", e); GetGeoidHeightResponse response = new GetGeoidHeightResponse(); response.success = false; return response; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 71576465e035..7aae91b98348 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1773,6 +1773,20 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @Override + public boolean writeRepairModeCredential(int userId) { + checkWritePermission(); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mSpManager) { + long protectorId = getCurrentLskfBasedProtectorId(userId); + return mSpManager.writeRepairModeCredentialLocked(protectorId, userId); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + /** * @param savedCredential if the user is a profile with * {@link UserManager#isCredentialSharableWithParent()} with unified challenge and diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 89c4f0f276af..91990048e356 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -4550,7 +4550,7 @@ final class InstallPackageHelper { PackageManagerException.INTERNAL_ERROR_SYSTEM_OVERLAY_STATIC); } } else { - if ((scanFlags & SCAN_AS_VENDOR) != 0) { + if ((scanFlags & (SCAN_AS_VENDOR | SCAN_AS_ODM)) != 0) { if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) { Slog.w(TAG, "System overlay " + pkg.getPackageName() + " targets an SDK below the required SDK level of vendor" diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index d1f91c89a04e..c4e3fa4213a9 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -16,6 +16,8 @@ package com.android.server.rollback; +import static android.crashrecovery.flags.Flags.deprecateFlagsAndSettingsResets; + import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure; import android.Manifest; @@ -622,8 +624,10 @@ class Rollback { parentSession.addChildSessionId(sessionId); } - // Clear flags. - RescueParty.resetDeviceConfigForPackages(packageNames); + if (!deprecateFlagsAndSettingsResets()) { + // Clear flags. + RescueParty.resetDeviceConfigForPackages(packageNames); + } Consumer<Intent> onResult = result -> { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index c85ceac9ea55..4f28e023da92 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -154,12 +154,22 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } + Slog.i(TAG, "Checking available remediations for health check failure." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " available impact: " + impact); return impact; } @Override public boolean execute(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason, int mitigationCount) { + Slog.i(TAG, "Executing remediation." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " rollbackReason: " + rollbackReason + + " mitigationCount: " + mitigationCount); if (Flags.recoverabilityDetection()) { List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { @@ -503,6 +513,10 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @FailureReasons int rollbackReason) { assertInWorkerThread(); + Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId() + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " rollbackReason: " + rollbackReason); final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); final String failedPackageToLog; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e19f08cb04a1..37f49e300766 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2461,11 +2461,11 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - t.traceBegin("CertBlacklister"); + t.traceBegin("CertBlocklister"); try { - CertBlacklister blacklister = new CertBlacklister(context); + CertBlocklister blocklister = new CertBlocklister(context); } catch (Throwable e) { - reportWtf("starting CertBlacklister", e); + reportWtf("starting CertBlocklister", e); } t.traceEnd(); diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 977a8a05d6f3..3ed6ad78343b 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -287,7 +287,7 @@ public final class ProfcollectForwardingService extends SystemService { if (randomNum < traceFrequency) { BackgroundThread.get().getThreadHandler().post(() -> { try { - mIProfcollect.trace_once("applaunch"); + mIProfcollect.trace_system("applaunch"); } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); } @@ -327,7 +327,7 @@ public final class ProfcollectForwardingService extends SystemService { // Dex2oat could take a while before it starts. Add a short delay before start tracing. BackgroundThread.get().getThreadHandler().postDelayed(() -> { try { - mIProfcollect.trace_once("dex2oat"); + mIProfcollect.trace_system("dex2oat"); } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); } @@ -398,14 +398,17 @@ public final class ProfcollectForwardingService extends SystemService { if (randomNum >= traceFrequency) { return; } - // Wait for 1s before starting tracing. + // For a small percentage a traces, we collect the initialization behavior. + boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1; + int traceDelay = traceInitialization ? 0 : 1000; + String traceTag = traceInitialization ? "camera_init" : "camera"; BackgroundThread.get().getThreadHandler().postDelayed(() -> { try { - mIProfcollect.trace_once("camera"); + mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider"); } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); } - }, 1000); + }, traceDelay); } }, null); } diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 337789950a32..f2acbc31b008 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -27,7 +27,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN; import static com.android.server.RescueParty.LEVEL_FACTORY_RESET; -import static com.android.server.RescueParty.RESCUE_LEVEL_FACTORY_RESET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -36,7 +35,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import android.content.ContentResolver; import android.content.Context; @@ -47,6 +45,9 @@ import android.crashrecovery.flags.Flags; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.provider.Settings; @@ -61,6 +62,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -77,10 +81,14 @@ import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; + /** * Test RescueParty. */ +@RunWith(Parameterized.class) public class RescuePartyTest { + @Rule + public SetFlagsRule mSetFlagsRule; private static final long CURRENT_NETWORK_TIME_MILLIS = 0L; private static final String FAKE_NATIVE_NAMESPACE1 = "native1"; private static final String FAKE_NATIVE_NAMESPACE2 = "native2"; @@ -103,9 +111,6 @@ public class RescuePartyTest { private static final String PROP_DISABLE_FACTORY_RESET_FLAG = "persist.device_config.configuration.disable_rescue_party_factory_reset"; - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mCrashRecoveryPropertiesMap; @@ -129,6 +134,17 @@ public class RescuePartyTest { @Captor private ArgumentCaptor<List<String>> mPackageListCaptor; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getFlags() { + return FlagsParameterization.allCombinationsOf( + Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS); + } + + public RescuePartyTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setUp() throws Exception { mSession = @@ -234,10 +250,10 @@ public class RescuePartyTest { } @Test - public void testBootLoopDetectionWithExecutionForAllRescueLevels() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testBootLoop() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -264,10 +280,22 @@ public class RescuePartyTest { noteBoot(5); assertTrue(RescueParty.isFactoryResetPropertySet()); } + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopNoFlags() { + // this is old test where the flag needs to be disabled + noteBoot(1); + assertTrue(RescueParty.isRebootPropertySet()); + + setCrashRecoveryPropAttemptingReboot(false); + noteBoot(2); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } @Test - public void testBootLoopDetectionWithExecutionForAllRescueLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopRecoverability() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -281,12 +309,14 @@ public class RescuePartyTest { final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; + noteBoot(1); noteBoot(2); assertTrue(RescueParty.isRebootPropertySet()); noteBoot(3); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); noteBoot(4); @@ -301,10 +331,10 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testPersistentAppCrash() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - noteAppCrash(1, true); noteAppCrash(2, true); noteAppCrash(3, true); @@ -318,8 +348,21 @@ public class RescuePartyTest { } @Test - public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevelsRecoverability() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testPersistentAppCrashNoFlags() { + // this is old test where the flag needs to be disabled + noteAppCrash(1, true); + assertTrue(RescueParty.isRebootPropertySet()); + + setCrashRecoveryPropAttemptingReboot(false); + noteAppCrash(2, true); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testPersistentAppCrashRecoverability() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -357,10 +400,10 @@ public class RescuePartyTest { } @Test - public void testNonPersistentAppDoesntDoAnything() { + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) + public void testNonPersistentApp() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - noteAppCrash(1, false); noteAppCrash(2, false); noteAppCrash(3, false); @@ -371,8 +414,9 @@ public class RescuePartyTest { } @Test + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testNonPersistentAppOnlyPerformsFlagResetsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), any(Executor.class), @@ -408,60 +452,6 @@ public class RescuePartyTest { } @Test - public void testNonPersistentAppCrashDetectionWithScopedResets() { - // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - - RescueParty.onSettingsProviderPublished(mMockContext); - verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), - any(Executor.class), - mMonitorCallbackCaptor.capture())); - - // Record DeviceConfig accesses - RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); - DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue(); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2); - monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3); - - // Fake DeviceConfig value changes - monitorCallback.onNamespaceUpdate(NAMESPACE1); - verify(mMockPackageWatchdog).startObservingHealth(observer, - Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS); - monitorCallback.onNamespaceUpdate(NAMESPACE2); - verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer), - mPackageListCaptor.capture(), - eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS)); - monitorCallback.onNamespaceUpdate(NAMESPACE3); - verify(mMockPackageWatchdog).startObservingHealth(observer, - Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS); - assertTrue(mPackageListCaptor.getValue().containsAll( - Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2))); - // Perform and verify scoped resets - final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; - final String[] expectedAllResetNamespaces = - new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3}; - HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); - assertFalse(RescueParty.isRebootPropertySet()); - - observer.execute(new VersionedPackage( - CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); - assertFalse(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testIsRecoveryTriggeredReboot() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); @@ -474,19 +464,6 @@ public class RescuePartyTest { } @Test - public void testIsRecoveryTriggeredRebootRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - assertFalse(RescueParty.isFactoryResetPropertySet()); - setCrashRecoveryPropAttemptingReboot(false); - noteBoot(RESCUE_LEVEL_FACTORY_RESET + 1); - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - assertTrue(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); @@ -505,25 +482,6 @@ public class RescuePartyTest { } @Test - public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompletedRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - int mitigationCount = RESCUE_LEVEL_FACTORY_RESET + 1; - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - assertFalse(RescueParty.isFactoryResetPropertySet()); - noteBoot(mitigationCount++); - setCrashRecoveryPropAttemptingReboot(false); - noteBoot(mitigationCount + 1); - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - assertTrue(RescueParty.isFactoryResetPropertySet()); - } - - @Test public void testThrottlingOnBootFailures() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -537,20 +495,6 @@ public class RescuePartyTest { } @Test - public void testThrottlingOnBootFailuresRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long beforeTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1); - setCrashRecoveryPropLastFactoryReset(beforeTimeout); - for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i); - } - assertFalse(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testThrottlingOnAppCrash() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -564,20 +508,6 @@ public class RescuePartyTest { } @Test - public void testThrottlingOnAppCrashRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long beforeTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1); - setCrashRecoveryPropLastFactoryReset(beforeTimeout); - for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteAppCrash(i + 1, true); - } - assertFalse(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testNotThrottlingAfterTimeoutOnBootFailures() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -591,20 +521,6 @@ public class RescuePartyTest { } @Test - public void testNotThrottlingAfterTimeoutOnBootFailuresRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long afterTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1); - setCrashRecoveryPropLastFactoryReset(afterTimeout); - for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i); - } - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); @@ -618,20 +534,7 @@ public class RescuePartyTest { } @Test - public void testNotThrottlingAfterTimeoutOnAppCrashRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - setCrashRecoveryPropAttemptingReboot(false); - long now = System.currentTimeMillis(); - long afterTimeout = now - TimeUnit.MINUTES.toMillis( - DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1); - setCrashRecoveryPropLastFactoryReset(afterTimeout); - for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { - noteAppCrash(i + 1, true); - } - assertTrue(RescueParty.isRecoveryTriggeredReboot()); - } - - @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testNativeRescuePartyResets() { doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed()); doReturn(FAKE_RESET_NATIVE_NAMESPACES).when( @@ -647,7 +550,6 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, @@ -660,7 +562,6 @@ public class RescuePartyTest { @Test public void testDisablingRescueByDeviceConfigFlag() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); @@ -686,24 +587,10 @@ public class RescuePartyTest { } @Test - public void testDisablingFactoryResetByDeviceConfigFlagRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); - - for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { - noteBoot(i + 1); - } - assertFalse(RescueParty.isFactoryResetPropertySet()); - - // Restore the property value initialized in SetUp() - SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, ""); - } - - @Test + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) public void testHealthCheckLevels() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); - RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); // Ensure that no action is taken for cases where the failure reason is unknown @@ -729,8 +616,9 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) public void testHealthCheckLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); // Ensure that no action is taken for cases where the failure reason is unknown @@ -767,11 +655,31 @@ public class RescuePartyTest { PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40); } + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testHealthCheckLevelsNoFlags() { + // this is old test where the flag needs to be disabled + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + // Ensure that no action is taken for cases where the failure reason is unknown + assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); + + // Ensure the correct user impact is returned for each mitigation count. + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onHealthCheckFailed(null, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); + } @Test + @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION, + Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS}) public void testBootLoopLevels() { // this is old test where the flag needs to be disabled - mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -784,8 +692,9 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION) public void testBootLoopLevelsRecoverabilityDetection() { - mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40); @@ -797,6 +706,16 @@ public class RescuePartyTest { } @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopLevelsNoFlags() { + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); + } + + @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesOnlyRuntimeMap() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -827,6 +746,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesOnlyPresetMap() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -835,7 +755,7 @@ public class RescuePartyTest { String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," - + NAMESPACE3 + ":" + CALLING_PACKAGE1; + + NAMESPACE3 + ":" + CALLING_PACKAGE1; doReturn(presetMapping).when(() -> DeviceConfig.getString( eq(RescueParty.NAMESPACE_CONFIGURATION), eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), @@ -848,6 +768,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigForPackagesBothMaps() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -884,6 +805,7 @@ public class RescuePartyTest { } @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) public void testResetDeviceConfigNoExceptionWhenFlagMalformed() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java index 7aec42b7eceb..00daf415375e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java @@ -43,6 +43,7 @@ import static java.util.Collections.singleton; import android.Manifest; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AlarmManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; @@ -142,6 +143,7 @@ public class TrustManagerServiceTest { private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>(); private @Mock ActivityManager mActivityManager; + private @Mock ActivityManagerInternal mActivityManagerInternal; private @Mock AlarmManager mAlarmManager; private @Mock BiometricManager mBiometricManager; private @Mock DevicePolicyManager mDevicePolicyManager; @@ -158,6 +160,7 @@ public class TrustManagerServiceTest { private HandlerThread mHandlerThread; private TrustManagerService mService; private ITrustManager mTrustManager; + private ActivityManagerInternal mPreviousActivityManagerInternal; @Before public void setUp() throws Exception { @@ -210,6 +213,11 @@ public class TrustManagerServiceTest { mMockContext.setMockPackageManager(mPackageManager); mMockContext.addMockSystemService(UserManager.class, mUserManager); doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService()); + mPreviousActivityManagerInternal = LocalServices.getService( + ActivityManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, + mActivityManagerInternal); LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class)); grantPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE); @@ -257,7 +265,14 @@ public class TrustManagerServiceTest { @After public void tearDown() { LocalServices.removeServiceForTest(SystemServiceManager.class); - mHandlerThread.quit(); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + if (mPreviousActivityManagerInternal != null) { + LocalServices.addService(ActivityManagerInternal.class, + mPreviousActivityManagerInternal); + } + if (mHandlerThread != null) { + mHandlerThread.quit(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java index 70150c507460..4396c679ec24 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java @@ -19,6 +19,8 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; @@ -199,6 +201,27 @@ public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { .getResponseCode()); } + @Test + public void writeRepairModeCredential_noLock() { + assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isFalse(); + } + + @Test + public void writeRepairModeCredential_hasLock() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isTrue(); + } + + @Test + public void writeRepairModeCredential_verifyRepairModeUser() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + mService.writeRepairModeCredential(PRIMARY_USER_ID); + setRepairModeActive(true); + + var response = mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0); + assertThat(response.isMatched()).isTrue(); + } + private void setRepairModeActive(boolean active) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0); diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index cf38bea55f2c..6bf7ff501217 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -479,6 +479,15 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, + String[] receiverPermissions, int appOp, Bundle options, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw new UnsupportedOperationException(); + } + @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java index 489ef4444e1d..3722fefb12ad 100644 --- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java +++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java @@ -47,6 +47,8 @@ import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener; import android.os.Handler; import android.os.SystemProperties; import android.os.test.TestLooper; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.util.AtomicFile; @@ -288,7 +290,8 @@ public class CrashRecoveryTest { } @Test - public void testBootLoopWithRescuePartyAndRollbackPackageHealthObserver() throws Exception { + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopWithRescuePartyAndRollbackObserver() throws Exception { PackageWatchdog watchdog = createWatchdog(); RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); RollbackPackageHealthObserver rollbackObserver = @@ -360,6 +363,56 @@ public class CrashRecoveryTest { verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); } + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testBootLoopWithRescuePartyAndRollbackObserverNoFlags() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); + RollbackPackageHealthObserver rollbackObserver = + setUpRollbackPackageHealthObserver(watchdog); + + verify(rescuePartyObserver, never()).executeBootLoopMitigation(1); + verify(rollbackObserver, never()).executeBootLoopMitigation(1); + for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { + watchdog.noteBoot(); + } + verify(rescuePartyObserver).executeBootLoopMitigation(1); + verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); + verify(rollbackObserver, never()).executeBootLoopMitigation(1); + + watchdog.noteBoot(); + + verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); + verify(rollbackObserver).executeBootLoopMitigation(1); + verify(rollbackObserver, never()).executeBootLoopMitigation(2); + // Update the list of available rollbacks after executing bootloop mitigation once + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH, + ROLLBACK_INFO_MANUAL)); + + watchdog.noteBoot(); + + verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); + verify(rollbackObserver).executeBootLoopMitigation(2); + verify(rollbackObserver, never()).executeBootLoopMitigation(3); + // Update the list of available rollbacks after executing bootloop mitigation + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL)); + + watchdog.noteBoot(); + + verify(rescuePartyObserver).executeBootLoopMitigation(2); + verify(rescuePartyObserver, never()).executeBootLoopMitigation(3); + verify(rollbackObserver, never()).executeBootLoopMitigation(3); + + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); + Mockito.reset(rescuePartyObserver); + + for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { + watchdog.noteBoot(); + } + verify(rescuePartyObserver).executeBootLoopMitigation(1); + verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); + } + RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) { RollbackPackageHealthObserver rollbackObserver = spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager)); diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml index 30cf345db34d..2f6c0dd14adc 100644 --- a/tests/TrustTests/AndroidManifest.xml +++ b/tests/TrustTests/AndroidManifest.xml @@ -78,6 +78,7 @@ <action android:name="android.service.trust.TrustAgentService" /> </intent-filter> </service> + <service android:name=".IsActiveUnlockRunningTrustAgent" android:exported="true" @@ -88,6 +89,16 @@ </intent-filter> </service> + <service + android:name=".UnlockAttemptTrustAgent" + android:exported="true" + android:label="Test Agent" + android:permission="android.permission.BIND_TRUST_AGENT"> + <intent-filter> + <action android:name="android.service.trust.TrustAgentService" /> + </intent-filter> + </service> + </application> <!-- self-instrumenting test package. --> diff --git a/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt b/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt new file mode 100644 index 000000000000..2c9361df63fd --- /dev/null +++ b/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt @@ -0,0 +1,227 @@ +/* + * 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 android.trust.test + +import android.app.trust.TrustManager +import android.content.Context +import android.trust.BaseTrustAgentService +import android.trust.TrustTestActivity +import android.trust.test.lib.LockStateTrackingRule +import android.trust.test.lib.ScreenLockRule +import android.trust.test.lib.TestTrustListener +import android.trust.test.lib.TrustAgentRule +import android.util.Log +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.runner.RunWith + +/** + * Test for the impacts of reporting unlock attempts. + * + * atest TrustTests:UnlockAttemptTest + */ +@RunWith(AndroidJUnit4::class) +class UnlockAttemptTest { + private val context = getApplicationContext<Context>() + private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager + private val userId = context.userId + private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java) + private val screenLockRule = ScreenLockRule(requireStrongAuth = true) + private val lockStateTrackingRule = LockStateTrackingRule() + private val trustAgentRule = + TrustAgentRule<UnlockAttemptTrustAgent>(startUnlocked = false, startEnabled = false) + + private val trustListener = UnlockAttemptTrustListener() + private val agent get() = trustAgentRule.agent + + @get:Rule + val rule: RuleChain = + RuleChain.outerRule(activityScenarioRule) + .around(screenLockRule) + .around(lockStateTrackingRule) + .around(trustAgentRule) + + @Before + fun setUp() { + trustManager.registerTrustListener(trustListener) + } + + @Test + fun successfulUnlockAttempt_allowsTrustAgentToStart() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = false, managingTrust = false) { + trustAgentRule.enableTrustAgent() + + triggerSuccessfulUnlock() + + trustAgentRule.verifyAgentIsRunning(MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START) + } + + @Test + fun successfulUnlockAttempt_notifiesTrustAgent() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) { + val oldSuccessfulCount = agent.successfulUnlockCallCount + val oldFailedCount = agent.failedUnlockCallCount + + triggerSuccessfulUnlock() + + assertThat(agent.successfulUnlockCallCount).isEqualTo(oldSuccessfulCount + 1) + assertThat(agent.failedUnlockCallCount).isEqualTo(oldFailedCount) + } + + @Test + fun successfulUnlockAttempt_notifiesTrustListenerOfManagedTrust() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) { + val oldTrustManagedChangedCount = trustListener.onTrustManagedChangedCount[userId] ?: 0 + + triggerSuccessfulUnlock() + + assertThat(trustListener.onTrustManagedChangedCount[userId] ?: 0).isEqualTo( + oldTrustManagedChangedCount + 1 + ) + } + + @Test + fun failedUnlockAttempt_doesNotAllowTrustAgentToStart() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = false, managingTrust = false) { + trustAgentRule.enableTrustAgent() + + triggerFailedUnlock() + + trustAgentRule.ensureAgentIsNotRunning(MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START) + } + + @Test + fun failedUnlockAttempt_notifiesTrustAgent() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) { + val oldSuccessfulCount = agent.successfulUnlockCallCount + val oldFailedCount = agent.failedUnlockCallCount + + triggerFailedUnlock() + + assertThat(agent.successfulUnlockCallCount).isEqualTo(oldSuccessfulCount) + assertThat(agent.failedUnlockCallCount).isEqualTo(oldFailedCount + 1) + } + + @Test + fun failedUnlockAttempt_doesNotNotifyTrustListenerOfManagedTrust() = + runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) { + val oldTrustManagedChangedCount = trustListener.onTrustManagedChangedCount[userId] ?: 0 + + triggerFailedUnlock() + + assertThat(trustListener.onTrustManagedChangedCount[userId] ?: 0).isEqualTo( + oldTrustManagedChangedCount + ) + } + + private fun runUnlockAttemptTest( + enableAndVerifyTrustAgent: Boolean, + managingTrust: Boolean, + testBlock: () -> Unit, + ) { + if (enableAndVerifyTrustAgent) { + Log.i(TAG, "Triggering successful unlock") + triggerSuccessfulUnlock() + Log.i(TAG, "Enabling and waiting for trust agent") + trustAgentRule.enableAndVerifyTrustAgentIsRunning( + MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START + ) + Log.i(TAG, "Managing trust: $managingTrust") + agent.setManagingTrust(managingTrust) + await() + } + testBlock() + } + + private fun triggerSuccessfulUnlock() { + screenLockRule.successfulScreenLockAttempt() + trustAgentRule.reportSuccessfulUnlock() + await() + } + + private fun triggerFailedUnlock() { + screenLockRule.failedScreenLockAttempt() + trustAgentRule.reportFailedUnlock() + await() + } + + companion object { + private const val TAG = "UnlockAttemptTest" + private fun await(millis: Long = 500) = Thread.sleep(millis) + private const val MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START = 10000L + } +} + +class UnlockAttemptTrustAgent : BaseTrustAgentService() { + var successfulUnlockCallCount: Long = 0 + private set + var failedUnlockCallCount: Long = 0 + private set + + override fun onUnlockAttempt(successful: Boolean) { + super.onUnlockAttempt(successful) + if (successful) { + successfulUnlockCallCount++ + } else { + failedUnlockCallCount++ + } + } +} + +private class UnlockAttemptTrustListener : TestTrustListener() { + var enabledTrustAgentsChangedCount = mutableMapOf<Int, Int>() + var onTrustManagedChangedCount = mutableMapOf<Int, Int>() + + override fun onEnabledTrustAgentsChanged(userId: Int) { + enabledTrustAgentsChangedCount.compute(userId) { _: Int, curr: Int? -> + if (curr == null) 0 else curr + 1 + } + } + + data class TrustChangedParams( + val enabled: Boolean, + val newlyUnlocked: Boolean, + val userId: Int, + val flags: Int, + val trustGrantedMessages: MutableList<String>? + ) + + val onTrustChangedCalls = mutableListOf<TrustChangedParams>() + + override fun onTrustChanged( + enabled: Boolean, + newlyUnlocked: Boolean, + userId: Int, + flags: Int, + trustGrantedMessages: MutableList<String> + ) { + onTrustChangedCalls += TrustChangedParams( + enabled, newlyUnlocked, userId, flags, trustGrantedMessages + ) + } + + override fun onTrustManagedChanged(enabled: Boolean, userId: Int) { + onTrustManagedChangedCount.compute(userId) { _: Int, curr: Int? -> + if (curr == null) 0 else curr + 1 + } + } +} diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt index f1edca3ff86e..1ccdcc623c5b 100644 --- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt +++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt @@ -24,6 +24,8 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.uiautomator.UiDevice import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN import com.android.internal.widget.LockscreenCredential import com.google.common.truth.Truth.assertWithMessage import org.junit.rules.TestRule @@ -32,13 +34,18 @@ import org.junit.runners.model.Statement /** * Sets a screen lock on the device for the duration of the test. + * + * @param requireStrongAuth Whether a strong auth is required at the beginning. + * If true, trust agents will not be available until the user verifies their credentials. */ -class ScreenLockRule : TestRule { +class ScreenLockRule(val requireStrongAuth: Boolean = false) : TestRule { private val context: Context = getApplicationContext() + private val userId = context.userId private val uiDevice = UiDevice.getInstance(getInstrumentation()) private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService()) private val lockPatternUtils = LockPatternUtils(context) private var instantLockSavedValue = false + private var strongAuthSavedValue: Int = 0 override fun apply(base: Statement, description: Description) = object : Statement() { override fun evaluate() { @@ -46,10 +53,12 @@ class ScreenLockRule : TestRule { dismissKeyguard() setScreenLock() setLockOnPowerButton() + configureStrongAuthState() try { base.evaluate() } finally { + restoreStrongAuthState() removeScreenLock() revertLockOnPowerButton() dismissKeyguard() @@ -57,6 +66,22 @@ class ScreenLockRule : TestRule { } } + private fun configureStrongAuthState() { + strongAuthSavedValue = lockPatternUtils.getStrongAuthForUser(userId) + if (requireStrongAuth) { + Log.d(TAG, "Triggering strong auth due to simulated lockdown") + lockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, userId) + wait("strong auth required after lockdown") { + lockPatternUtils.getStrongAuthForUser(userId) == + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN + } + } + } + + private fun restoreStrongAuthState() { + lockPatternUtils.requireStrongAuth(strongAuthSavedValue, userId) + } + private fun verifyNoScreenLockAlreadySet() { assertWithMessage("Screen Lock must not already be set on device") .that(lockPatternUtils.isSecure(context.userId)) @@ -82,6 +107,22 @@ class ScreenLockRule : TestRule { } } + fun successfulScreenLockAttempt() { + lockPatternUtils.verifyCredential(LockscreenCredential.createPin(PIN), context.userId, 0) + lockPatternUtils.userPresent(context.userId) + wait("strong auth not required") { + lockPatternUtils.getStrongAuthForUser(context.userId) == STRONG_AUTH_NOT_REQUIRED + } + } + + fun failedScreenLockAttempt() { + lockPatternUtils.verifyCredential( + LockscreenCredential.createPin(WRONG_PIN), + context.userId, + 0 + ) + } + private fun setScreenLock() { lockPatternUtils.setLockCredential( LockscreenCredential.createPin(PIN), @@ -121,5 +162,6 @@ class ScreenLockRule : TestRule { companion object { private const val TAG = "ScreenLockRule" private const val PIN = "0000" + private const val WRONG_PIN = "0001" } } diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt index 18bc029b6845..404c6d968b3a 100644 --- a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt +++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt @@ -20,14 +20,15 @@ import android.app.trust.TrustManager import android.content.ComponentName import android.content.Context import android.trust.BaseTrustAgentService +import android.trust.test.lib.TrustAgentRule.Companion.invoke import android.util.Log import androidx.test.core.app.ApplicationProvider.getApplicationContext import com.android.internal.widget.LockPatternUtils import com.google.common.truth.Truth.assertWithMessage +import kotlin.reflect.KClass import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement -import kotlin.reflect.KClass /** * Enables a trust agent and causes the system service to bind to it. @@ -37,7 +38,9 @@ import kotlin.reflect.KClass * @constructor Creates the rule. Do not use; instead, use [invoke]. */ class TrustAgentRule<T : BaseTrustAgentService>( - private val serviceClass: KClass<T> + private val serviceClass: KClass<T>, + private val startUnlocked: Boolean, + private val startEnabled: Boolean, ) : TestRule { private val context: Context = getApplicationContext() private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager @@ -48,11 +51,18 @@ class TrustAgentRule<T : BaseTrustAgentService>( override fun apply(base: Statement, description: Description) = object : Statement() { override fun evaluate() { verifyTrustServiceRunning() - unlockDeviceWithCredential() - enableTrustAgent() + if (startUnlocked) { + reportSuccessfulUnlock() + } else { + Log.i(TAG, "Trust manager not starting in unlocked state") + } try { - verifyAgentIsRunning() + if (startEnabled) { + enableAndVerifyTrustAgentIsRunning() + } else { + Log.i(TAG, "Trust agent ${serviceClass.simpleName} not enabled") + } base.evaluate() } finally { disableTrustAgent() @@ -64,12 +74,22 @@ class TrustAgentRule<T : BaseTrustAgentService>( assertWithMessage("Trust service is not running").that(trustManager).isNotNull() } - private fun unlockDeviceWithCredential() { - Log.d(TAG, "Unlocking device with credential") + fun reportSuccessfulUnlock() { + Log.i(TAG, "Reporting successful unlock") trustManager.reportUnlockAttempt(true, context.userId) } - private fun enableTrustAgent() { + fun reportFailedUnlock() { + Log.i(TAG, "Reporting failed unlock") + trustManager.reportUnlockAttempt(false, context.userId) + } + + fun enableAndVerifyTrustAgentIsRunning(maxWait: Long = 30000L) { + enableTrustAgent() + verifyAgentIsRunning(maxWait) + } + + fun enableTrustAgent() { val componentName = ComponentName(context, serviceClass.java) val userId = context.userId Log.i(TAG, "Enabling trust agent ${componentName.flattenToString()} for user $userId") @@ -79,12 +99,18 @@ class TrustAgentRule<T : BaseTrustAgentService>( lockPatternUtils.setEnabledTrustAgents(agents, userId) } - private fun verifyAgentIsRunning() { - wait("${serviceClass.simpleName} to be running") { + fun verifyAgentIsRunning(maxWait: Long = 30000L) { + wait("${serviceClass.simpleName} to be running", maxWait) { BaseTrustAgentService.instance(serviceClass) != null } } + fun ensureAgentIsNotRunning(window: Long = 30000L) { + ensure("${serviceClass.simpleName} is not running", window) { + BaseTrustAgentService.instance(serviceClass) == null + } + } + private fun disableTrustAgent() { val componentName = ComponentName(context, serviceClass.java) val userId = context.userId @@ -97,13 +123,23 @@ class TrustAgentRule<T : BaseTrustAgentService>( companion object { /** - * Creates a new rule for the specified agent class. Example usage: + * Creates a new rule for the specified agent class. Starts with the device unlocked and + * the trust agent enabled. Example usage: * ``` * @get:Rule val rule = TrustAgentRule<MyTestAgent>() * ``` + * + * Also supports setting different device lock and trust agent enablement states: + * ``` + * @get:Rule val rule = TrustAgentRule<MyTestAgent>(startUnlocked = false, startEnabled = false) + * ``` */ - inline operator fun <reified T : BaseTrustAgentService> invoke() = - TrustAgentRule(T::class) + inline operator fun <reified T : BaseTrustAgentService> invoke( + startUnlocked: Boolean = true, + startEnabled: Boolean = true, + ) = + TrustAgentRule(T::class, startUnlocked, startEnabled) + private const val TAG = "TrustAgentRule" } diff --git a/tests/TrustTests/src/android/trust/test/lib/utils.kt b/tests/TrustTests/src/android/trust/test/lib/Utils.kt index e047202f6740..3b32b47a6160 100644 --- a/tests/TrustTests/src/android/trust/test/lib/utils.kt +++ b/tests/TrustTests/src/android/trust/test/lib/Utils.kt @@ -39,7 +39,7 @@ internal fun wait( ) { var waited = 0L var count = 0 - while (!conditionFunction.invoke(count)) { + while (!conditionFunction(count)) { assertWithMessage("Condition exceeded maximum wait time of $maxWait ms: $description") .that(waited <= maxWait) .isTrue() @@ -49,3 +49,34 @@ internal fun wait( Thread.sleep(rate) } } + +/** + * Ensures that [conditionFunction] is true with a failed assertion if it is not within [window] + * ms. + * + * The condition function can perform additional logic (for example, logging or attempting to make + * the condition become true). + * + * @param conditionFunction function which takes the attempt count & returns whether the condition + * is met + */ +internal fun ensure( + description: String? = null, + window: Long = 30000L, + rate: Long = 50L, + conditionFunction: (count: Int) -> Boolean +) { + var waited = 0L + var count = 0 + while (waited <= window) { + assertWithMessage("Condition failed within $window ms: $description").that( + conditionFunction( + count + ) + ).isTrue() + waited += rate + count++ + Log.i(TAG, "Ensuring $description ($waited/$window) #$count") + Thread.sleep(rate) + } +} |