summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt8
-rw-r--r--core/java/android/content/pm/PackageInstaller.java1
-rw-r--r--core/java/android/content/pm/PackageManager.java3
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java60
-rw-r--r--core/java/android/window/DesktopModeFlags.java1
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig11
-rw-r--r--core/java/com/android/internal/app/MediaRouteControllerContentManager.java109
-rw-r--r--core/java/com/android/internal/app/MediaRouteControllerDialog.java120
-rw-r--r--core/res/res/layout/media_route_controller_dialog.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt12
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt34
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt111
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt100
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt5
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java59
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java11
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java3
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java5
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java17
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java12
-rw-r--r--services/java/com/android/server/SystemServer.java36
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java62
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java22
-rw-r--r--tests/PackageWatchdog/Android.bp14
-rw-r--r--tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java5
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java248
35 files changed, 489 insertions, 688 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0d5ec199b953..7d408888ef21 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4199,7 +4199,7 @@ package android.content.pm {
method public void setInstallAsInstantApp(boolean);
method public void setInstallAsVirtualPreload();
method public void setRequestDowngrade(boolean);
- method @FlaggedApi("android.content.pm.recoverability_detection") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackImpactLevel(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackImpactLevel(int);
method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
}
@@ -4374,9 +4374,9 @@ package android.content.pm {
field public static final int ROLLBACK_DATA_POLICY_RESTORE = 0; // 0x0
field public static final int ROLLBACK_DATA_POLICY_RETAIN = 2; // 0x2
field public static final int ROLLBACK_DATA_POLICY_WIPE = 1; // 0x1
- field @FlaggedApi("android.content.pm.recoverability_detection") public static final int ROLLBACK_USER_IMPACT_HIGH = 1; // 0x1
- field @FlaggedApi("android.content.pm.recoverability_detection") public static final int ROLLBACK_USER_IMPACT_LOW = 0; // 0x0
- field @FlaggedApi("android.content.pm.recoverability_detection") public static final int ROLLBACK_USER_IMPACT_ONLY_MANUAL = 2; // 0x2
+ field public static final int ROLLBACK_USER_IMPACT_HIGH = 1; // 0x1
+ field public static final int ROLLBACK_USER_IMPACT_LOW = 0; // 0x0
+ field public static final int ROLLBACK_USER_IMPACT_ONLY_MANUAL = 2; // 0x2
field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0
field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1
field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d64ef75a8f7c..4bbbad4d1667 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3250,7 +3250,6 @@ public class PackageInstaller {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
- @FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION)
public void setRollbackImpactLevel(@PackageManager.RollbackImpactLevel int impactLevel) {
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
throw new IllegalArgumentException(
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6ae2df2cd7a2..f91b2474fdac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1619,7 +1619,6 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
- @FlaggedApi(android.content.pm.Flags.FLAG_RECOVERABILITY_DETECTION)
public static final int ROLLBACK_USER_IMPACT_LOW = 0;
/**
@@ -1629,7 +1628,6 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
- @FlaggedApi(android.content.pm.Flags.FLAG_RECOVERABILITY_DETECTION)
public static final int ROLLBACK_USER_IMPACT_HIGH = 1;
/**
@@ -1638,7 +1636,6 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
- @FlaggedApi(android.content.pm.Flags.FLAG_RECOVERABILITY_DETECTION)
public static final int ROLLBACK_USER_IMPACT_ONLY_MANUAL = 2;
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 56f0415b40cc..421001f385d4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -466,6 +466,26 @@ public final class InputMethodManager {
private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
/**
+ * Always return {@code true} when {@link #hideSoftInputFromWindow(IBinder, int)} and
+ * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver, int, ImeTracker.Token)} is
+ * called.
+ * <p>
+ * Apps can incorrectly rely on the return value of
+ * {@link #hideSoftInputFromWindow(IBinder, int)} to guess whether the IME was hidden.
+ * However, it was never a guarantee that the IME will actually hide.
+ * Therefore, it was recommended using the {@link View.OnApplyWindowInsetsListener}.
+ * <p>
+ * Starting Android {@link Build.VERSION_CODES.BAKLAVA}, the return value will always be
+ * true, independent from whether the request was eventually sent to system server or not.
+ * Apps that need to know when the IME visibility changes should use
+ * {@link View.OnApplyWindowInsetsListener}. For apps that target earlier releases, it
+ * returns an estimate ({@code true} if the IME was showing before).
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
+ private static final long ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW = 395521150L; // bug id
+
+ /**
* If {@code true}, avoid calling the
* {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
* by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus}
@@ -2531,9 +2551,14 @@ public final class InputMethodManager {
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
- * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
- * this does not return result of the request. For result use {@link ResultReceiver} in
- * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
+ * @return <p>For apps targeting Android {@link Build.VERSION_CODES.BAKLAVA}, onwards, it
+ * will always return {@code true}. To see when the IME is hidden, use
+ * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+ * the visibility of IME.
+ *
+ * <p>For apps targeting releases before Android Baklava: returns {@code true} if a request
+ * was sent to system_server, {@code false} otherwise. Note: This does not return the result
+ * of that request (i.e. whether the IME was actually hidden).
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2563,7 +2588,8 @@ public final class InputMethodManager {
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
* @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
- * this does not return result of the request. For result use {@param resultReceiver} instead.
+ * This does not return the result of that request (i.e. whether the IME was actually hidden).
+ * For result use {@param resultReceiver} instead.
*
* @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
* Input Method is actually shown or hidden. If result is needed, use
@@ -2603,7 +2629,10 @@ public final class InputMethodManager {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
ImeTracker.forLatency().onHideFailed(statsToken,
ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
- return false;
+ // with the flag enabled and targeting Android Baklava onwards, the return value
+ // should be always true (was false before).
+ return Flags.refactorInsetsController() && CompatChanges.isChangeEnabled(
+ ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
@@ -2618,15 +2647,18 @@ public final class InputMethodManager {
// under us. The current input has been closed before (from checkFocus).
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE);
- return false;
+ // with the flag enabled and targeting Android Baklava onwards, the
+ // return value should be always true (was false before).
+ return Flags.refactorInsetsController() && CompatChanges.isChangeEnabled(
+ ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
}
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE);
+ final boolean imeReqVisible =
+ (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+ & WindowInsets.Type.ime()) != 0;
if (resultReceiver != null) {
- final boolean imeReqVisible =
- (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
- & WindowInsets.Type.ime()) != 0;
resultReceiver.send(
!imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
: InputMethodManager.RESULT_HIDDEN, null);
@@ -2642,8 +2674,16 @@ public final class InputMethodManager {
viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime(),
false /* fromIme */, statsToken);
}
+ if (!CompatChanges.isChangeEnabled(
+ ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW)) {
+ // if the IME was not visible before, the additional hide won't change
+ // anything.
+ return imeReqVisible;
+ }
}
- return true;
+ // Targeting Android Baklava onwards, this method will always return true.
+ return CompatChanges.isChangeEnabled(
+ ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
} else {
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
statsToken, flags, resultReceiver, reason, mAsyncShowHideMethodEnabled);
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 23da7475f7f7..b44620f12bbb 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -101,6 +101,7 @@ public enum DesktopModeFlags {
ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
+ ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 79120b22c205..891c5e382a58 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -16,6 +16,17 @@ flag {
}
flag {
+ name: "enable_modals_fullscreen_with_permission"
+ namespace: "lse_desktop_experience"
+ description: "Uses permissions to understand if modal fullscreen is allowed for /n"
+ "transparent activities."
+ bug: "394714626"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "include_top_transparent_fullscreen_task_in_desktop_heuristic"
namespace: "lse_desktop_experience"
description: "Whether to include any top transparent fullscreen task launched in desktop /n"
diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
new file mode 100644
index 000000000000..83ae7edd796b
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2025 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.internal.app;
+
+import android.content.Context;
+import android.media.MediaRouter;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.internal.R;
+
+/**
+ * This class manages the content display within the media route controller UI.
+ */
+public class MediaRouteControllerContentManager {
+ // Time to wait before updating the volume when the user lets go of the seek bar
+ // to allow the route provider time to propagate the change and publish a new
+ // route descriptor.
+ private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
+
+ private final MediaRouter.RouteInfo mRoute;
+
+ private LinearLayout mVolumeLayout;
+ private SeekBar mVolumeSlider;
+ private boolean mVolumeSliderTouched;
+
+ public MediaRouteControllerContentManager(Context context) {
+ MediaRouter mRouter = context.getSystemService(MediaRouter.class);
+ mRoute = mRouter.getSelectedRoute();
+ }
+
+ /**
+ * Starts binding all the views (volume layout, slider, etc.) using the
+ * given container view.
+ */
+ public void bindViews(View containerView) {
+ mVolumeLayout = containerView.findViewById(R.id.media_route_volume_layout);
+ mVolumeSlider = containerView.findViewById(R.id.media_route_volume_slider);
+ mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ private final Runnable mStopTrackingTouch = new Runnable() {
+ @Override
+ public void run() {
+ if (mVolumeSliderTouched) {
+ mVolumeSliderTouched = false;
+ updateVolume();
+ }
+ }
+ };
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mVolumeSliderTouched) {
+ mVolumeSlider.removeCallbacks(mStopTrackingTouch);
+ } else {
+ mVolumeSliderTouched = true;
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Defer resetting mVolumeSliderTouched to allow the media route provider
+ // a little time to settle into its new state and publish the final
+ // volume update.
+ mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ mRoute.requestSetVolume(progress);
+ }
+ }
+ });
+ }
+
+ /**
+ * Updates the volume layout and slider.
+ */
+ public void updateVolume() {
+ if (!mVolumeSliderTouched) {
+ if (isVolumeControlAvailable()) {
+ mVolumeLayout.setVisibility(View.VISIBLE);
+ mVolumeSlider.setMax(mRoute.getVolumeMax());
+ mVolumeSlider.setProgress(mRoute.getVolume());
+ } else {
+ mVolumeLayout.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private boolean isVolumeControlAvailable() {
+ return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 621ec5037cbd..c79f3c7bf76f 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -32,9 +32,6 @@ import android.os.Bundle;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
import com.android.internal.R;
@@ -50,11 +47,6 @@ import com.android.internal.R;
* TODO: Move this back into the API, as in the support library media router.
*/
public class MediaRouteControllerDialog extends AlertDialog {
- // Time to wait before updating the volume when the user lets go of the seek bar
- // to allow the route provider time to propagate the change and publish a new
- // route descriptor.
- private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
-
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
private final MediaRouter.RouteInfo mRoute;
@@ -64,49 +56,19 @@ public class MediaRouteControllerDialog extends AlertDialog {
private int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
private Drawable mCurrentIconDrawable;
- private boolean mVolumeControlEnabled = true;
- private LinearLayout mVolumeLayout;
- private SeekBar mVolumeSlider;
- private boolean mVolumeSliderTouched;
-
- private View mControlView;
private boolean mAttachedToWindow;
+ private final MediaRouteControllerContentManager mContentManager;
+
public MediaRouteControllerDialog(Context context, int theme) {
super(context, theme);
+ mContentManager = new MediaRouteControllerContentManager(context);
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
}
- /**
- * Gets the route that this dialog is controlling.
- */
- public MediaRouter.RouteInfo getRoute() {
- return mRoute;
- }
-
- /**
- * Provides the subclass an opportunity to create a view that will
- * be included within the body of the dialog to offer additional media controls
- * for the currently playing content.
- *
- * @param savedInstanceState The dialog's saved instance state.
- * @return The media control view, or null if none.
- */
- public View onCreateMediaControlView(Bundle savedInstanceState) {
- return null;
- }
-
- /**
- * Returns whether to enable the volume slider and volume control using the volume keys
- * when the route supports it.
- */
- public boolean isVolumeControlEnabled() {
- return mVolumeControlEnabled;
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
setTitle(mRoute.getName());
@@ -126,60 +88,15 @@ public class MediaRouteControllerDialog extends AlertDialog {
setView(customView, 0, 0, 0, 0);
super.onCreate(savedInstanceState);
+ mContentManager.bindViews(customView);
+
View customPanelView = getWindow().findViewById(R.id.customPanel);
if (customPanelView != null) {
customPanelView.setMinimumHeight(0);
}
- mVolumeLayout = customView.findViewById(R.id.media_route_volume_layout);
- mVolumeSlider = customView.findViewById(R.id.media_route_volume_slider);
- mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- private final Runnable mStopTrackingTouch = new Runnable() {
- @Override
- public void run() {
- if (mVolumeSliderTouched) {
- mVolumeSliderTouched = false;
- updateVolume();
- }
- }
- };
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- if (mVolumeSliderTouched) {
- mVolumeSlider.removeCallbacks(mStopTrackingTouch);
- } else {
- mVolumeSliderTouched = true;
- }
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- // Defer resetting mVolumeSliderTouched to allow the media route provider
- // a little time to settle into its new state and publish the final
- // volume update.
- mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (fromUser) {
- mRoute.requestSetVolume(progress);
- }
- }
- });
mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
- if (update()) {
- mControlView = onCreateMediaControlView(savedInstanceState);
- FrameLayout controlFrame =
- customView.findViewById(R.id.media_route_control_frame);
- if (mControlView != null) {
- controlFrame.addView(mControlView);
- controlFrame.setVisibility(View.VISIBLE);
- } else {
- controlFrame.setVisibility(View.GONE);
- }
- }
+ update();
}
@Override
@@ -218,14 +135,13 @@ public class MediaRouteControllerDialog extends AlertDialog {
return super.onKeyUp(keyCode, event);
}
- private boolean update() {
+ private void update() {
if (!mRoute.isSelected() || mRoute.isDefault()) {
dismiss();
- return false;
}
setTitle(mRoute.getName());
- updateVolume();
+ mContentManager.updateVolume();
Drawable icon = getIconDrawable();
if (icon != mCurrentIconDrawable) {
@@ -244,7 +160,6 @@ public class MediaRouteControllerDialog extends AlertDialog {
}
setIcon(icon);
}
- return true;
}
private Drawable obtainMediaRouteButtonDrawable() {
@@ -274,23 +189,6 @@ public class MediaRouteControllerDialog extends AlertDialog {
}
}
- private void updateVolume() {
- if (!mVolumeSliderTouched) {
- if (isVolumeControlAvailable()) {
- mVolumeLayout.setVisibility(View.VISIBLE);
- mVolumeSlider.setMax(mRoute.getVolumeMax());
- mVolumeSlider.setProgress(mRoute.getVolume());
- } else {
- mVolumeLayout.setVisibility(View.GONE);
- }
- }
- }
-
- private boolean isVolumeControlAvailable() {
- return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
- MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
- }
-
private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
@Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
@@ -305,7 +203,7 @@ public class MediaRouteControllerDialog extends AlertDialog {
@Override
public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
if (route == mRoute) {
- updateVolume();
+ mContentManager.updateVolume();
}
}
diff --git a/core/res/res/layout/media_route_controller_dialog.xml b/core/res/res/layout/media_route_controller_dialog.xml
index 24a25353f40d..a5cd83be9f6c 100644
--- a/core/res/res/layout/media_route_controller_dialog.xml
+++ b/core/res/res/layout/media_route_controller_dialog.xml
@@ -41,11 +41,5 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
</LinearLayout>
-
- <!-- Optional content view section. -->
- <FrameLayout android:id="@+id/media_route_control_frame"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone" />
</LinearLayout>
</ScrollView>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 06861b113e64..2c68bd294397 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1743,7 +1743,6 @@
<java-symbol type="id" name="media_route_list" />
<java-symbol type="id" name="media_route_volume_layout" />
<java-symbol type="id" name="media_route_volume_slider" />
- <java-symbol type="id" name="media_route_control_frame" />
<java-symbol type="id" name="media_route_extended_settings_button" />
<java-symbol type="id" name="media_route_progress_bar" />
<java-symbol type="string" name="media_route_chooser_title" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 3ada988ba2a3..9a97ae8d61a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -186,14 +186,16 @@ class DesktopTasksTransitionObserver(
for (change in info.changes) {
val taskInfo = change.taskInfo
if (taskInfo == null || taskInfo.taskId == -1) continue
- if (change.mode != TRANSIT_CLOSE) continue
- if (minimizingTask == null) {
- minimizingTask = getMinimizingTaskForClosingTransition(taskInfo)
+ if (
+ TransitionUtil.isClosingMode(change.mode) &&
+ DesktopWallpaperActivity.isWallpaperTask(taskInfo)
+ ) {
+ hasWallpaperClosing = true
}
- if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
- hasWallpaperClosing = true
+ if (change.mode == TRANSIT_CLOSE && minimizingTask == null) {
+ minimizingTask = getMinimizingTaskForClosingTransition(taskInfo)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index 2115f70faad0..af2840e9c34a 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -45,6 +45,7 @@ constructor(
tapl.setExpectedRotation(rotation.value)
ChangeDisplayOrientationRule.setRotation(rotation)
tapl.enableTransientTaskbar(false)
+ testApp.exit(wmHelper)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index c29edece5537..dd577f402952 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -24,6 +24,7 @@ import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.IBinder
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
@@ -168,7 +169,8 @@ class DesktopTasksTransitionObserverTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
- fun backNavigation_withCloseTransitionLastTask_taskMinimized() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
+ fun backNavigation_withCloseTransitionLastTask_wallpaperActivityClosed_taskMinimized() {
val task = createTaskInfo(1)
val transition = mock<IBinder>()
whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
@@ -193,6 +195,35 @@ class DesktopTasksTransitionObserverTest {
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun backNavigation_withCloseTransitionLastTask_wallpaperActivityReordered_taskMinimized() {
+ val task = createTaskInfo(1)
+ val transition = mock<IBinder>()
+ whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+ whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = transition,
+ info = createBackNavigationTransition(task, TRANSIT_CLOSE, true, TRANSIT_TO_BACK),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ val pendingTransition =
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition,
+ task.taskId,
+ isLastTask = true,
+ )
+ verify(mixedHandler).addPendingMixedTransition(pendingTransition)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun backNavigation_nullTaskInfo_taskNotMinimized() {
val task = createTaskInfo(1)
@@ -434,6 +465,7 @@ class DesktopTasksTransitionObserverTest {
task: RunningTaskInfo?,
type: Int = TRANSIT_TO_BACK,
withWallpaper: Boolean = false,
+ wallpaperChangeMode: Int = TRANSIT_CLOSE,
): TransitionInfo {
return TransitionInfo(type, /* flags= */ 0).apply {
addChange(
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index ae75e6c089ca..a1d362a4a11d 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -39,7 +39,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
@@ -175,21 +174,7 @@ fun Expandable(
val wrappedContent =
remember(content) {
movableContentOf { expandable: Expandable ->
- CompositionLocalProvider(LocalContentColor provides contentColor) {
- // We make sure that the content itself (wrapped by the background) is at least
- // 40.dp, which is the same as the M3 buttons. This applies even if onClick is
- // null, to make it easier to write expandables that are sometimes clickable and
- // sometimes not. There shouldn't be any Expandable smaller than 40dp because if
- // the expandable is not clickable directly, then something in its content
- // should be (and with a size >= 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
- content(expandable)
- }
- }
+ WrappedContent(expandable, contentColor, content)
}
}
@@ -209,11 +194,7 @@ fun Expandable(
// Make sure we don't read animatorState directly here to avoid recomposition every time the
// state changes (i.e. every frame of the animation).
- val isAnimating by remember {
- derivedStateOf {
- controller.animatorState.value != null && controller.overlay.value != null
- }
- }
+ val isAnimating = controller.isAnimating
// If this expandable is expanded when it's being directly clicked on, let's ensure that it has
// the minimum interactive size followed by all M3 components (48.dp).
@@ -237,58 +218,36 @@ fun Expandable(
// animating.
AnimatedContentInOverlay(
color,
- controller.boundsInComposeViewRoot.value.size,
- controller.animatorState,
- controller.overlay.value
+ controller.boundsInComposeViewRoot.size,
+ controller.overlay
?: error("AnimatedContentInOverlay shouldn't be composed with null overlay."),
controller,
wrappedContent,
controller.composeViewRoot,
- { controller.currentComposeViewInOverlay.value = it },
+ { controller.currentComposeViewInOverlay = it },
controller.density,
)
}
- controller.isDialogShowing.value -> {
+ controller.isDialogShowing -> {
Box(
modifier
.updateExpandableSize()
.then(minInteractiveSizeModifier)
.drawWithContent { /* Don't draw anything when the dialog is shown. */ }
- .onGloballyPositioned {
- controller.boundsInComposeViewRoot.value = it.boundsInRoot()
- }
+ .onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
) {
wrappedContent(controller.expandable)
}
}
else -> {
- val clickModifier =
- if (onClick != null) {
- if (interactionSource != null) {
- // If the caller provided an interaction source, then that means that they
- // will draw the click indication themselves.
- Modifier.clickable(interactionSource, indication = null) {
- onClick(controller.expandable)
- }
- } else {
- // If no interaction source is provided, we draw the default indication (a
- // ripple) and make sure it's clipped by the expandable shape.
- Modifier.clip(shape).clickable { onClick(controller.expandable) }
- }
- } else {
- Modifier
- }
-
Box(
modifier
.updateExpandableSize()
.then(minInteractiveSizeModifier)
- .then(clickModifier)
+ .then(clickModifier(controller, onClick, interactionSource))
.background(color, shape)
.border(controller)
- .onGloballyPositioned {
- controller.boundsInComposeViewRoot.value = it.boundsInRoot()
- }
+ .onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
) {
wrappedContent(controller.expandable)
}
@@ -296,12 +255,55 @@ fun Expandable(
}
}
+@Composable
+private fun WrappedContent(
+ expandable: Expandable,
+ contentColor: Color,
+ content: @Composable (Expandable) -> Unit,
+) {
+ CompositionLocalProvider(LocalContentColor provides contentColor) {
+ // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
+ // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
+ // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
+ // Expandable smaller than 40dp because if the expandable is not clickable directly, then
+ // something in its content should be (and with a size >= 40dp).
+ val minSize = 40.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
+ }
+}
+
+private fun clickModifier(
+ controller: ExpandableControllerImpl,
+ onClick: ((Expandable) -> Unit)?,
+ interactionSource: MutableInteractionSource?,
+): Modifier {
+ if (onClick == null) {
+ return Modifier
+ }
+
+ if (interactionSource != null) {
+ // If the caller provided an interaction source, then that means that they will draw the
+ // click indication themselves.
+ return Modifier.clickable(interactionSource, indication = null) {
+ onClick(controller.expandable)
+ }
+ }
+
+ // If no interaction source is provided, we draw the default indication (a ripple) and make sure
+ // it's clipped by the expandable shape.
+ return Modifier.clip(controller.shape).clickable { onClick(controller.expandable) }
+}
+
/** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
@Composable
private fun AnimatedContentInOverlay(
color: Color,
sizeInOriginalLayout: Size,
- animatorState: State<TransitionAnimator.State?>,
overlay: ViewGroupOverlay,
controller: ExpandableControllerImpl,
content: @Composable (Expandable) -> Unit,
@@ -324,7 +326,7 @@ private fun AnimatedContentInOverlay(
// so that its content is laid out exactly the same way.
.requiredSize(with(density) { sizeInOriginalLayout.toDpSize() })
.drawWithContent {
- val animatorState = animatorState.value ?: return@drawWithContent
+ val animatorState = controller.animatorState ?: return@drawWithContent
// Scale the content with the background while keeping its aspect ratio.
val widthRatio =
@@ -348,7 +350,8 @@ private fun AnimatedContentInOverlay(
setContent {
Box(
Modifier.fillMaxSize().drawWithContent {
- val animatorState = animatorState.value ?: return@drawWithContent
+ val animatorState =
+ controller.animatorState ?: return@drawWithContent
if (!animatorState.visible) {
return@drawWithContent
}
@@ -385,7 +388,7 @@ private fun AnimatedContentInOverlay(
overlay.add(composeViewInOverlay)
val startState =
- animatorState.value
+ controller.animatorState
?: throw IllegalStateException(
"AnimatedContentInOverlay shouldn't be composed with null animatorState."
)
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index c5d2802c8941..377ea96c5723 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -25,10 +25,11 @@ import androidx.compose.foundation.BorderStroke
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
@@ -53,6 +54,9 @@ interface ExpandableController {
/** The [Expandable] controlled by this controller. */
val expandable: Expandable
+ /** Whether this controller is currently animating a launch. */
+ val isAnimating: Boolean
+
/** Called when the [Expandable] stop being included in the composition. */
fun onDispose()
}
@@ -73,24 +77,9 @@ fun rememberExpandableController(
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
- // The current animation state, if we are currently animating a dialog or activity.
- val animatorState = remember { mutableStateOf<TransitionAnimator.State?>(null) }
-
- // Whether a dialog controlled by this ExpandableController is currently showing.
- val isDialogShowing = remember { mutableStateOf(false) }
-
- // The overlay in which we should animate the launch.
- val overlay = remember { mutableStateOf<ViewGroupOverlay?>(null) }
-
- // The current [ComposeView] being animated in the [overlay], if any.
- val currentComposeViewInOverlay = remember { mutableStateOf<View?>(null) }
-
- // The bounds in [composeViewRoot] of the expandable controlled by this controller.
- val boundsInComposeViewRoot = remember { mutableStateOf(Rect.Zero) }
-
// Whether this composable is still composed. We only do the dialog exit animation if this is
// true.
- val isComposed = remember { mutableStateOf(true) }
+ var isComposed by remember { mutableStateOf(true) }
val controller =
remember(
@@ -109,19 +98,14 @@ fun rememberExpandableController(
borderStroke,
composeViewRoot,
density,
- animatorState,
- isDialogShowing,
- overlay,
- currentComposeViewInOverlay,
- boundsInComposeViewRoot,
layoutDirection,
- isComposed,
+ { isComposed },
)
}
DisposableEffect(Unit) {
onDispose {
- isComposed.value = false
+ isComposed = false
if (TransitionAnimator.returnAnimationsEnabled()) {
controller.onDispose()
}
@@ -138,14 +122,27 @@ internal class ExpandableControllerImpl(
internal val borderStroke: BorderStroke?,
internal val composeViewRoot: View,
internal val density: Density,
- internal val animatorState: MutableState<TransitionAnimator.State?>,
- internal val isDialogShowing: MutableState<Boolean>,
- internal val overlay: MutableState<ViewGroupOverlay?>,
- internal val currentComposeViewInOverlay: MutableState<View?>,
- internal val boundsInComposeViewRoot: MutableState<Rect>,
private val layoutDirection: LayoutDirection,
- private val isComposed: State<Boolean>,
+ private val isComposed: () -> Boolean,
) : ExpandableController {
+ /** The current animation state, if we are currently animating a dialog or activity. */
+ var animatorState by mutableStateOf<TransitionAnimator.State?>(null)
+ private set
+
+ /** Whether a dialog controlled by this ExpandableController is currently showing. */
+ var isDialogShowing by mutableStateOf(false)
+ private set
+
+ /** The overlay in which we should animate the launch. */
+ var overlay by mutableStateOf<ViewGroupOverlay?>(null)
+ private set
+
+ /** The current [ComposeView] being animated in the [overlay], if any. */
+ var currentComposeViewInOverlay by mutableStateOf<View?>(null)
+
+ /** The bounds in [composeViewRoot] of the expandable controlled by this controller. */
+ var boundsInComposeViewRoot by mutableStateOf(Rect.Zero)
+
/** The [ActivityTransitionAnimator.Controller] to be cleaned up [onDispose]. */
private var activityControllerForDisposal: ActivityTransitionAnimator.Controller? = null
@@ -158,7 +155,7 @@ internal class ExpandableControllerImpl(
returnCujType: Int?,
isEphemeral: Boolean,
): ActivityTransitionAnimator.Controller? {
- if (!isComposed.value) {
+ if (!isComposed()) {
return null
}
@@ -174,7 +171,7 @@ internal class ExpandableControllerImpl(
override fun dialogTransitionController(
cuj: DialogCuj?
): DialogTransitionAnimator.Controller? {
- if (!isComposed.value) {
+ if (!isComposed()) {
return null
}
@@ -182,6 +179,8 @@ internal class ExpandableControllerImpl(
}
}
+ override val isAnimating: Boolean by derivedStateOf { animatorState != null && overlay != null }
+
override fun onDispose() {
activityControllerForDisposal?.onDispose()
activityControllerForDisposal = null
@@ -204,7 +203,7 @@ internal class ExpandableControllerImpl(
override val isLaunching: Boolean = true
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- animatorState.value = null
+ animatorState = null
}
override fun onTransitionAnimationProgress(
@@ -214,7 +213,7 @@ internal class ExpandableControllerImpl(
) {
// We copy state given that it's always the same object that is mutated by
// ActivityTransitionAnimator.
- animatorState.value =
+ animatorState =
TransitionAnimator.State(
state.top,
state.bottom,
@@ -227,13 +226,11 @@ internal class ExpandableControllerImpl(
// Force measure and layout the ComposeView in the overlay whenever the animation
// state changes.
- currentComposeViewInOverlay.value?.let {
- measureAndLayoutComposeViewInOverlay(it, state)
- }
+ currentComposeViewInOverlay?.let { measureAndLayoutComposeViewInOverlay(it, state) }
}
override fun createAnimatorState(): TransitionAnimator.State {
- val boundsInRoot = boundsInComposeViewRoot.value
+ val boundsInRoot = boundsInComposeViewRoot
val outline =
shape.createOutline(
Size(boundsInRoot.width, boundsInRoot.height),
@@ -285,7 +282,7 @@ internal class ExpandableControllerImpl(
private fun rootLocationOnScreen(): Offset {
composeViewRoot.getLocationOnScreen(rootLocationOnScreen)
- val boundsInRoot = boundsInComposeViewRoot.value
+ val boundsInRoot = boundsInComposeViewRoot
val x = rootLocationOnScreen[0] + boundsInRoot.left
val y = rootLocationOnScreen[1] + boundsInRoot.top
return Offset(x, y)
@@ -319,14 +316,14 @@ internal class ExpandableControllerImpl(
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
- overlay.value = transitionContainer.overlay as ViewGroupOverlay
+ overlay = transitionContainer.overlay as ViewGroupOverlay
cujType?.let { InteractionJankMonitor.getInstance().begin(composeViewRoot, it) }
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
cujType?.let { InteractionJankMonitor.getInstance().end(it) }
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
- overlay.value = null
+ overlay = null
}
}
}
@@ -339,14 +336,14 @@ internal class ExpandableControllerImpl(
override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
val newOverlay = viewGroup.overlay as ViewGroupOverlay
- if (newOverlay != overlay.value) {
- overlay.value = newOverlay
+ if (newOverlay != overlay) {
+ overlay = newOverlay
}
}
override fun stopDrawingInOverlay() {
- if (overlay.value != null) {
- overlay.value = null
+ if (overlay != null) {
+ overlay = null
}
}
@@ -357,7 +354,7 @@ internal class ExpandableControllerImpl(
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
// Make sure we don't draw this expandable when the dialog is showing.
- isDialogShowing.value = true
+ isDialogShowing = true
}
}
}
@@ -367,16 +364,17 @@ internal class ExpandableControllerImpl(
return object : TransitionAnimator.Controller by delegate {
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
- isDialogShowing.value = false
+ isDialogShowing = false
}
}
}
- override fun shouldAnimateExit(): Boolean =
- isComposed.value && composeViewRoot.isAttachedToWindow && composeViewRoot.isShown
+ override fun shouldAnimateExit(): Boolean {
+ return isComposed() && composeViewRoot.isAttachedToWindow && composeViewRoot.isShown
+ }
override fun onExitAnimationCancelled() {
- isDialogShowing.value = false
+ isDialogShowing = false
}
override fun jankConfigurationBuilder(): InteractionJankMonitor.Configuration.Builder? {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt
index f5c3a834a8d7..089da4b932b2 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt
@@ -42,13 +42,19 @@ import androidx.savedstate.setViewTreeSavedStateRegistryOwner
@Composable
fun Modifier.drawInOverlay(): Modifier {
val containerState = remember { ContainerState() }
+ FullScreenComposeViewInOverlay { Modifier.container(containerState) }
+ return this.drawInContainer(containerState, enabled = { true })
+}
+
+@Composable
+internal fun FullScreenComposeViewInOverlay(modifier: (ComposeView) -> Modifier = { Modifier }) {
val context = LocalContext.current
val localView = LocalView.current
val compositionContext = rememberCompositionContext()
val displayMetrics = context.resources.displayMetrics
val displaySize = IntSize(displayMetrics.widthPixels, displayMetrics.heightPixels)
- DisposableEffect(containerState, context, localView, compositionContext, displaySize) {
+ DisposableEffect(context, localView, compositionContext, displaySize) {
val overlay = localView.rootView.overlay as ViewGroupOverlay
val view =
ComposeView(context).apply {
@@ -59,7 +65,8 @@ fun Modifier.drawInOverlay(): Modifier {
setViewTreeViewModelStoreOwner(localView.findViewTreeViewModelStoreOwner())
setViewTreeSavedStateRegistryOwner(localView.findViewTreeSavedStateRegistryOwner())
- setContent { Box(Modifier.fillMaxSize().container(containerState)) }
+ val view = this
+ setContent { Box(modifier(view).fillMaxSize()) }
}
overlay.add(view)
@@ -74,6 +81,4 @@ fun Modifier.drawInOverlay(): Modifier {
onDispose { overlay.remove(view) }
}
-
- return this.drawInContainer(containerState, enabled = { true })
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
index 35368ca8734d..9498daaf0b07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt
@@ -60,6 +60,7 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() {
policies,
shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
keyguardRepository,
+ displayRepository,
)
@Test
@@ -90,6 +91,30 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() {
}
@Test
+ fun displayId_afterDisplayDisconnected_fallsBackToDefaultDisplay() =
+ testScope.runTest {
+ val underTest = createUnderTest()
+ globalSettings.putString(
+ DEVELOPMENT_SHADE_DISPLAY_AWARENESS,
+ FakeShadeDisplayPolicy.name,
+ )
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplay(displayId = 1)
+
+ FakeShadeDisplayPolicy.setDisplayId(1)
+ assertThat(displayId).isEqualTo(1)
+
+ // Let's disconnect and make sure it goes back to the default one
+ displayRepository.removeDisplay(displayId = 1)
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ // Let's re-connect it and make sure it goes back to the non-default one
+ displayRepository.addDisplay(displayId = 1)
+ assertThat(displayId).isEqualTo(1)
+ }
+
+ @Test
fun policy_updatesBasedOnSettingValue_defaultDisplay() =
testScope.runTest {
val underTest = createUnderTest()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
index 4eb707297073..2a14ca44386d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt
@@ -20,6 +20,7 @@ import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
import com.android.systemui.shade.display.ShadeDisplayPolicy
@@ -45,7 +46,12 @@ interface ShadeDisplaysRepository {
val currentPolicy: ShadeDisplayPolicy
}
-/** Keeps the policy and propagates the display id for the shade from it. */
+/**
+ * Keeps the policy and propagates the display id for the shade from it.
+ *
+ * If the display set by the policy is not available (e.g. after the cable is disconnected), this
+ * falls back to the [Display.DEFAULT_DISPLAY].
+ */
@SysUISingleton
class ShadeDisplaysRepositoryImpl
@Inject
@@ -56,6 +62,7 @@ constructor(
policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
@ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
keyguardRepository: KeyguardRepository,
+ displayRepository: DisplayRepository,
) : ShadeDisplaysRepository {
private val policy: StateFlow<ShadeDisplayPolicy> =
@@ -73,7 +80,12 @@ constructor(
.distinctUntilChanged()
.stateIn(bgScope, SharingStarted.Eagerly, defaultPolicy)
- private val displayIdFromPolicy: Flow<Int> = policy.flatMapLatest { it.displayId }
+ private val displayIdFromPolicy: Flow<Int> =
+ policy
+ .flatMapLatest { it.displayId }
+ .combine(displayRepository.displayIds) { policyDisplayId, availableIds ->
+ if (policyDisplayId !in availableIds) Display.DEFAULT_DISPLAY else policyDisplayId
+ }
private val keyguardAwareDisplayPolicy: Flow<Int> =
if (!shadeOnDefaultDisplayWhenLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
index 1f534a5c191a..4ebdb6023268 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -26,7 +26,6 @@ import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.shade.domain.interactor.NotificationShadeElement
import com.android.systemui.shade.domain.interactor.QSShadeElement
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import dagger.Lazy
import java.util.concurrent.atomic.AtomicReference
@@ -53,7 +52,6 @@ class StatusBarTouchShadeDisplayPolicy
constructor(
displayRepository: DisplayRepository,
@Background private val backgroundScope: CoroutineScope,
- private val shadeInteractor: Lazy<ShadeInteractor>,
private val qsShadeElement: Lazy<QSShadeElement>,
private val notificationElement: Lazy<NotificationShadeElement>,
) : ShadeDisplayPolicy, ShadeExpansionIntent {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index ba7557ef7f71..26a441bc8ca3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -30,7 +30,6 @@ import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.notificationElement
import com.android.systemui.shade.domain.interactor.qsElement
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.util.settings.fakeGlobalSettings
val Kosmos.defaultShadeDisplayPolicy: DefaultDisplayShadePolicy by
@@ -49,9 +48,8 @@ val Kosmos.statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
StatusBarTouchShadeDisplayPolicy(
displayRepository = displayRepository,
backgroundScope = testScope.backgroundScope,
- shadeInteractor = { shadeInteractor },
- notificationElement = { notificationElement },
qsShadeElement = { qsElement },
+ notificationElement = { notificationElement },
)
}
val Kosmos.shadeExpansionIntent: ShadeExpansionIntent by
@@ -65,6 +63,7 @@ val Kosmos.shadeDisplaysRepository: ShadeDisplaysRepository by
defaultPolicy = defaultShadeDisplayPolicy,
shadeOnDefaultDisplayWhenLocked = true,
keyguardRepository = keyguardRepository,
+ displayRepository = displayRepository,
)
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 15688c0f7366..28117470e7a3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -204,6 +204,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -1023,6 +1024,7 @@ final class InstallPackageHelper {
*/
void installPackagesTraced(List<InstallRequest> requests, MoveInfo moveInfo) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+ boolean pendingForDexopt = false;
boolean success = false;
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
@@ -1036,17 +1038,41 @@ final class InstallPackageHelper {
if (reconciledPackages == null) {
return;
}
+
if (renameAndUpdatePaths(requests)) {
// rename before dexopt because art will encoded the path in the odex/vdex file
if (Flags.improveInstallFreeze()) {
- prepPerformDexoptIfNeeded(reconciledPackages);
- }
- if (commitInstallPackages(reconciledPackages)) {
- success = true;
+ pendingForDexopt = true;
+ final Runnable actionsAfterDexopt = () ->
+ doPostDexopt(reconciledPackages, requests,
+ createdAppId, moveInfo, acquireTime);
+ prepPerformDexoptIfNeeded(reconciledPackages, actionsAfterDexopt);
+ } else {
+ if (commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
}
}
}
} finally {
+ if (!pendingForDexopt) {
+ completeInstallProcess(requests, createdAppId, success);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ doPostInstall(requests, moveInfo);
+ releaseWakeLock(acquireTime, requests.size());
+ }
+ }
+ }
+
+ void doPostDexopt(List<ReconciledPackage> reconciledPackages,
+ List<InstallRequest> requests, Map<String, Boolean> createdAppId,
+ MoveInfo moveInfo, long acquireTime) {
+ boolean success = false;
+ try {
+ if (commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
+ } finally {
completeInstallProcess(requests, createdAppId, success);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
doPostInstall(requests, moveInfo);
@@ -1123,7 +1149,7 @@ final class InstallPackageHelper {
throws PackageManagerException {
final int userId = installRequest.getUserId();
if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
- && !mPm.mUserManager.exists(userId)) {
+ && !ArrayUtils.contains(allUsers, userId)) {
throw new PackageManagerException(PackageManagerException.INTERNAL_ERROR_MISSING_USER,
"User " + userId + " doesn't exist or has been removed");
}
@@ -1155,7 +1181,9 @@ final class InstallPackageHelper {
}
}
- private void prepPerformDexoptIfNeeded(List<ReconciledPackage> reconciledPackages) {
+ private void prepPerformDexoptIfNeeded(List<ReconciledPackage> reconciledPackages,
+ Runnable actionsAfterDexopt) {
+ List<CompletableFuture<Void>> completableFutures = new ArrayList<>();
for (ReconciledPackage reconciledPkg : reconciledPackages) {
final InstallRequest request = reconciledPkg.mInstallRequest;
// prepare profiles
@@ -1171,6 +1199,7 @@ final class InstallPackageHelper {
mSharedLibraries.executeSharedLibrariesUpdate(request.getParsedPackage(), ps,
null, null, reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
}
+
try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
final int[] newUsers = getNewUsers(request, allUsers);
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
@@ -1182,11 +1211,22 @@ final class InstallPackageHelper {
}
} catch (PackageManagerException e) {
request.setError(e.error, e.getMessage());
- return;
+ break;
}
request.setKeepArtProfile(true);
- // TODO(b/388159696): Use performDexoptIfNeededAsync.
- DexOptHelper.performDexoptIfNeeded(request, mDexManager, null /* installLock */);
+
+ CompletableFuture<Void> future =
+ DexOptHelper.performDexoptIfNeededAsync(request, mDexManager);
+ completableFutures.add(future);
+ }
+
+ if (!completableFutures.isEmpty()) {
+ CompletableFuture<Void> allFutures =
+ CompletableFuture.allOf(
+ completableFutures.toArray(CompletableFuture[]::new));
+ var unused = allFutures.thenRun(() -> mPm.mHandler.post(actionsAfterDexopt));
+ } else {
+ actionsAfterDexopt.run();
}
}
@@ -2759,6 +2799,7 @@ final class InstallPackageHelper {
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
+ // run synchronous dexopt if the freeze improvement is not supported
DexOptHelper.performDexoptIfNeeded(
installRequest, mDexManager, mPm.mInstallLock.getRawLock());
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index e1fcc6650650..2d0bb258e89f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -805,22 +805,20 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- if (Flags.recoverabilityDetection()) {
- if (params.rollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH
- || params.rollbackImpactLevel
- == PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
- if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
- throw new IllegalArgumentException(
- "Can't set rollbackImpactLevel when rollback is not enabled");
- }
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Setting rollbackImpactLevel requires the MANAGE_ROLLBACKS permission");
- }
- } else if (params.rollbackImpactLevel < 0) {
- throw new IllegalArgumentException("rollbackImpactLevel can't be negative.");
+ if (params.rollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH
+ || params.rollbackImpactLevel
+ == PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
+ if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
+ throw new IllegalArgumentException(
+ "Can't set rollbackImpactLevel when rollback is not enabled");
+ }
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Setting rollbackImpactLevel requires the MANAGE_ROLLBACKS permission");
}
+ } else if (params.rollbackImpactLevel < 0) {
+ throw new IllegalArgumentException("rollbackImpactLevel can't be negative.");
}
boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index aa235c2258ac..cf598e89c988 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3561,9 +3561,6 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.setEnableRollback(true, rollbackStrategy);
break;
case "--rollback-impact-level":
- if (!Flags.recoverabilityDetection()) {
- throw new IllegalArgumentException("Unknown option " + opt);
- }
int rollbackImpactLevel = Integer.parseInt(peekNextArg());
if (rollbackImpactLevel < PackageManager.ROLLBACK_USER_IMPACT_LOW
|| rollbackImpactLevel
@@ -4775,11 +4772,9 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
pw.println(" --enable-rollback: enable rollbacks for the upgrade.");
pw.println(" 0=restore (default), 1=wipe, 2=retain");
- if (Flags.recoverabilityDetection()) {
- pw.println(
- " --rollback-impact-level: set device impact required for rollback.");
- pw.println(" 0=low (default), 1=high, 2=manual only");
- }
+ pw.println(
+ " --rollback-impact-level: set device impact required for rollback.");
+ pw.println(" 0=low (default), 1=high, 2=manual only");
pw.println(" --install-location: force the install location:");
pw.println(" 0=auto, 1=internal only, 2=prefer external");
pw.println(" --install-reason: indicates why the app is being installed:");
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index dd60a155f2fb..8510ee70cc56 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -179,8 +179,7 @@ final class VerifyingSession {
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!mOriginInfo.mExisting) {
- final boolean verifyForRollback = Flags.recoverabilityDetection()
- ? !isARollback() : true;
+ final boolean verifyForRollback = !isARollback();
if (!isApex() && !isArchivedInstallation() && verifyForRollback) {
// TODO(b/182426975): treat APEX as APK when APK verification is concerned
sendApkVerificationRequest(pkgLite);
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index ab756f2a755b..5347ca46ab31 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -29,7 +29,6 @@ import android.annotation.WorkerThread;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -965,9 +964,7 @@ class Rollback {
ipw.println("-stateDescription: " + mStateDescription);
ipw.println("-timestamp: " + getTimestamp());
ipw.println("-rollbackLifetimeMillis: " + getRollbackLifetimeMillis());
- if (Flags.recoverabilityDetection()) {
- ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel());
- }
+ ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel());
ipw.println("-isStaged: " + isStaged());
ipw.println("-originalSessionId: " + getOriginalSessionId());
ipw.println("-packages:");
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 2e6be5bb56a8..9ed52d8b3504 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1244,17 +1244,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
rollback.makeAvailable();
mPackageHealthObserver.notifyRollbackAvailable(rollback.info);
- if (Flags.recoverabilityDetection()) {
- if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
- // TODO(zezeozue): Provide API to explicitly start observing instead
- // of doing this for all rollbacks. If we do this for all rollbacks,
- // should document in PackageInstaller.SessionParams#setEnableRollback
- // After enabling and committing any rollback, observe packages and
- // prepare to rollback if packages crashes too frequently.
- mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
- }
- } else {
+ if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
+ // TODO(zezeozue): Provide API to explicitly start observing instead
+ // of doing this for all rollbacks. If we do this for all rollbacks,
+ // should document in PackageInstaller.SessionParams#setEnableRollback
+ // After enabling and committing any rollback, observe packages and
+ // prepare to rollback if packages crashes too frequently.
mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 50db1e4ac30e..6dc40323f2ee 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -197,9 +197,7 @@ class RollbackStore {
json.put("isStaged", rollback.isStaged());
json.put("causePackages", versionedPackagesToJson(rollback.getCausePackages()));
json.put("committedSessionId", rollback.getCommittedSessionId());
- if (Flags.recoverabilityDetection()) {
- json.put("rollbackImpactLevel", rollback.getRollbackImpactLevel());
- }
+ json.put("rollbackImpactLevel", rollback.getRollbackImpactLevel());
return json;
}
@@ -211,11 +209,9 @@ class RollbackStore {
versionedPackagesFromJson(json.getJSONArray("causePackages")),
json.getInt("committedSessionId"));
- if (Flags.recoverabilityDetection()) {
- // to make it backward compatible.
- rollbackInfo.setRollbackImpactLevel(json.optInt("rollbackImpactLevel",
- PackageManager.ROLLBACK_USER_IMPACT_LOW));
- }
+ // to make it backward compatible.
+ rollbackInfo.setRollbackImpactLevel(json.optInt("rollbackImpactLevel",
+ PackageManager.ROLLBACK_USER_IMPACT_LOW));
return rollbackInfo;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2bbd69c65eb8..e158310455ac 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1279,12 +1279,6 @@ public final class SystemServer implements Dumpable {
if (!Flags.refactorCrashrecovery()) {
// Initialize RescueParty.
CrashRecoveryAdaptor.rescuePartyRegisterHealthObserver(mSystemContext);
- if (!Flags.recoverabilityDetection()) {
- // Now that we have the bare essentials of the OS up and running, take
- // note that we just booted, which might send out a rescue party if
- // we're stuck in a runtime restart loop.
- CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
- }
}
@@ -1558,14 +1552,6 @@ public final class SystemServer implements Dumpable {
boolean enableVrService = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
- if (!Flags.recoverabilityDetection()) {
- // For debugging RescueParty
- if (Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.crash_system", false)) {
- throw new RuntimeException();
- }
- }
-
try {
final String SECONDARY_ZYGOTE_PRELOAD = "SecondaryZygotePreload";
// We start the preload ~1s before the webview factory preparation, to
@@ -3091,13 +3077,11 @@ public final class SystemServer implements Dumpable {
CrashRecoveryAdaptor.initializeCrashrecoveryModuleService(mSystemServiceManager);
t.traceEnd();
} else {
- if (Flags.recoverabilityDetection()) {
- // Now that we have the essential services needed for mitigations, register the boot
- // with package watchdog.
- // Note that we just booted, which might send out a rescue party if we're stuck in a
- // runtime restart loop.
- CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
- }
+ // Now that we have the essential services needed for mitigations, register the boot
+ // with package watchdog.
+ // Note that we just booted, which might send out a rescue party if we're stuck in a
+ // runtime restart loop.
+ CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
}
t.traceBegin("MakeDisplayManagerServiceReady");
@@ -3511,12 +3495,10 @@ public final class SystemServer implements Dumpable {
* are updated outside of OTA; and to avoid breaking dependencies from system into apexes.
*/
private void startApexServices(@NonNull TimingsTraceAndSlog t) {
- if (Flags.recoverabilityDetection()) {
- // For debugging RescueParty
- if (Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.crash_system", false)) {
- throw new RuntimeException();
- }
+ // For debugging RescueParty
+ if (Build.IS_DEBUGGABLE
+ && SystemProperties.getBoolean("debug.crash_system", false)) {
+ throw new RuntimeException();
}
t.traceBegin("startApexServices");
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 6c73f0a33a67..eda5e8613dba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -23,7 +23,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
@@ -34,8 +33,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import android.content.ContentResolver;
@@ -43,12 +40,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
-import android.crashrecovery.flags.Flags;
import android.os.RecoverySystem;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -59,14 +52,8 @@ import com.android.server.am.SettingsToPropertiesMapper;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -75,34 +62,19 @@ import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Test RescueParty.
*/
-@RunWith(Parameterized.class)
public class RescuePartyTest {
- @Rule
- public SetFlagsRule mSetFlagsRule;
private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
- private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
- private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
- private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
- {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
- private static final String CALLING_PACKAGE1 = "com.package.name1";
- private static final String CALLING_PACKAGE2 = "com.package.name2";
- private static final String CALLING_PACKAGE3 = "com.package.name3";
private static final String PERSISTENT_PACKAGE = "com.persistent.package";
private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
- private static final String NAMESPACE1 = "namespace1";
- private static final String NAMESPACE2 = "namespace2";
- private static final String NAMESPACE3 = "namespace3";
- private static final String NAMESPACE4 = "namespace4";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
@@ -126,21 +98,6 @@ public class RescuePartyTest {
// Mock only sysprop apis
private PackageWatchdog.BootThreshold mSpyBootThreshold;
- @Captor
- private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor;
- @Captor
- private ArgumentCaptor<List<String>> mPackageListCaptor;
-
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getFlags() {
- return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_RECOVERABILITY_DETECTION);
- }
-
- public RescuePartyTest(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(flags);
- }
-
@Before
public void setUp() throws Exception {
mSession =
@@ -419,25 +376,6 @@ public class RescuePartyTest {
}
- private void verifySettingsResets(int resetMode, String[] resetNamespaces,
- HashMap<String, Integer> configResetVerifiedTimesMap) {
- verifyOnlySettingsReset(resetMode);
- }
-
- private void verifyOnlySettingsReset(int resetMode) {
- verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
- resetMode, UserHandle.USER_SYSTEM));
- verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
- eq(resetMode), anyInt()));
- }
-
- private void verifyNoSettingsReset(int resetMode) {
- verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
- resetMode, UserHandle.USER_SYSTEM), never());
- verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
- eq(resetMode), anyInt()), never());
- }
-
private void noteBoot(int mitigationCount) {
RescuePartyObserver.getInstance(mMockContext).onExecuteBootLoopMitigation(mitigationCount);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
index 8eae9c7d71fa..e030b3f19e4f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
@@ -32,18 +32,18 @@ android_test {
static_libs: [
"androidx.test.core",
"androidx.test.runner",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
"services.core",
"truth",
- "flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
libs: [
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -55,7 +55,9 @@ android_test {
certificate: "platform",
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
+ "mts-crashrecovery",
],
+ min_sdk_version: "36",
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 5a802d9de2ff..36b064b9b090 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -30,18 +30,18 @@ android_test {
static_libs: [
"androidx.test.runner",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
"services.core",
"truth",
- "flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
libs: [
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -53,9 +53,11 @@ android_test {
certificate: "platform",
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
+ "mts-crashrecovery",
],
+ min_sdk_version: "36",
}
test_module_config {
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 347dc81c6734..fb4d81ac831c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -43,7 +43,6 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
-import android.crashrecovery.flags.Flags;
import android.os.Handler;
import android.os.MessageQueue;
import android.os.SystemProperties;
@@ -273,7 +272,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelLow_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -304,7 +302,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -335,7 +332,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelManualOnly_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -365,7 +361,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelLowAndHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -404,7 +399,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_nativeCrash_rollback()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -438,7 +432,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_rollbackFailedPackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -483,7 +476,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_rollbackAll()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -530,7 +522,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLowAndHigh_rollbackLow()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -578,7 +569,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelHigh_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId2 = 2;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -612,7 +602,6 @@ public class RollbackPackageHealthObserverTest {
*/
@Test
public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -637,7 +626,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -662,7 +650,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -692,7 +679,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelManualOnly_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -720,7 +706,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelLowAndHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -757,7 +742,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelLow_rollbackAll()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -802,7 +786,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -847,7 +830,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId2 = 2;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -882,7 +864,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLowAndManual_rollbackLowImpactOnly()
throws PackageManager.NameNotFoundException, InterruptedException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -928,7 +909,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelManual_rollbackLowImpactOnly()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -962,7 +942,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -1008,7 +987,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
int rollbackId1 = 1;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 8be74eaccd20..44e545bac0ce 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -26,12 +26,12 @@ android_test {
name: "PackageWatchdogTest",
srcs: ["src/**/*.java"],
static_libs: [
- "junit",
- "mockito-target-extended-minus-junit4",
+ "PlatformProperties",
+ "androidx.test.rules",
"flag-junit",
"frameworks-base-testutils",
- "androidx.test.rules",
- "PlatformProperties",
+ "junit",
+ "mockito-target-extended-minus-junit4",
"services.core",
"services.net",
"truth",
@@ -49,5 +49,9 @@ android_test {
"libstaticjvmtiagent",
],
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "mts-crashrecovery",
+ ],
+ min_sdk_version: "36",
}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 4c16a756fbdc..c2ab0550ea05 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -133,7 +133,6 @@ public class CrashRecoveryTest {
@Before
public void setUp() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
MockitoAnnotations.initMocks(this);
new File(InstrumentationRegistry.getContext().getFilesDir(),
"package-watchdog.xml").delete();
@@ -766,8 +765,6 @@ public class CrashRecoveryTest {
watchdog.notifyPackageFailure(packages, failureReason);
}
mTestLooper.dispatchAll();
- if (Flags.recoverabilityDetection()) {
- moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
- }
+ moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
}
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 1c50cb1b55fd..b8274841f65b 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -150,7 +150,6 @@ public class PackageWatchdogTest {
@Before
public void setUp() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
MockitoAnnotations.initMocks(this);
new File(InstrumentationRegistry.getContext().getFilesDir(),
"package-watchdog.xml").delete();
@@ -480,60 +479,6 @@ public class PackageWatchdogTest {
assertThat(observer.mHealthCheckFailedPackages).isEmpty();
}
-
- /**
- * Test package failure and notifies only least impact observers.
- */
- @Test
- public void testPackageFailureNotifyAllDifferentImpacts() throws Exception {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver observerNone = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
- TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- TestObserver observerMid = new TestObserver(OBSERVER_NAME_3,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- TestObserver observerLow = new TestObserver(OBSERVER_NAME_4,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
-
- // Start observing for all impact observers
- watchdog.registerHealthObserver(mTestExecutor, observerNone);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C, APP_D),
- SHORT_DURATION, observerNone);
- watchdog.registerHealthObserver(mTestExecutor, observerHigh);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B, APP_C), SHORT_DURATION,
- observerHigh);
- watchdog.registerHealthObserver(mTestExecutor, observerMid);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A, APP_B), SHORT_DURATION,
- observerMid);
- watchdog.registerHealthObserver(mTestExecutor, observerLow);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observerLow);
-
- // Then fail all apps above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
- new VersionedPackage(APP_B, VERSION_CODE),
- new VersionedPackage(APP_C, VERSION_CODE),
- new VersionedPackage(APP_D, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify least impact observers are notifed of package failures
- List<String> observerNonePackages = observerNone.mMitigatedPackages;
- List<String> observerHighPackages = observerHigh.mMitigatedPackages;
- List<String> observerMidPackages = observerMid.mMitigatedPackages;
- List<String> observerLowPackages = observerLow.mMitigatedPackages;
-
- // APP_D failure observed by only observerNone is not caught cos its impact is none
- assertThat(observerNonePackages).isEmpty();
- // APP_C failure is caught by observerHigh cos it's the lowest impact observer
- assertThat(observerHighPackages).containsExactly(APP_C);
- // APP_B failure is caught by observerMid cos it's the lowest impact observer
- assertThat(observerMidPackages).containsExactly(APP_B);
- // APP_A failure is caught by observerLow cos it's the lowest impact observer
- assertThat(observerLowPackages).containsExactly(APP_A);
- }
-
@Test
public void testPackageFailureNotifyAllDifferentImpactsRecoverability() throws Exception {
PackageWatchdog watchdog = createWatchdog();
@@ -583,84 +528,6 @@ public class PackageWatchdogTest {
assertThat(observerLowPackages).containsExactly(APP_A);
}
- /**
- * Test package failure and least impact observers are notified successively.
- * State transistions:
- *
- * <ul>
- * <li>(observer1:low, observer2:mid) -> {observer1}
- * <li>(observer1:high, observer2:mid) -> {observer2}
- * <li>(observer1:high, observer2:none) -> {observer1}
- * <li>(observer1:none, observer2:none) -> {}
- * <ul>
- */
- @Test
- public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
- TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
-
- // Start observing for observerFirst and observerSecond with failure handling
- watchdog.registerHealthObserver(mTestExecutor, observerFirst);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerFirst);
- watchdog.registerHealthObserver(mTestExecutor, observerSecond);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), LONG_DURATION, observerSecond);
-
- // Then fail APP_A above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify only observerFirst is notifed
- assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
- assertThat(observerSecond.mMitigatedPackages).isEmpty();
-
- // After observerFirst handles failure, next action it has is high impact
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
- observerFirst.mMitigatedPackages.clear();
- observerSecond.mMitigatedPackages.clear();
-
- // Then fail APP_A again above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify only observerSecond is notifed cos it has least impact
- assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A);
- assertThat(observerFirst.mMitigatedPackages).isEmpty();
-
- // After observerSecond handles failure, it has no further actions
- observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
- observerFirst.mMitigatedPackages.clear();
- observerSecond.mMitigatedPackages.clear();
-
- // Then fail APP_A again above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify only observerFirst is notifed cos it has the only action
- assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
- assertThat(observerSecond.mMitigatedPackages).isEmpty();
-
- // After observerFirst handles failure, it too has no further actions
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
- observerFirst.mMitigatedPackages.clear();
- observerSecond.mMitigatedPackages.clear();
-
- // Then fail APP_A again above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify no observer is notified cos no actions left
- assertThat(observerFirst.mMitigatedPackages).isEmpty();
- assertThat(observerSecond.mMitigatedPackages).isEmpty();
- }
-
@Test
public void testPackageFailureNotifyLeastImpactSuccessivelyRecoverability() throws Exception {
PackageWatchdog watchdog = createWatchdog();
@@ -727,34 +594,6 @@ public class PackageWatchdogTest {
assertThat(observerSecond.mMitigatedPackages).isEmpty();
}
- /**
- * Test package failure and notifies only one observer even with observer impact tie.
- */
- @Test
- public void testPackageFailureNotifyOneSameImpact() throws Exception {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
-
- // Start observing for observer1 and observer2 with failure handling
- watchdog.registerHealthObserver(mTestExecutor, observer2);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer2);
- watchdog.registerHealthObserver(mTestExecutor, observer1);
- watchdog.startExplicitHealthCheck(Arrays.asList(APP_A), SHORT_DURATION, observer1);
-
- // Then fail APP_A above the threshold
- raiseFatalFailureAndDispatch(watchdog,
- Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
- PackageWatchdog.FAILURE_REASON_UNKNOWN);
-
- // Verify only one observer is notifed
- assertThat(observer1.mMitigatedPackages).containsExactly(APP_A);
- assertThat(observer2.mMitigatedPackages).isEmpty();
- }
-
@Test
public void testPackageFailureNotifyOneSameImpactRecoverabilityDetection() throws Exception {
PackageWatchdog watchdog = createWatchdog();
@@ -1015,27 +854,6 @@ public class PackageWatchdogTest {
@Test
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_CRASHRECOVERY)
- public void testNetworkStackFailure() {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- final PackageWatchdog wd = createWatchdog();
-
- // Start observing with failure handling
- TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- wd.startExplicitHealthCheck(Collections.singletonList(APP_A), SHORT_DURATION, observer);
-
- // Notify of NetworkStack failure
- mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
-
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
-
- // Verify the NetworkStack observer is notified
- assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_CRASHRECOVERY)
public void testNetworkStackFailureRecoverabilityDetection() {
final PackageWatchdog wd = createWatchdog();
@@ -1270,21 +1088,6 @@ public class PackageWatchdogTest {
assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
}
-
- /** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
- @Test
- public void testBootLoopDetection_meetsThreshold() {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(mTestExecutor, bootObserver);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
- watchdog.noteBoot();
- }
- mTestLooper.dispatchAll();
- assertThat(bootObserver.mitigatedBootLoop()).isTrue();
- }
-
@Test
public void testBootLoopDetection_meetsThresholdRecoverability() {
PackageWatchdog watchdog = createWatchdog();
@@ -1330,27 +1133,6 @@ public class PackageWatchdogTest {
assertThat(bootObserver.mitigatedBootLoop()).isFalse();
}
- /**
- * Ensure that boot loop mitigation is done for the observer with the lowest user impact
- */
- @Test
- public void testBootLoopMitigationDoneForLowestUserImpact() {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
- bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
- TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
- bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
- watchdog.registerHealthObserver(mTestExecutor, bootObserver1);
- watchdog.registerHealthObserver(mTestExecutor, bootObserver2);
- for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
- watchdog.noteBoot();
- }
- mTestLooper.dispatchAll();
- assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
- assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
- }
-
@Test
public void testBootLoopMitigationDoneForLowestUserImpactRecoverability() {
PackageWatchdog watchdog = createWatchdog();
@@ -1368,32 +1150,6 @@ public class PackageWatchdogTest {
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
- /**
- * Ensure that the correct mitigation counts are sent to the boot loop observer.
- */
- @Test
- public void testMultipleBootLoopMitigation() {
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- PackageWatchdog watchdog = createWatchdog();
- TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
- watchdog.registerHealthObserver(mTestExecutor, bootObserver);
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) {
- watchdog.noteBoot();
- }
- }
-
- moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) {
- watchdog.noteBoot();
- }
- }
- mTestLooper.dispatchAll();
- assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4));
- }
-
@Test
public void testMultipleBootLoopMitigationRecoverabilityLowImpact() {
PackageWatchdog watchdog = createWatchdog();
@@ -1800,9 +1556,7 @@ public class PackageWatchdogTest {
watchdog.notifyPackageFailure(packages, failureReason);
}
mTestLooper.dispatchAll();
- if (Flags.recoverabilityDetection()) {
- moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
- }
+ moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
}
private PackageWatchdog createWatchdog() {