diff options
35 files changed, 1451 insertions, 183 deletions
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index b6dc32a29f04..088372702ebc 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -1246,6 +1246,40 @@ public class Bmgr { return "TRANSPORT_IS_NULL"; case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS: return "AGENT_LOGGING_RESULTS"; + case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE: + return "START_SYSTEM_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL: + return "START_RESTORE_AT_INSTALL"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE: + return "TRANSPORT_ERROR_DURING_START_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME: + return "CANNOT_GET_NEXT_PKG_NAME"; + case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE: + return "UNKNOWN_RESTORE_TYPE"; + case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE: + return "KV_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE: + return "FULL_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET: + return "NO_NEXT_RESTORE_TARGET"; + case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR: + return "KV_AGENT_ERROR"; + case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED: + return "PACKAGE_RESTORE_FINISHED"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE: + return "TRANSPORT_ERROR_KV_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD: + return "NO_FEEDER_THREAD"; + case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR: + return "FULL_AGENT_ERROR"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE: + return "TRANSPORT_ERROR_FULL_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE: + return "RESTORE_COMPLETE"; + case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE: + return "START_PACKAGE_RESTORE"; + case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE: + return "AGENT_FAILURE"; default: return "UNKNOWN_ID"; } diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 634089b73618..5f8b76508ef3 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -255,6 +255,12 @@ public class TaskInfo { public boolean isUserFullscreenOverrideEnabled; /** + * Whether the top activity fillsParent() is false + * @hide + */ + public boolean isTopActivityTransparent; + + /** * Hint about the letterbox state of the top activity. * @hide */ @@ -551,7 +557,8 @@ public class TaskInfo { && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId) && parentTaskId == that.parentTaskId && Objects.equals(topActivity, that.topActivity) - && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled; + && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled + && isTopActivityTransparent == that.isTopActivityTransparent; } /** @@ -583,7 +590,9 @@ public class TaskInfo { == that.configuration.getLayoutDirection()) && (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode) && (!hasCompatUI() || isVisible == that.isVisible) - && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled; + && isFocused == that.isFocused + && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled + && isTopActivityTransparent == that.isTopActivityTransparent; } /** @@ -640,6 +649,7 @@ public class TaskInfo { topActivityLetterboxWidth = source.readInt(); topActivityLetterboxHeight = source.readInt(); isUserFullscreenOverrideEnabled = source.readBoolean(); + isTopActivityTransparent = source.readBoolean(); } /** @@ -697,6 +707,7 @@ public class TaskInfo { dest.writeInt(topActivityLetterboxWidth); dest.writeInt(topActivityLetterboxHeight); dest.writeBoolean(isUserFullscreenOverrideEnabled); + dest.writeBoolean(isTopActivityTransparent); } @Override @@ -744,6 +755,7 @@ public class TaskInfo { + " topActivityLetterboxWidth=" + topActivityLetterboxWidth + " topActivityLetterboxHeight=" + topActivityLetterboxHeight + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled + + " isTopActivityTransparent=" + isTopActivityTransparent + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java index f73366b6af0c..812bf8e6be6a 100644 --- a/core/java/android/app/backup/BackupManagerMonitor.java +++ b/core/java/android/app/backup/BackupManagerMonitor.java @@ -18,6 +18,7 @@ package android.app.backup; import android.annotation.SystemApi; import android.app.backup.BackupAnnotations.OperationType; +import android.content.pm.PackageInfo; import android.os.Bundle; /** @@ -190,6 +191,56 @@ public class BackupManagerMonitor { public static final int LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED = 51; public static final int LOG_EVENT_ID_AGENT_LOGGING_RESULTS = 52; + /** @hide */ + public static final int LOG_EVENT_ID_START_SYSTEM_RESTORE = 53; + /** @hide */ + public static final int LOG_EVENT_ID_START_RESTORE_AT_INSTALL = 54; + /** A transport error happened during {@link PerformUnifiedRestoreTask#startRestore()} + @hide */ + public static final int LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE = 55; + /** Unable to get the name of the next package in the queue during a restore operation + @hide */ + public static final int LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME = 56; + /** Attempting a restore operation that is neither KV nor full + @hide */ + public static final int LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE = 57; + /** The package is part of KeyValue restore + @hide */ + public static final int LOG_EVENT_ID_KV_RESTORE = 58; + /** The package is part of Full restore + @hide */ + public static final int LOG_EVENT_ID_FULL_RESTORE = 59; + /** Unable to fetch the nest restore target in the queue + @hide */ + public static final int LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET = 60; + /** An error occurred while attempting KeyValueRestore + @hide */ + public static final int LOG_EVENT_ID_KV_AGENT_ERROR = 61; + /** Restore operation finished for the given package + @hide */ + public static final int LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED= 62; + /** A transport error happened during + * {@link PerformUnifiedRestoreTask#initiateOneRestore(PackageInfo, long)} + @hide */ + public static final int LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE = 63; + /** Unable to instantiate the feeder thread in full restore + @hide */ + public static final int LOG_EVENT_ID_NO_FEEDER_THREAD = 64; + /** An error occurred while attempting Full restore + @hide */ + public static final int LOG_EVENT_ID_FULL_AGENT_ERROR = 65; + /** A transport error happened during a full restore + @hide */ + public static final int LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE = 66; + /** Start restore operation for a given package + @hide */ + public static final int LOG_EVENT_ID_START_PACKAGE_RESTORE = 67; + /** Whole restore operation is complete + @hide */ + public static final int LOG_EVENT_ID_RESTORE_COMPLETE = 68; + /** Agent error during {@link PerformUnifiedRestoreTask#restoreFinished()} + @hide */ + public static final int LOG_EVENT_ID_AGENT_FAILURE = 69; /** * This method will be called each time something important happens on BackupManager. diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml index 37d2fa0540f0..196af6dfb6df 100644 --- a/core/res/res/layout/autofill_fill_dialog.xml +++ b/core/res/res/layout/autofill_fill_dialog.xml @@ -27,7 +27,7 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" - android:layout_marginTop="@dimen/autofill_save_outer_top_margin" + android:layout_marginTop="@dimen/autofill_save_outer_margin" android:layout_marginBottom="24dp" android:layout_marginStart="24dp" android:layout_marginEnd="24dp" diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index bed19a87eb16..8b6c90141bb7 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -22,59 +22,66 @@ android:background="@drawable/autofill_bottomsheet_background" android:orientation="vertical"> - <LinearLayout + <com.android.server.autofill.ui.BottomSheetLayout android:id="@+id/autofill_save" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/autofill_save_outer_top_margin" - android:layout_marginStart="24dp" - android:layout_marginEnd="24dp" + android:layout_marginTop="@dimen/autofill_save_outer_margin" android:background="?android:attr/colorSurface" android:gravity="center_horizontal" android:orientation="vertical"> - - <LinearLayout + <ScrollView android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <ImageView - android:id="@+id/autofill_save_icon" - android:scaleType="fitCenter" - android:layout_gravity="center" - android:layout_height="@dimen/autofill_save_icon_max_height" - android:layout_width="fill_parent"/> - - <TextView - android:id="@+id/autofill_save_title" + android:layout_height="0dp" + android:fillViewport="true" + android:layout_weight="1" + android:layout_marginBottom="8dp"> + <LinearLayout + android:layout_marginStart="@dimen/autofill_save_outer_margin" + android:layout_marginEnd="@dimen/autofill_save_outer_margin" android:layout_width="fill_parent" + android:orientation="vertical" android:layout_height="wrap_content" - android:text="@string/autofill_save_title" - android:layout_marginTop="16dp" - android:paddingBottom="24dp" - android:gravity="center" - android:textAppearance="@style/AutofillSaveUiTitle"> - </TextView> - - <com.android.server.autofill.ui.CustomScrollView - android:id="@+id/autofill_save_custom_subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="gone"/> - - </LinearLayout> + > + <ImageView + android:id="@+id/autofill_save_icon" + android:scaleType="fitCenter" + android:layout_gravity="center" + android:layout_height="@dimen/autofill_save_icon_max_height" + android:layout_width="fill_parent"/> + + <TextView + android:id="@+id/autofill_save_title" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/autofill_save_title" + android:layout_marginTop="16dp" + android:paddingBottom="24dp" + android:gravity="center" + android:textAppearance="@style/AutofillSaveUiTitle"> + </TextView> + <LinearLayout + android:id="@+id/autofill_save_custom_subtitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"/> + + </LinearLayout> + </ScrollView> <com.android.internal.widget.ButtonBarLayout android:layout_width="match_parent" android:layout_height="48dp" android:layout_gravity="end" android:clipToPadding="false" - android:layout_marginTop="32dp" - android:layout_marginBottom="18dp" + android:layout_marginTop="16dp" + android:layout_marginBottom="8dp" android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogButton" android:orientation="horizontal" - android:gravity="center_vertical"> - + android:gravity="center_vertical" + android:layout_marginStart="@dimen/autofill_save_outer_margin" + android:layout_marginEnd="@dimen/autofill_save_outer_margin" + > <Button android:id="@+id/autofill_save_no" android:layout_width="wrap_content" @@ -106,6 +113,5 @@ </com.android.internal.widget.ButtonBarLayout> - </LinearLayout> - -</LinearLayout> + </com.android.server.autofill.ui.BottomSheetLayout> +</LinearLayout>
\ No newline at end of file diff --git a/core/res/res/values-sw640dp/dimens.xml b/core/res/res/values-sw640dp/dimens.xml new file mode 100644 index 000000000000..c632176ba1dd --- /dev/null +++ b/core/res/res/values-sw640dp/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- Top and side margins for autofill dialog on tablets --> + <dimen name="autofill_save_outer_margin">32dp</dimen> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-w640dp/bools.xml b/core/res/res/values-w640dp/bools.xml new file mode 100644 index 000000000000..64b20f7eaa96 --- /dev/null +++ b/core/res/res/values-w640dp/bools.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Whether or not to include horizontal space around the dialog --> + <bool name="autofill_dialog_horizontal_space_included">true</bool> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-w640dp/dimens.xml b/core/res/res/values-w640dp/dimens.xml new file mode 100644 index 000000000000..1f0c0b878610 --- /dev/null +++ b/core/res/res/values-w640dp/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- How much extra space should be left around the autofill dialog --> + <dimen name="autofill_dialog_offset">56dp</dimen> +</resources>
\ No newline at end of file diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index fe296c704095..b097a613ebc1 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -30,4 +30,6 @@ lockscreen, setting this to true should come with customized drawables. --> <bool name="use_lock_pattern_drawable">false</bool> <bool name="resolver_landscape_phone">true</bool> + <!-- Whether or not to include horizontal space around the dialog --> + <bool name="autofill_dialog_horizontal_space_included">false</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b129321008d8..190b7a62e2e0 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -871,7 +871,7 @@ <dimen name="autofill_dataset_picker_max_height">90%</dimen> <!-- Autofill save dialog padding --> - <dimen name="autofill_save_outer_top_margin">24dp</dimen> + <dimen name="autofill_save_outer_margin">24dp</dimen> <dimen name="autofill_save_outer_top_padding">16dp</dimen> <dimen name="autofill_elevation">32dp</dimen> <dimen name="autofill_save_inner_padding">16dp</dimen> @@ -882,6 +882,9 @@ <dimen name="autofill_save_button_bar_padding">16dp</dimen> <dimen name="autofill_dialog_corner_radius">24dp</dimen> + <!-- How much extra space should be left around the autofill dialog --> + <dimen name="autofill_dialog_offset">72dp</dimen> + <!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height --> <dimen name="autofill_save_custom_subtitle_max_height">20%</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index be43a4fc238d..e661eda96a02 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3629,6 +3629,7 @@ <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/> <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/> <java-symbol type="layout" name="autofill_fill_dialog"/> + <java-symbol type="id" name="autofill_save_icon"/> <java-symbol type="id" name="autofill" /> <java-symbol type="id" name="autofill_dataset_footer"/> <java-symbol type="id" name="autofill_dataset_header"/> @@ -3685,6 +3686,10 @@ <java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/> <java-symbol type="integer" name="autofill_max_visible_datasets" /> <java-symbol type="dimen" name="autofill_dialog_max_width" /> + <java-symbol type="dimen" name="autofill_dialog_offset"/> + <java-symbol type="dimen" name="autofill_save_outer_margin"/> + + <java-symbol type="bool" name="autofill_dialog_horizontal_space_included"/> <java-symbol type="style" name="Theme.DeviceDefault.Autofill" /> <java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 0e6b20332a4f..953efa78326c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -176,6 +176,18 @@ public class CompatUIController implements OnDisplaysChangedListener, // be shown. private boolean mKeyguardShowing; + /** + * The id of the task for the application we're currently attempting to show the user aspect + * ratio settings button for, or have most recently shown the button for. + */ + private int mTopActivityTaskId; + + /** + * Whether the user aspect ratio settings button has been shown for the current application + * associated with the task id stored in {@link CompatUIController#mTopActivityTaskId}. + */ + private boolean mHasShownUserAspectRatioSettingsButton = false; + public CompatUIController(@NonNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, @@ -230,6 +242,11 @@ public class CompatUIController implements OnDisplaysChangedListener, if (taskInfo != null && !taskInfo.topActivityInSizeCompat) { mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId); } + + if (taskInfo != null && taskListener != null) { + updateActiveTaskInfo(taskInfo); + } + if (taskInfo.configuration == null || taskListener == null) { // Null token means the current foreground activity is not in compatibility mode. removeLayouts(taskInfo.taskId); @@ -322,6 +339,46 @@ public class CompatUIController implements OnDisplaysChangedListener, forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId()))); } + /** + * Invoked when a new task is created or the info of an existing task has changed. Updates the + * shown status of the user aspect ratio settings button and the task id it relates to. + */ + void updateActiveTaskInfo(@NonNull TaskInfo taskInfo) { + // If the activity belongs to the task we are currently tracking, don't update any variables + // as they are still relevant. Else, if the activity is visible and focused (the one the + // user can see and is using), the user aspect ratio button can potentially be displayed so + // start tracking the buttons visibility for this task. + if (mTopActivityTaskId != taskInfo.taskId && !taskInfo.isTopActivityTransparent + && taskInfo.isVisible && taskInfo.isFocused) { + mTopActivityTaskId = taskInfo.taskId; + setHasShownUserAspectRatioSettingsButton(false); + } + } + + /** + * Informs the system that the user aspect ratio button has been displayed for the application + * associated with the task id in {@link CompatUIController#mTopActivityTaskId}. + */ + void setHasShownUserAspectRatioSettingsButton(boolean state) { + mHasShownUserAspectRatioSettingsButton = state; + } + + /** + * Returns whether the user aspect ratio settings button has been show for the application + * associated with the task id in {@link CompatUIController#mTopActivityTaskId}. + */ + boolean hasShownUserAspectRatioSettingsButton() { + return mHasShownUserAspectRatioSettingsButton; + } + + /** + * Returns the task id of the application we are currently attempting to show, of have most + * recently shown, the user aspect ratio settings button for. + */ + int getTopActivityTaskId() { + return mTopActivityTaskId; + } + private boolean showOnDisplay(int displayId) { return !mKeyguardShowing && !isImeShowingOnDisplay(displayId); } @@ -572,7 +629,8 @@ public class CompatUIController implements OnDisplaysChangedListener, return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor, - mDisappearTimeSupplier); + mDisappearTimeSupplier, this::hasShownUserAspectRatioSettingsButton, + this::setHasShownUserAspectRatioSettingsButton); } private void launchUserAspectRatioSettings( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java index 77aefc8f7e4a..c2dec623416b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java @@ -37,7 +37,9 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; /** * Window manager for the user aspect ratio settings button which allows users to go to @@ -55,6 +57,12 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract private final ShellExecutor mShellExecutor; + @NonNull + private final Supplier<Boolean> mUserAspectRatioButtonShownChecker; + + @NonNull + private final Consumer<Boolean> mUserAspectRatioButtonStateConsumer; + @VisibleForTesting @NonNull final CompatUIHintsState mCompatUIHintsState; @@ -72,9 +80,13 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract @NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState, @NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked, @NonNull ShellExecutor shellExecutor, - @NonNull Function<Integer, Integer> disappearTimeSupplier) { + @NonNull Function<Integer, Integer> disappearTimeSupplier, + @NonNull Supplier<Boolean> userAspectRatioButtonStateChecker, + @NonNull Consumer<Boolean> userAspectRatioButtonShownConsumer) { super(context, taskInfo, syncQueue, taskListener, displayLayout); mShellExecutor = shellExecutor; + mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker; + mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer; mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo); mCompatUIHintsState = compatUIHintsState; mOnButtonClicked = onButtonClicked; @@ -180,11 +192,18 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract } } + @VisibleForTesting + boolean isShowingButton() { + return (mUserAspectRatioButtonShownChecker.get() + && !isHideDelayReached(mNextButtonHideTimeMs)); + } + private void showUserAspectRatioButton() { if (mLayout == null) { return; } mLayout.setUserAspectRatioButtonVisibility(true); + mUserAspectRatioButtonStateConsumer.accept(true); // Only show by default for the first time. if (!mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint) { mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true); @@ -210,7 +229,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) { return taskInfo.topActivityEligibleForUserAspectRatioButton && (taskInfo.topActivityBoundsLetterboxed - || taskInfo.isUserFullscreenOverrideEnabled); + || taskInfo.isUserFullscreenOverrideEnabled) + && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton()); } private long getDisappearTimeMs() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 9b9600e4a51e..f85d707d55f9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -61,6 +61,7 @@ import com.android.wm.shell.transition.Transitions; import dagger.Lazy; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -523,13 +524,175 @@ public class CompatUIControllerTest extends ShellTestCase { .createLayout(anyBoolean()); } + @Test + public void testUpdateActiveTaskInfo_newTask_visibleAndFocused_updated() { + // Simulate user aspect ratio button being shown for previous task + mController.setHasShownUserAspectRatioSettingsButton(true); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + // Create new task + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ true); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo); + + // Check topActivityTaskId is updated to the taskId of the new task and + // hasShownUserAspectRatioSettingsButton has been reset to false + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton()); + } + + @Test + public void testUpdateActiveTaskInfo_newTask_notVisibleOrFocused_notUpdated() { + // Create new task + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ true); + + // Simulate task being shown + mController.updateActiveTaskInfo(taskInfo); + + // Check topActivityTaskId is updated to the taskId of the new task and + // hasShownUserAspectRatioSettingsButton has been reset to false + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton()); + + // Simulate user aspect ratio button being shown + mController.setHasShownUserAspectRatioSettingsButton(true); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + final int newTaskId = TASK_ID + 1; + + // Create visible but NOT focused task + final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ false); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo1); + + // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton + // remains true + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + // Create focused but NOT visible task + final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false, + /* isFocused */ true); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo2); + + // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton + // remains true + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + // Create NOT focused but NOT visible task + final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false, + /* isFocused */ false); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo3); + + // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton + // remains true + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + } + + @Test + public void testUpdateActiveTaskInfo_sameTask_notUpdated() { + // Create new task + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ true); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo); + + // Check topActivityTaskId is updated to the taskId of the new task and + // hasShownUserAspectRatioSettingsButton has been reset to false + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton()); + + // Simulate user aspect ratio button being shown + mController.setHasShownUserAspectRatioSettingsButton(true); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + // Simulate same task being re-shown + mController.updateActiveTaskInfo(taskInfo); + + // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton + // remains true + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + } + + @Test + public void testUpdateActiveTaskInfo_transparentTask_notUpdated() { + // Create new task + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ true); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo); + + // Check topActivityTaskId is updated to the taskId of the new task and + // hasShownUserAspectRatioSettingsButton has been reset to false + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton()); + + // Simulate user aspect ratio button being shown + mController.setHasShownUserAspectRatioSettingsButton(true); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + + final int newTaskId = TASK_ID + 1; + + // Create transparent task + final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true, + /* isFocused */ true, /* isTopActivityTransparent */ true); + + // Simulate new task being shown + mController.updateActiveTaskInfo(taskInfo1); + + // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton + // remains true + Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId()); + Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton()); + } + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, @CameraCompatControlState int cameraCompatControlState) { + return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState, + /* isVisible */ false, /* isFocused */ false, + /* isTopActivityTransparent */ false); + } + + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, + @CameraCompatControlState int cameraCompatControlState, boolean isVisible, + boolean isFocused) { + return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState, + isVisible, isFocused, /* isTopActivityTransparent */ false); + } + + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, + @CameraCompatControlState int cameraCompatControlState, boolean isVisible, + boolean isFocused, boolean isTopActivityTransparent) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; taskInfo.displayId = displayId; taskInfo.topActivityInSizeCompat = hasSizeCompat; taskInfo.cameraCompatControlState = cameraCompatControlState; + taskInfo.isVisible = isVisible; + taskInfo.isFocused = isFocused; + taskInfo.isTopActivityTransparent = isTopActivityTransparent; return taskInfo; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java index ce1290b38830..f460d1b09e34 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java @@ -93,7 +93,8 @@ public class UserAspectRatioSettingsLayoutTest extends ShellTestCase { mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIController.CompatUIHintsState(), - mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0); + mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0, + () -> false, s -> {}); mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate( R.layout.user_aspect_ratio_settings_layout, null); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java index 08cc2f763135..5a4d6c812c17 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java @@ -36,6 +36,7 @@ import android.content.ComponentName; import android.content.res.Configuration; import android.graphics.Rect; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; import android.util.Pair; import android.view.DisplayInfo; import android.view.InsetsSource; @@ -66,6 +67,7 @@ import org.mockito.MockitoAnnotations; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.Supplier; /** * Tests for {@link UserAspectRatioSettingsWindowManager}. @@ -74,6 +76,7 @@ import java.util.function.BiConsumer; * atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest */ @RunWith(AndroidTestingRunner.class) +@RunWithLooper @SmallTest public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { @@ -81,6 +84,8 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { @Mock private SyncTransactionQueue mSyncTransactionQueue; @Mock + private Supplier<Boolean> mUserAspectRatioButtonShownChecker; + @Mock private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnUserAspectRatioSettingsButtonClicked; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @@ -106,10 +111,12 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { false, /* topActivityBoundsLetterboxed */ true); mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), - mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0); + mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0, + mUserAspectRatioButtonShownChecker, s -> {}); spyOn(mWindowManager); doReturn(mLayout).when(mWindowManager).inflateLayout(); doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); + doReturn(false).when(mUserAspectRatioButtonShownChecker).get(); } @Test @@ -293,6 +300,39 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { } @Test + public void testLayoutHasUserAspectRatioSettingsButton() { + clearInvocations(mWindowManager); + spyOn(mWindowManager); + TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + true, /* topActivityBoundsLetterboxed */ true); + + // User aspect ratio settings button has not yet been shown. + doReturn(false).when(mUserAspectRatioButtonShownChecker).get(); + + // Check the layout has the user aspect ratio settings button. + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton); + + // User aspect ratio settings button has been shown and is still visible. + spyOn(mWindowManager); + doReturn(true).when(mWindowManager).isShowingButton(); + doReturn(true).when(mUserAspectRatioButtonShownChecker).get(); + + // Check the layout still has the user aspect ratio settings button. + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton); + + // User aspect ratio settings button has been shown and has timed out so is no longer + // visible. + doReturn(false).when(mWindowManager).isShowingButton(); + doReturn(true).when(mUserAspectRatioButtonShownChecker).get(); + + // Check the layout no longer has the user aspect ratio button. + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + assertFalse(mWindowManager.mHasUserAspectRatioSettingsButton); + } + + @Test public void testAttachToParentSurface() { final SurfaceControl.Builder b = new SurfaceControl.Builder(); mWindowManager.attachToParentSurface(b); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index c29f884c848d..3472a859ac82 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -562,7 +562,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { + mOverlay.getRequestId()); return false; } - if (mLockscreenShadeTransitionController.getQSDragProgress() != 0f + + if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f + && !mAlternateBouncerInteractor.isVisibleState()) || mPrimaryBouncerInteractor.isInTransit()) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index b6c50f7603c7..f2d9144c2461 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -389,7 +389,8 @@ object Flags { @JvmField val NEW_BLUETOOTH_REPOSITORY = releasedFlag("new_bluetooth_repository") // TODO(b/292533677): Tracking Bug - val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon") + val WIFI_TRACKER_LIB_FOR_WIFI_ICON = + unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true) // TODO(b/293863612): Tracking Bug @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 6483b7c647e8..7ec8e12e557e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -25,6 +25,7 @@ import android.widget.ImageView import android.widget.Space import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.android.settingslib.graph.SignalDrawable import com.android.systemui.R @@ -62,7 +63,7 @@ object MobileIconBinder { val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space) val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot) - view.isVisible = true + view.isVisible = viewModel.isVisible.value iconView.isVisible = true // TODO(b/238425913): We should log this visibility state. @@ -75,99 +76,113 @@ object MobileIconBinder { var isCollecting = false view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { - logger.logCollectionStarted(view, viewModel) - isCollecting = true - - launch { - visibilityState.collect { state -> - ModernStatusBarViewVisibilityHelper.setVisibilityState( - state, - mobileGroupView, - dotView, - ) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + // isVisible controls the visibility state of the outer group, and thus it needs + // to run in the CREATED lifecycle so it can continue to watch while invisible + // See (b/291031862) for details + launch { + viewModel.isVisible.collect { isVisible -> + viewModel.verboseLogger?.logBinderReceivedVisibility( + view, + viewModel.subscriptionId, + isVisible + ) + view.isVisible = isVisible + // [StatusIconContainer] can get out of sync sometimes. Make sure to + // request another layout when this changes. + view.requestLayout() + } } } + } - launch { - viewModel.isVisible.collect { isVisible -> - viewModel.verboseLogger?.logBinderReceivedVisibility( - view, - viewModel.subscriptionId, - isVisible - ) - view.isVisible = isVisible + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + logger.logCollectionStarted(view, viewModel) + isCollecting = true + + launch { + visibilityState.collect { state -> + ModernStatusBarViewVisibilityHelper.setVisibilityState( + state, + mobileGroupView, + dotView, + ) + } } - } - // Set the icon for the triangle - launch { - viewModel.icon.distinctUntilChanged().collect { icon -> - viewModel.verboseLogger?.logBinderReceivedSignalIcon( - view, - viewModel.subscriptionId, - icon, - ) - mobileDrawable.level = icon.toSignalDrawableState() + // Set the icon for the triangle + launch { + viewModel.icon.distinctUntilChanged().collect { icon -> + viewModel.verboseLogger?.logBinderReceivedSignalIcon( + view, + viewModel.subscriptionId, + icon, + ) + mobileDrawable.level = icon.toSignalDrawableState() + } } - } - launch { - viewModel.contentDescription.distinctUntilChanged().collect { - ContentDescriptionViewBinder.bind(it, view) + launch { + viewModel.contentDescription.distinctUntilChanged().collect { + ContentDescriptionViewBinder.bind(it, view) + } } - } - // Set the network type icon - launch { - viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId -> - viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon( - view, - viewModel.subscriptionId, - dataTypeId, - ) - dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) } - networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE + // Set the network type icon + launch { + viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId -> + viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon( + view, + viewModel.subscriptionId, + dataTypeId, + ) + dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) } + networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE + } } - } - // Set the roaming indicator - launch { - viewModel.roaming.distinctUntilChanged().collect { isRoaming -> - roamingView.isVisible = isRoaming - roamingSpace.isVisible = isRoaming + // Set the roaming indicator + launch { + viewModel.roaming.distinctUntilChanged().collect { isRoaming -> + roamingView.isVisible = isRoaming + roamingSpace.isVisible = isRoaming + } } - } - // Set the activity indicators - launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } } + // Set the activity indicators + launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } } - launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } } + launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } } - launch { - viewModel.activityContainerVisible.collect { activityContainer.isVisible = it } - } + launch { + viewModel.activityContainerVisible.collect { + activityContainer.isVisible = it + } + } - // Set the tint - launch { - iconTint.collect { tint -> - val tintList = ColorStateList.valueOf(tint) - iconView.imageTintList = tintList - networkTypeView.imageTintList = tintList - roamingView.imageTintList = tintList - activityIn.imageTintList = tintList - activityOut.imageTintList = tintList - dotView.setDecorColor(tint) + // Set the tint + launch { + iconTint.collect { tint -> + val tintList = ColorStateList.valueOf(tint) + iconView.imageTintList = tintList + networkTypeView.imageTintList = tintList + roamingView.imageTintList = tintList + activityIn.imageTintList = tintList + activityOut.imageTintList = tintList + dotView.setDecorColor(tint) + } } - } - launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } } + launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } } - try { - awaitCancellation() - } finally { - isCollecting = false - logger.logCollectionStopped(view, viewModel) + try { + awaitCancellation() + } finally { + isCollecting = false + logger.logCollectionStopped(view, viewModel) + } } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 7dd88b437f17..e7fc870fab33 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -1597,6 +1597,53 @@ public class UdfpsControllerTest extends SysuiTestCase { anyBoolean()); } + + @Test + public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible() + throws RemoteException { + final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, + 0L); + final TouchProcessorResult processorResultMove = + new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, + 1 /* pointerId */, touchData); + + // Enable new touch detection. + when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true); + + // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. + initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */); + + // Configure UdfpsView to accept the ACTION_MOVE event + when(mUdfpsView.isDisplayConfigured()).thenReturn(false); + when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); + + // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled + when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + mFgExecutor.runAllReady(); + + verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); + + // GIVEN swipe down for QS + when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false); + when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f); + + // WHEN ACTION_MOVE is received and touch is within sensor + when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( + processorResultMove); + MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); + mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); + mBiometricExecutor.runAllReady(); + moveEvent.recycle(); + + // THEN the touch is still processed + verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), + anyBoolean()); + } + @Test public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException { // GIVEN UDFPS overlay is showing diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java index 5fa6b3a15d35..e7056c7b0b9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel; import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.kotlin.FlowProviderKt; import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.systemui.utils.os.FakeHandler; @@ -72,6 +73,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import kotlinx.coroutines.flow.MutableStateFlow; + @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest @@ -105,7 +108,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { private ShadeCarrier mShadeCarrier3; private TestableLooper mTestableLooper; @Mock - private ShadeCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener; + private ShadeCarrierGroupController.OnSingleCarrierChangedListener + mOnSingleCarrierChangedListener; @Mock private MobileUiAdapter mMobileUiAdapter; @Mock @@ -119,6 +123,9 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { @Mock private StatusBarPipelineFlags mStatusBarPipelineFlags; + private final MutableStateFlow<Boolean> mIsVisibleFlow = + FlowProviderKt.getMutableStateFlow(true); + private FakeSlotIndexResolver mSlotIndexResolver; private ClickListenerTextView mNoCarrierTextView; @@ -170,7 +177,7 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { mMobileUiAdapter, mMobileContextProvider, mStatusBarPipelineFlags - ) + ) .setShadeCarrierGroup(mShadeCarrierGroup) .build(); @@ -181,6 +188,7 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { when(mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()).thenReturn(true); when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext); when(mMobileIconsViewModel.getLogger()).thenReturn(mMobileViewLogger); + when(mShadeCarrierGroupMobileIconViewModel.isVisible()).thenReturn(mIsVisibleFlow); when(mMobileIconsViewModel.viewModelForSub(anyInt(), any())) .thenReturn(mShadeCarrierGroupMobileIconViewModel); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt new file mode 100644 index 000000000000..274acc9a8c23 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.util.kotlin + +import kotlinx.coroutines.flow.MutableStateFlow + +/** Wrapper for flow constructors that can be retrieved from java tests */ +fun <T> getMutableStateFlow(value: T): MutableStateFlow<T> = MutableStateFlow(value) diff --git a/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java new file mode 100644 index 000000000000..06bbc2b0bdcb --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.autofill.ui; + +import static com.android.server.autofill.Helper.sDebug; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.widget.LinearLayout; + +import com.android.internal.R; + +/** + {@link LinearLayout} that displays content of save dialog. + */ +public class BottomSheetLayout extends LinearLayout { + + private static final String TAG = "BottomSheetLayout"; + + public BottomSheetLayout(Context context) { + super(context); + } + + public BottomSheetLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BottomSheetLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void onMeasure(int widthSpec, int heightSpec) { + if (getContext() == null || getContext().getResources() == null) { + super.onMeasure(widthSpec, heightSpec); + Slog.w(TAG, "onMeasure failed due to missing context or missing resources."); + return; + } + + if (getChildCount() == 0) { + // Should not happen + super.onMeasure(widthSpec, heightSpec); + Slog.wtf(TAG, "onMeasure failed due to missing children views."); + return; + } + + DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); + + final int pxOffset = getContext().getResources().getDimensionPixelSize( + R.dimen.autofill_dialog_offset); + final int outerMargin = getContext().getResources().getDimensionPixelSize( + R.dimen.autofill_save_outer_margin); + + final boolean includeHorizontalSpace = + getContext().getResources().getBoolean( + R.bool.autofill_dialog_horizontal_space_included); + + + final int screenHeight = displayMetrics.heightPixels; + final int screenWidth = displayMetrics.widthPixels; + + final int maxHeight = screenHeight - pxOffset - outerMargin; + + int maxWidth = screenWidth; + + if (includeHorizontalSpace) { + maxWidth -= 2 * pxOffset; + } + + maxWidth = + Math.min(maxWidth, getContext().getResources().getDimensionPixelSize( + R.dimen.autofill_dialog_max_width)); + + super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST)); + + + if (sDebug) { + Slog.d(TAG, "onMeasure() values in dp:" + + " screenHeight: " + screenHeight / displayMetrics.density + ", screenWidth: " + + screenWidth / displayMetrics.density + + ", maxHeight: " + maxHeight / displayMetrics.density + + ", maxWidth: " + maxWidth / displayMetrics.density + ", getMeasuredWidth(): " + + getMeasuredWidth() / displayMetrics.density + ", getMeasuredHeight(): " + + getMeasuredHeight() / displayMetrics.density); + } + setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight()); + } + + +} diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 70382f1d5274..3581db238ff3 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -48,7 +48,6 @@ import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.util.ArraySet; -import android.util.DisplayMetrics; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -363,13 +362,6 @@ final class SaveUi { window.setCloseOnTouchOutside(true); final WindowManager.LayoutParams params = window.getAttributes(); - DisplayMetrics displayMetrics = new DisplayMetrics(); - window.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); - final int screenWidth = displayMetrics.widthPixels; - final int maxWidth = - context.getResources().getDimensionPixelSize(R.dimen.autofill_dialog_max_width); - params.width = Math.min(screenWidth, maxWidth); - params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title); params.windowAnimations = R.style.AutofillSaveAnimation; params.setTrustedOverlay(); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 5493da6331fa..5bb0f86e1b9b 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -486,6 +486,13 @@ public class UserBackupManagerService { File baseStateDir, File dataDir, TransportManager transportManager) { + // check if we are past the retention period for BMM Events, + // if so delete expired events and do not print them to dumpsys + BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils = + new BackupManagerMonitorDumpsysUtils(); + if (backupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents() && DEBUG){ + Slog.d(TAG, "BMM Events recorded for dumpsys have expired"); + } return new UserBackupManagerService( userId, context, @@ -654,6 +661,13 @@ public class UserBackupManagerService { // the pending backup set mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS); + // check if we are past the retention period for BMM Events, + // if so delete expired events and do not print them to dumpsys + BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils = + new BackupManagerMonitorDumpsysUtils(); + mBackupHandler.postDelayed(backupManagerMonitorDumpsysUtils::deleteExpiredBMMEvents, + INITIALIZATION_DELAY_MILLIS); + mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir); // Power management @@ -4177,7 +4191,19 @@ public class UserBackupManagerService { private void dumpBMMEvents(PrintWriter pw) { BackupManagerMonitorDumpsysUtils bm = new BackupManagerMonitorDumpsysUtils(); + if (bm.deleteExpiredBMMEvents()) { + pw.println("BACKUP MANAGER MONITOR EVENTS HAVE EXPIRED"); + return; + } File events = bm.getBMMEventsFile(); + if (events.length() == 0){ + // We have not recorded BMMEvents yet. + pw.println("NO BACKUP MANAGER MONITOR EVENTS"); + return; + } else if (bm.isFileLargerThanSizeLimit(events)){ + pw.println("BACKUP MANAGER MONITOR EVENTS FILE OVER SIZE LIMIT - " + + "future events will not be recorded"); + } pw.println("START OF BACKUP MANAGER MONITOR EVENTS"); try (BufferedReader reader = new BufferedReader(new FileReader(events))) { String line; diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index e04bf11dad9f..bbec79d6cd47 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -394,6 +394,20 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // If we're starting a full-system restore, set up to begin widget ID remapping if (mIsSystemRestore) { AppWidgetBackupBridge.systemRestoreStarting(mUserId); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE, + null, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } else { + //We are either performing RestoreAtInstall or Bmgr. + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL, + null, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); } try { @@ -421,6 +435,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mStatus = transport.startRestore(mToken, packages); if (mStatus != BackupTransport.TRANSPORT_OK) { Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, + monitoringExtras); mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(UnifiedRestoreState.FINAL); return; @@ -528,6 +548,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { final String pkgName = (mRestoreDescription != null) ? mRestoreDescription.getPackageName() : null; if (pkgName == null) { + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME, + null, + BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, + monitoringExtras); Slog.e(TAG, "Failure getting next package name"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); nextState = UnifiedRestoreState.FINAL; @@ -550,6 +576,14 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); if (metaInfo == null) { + PackageInfo pkgInfo = new PackageInfo(); + pkgInfo.packageName = pkgName; + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA, + pkgInfo, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); Slog.e(TAG, "No metadata for " + pkgName); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, "Package metadata missing"); @@ -560,6 +594,13 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { try { mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser( pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } catch (NameNotFoundException e) { // Whoops, we thought we could restore this package but it // turns out not to be present. Skip it. @@ -641,12 +682,24 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } else { // Unknown restore type; ignore this package and move on Slog.e(TAG, "Unrecognized restore type " + type); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);; + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); nextState = UnifiedRestoreState.RUNNING_QUEUE; return; } } catch (Exception e) { Slog.e(TAG, "Can't get next restore target from transport; halting: " + e.getMessage()); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);; + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); nextState = UnifiedRestoreState.FINAL; return; @@ -663,6 +716,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { final String packageName = mCurrentPackage.packageName; // Validate some semantic requirements that apply in this way // only to the key/value restore API flow + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null)); if (mCurrentPackage.applicationInfo.backupAgentName == null || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { if (MORE_DEBUG) { @@ -721,6 +778,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ++mCount; } catch (Exception e) { Slog.e(TAG, "Error when attempting restore: " + e.toString()); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, + monitoringExtras); keyValueAgentErrorCleanup(false); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } @@ -759,6 +821,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Transport-level failure. This failure could be specific to package currently in // restore. Slog.e(TAG, "Error getting restore data for " + packageName); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); stage.close(); downloadFile.delete(); @@ -815,6 +883,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { new ArrayList<>(getExcludedKeysForPackage(packageName))); } catch (Exception e) { Slog.e(TAG, "Unable to call app for restore: " + packageName, e); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); // Clears any pending timeout messages as well. @@ -888,6 +961,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // // When finished, StreamFeederThread executes next state as appropriate on the // backup looper, and the overall unified restore task resumes + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null)); try { StreamFeederThread feeder = new StreamFeederThread(); if (MORE_DEBUG) { @@ -903,6 +980,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // current target. We haven't asked the transport for data yet, though, // so we can do that simply by going back to running the restore queue. Slog.e(TAG, "Unable to construct pipes for stream restore!"); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } } @@ -927,6 +1009,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } catch (Exception e) { final String packageName = mCurrentPackage.packageName; Slog.e(TAG, "Unable to finalize restore of " + packageName); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); keyValueAgentErrorCleanup(true); @@ -1020,6 +1106,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // handling will deal properly with that. Slog.e(TAG, "Error " + result + " streaming restore for " + mCurrentPackage.packageName); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); status = result; } @@ -1032,6 +1124,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // but potentially recoverable; abandon this package's restore but // carry on with the next restore target. Slog.e(TAG, "Unable to route data for restore"); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName, "I/O error on pipes"); status = BackupTransport.AGENT_ERROR; @@ -1040,6 +1138,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // the sockets will wake up the engine and it will then tidy up the // remote end. Slog.e(TAG, "Transport failed during restore: " + e.getMessage()); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE, + mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); status = BackupTransport.TRANSPORT_ERROR; } finally { @@ -1213,6 +1317,13 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } Slog.i(TAG, "Restore complete."); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE, + null, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + mListener.onFinished(callerLogString); } @@ -1313,6 +1424,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { @Override public void operationComplete(long unusedResult) { mOperationStorage.removeOperation(mEphemeralOpToken); + if (MORE_DEBUG) { Slog.i(TAG, "operationComplete() during restore: target=" + mCurrentPackage.packageName @@ -1341,6 +1453,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Okay, we're done with this package. Tidy up and go on to the next // app in the queue. int size = (int) mBackupDataName.length(); + Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null); + mBackupManagerMonitorEventSender.monitorEvent( + BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED, mCurrentPackage, + BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size); diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java index 0b55ca21371b..797aed9297a3 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java +++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java @@ -23,15 +23,22 @@ import android.os.Bundle; import android.os.Environment; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastPrintWriter; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Map; +import java.util.concurrent.TimeUnit; /* @@ -46,12 +53,27 @@ public class BackupManagerMonitorDumpsysUtils { // Name of the subdirectory where the text file containing the BMM events will be stored. // Same as {@link UserBackupManagerFiles} private static final String BACKUP_PERSISTENT_DIR = "backup"; + private static final String INITIAL_SETUP_TIMESTAMP_KEY = "initialSetupTimestamp"; + // Retention period of 60 days (in millisec) for the BMM Events. + // After tha time has passed the text file containing the BMM events will be emptied + private static final long LOGS_RETENTION_PERIOD_MILLISEC = 60 * TimeUnit.DAYS.toMillis(1); + // Size limit for the text file containing the BMM events + private static final long BMM_FILE_SIZE_LIMIT_BYTES = 25 * 1024 * 1000; // 2.5 MB + + // We cache the value of IsAfterRetentionPeriod() to avoid unnecessary disk I/O + // mIsAfterRetentionPeriodCached tracks if we have cached the value of IsAfterRetentionPeriod() + private boolean mIsAfterRetentionPeriodCached = false; + // The cached value of IsAfterRetentionPeriod() + private boolean mIsAfterRetentionPeriod; + // If isFileLargerThanSizeLimit(bmmEvents) returns true we cache the value to avoid + // unnecessary disk I/O + private boolean mIsFileLargerThanSizeLimit = false; /** * Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that * will be persisted in a text file and printed in the dumpsys. * - * If the evenntBundle passed is not a RESTORE event, return early + * If the eventBundle passed is not a RESTORE event, return early * * Key information related to the event: * - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT) @@ -62,17 +84,22 @@ public class BackupManagerMonitorDumpsysUtils { * - Agent logs (if available) * * Example of formatting: - * RESTORE Event: [2023-08-18 17:16:00.735] Agent - Agent logging results - * Package name: com.android.wallpaperbackup - * Agent Logs: - * Data Type: wlp_img_system - * Item restored: 0/1 - * Agent Error - Category: no_wallpaper, Count: 1 - * Data Type: wlp_img_lock - * Item restored: 0/1 - * Agent Error - Category: no_wallpaper, Count: 1 + * [2023-09-21 14:43:33.824] - Agent logging results + * Package: com.android.wallpaperbackup + * Agent Logs: + * Data Type: wlp_img_system + * Item restored: 0/1 + * Agent Error - Category: no_wallpaper, Count: 1 + * Data Type: wlp_img_lock + * Item restored: 0/1 + * Agent Error - Category: no_wallpaper, Count: 1 */ public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) { + if (isAfterRetentionPeriod()) { + // We only log data for the first 60 days since setup + return; + } + if (eventBundle == null) { return; } @@ -89,8 +116,19 @@ public class BackupManagerMonitorDumpsysUtils { } File bmmEvents = getBMMEventsFile(); + if (bmmEvents.length() == 0) { + // We are parsing the first restore event. + // Time to also record the setup timestamp of the device + recordSetUpTimestamp(); + } + + if(isFileLargerThanSizeLimit(bmmEvents)){ + // Do not write more events if the file is over size limit + return; + } + try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true); - PrintWriter pw = new FastPrintWriter(out);) { + PrintWriter pw = new FastPrintWriter(out);) { int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY); int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID); @@ -101,17 +139,16 @@ public class BackupManagerMonitorDumpsysUtils { return; } - pw.println("RESTORE Event: [" + timestamp() + "] " + - getCategory(eventCategory) + " - " + - getId(eventId)); + pw.println("[" + timestamp() + "] - " + getId(eventId)); if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) { - pw.println("\tPackage name: " + pw.println("\tPackage: " + eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)); } // TODO(b/296818666): add extras to the events addAgentLogsIfAvailable(eventBundle, pw); + addExtrasIfAvailable(eventBundle, pw); } catch (java.io.IOException e) { Slog.e(TAG, "IO Exception when writing BMM events to file: " + e); } @@ -156,6 +193,37 @@ public class BackupManagerMonitorDumpsysUtils { } } + /** + * Extracts some extras (defined in BackupManagerMonitor as EXTRA_LOG_<description>) + * from the BackupManagerMonitor event. Not all extras have the same importance. For now only + * focus on extras relating to version mismatches between packages on the source and target. + * + * When an event with ID LOG_EVENT_ID_RESTORE_VERSION_HIGHER (trying to restore from higher to + * lower version of a package) parse: + * EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source + * EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version + * EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target + */ + private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) { + if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) == + BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER) { + if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)) { + pw.println("\t\tPackage supports RestoreAnyVersion: " + + eventBundle.getBoolean(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)); + } + if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)) { + pw.println("\t\tPackage version on source: " + + eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)); + } + if (eventBundle.containsKey( + BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) { + pw.println("\t\tPackage version on target: " + + eventBundle.getLong( + BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)); + } + } + } + /* * Get the path of the text files which stores the BMM events */ @@ -165,6 +233,13 @@ public class BackupManagerMonitorDumpsysUtils { return fname; } + public boolean isFileLargerThanSizeLimit(File events){ + if (!mIsFileLargerThanSizeLimit) { + mIsFileLargerThanSizeLimit = events.length() > getBMMEventsFileSizeLimit(); + } + return mIsFileLargerThanSizeLimit; + } + private String timestamp() { long currentTime = System.currentTimeMillis(); Date date = new Date(currentTime); @@ -245,6 +320,32 @@ public class BackupManagerMonitorDumpsysUtils { case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED -> "Transport non-incremental backup required"; case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results"; + case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE -> "Start system restore"; + case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL -> + "Start restore at install"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE -> + "Transport error during start restore"; + case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME -> + "Cannot get next package name"; + case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE -> "Unknown restore type"; + case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE -> "KV restore"; + case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE -> "Full restore"; + case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET -> + "No next restore target"; + case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR -> "KV agent error"; + case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED -> + "Package restore finished"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE -> + "Transport error KV restore"; + case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD -> "No feeder thread"; + case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR -> "Full agent error"; + case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE -> + "Transport error full restore"; + case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete"; + case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE -> + "Start package restore"; + case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE -> + "Agent failure"; default -> "Unknown log event ID: " + code; }; return id; @@ -257,4 +358,128 @@ public class BackupManagerMonitorDumpsysUtils { default -> false; }; } + + /** + * Store the timestamp when the device was set up (date when the first BMM event is parsed) + * in a text file. + */ + @VisibleForTesting + void recordSetUpTimestamp() { + File setupDateFile = getSetUpDateFile(); + // record setup timestamp only once + if (setupDateFile.length() == 0) { + try (FileOutputStream out = new FileOutputStream(setupDateFile, /*append*/ true); + PrintWriter pw = new FastPrintWriter(out);) { + long currentDate = System.currentTimeMillis(); + pw.println(currentDate); + } catch (IOException e) { + Slog.w(TAG, "An error occurred while recording the setup date: " + + e.getMessage()); + } + } + + } + + @VisibleForTesting + String getSetUpDate() { + File fname = getSetUpDateFile(); + try (FileInputStream inputStream = new FileInputStream(fname); + InputStreamReader reader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(reader);) { + return bufferedReader.readLine(); + } catch (Exception e) { + Slog.w(TAG, "An error occurred while reading the date: " + e.getMessage()); + return "Could not retrieve setup date"; + } + } + + @VisibleForTesting + static boolean isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec) { + if (startTimeStamp > endTimeStamp) { + // Something has gone wrong, timeStamp1 should always precede timeStamp2. + // Out of caution return true: we would delete the logs rather than + // risking them being kept for longer than the retention period + return true; + } + long timeDifferenceMillis = endTimeStamp - startTimeStamp; + return (timeDifferenceMillis >= millisec); + } + + /** + * Check if current date is after retention period + */ + @VisibleForTesting + boolean isAfterRetentionPeriod() { + if (mIsAfterRetentionPeriodCached) { + return mIsAfterRetentionPeriod; + } else { + File setUpDateFile = getSetUpDateFile(); + if (setUpDateFile.length() == 0) { + // We are yet to record a setup date. This means we haven't parsed the first event. + mIsAfterRetentionPeriod = false; + mIsAfterRetentionPeriodCached = true; + return false; + } + try { + long setupTimestamp = Long.parseLong(getSetUpDate()); + long currentTimestamp = System.currentTimeMillis(); + mIsAfterRetentionPeriod = isDateAfterNMillisec(setupTimestamp, currentTimestamp, + getRetentionPeriodInMillisec()); + mIsAfterRetentionPeriodCached = true; + return mIsAfterRetentionPeriod; + } catch (NumberFormatException e) { + // An error occurred when parsing the setup timestamp. + // Out of caution return true: we would delete the logs rather than + // risking them being kept for longer than the retention period + mIsAfterRetentionPeriod = true; + mIsAfterRetentionPeriodCached = true; + return true; + } + } + } + + @VisibleForTesting + File getSetUpDateFile() { + File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR); + File setupDateFile = new File(dataDir, INITIAL_SETUP_TIMESTAMP_KEY + ".txt"); + return setupDateFile; + } + + @VisibleForTesting + long getRetentionPeriodInMillisec() { + return LOGS_RETENTION_PERIOD_MILLISEC; + } + + @VisibleForTesting + long getBMMEventsFileSizeLimit(){ + return BMM_FILE_SIZE_LIMIT_BYTES; + } + + /** + * Delete the BMM Events file after the retention period has passed. + * + * @return true if the retention period has passed false otherwise. + * we want to return true even if we were unable to delete the file, as this will prevent + * expired BMM events from being printed to the dumpsys + */ + public boolean deleteExpiredBMMEvents() { + try { + if (isAfterRetentionPeriod()) { + File bmmEvents = getBMMEventsFile(); + if (bmmEvents.exists()) { + if (bmmEvents.delete()) { + Slog.i(TAG, "Deleted expired BMM Events"); + } else { + Slog.e(TAG, "Unable to delete expired BMM Events"); + } + } + return true; + } + return false; + } catch (Exception e) { + // Handle any unexpected exceptions + // To be safe we return true as we want to avoid exposing expired BMMEvents + return true; + } + } } diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java index 92e3107b6977..549d08c03933 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java +++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java @@ -222,4 +222,21 @@ public class BackupManagerMonitorEventSender { extras.putBoolean(key, value); return extras; } + + /** + * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will + * be created. + * + * @param extras - bundle where to add key-value to, if null a new bundle will be created. + * @param key - key. + * @param value - value. + * @return extras if it was not null and new bundle otherwise. + */ + public static Bundle putMonitoringExtra(Bundle extras, String key, int value) { + if (extras == null) { + extras = new Bundle(); + } + extras.putInt(key, value); + return extras; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 47abc1092c49..43dc307d0d99 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -314,7 +314,6 @@ import android.net.ConnectivityManager; import android.net.Proxy; import android.net.Uri; import android.os.AppZygote; -import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Binder; import android.os.BinderProxy; @@ -15030,16 +15029,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - // STOPSHIP(b/298884211): Remove this logging - if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - if (level < 0) { - Slog.wtf(BroadcastQueue.TAG, "Unexpected broadcast: " + intent - + "; callingUid: " + callingUid + ", callingPid: " + callingPid, - new Throwable()); - } - } - int[] users; if (userId == UserHandle.USER_ALL) { // Caller wants broadcast to go to all started users. diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index d5343a9777a7..5d0fefc75a28 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -59,7 +59,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.os.BatteryManager; import android.os.Bundle; import android.os.BundleMerger; import android.os.Handler; @@ -1075,16 +1074,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { queue.lastProcessState = app.mState.getCurProcState(); if (receiver instanceof BroadcastFilter) { notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver); - // STOPSHIP(b/298884211): Remove this logging - if (Intent.ACTION_BATTERY_CHANGED.equals(receiverIntent.getAction())) { - int level = receiverIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - if (level < 0) { - Slog.wtf(TAG, "Dispatching unexpected broadcast: " + receiverIntent - + " to " + receiver - + "; callingUid: " + r.callingUid - + ", callingPid: " + r.callingPid); - } - } thread.scheduleRegisteredReceiver( ((BroadcastFilter) receiver).receiverList.receiver, receiverIntent, r.resultCode, r.resultData, r.resultExtras, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index e1f010f62232..b6f4aeea3704 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -4126,6 +4126,10 @@ class PackageManagerShellCommand extends ShellCommand { PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, 0); + if (ai == null) { + Slog.e(TAG, "Failed to get ApplicationInfo for package name(" + pii.packageName + ")."); + return null; + } AssetManager am = new AssetManager(); am.addAssetPath(ai.publicSourceDir); res = new Resources(am, null, null); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 60b6a8879ecf..c322563e9ae1 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3461,6 +3461,7 @@ class Task extends TaskFragment { info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET; info.isUserFullscreenOverrideEnabled = top != null && top.mLetterboxUiController.shouldApplyUserFullscreenOverride(); + info.isTopActivityTransparent = top != null && !top.fillsParent(); info.isFromLetterboxDoubleTap = top != null && top.mLetterboxUiController.isFromDoubleTap(); if (info.isLetterboxDoubleTapEnabled) { info.topActivityLetterboxWidth = top.getBounds().width(); diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java index 8e17b3a58769..dcd531751cd7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java @@ -17,26 +17,33 @@ package com.android.server.backup.utils; import static org.junit.Assert.assertTrue; - +import static org.testng.AssertJUnit.assertFalse; +import android.app.backup.BackupAnnotations; import android.app.backup.BackupManagerMonitor; import android.os.Bundle; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; - import java.io.File; +import java.io.FileWriter; public class BackupManagerMonitorDumpsysUtilsTest { - private File mTempFile; + private long mRetentionPeriod; + private File mTempBMMEventsFile; + private File mTempSetUpDateFile; + + private long mSizeLimit; private TestBackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils; @Rule public TemporaryFolder tmp = new TemporaryFolder(); @Before public void setUp() throws Exception { - mTempFile = tmp.newFile("testbmmevents.txt"); + mRetentionPeriod = 30 * 60 * 1000; + mSizeLimit = 25 * 1024 * 1000; + mTempBMMEventsFile = tmp.newFile("testbmmevents.txt"); + mTempSetUpDateFile = tmp.newFile("testSetUpDate.txt"); mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils(); } @@ -46,7 +53,7 @@ public class BackupManagerMonitorDumpsysUtilsTest { throws Exception { mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(null); - assertTrue(mTempFile.length() == 0); + assertTrue(mTempBMMEventsFile.length() == 0); } @@ -57,7 +64,7 @@ public class BackupManagerMonitorDumpsysUtilsTest { event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1); mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); - assertTrue(mTempFile.length() == 0); + assertTrue(mTempBMMEventsFile.length() == 0); } @Test @@ -67,18 +74,236 @@ public class BackupManagerMonitorDumpsysUtilsTest { event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1); mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); - assertTrue(mTempFile.length() == 0); + assertTrue(mTempBMMEventsFile.length() == 0); + } + + @Test + public void parseBackupManagerMonitorEventForDumpsys_eventWithCategoryAndId_eventIsWrittenToFile() + throws Exception { + Bundle event = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + + assertTrue(mTempBMMEventsFile.length() != 0); + } + + @Test + public void parseBackupManagerMonitorEventForDumpsys_firstEvent_recordSetUpTimestamp() + throws Exception { + assertTrue(mTempBMMEventsFile.length()==0); + assertTrue(mTempSetUpDateFile.length()==0); + + Bundle event = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + + assertTrue(mTempBMMEventsFile.length() != 0); + assertTrue(mTempSetUpDateFile.length()!=0); + } + + @Test + public void parseBackupManagerMonitorEventForDumpsys_notFirstEvent_doNotChangeSetUpTimestamp() + throws Exception { + Bundle event1 = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event1); + String setUpTimestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate(); + + Bundle event2 = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event2); + String setUpTimestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate(); + + assertTrue(setUpTimestampBefore.equals(setUpTimestampAfter)); + } + + + @Test + public void parseBackupManagerMonitorEventForDumpsys_fileOverSizeLimit_doNotRecordEvents() + throws Exception { + assertTrue(mTempBMMEventsFile.length() == 0); + Bundle event = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + long fileSizeBefore = mTempBMMEventsFile.length(); + + mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(0); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + long fileSizeAfter = mTempBMMEventsFile.length(); + assertTrue(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile)); + assertTrue(fileSizeBefore == fileSizeAfter); + } + + @Test + public void parseBackupManagerMonitorEventForDumpsys_fileUnderSizeLimit_recordEvents() + throws Exception { + assertTrue(mTempBMMEventsFile.length() == 0); + Bundle event = createRestoreBMMEvent(); + + mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(25 * 1024 * 1000); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + assertFalse(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile)); + assertTrue(mTempBMMEventsFile.length() != 0); + } + + @Test + public void deleteExpiredBackupManagerMonitorEvent_eventsAreExpired_deleteEventsAndReturnTrue() + throws Exception { + Bundle event = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + assertTrue(mTempBMMEventsFile.length() != 0); + // Re-initialise the test BackupManagerMonitorDumpsysUtils to + // clear the cached value of isAfterRetentionPeriod + mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils(); + + // set a retention period of 0 second + mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0); + + assertTrue(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents()); + assertFalse(mTempBMMEventsFile.exists()); + } + + @Test + public void deleteExpiredBackupManagerMonitorEvent_eventsAreNotExpired_returnFalse() throws + Exception { + Bundle event = createRestoreBMMEvent(); + mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event); + assertTrue(mTempBMMEventsFile.length() != 0); + + // set a retention period of 30 minutes + mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000); + + assertFalse(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents()); + assertTrue(mTempBMMEventsFile.length() != 0); + } + + @Test + public void isAfterRetentionPeriod_afterRetentionPeriod_returnTrue() throws + Exception { + mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp(); + + // set a retention period of 0 second + mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0); + + assertTrue(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod()); + } + + @Test + public void isAfterRetentionPeriod_beforeRetentionPeriod_returnFalse() throws + Exception { + mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp(); + + // set a retention period of 30 minutes + mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000); + + assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod()); + } + + @Test + public void isAfterRetentionPeriod_noSetupDate_returnFalse() throws + Exception { + assertTrue(mTempSetUpDateFile.length() == 0); + + assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod()); + } + + @Test + public void isDateAfterNMillisec_date1IsAfterThanDate2_returnTrue() throws + Exception { + long timestamp1 = System.currentTimeMillis(); + long timestamp2 = timestamp1 - 1; + + assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2, + 0)); + } + + @Test + public void isDateAfterNMillisec_date1IsAfterNMillisecFromDate2_returnTrue() throws + Exception { + long timestamp1 = System.currentTimeMillis(); + long timestamp2 = timestamp1 + 10; + + assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2, + 10)); + } + + @Test + public void isDateAfterNMillisec_date1IsLessThanNMillisecFromDate2_returnFalse() throws + Exception { + long timestamp1 = System.currentTimeMillis(); + long timestamp2 = timestamp1 + 10; + + assertFalse(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2, + 11)); + } + + @Test + public void recordSetUpTimestamp_timestampNotSetBefore_setTimestamp() throws + Exception { + assertTrue(mTempSetUpDateFile.length() == 0); + + mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp(); + + assertTrue(mTempSetUpDateFile.length() != 0); + } + + @Test + public void recordSetUpTimestamp_timestampSetBefore_doNothing() throws + Exception { + mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp(); + assertTrue(mTempSetUpDateFile.length() != 0); + String timestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate(); + + mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp(); + + assertTrue(mTempSetUpDateFile.length() != 0); + String timestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate(); + assertTrue(timestampAfter.equals(timestampBefore)); + } + + private Bundle createRestoreBMMEvent() { + Bundle event = new Bundle(); + event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1); + event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1); + event.putInt(BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, + BackupAnnotations.OperationType.RESTORE); + return event; } private class TestBackupManagerMonitorDumpsysUtils extends BackupManagerMonitorDumpsysUtils { + + private long testRetentionPeriod; + private long testSizeLimit; + TestBackupManagerMonitorDumpsysUtils() { super(); + this.testRetentionPeriod = mRetentionPeriod; + this.testSizeLimit = mSizeLimit; + } + + public void setTestRetentionPeriod(long testRetentionPeriod) { + this.testRetentionPeriod = testRetentionPeriod; + } + public void setTestSizeLimit(long testSizeLimit) { + this.testSizeLimit = testSizeLimit; } @Override public File getBMMEventsFile() { - return mTempFile; + return mTempBMMEventsFile; } + + @Override + File getSetUpDateFile() { + return mTempSetUpDateFile; + } + + @Override + long getRetentionPeriodInMillisec() { + return testRetentionPeriod; + } + + @Override + long getBMMEventsFileSizeLimit(){ + return testSizeLimit; + } + + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java index 3af2932ee937..604a68d12f5e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java @@ -340,8 +340,9 @@ public class BackupManagerMonitorEventSenderTest { @Test public void putMonitoringExtraLong_bundleExists_fillsBundleCorrectly() throws Exception { Bundle bundle = new Bundle(); + long value = 123; - Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 123); + Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", value); assertThat(result).isEqualTo(bundle); assertThat(result.size()).isEqualTo(1); @@ -350,7 +351,8 @@ public class BackupManagerMonitorEventSenderTest { @Test public void putMonitoringExtraLong_bundleDoesNotExist_fillsBundleCorrectly() throws Exception { - Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 123); + long value = 123; + Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", value); assertThat(result).isNotNull(); assertThat(result.size()).isEqualTo(1); @@ -377,4 +379,25 @@ public class BackupManagerMonitorEventSenderTest { assertThat(result.size()).isEqualTo(1); assertThat(result.getBoolean("key")).isTrue(); } + + @Test + public void putMonitoringExtraInt_bundleExists_fillsBundleCorrectly() throws Exception { + Bundle bundle = new Bundle(); + + Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 1); + + assertThat(result).isEqualTo(bundle); + assertThat(result.size()).isEqualTo(1); + assertThat(result.getInt("key")).isEqualTo(1); + } + + @Test + public void putMonitoringExtraInt_bundleDoesNotExist_fillsBundleCorrectly() + throws Exception { + Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 1); + + assertThat(result).isNotNull(); + assertThat(result.size()).isEqualTo(1); + assertThat(result.getInt("key")).isEqualTo(1); + } } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java index bc41829f16d2..71ac94ba2ee4 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java @@ -334,6 +334,7 @@ public class SharedConnectivityManager { if (mServiceConnection != null) { mContext.unbindService(mServiceConnection); mServiceConnection = null; + mService = null; } } |