Merge "Rename InputMethodManagerServiceTestBase#mCallingUserId to mUserId" into main
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 000a537..623196b 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -209,7 +209,8 @@
POWER_COMPONENT_VIDEO,
POWER_COMPONENT_FLASHLIGHT,
POWER_COMPONENT_CAMERA,
- POWER_COMPONENT_GNSS};
+ POWER_COMPONENT_GNSS,
+ POWER_COMPONENT_SENSORS};
Arrays.sort(supportedPowerComponents);
SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = IntArray.wrap(supportedPowerComponents);
};
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cdffea4..c7751e3 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3077,7 +3077,7 @@
public static final String[] HISTORY_EVENT_NAMES = new String[] {
"null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
"active", "pkginst", "pkgunin", "alarm", "stats", "pkginactive", "pkgactive",
- "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity", "state"
+ "tmpwhitelist", "screenwake", "wakeupap", "longwake", "state"
};
public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index d454716..74545a8 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1346,7 +1346,11 @@
Slog.w(mTag, "WakeUp was called before the dream was attached.");
} else {
try {
- mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
+ if (startAndStopDozingInBackground()) {
+ mDreamManager.finishSelfOneway(mDreamToken, false /*immediate*/);
+ } else {
+ mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
+ }
} catch (RemoteException ex) {
// system server died
}
@@ -1497,7 +1501,11 @@
if (mFinished || mWaking) {
Slog.w(mTag, "attach() called after dream already finished");
try {
- mDreamManager.finishSelf(dreamToken, true /*immediate*/);
+ if (startAndStopDozingInBackground()) {
+ mDreamManager.finishSelfOneway(dreamToken, true /*immediate*/);
+ } else {
+ mDreamManager.finishSelf(dreamToken, true /*immediate*/);
+ }
} catch (RemoteException ex) {
// system server died
}
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 0861454..8860452 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -90,6 +90,16 @@
}
flag {
+ name: "update_corner_radius_on_display_changed"
+ namespace: "accessibility"
+ description: "Updates the corner radius to the magnification fullscreen border when the display changes."
+ bug: "335113174"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "hearing_devices_dialog_related_tools"
namespace: "accessibility"
description: "Shows the related tools for hearing devices dialog."
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 3c0ac9a..394f8dd 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -30,6 +30,8 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.GradientDrawable;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.util.Log;
import android.view.AttachedSurfaceControl;
@@ -49,6 +51,8 @@
import androidx.annotation.UiThread;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
import com.android.systemui.util.leak.RotationUtils;
@@ -70,6 +74,7 @@
private SurfaceControl.Transaction mTransaction;
private View mFullscreenBorder = null;
private int mBorderOffset;
+ private int mBorderStoke;
private final int mDisplayId;
private static final Region sEmptyRegion = new Region();
private ValueAnimator mShowHideBorderAnimator;
@@ -86,16 +91,20 @@
}
};
private final long mLongAnimationTimeMs;
+ private final DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener;
+ private String mCurrentDisplayUniqueId;
public FullscreenMagnificationController(
@UiContext Context context,
@Main Handler handler,
@Main Executor executor,
+ DisplayManager displayManager,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier) {
- this(context, handler, executor, accessibilityManager,
+ this(context, handler, executor, displayManager, accessibilityManager,
windowManager, iWindowManager, scvhSupplier,
new SurfaceControl.Transaction(), null);
}
@@ -105,6 +114,7 @@
@UiContext Context context,
@Main Handler handler,
@Main Executor executor,
+ DisplayManager displayManager,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
IWindowManager iWindowManager,
@@ -120,10 +130,7 @@
mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
mTransaction = transaction;
mScvhSupplier = scvhSupplier;
- mBorderOffset = mContext.getResources().getDimensionPixelSize(
- R.dimen.magnifier_border_width_fullscreen_with_offset)
- - mContext.getResources().getDimensionPixelSize(
- R.dimen.magnifier_border_width_fullscreen);
+ updateDimensions();
mDisplayId = mContext.getDisplayId();
mConfiguration = new Configuration(context.getResources().getConfiguration());
mLongAnimationTimeMs = mContext.getResources().getInteger(
@@ -140,6 +147,31 @@
}
}
});
+ mCurrentDisplayUniqueId = mContext.getDisplayNoVerify().getUniqueId();
+ mDisplayManager = displayManager;
+ mDisplayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ // Do nothing
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // Do nothing
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ final String uniqueId = mContext.getDisplayNoVerify().getUniqueId();
+ if (uniqueId.equals(mCurrentDisplayUniqueId)) {
+ // Same unique ID means the physical display doesn't change. Early return.
+ return;
+ }
+
+ mCurrentDisplayUniqueId = uniqueId;
+ applyCornerRadiusToBorder();
+ }
+ };
}
private ValueAnimator createNullTargetObjectAnimator() {
@@ -180,10 +212,15 @@
}
mContext.unregisterComponentCallbacks(this);
+
mShowHideBorderAnimator.reverse();
}
private void cleanUpBorder() {
+ if (Flags.updateCornerRadiusOnDisplayChanged()) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
if (mSurfaceControlViewHost != null) {
mSurfaceControlViewHost.release();
mSurfaceControlViewHost = null;
@@ -226,6 +263,9 @@
} catch (Exception e) {
Log.w(TAG, "Failed to register rotation watcher", e);
}
+ if (Flags.updateCornerRadiusOnDisplayChanged()) {
+ mHandler.post(this::applyCornerRadiusToBorder);
+ }
}
mTransaction
@@ -247,6 +287,9 @@
mAccessibilityManager.attachAccessibilityOverlayToDisplay(
mDisplayId, mBorderSurfaceControl);
+ if (Flags.updateCornerRadiusOnDisplayChanged()) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ }
applyTouchableRegion();
}
@@ -304,6 +347,11 @@
final int newWidth = mWindowBounds.width() + 2 * mBorderOffset;
final int newHeight = mWindowBounds.height() + 2 * mBorderOffset;
mSurfaceControlViewHost.relayout(newWidth, newHeight);
+ if (Flags.updateCornerRadiusOnDisplayChanged()) {
+ // Recenter the border
+ mTransaction.setPosition(
+ mBorderSurfaceControl, -mBorderOffset, -mBorderOffset).apply();
+ }
}
// Rotating from Landscape to ReverseLandscape will not trigger the config changes in
@@ -352,6 +400,22 @@
R.dimen.magnifier_border_width_fullscreen_with_offset)
- mContext.getResources().getDimensionPixelSize(
R.dimen.magnifier_border_width_fullscreen);
+ mBorderStoke = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnifier_border_width_fullscreen_with_offset);
+ }
+
+ private void applyCornerRadiusToBorder() {
+ if (!isActivated()) {
+ return;
+ }
+
+ float cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+ GradientDrawable backgroundDrawable = (GradientDrawable) mFullscreenBorder.getBackground();
+ backgroundDrawable.setStroke(
+ mBorderStoke,
+ mContext.getResources().getColor(
+ R.color.magnification_border_color, mContext.getTheme()));
+ backgroundDrawable.setCornerRadius(cornerRadius);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index e9c9bc7..93c4630 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -149,6 +149,7 @@
private final Context mContext;
private final Handler mHandler;
private final Executor mExecutor;
+ private final DisplayManager mDisplayManager;
private final IWindowManager mIWindowManager;
FullscreenMagnificationControllerSupplier(Context context,
@@ -159,6 +160,7 @@
mContext = context;
mHandler = handler;
mExecutor = executor;
+ mDisplayManager = displayManager;
mIWindowManager = iWindowManager;
}
@@ -173,6 +175,7 @@
windowContext,
mHandler,
mExecutor,
+ mDisplayManager,
windowContext.getSystemService(AccessibilityManager.class),
windowContext.getSystemService(WindowManager.class),
mIWindowManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index cbd535b..530ae15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,7 +37,10 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
@@ -54,6 +58,7 @@
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
@@ -61,6 +66,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -76,6 +82,12 @@
private static final long WAIT_TIMEOUT_S = 5L * HW_TIMEOUT_MULTIPLIER;
private static final long ANIMATION_TIMEOUT_MS =
5L * ANIMATION_DURATION_MS * HW_TIMEOUT_MULTIPLIER;
+
+ private static final String UNIQUE_DISPLAY_ID_PRIMARY = "000";
+ private static final String UNIQUE_DISPLAY_ID_SECONDARY = "111";
+ private static final int CORNER_RADIUS_PRIMARY = 10;
+ private static final int CORNER_RADIUS_SECONDARY = 20;
+
private FullscreenMagnificationController mFullscreenMagnificationController;
private SurfaceControlViewHost mSurfaceControlViewHost;
private ValueAnimator mShowHideBorderAnimator;
@@ -83,10 +95,35 @@
private TestableWindowManager mWindowManager;
@Mock
private IWindowManager mIWindowManager;
+ @Mock
+ private DisplayManager mDisplayManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = spy(mContext);
+ Display display = mock(Display.class);
+ when(display.getUniqueId()).thenReturn(UNIQUE_DISPLAY_ID_PRIMARY);
+ when(mContext.getDisplayNoVerify()).thenReturn(display);
+
+ // Override the resources to Display Primary
+ mContext.getOrCreateTestableResources()
+ .addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius,
+ CORNER_RADIUS_PRIMARY);
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.dimen.rounded_corner_radius_adjustment, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top_adjustment, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom_adjustment, 0);
+
getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(),
new InputTransferToken(), "FullscreenMagnification")));
@@ -101,6 +138,7 @@
mContext,
mContext.getMainThreadHandler(),
mContext.getMainExecutor(),
+ mDisplayManager,
mContext.getSystemService(AccessibilityManager.class),
mContext.getSystemService(WindowManager.class),
mIWindowManager,
@@ -259,6 +297,87 @@
verify(mSurfaceControlViewHost).relayout(newWidth, newHeight);
}
+ @EnableFlags(Flags.FLAG_UPDATE_CORNER_RADIUS_ON_DISPLAY_CHANGED)
+ @Test
+ public void enableFullscreenMagnification_applyPrimaryCornerRadius()
+ throws InterruptedException {
+ CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
+ CountDownLatch animationEndLatch = new CountDownLatch(1);
+ mTransaction.addTransactionCommittedListener(
+ Runnable::run, transactionCommittedLatch::countDown);
+ mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animationEndLatch.countDown();
+ }
+ });
+
+ getInstrumentation().runOnMainSync(() ->
+ //Enable fullscreen magnification
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(true));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+
+ // Verify the initial corner radius is applied
+ GradientDrawable backgroundDrawable =
+ (GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
+ assertThat(backgroundDrawable.getCornerRadius()).isEqualTo(CORNER_RADIUS_PRIMARY);
+ }
+
+ @EnableFlags(Flags.FLAG_UPDATE_CORNER_RADIUS_ON_DISPLAY_CHANGED)
+ @Test
+ public void onDisplayChanged_updateCornerRadiusToSecondary() throws InterruptedException {
+ CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
+ CountDownLatch animationEndLatch = new CountDownLatch(1);
+ mTransaction.addTransactionCommittedListener(
+ Runnable::run, transactionCommittedLatch::countDown);
+ mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animationEndLatch.countDown();
+ }
+ });
+
+ getInstrumentation().runOnMainSync(() ->
+ //Enable fullscreen magnification
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(true));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
+
+ ArgumentCaptor<DisplayManager.DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayManager.DisplayListener.class);
+ verify(mDisplayManager).registerDisplayListener(displayListenerCaptor.capture(), any());
+
+ Display newDisplay = mock(Display.class);
+ when(newDisplay.getUniqueId()).thenReturn(UNIQUE_DISPLAY_ID_SECONDARY);
+ when(mContext.getDisplayNoVerify()).thenReturn(newDisplay);
+ // Override the resources to Display Secondary
+ mContext.getOrCreateTestableResources()
+ .removeOverride(com.android.internal.R.dimen.rounded_corner_radius);
+ mContext.getOrCreateTestableResources()
+ .addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius,
+ CORNER_RADIUS_SECONDARY);
+ getInstrumentation().runOnMainSync(() ->
+ displayListenerCaptor.getValue().onDisplayChanged(Display.DEFAULT_DISPLAY));
+ waitForIdleSync();
+ // Verify the corner radius is updated
+ GradientDrawable backgroundDrawable2 =
+ (GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
+ assertThat(backgroundDrawable2.getCornerRadius()).isEqualTo(CORNER_RADIUS_SECONDARY);
+ }
+
+
private ValueAnimator newNullTargetObjectAnimator() {
final ValueAnimator animator =
ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e46ab8f..03fbfd37 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -144,6 +144,7 @@
import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.ScreenPowerStatsProcessor;
+import com.android.server.power.stats.SensorPowerStatsProcessor;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.VideoPowerStatsProcessor;
import com.android.server.power.stats.WifiPowerStatsProcessor;
@@ -595,6 +596,17 @@
.setProcessor(
new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(new SensorPowerStatsProcessor(
+ () -> mContext.getSystemService(SensorManager.class)));
+
config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
.trackDeviceStates(
AggregatedPowerStatsConfig.STATE_POWER,
@@ -706,6 +718,10 @@
BatteryConsumer.POWER_COMPONENT_GNSS,
Flags.streamlinedMiscBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_SENSORS,
+ Flags.streamlinedMiscBatteryStats());
+
mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
Flags.streamlinedMiscBatteryStats());
mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a1dffc6..9757582 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1452,6 +1452,13 @@
}
if (!ArrayUtils.isEmpty(after.splitNames)) {
+ if (beforeSplitNames.length != beforeSplitRevisionCodes.length) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Current split names and the split revision codes are not 1:1 mapping."
+ + "This indicates that the package info data has been"
+ + " corrupted.");
+ }
+
for (int i = 0; i < after.splitNames.length; i++) {
final String splitName = after.splitNames[i];
final int j = ArrayUtils.indexOf(beforeSplitNames, splitName);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9177e2b..b7dfd8d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4489,10 +4489,24 @@
String splitName = parser.getAttributeValue(null, ATTR_NAME);
int splitRevision = parser.getAttributeInt(null, ATTR_VERSION, -1);
if (splitName != null && splitRevision >= 0) {
+ final int beforeSplitNamesLength = outPs.getSplitNames().length;
+ // If the split name already exists in the outPs#getSplitNames, don't add it
+ // into the array and update its revision code below
outPs.setSplitNames(ArrayUtils.appendElement(String.class,
outPs.getSplitNames(), splitName));
- outPs.setSplitRevisionCodes(ArrayUtils.appendInt(
- outPs.getSplitRevisionCodes(), splitRevision));
+
+ // If the same split name has already been added before, update the latest
+ // revision code
+ final int afterSplitNamesLength = outPs.getSplitNames().length;
+ if (beforeSplitNamesLength == afterSplitNamesLength) {
+ final int index = ArrayUtils.indexOf(outPs.getSplitNames(), splitName);
+ final int[] splitRevisionCodes = outPs.getSplitRevisionCodes();
+ splitRevisionCodes[index] = splitRevision;
+ outPs.setSplitRevisionCodes(splitRevisionCodes);
+ } else {
+ outPs.setSplitRevisionCodes(ArrayUtils.appendInt(
+ outPs.getSplitRevisionCodes(), splitRevision, /* allowDuplicates= */ true));
+ }
}
XmlUtils.skipCurrentTag(parser);
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index c4b37c69..143b3ff 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -5431,8 +5431,6 @@
}
}
- int mSensorNesting;
-
@GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor) {
noteStartSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
@@ -5441,11 +5439,8 @@
@GuardedBy("this")
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
- if (mSensorNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_SENSOR_ON_FLAG);
- }
- mSensorNesting++;
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_SENSOR_ON_FLAG, uid, "sensor:0x" + Integer.toHexString(sensor));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStartSensor(sensor, elapsedRealtimeMs);
}
@@ -5458,11 +5453,8 @@
@GuardedBy("this")
public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
- mSensorNesting--;
- if (mSensorNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_SENSOR_ON_FLAG);
- }
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_SENSOR_ON_FLAG, uid, "sensor:0x" + Integer.toHexString(sensor));
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopSensor(sensor, elapsedRealtimeMs);
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index a5e4cf5..b308f38 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -93,8 +93,10 @@
if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
}
- mPowerCalculators.add(new SensorPowerCalculator(
- mContext.getSystemService(SensorManager.class)));
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SENSORS)) {
+ mPowerCalculators.add(new SensorPowerCalculator(
+ mContext.getSystemService(SensorManager.class)));
+ }
if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 7d7b3c2..c81c7ff 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -257,8 +257,8 @@
for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
deviceStateEstimations.get(i).intermediates = null;
}
- for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
- deviceStateEstimations.get(i).intermediates = null;
+ for (int i = combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ combinedDeviceStateEstimations.get(i).intermediates = null;
}
for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
index e203e4a..908c751 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
@@ -119,6 +119,7 @@
if (!uids.isEmpty()) {
computeUidPowerEstimates(stats, uids);
}
+ mPlan.resetIntermediates();
}
private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
new file mode 100644
index 0000000..e66cd39
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+public class SensorPowerStatsLayout extends PowerStatsLayout {
+ private static final String TAG = "SensorPowerStatsLayout";
+ private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
+ private static final String EXTRA_UID_SENSOR_POSITIONS = "usp";
+
+ private final SparseIntArray mSensorPositions = new SparseIntArray();
+
+ void addUidSensorSection(int handle, String label) {
+ mSensorPositions.put(handle, addUidSection(1, label, FLAG_OPTIONAL));
+ }
+
+ /**
+ * Returns the position in the uid stats array of the duration element corresponding
+ * to the specified sensor identified by its handle.
+ */
+ public int getUidSensorDurationPosition(int handle) {
+ return mSensorPositions.get(handle, UNSUPPORTED);
+ }
+
+ /**
+ * Adds the specified duration to the accumulated timer for the specified sensor.
+ */
+ public void addUidSensorDuration(long[] stats, int handle, long durationMs) {
+ int position = mSensorPositions.get(handle, UNSUPPORTED);
+ if (position == UNSUPPORTED) {
+ Slog.e(TAG, "Unknown sensor: " + handle);
+ return;
+ }
+ stats[position] += durationMs;
+ }
+
+ @Override
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+
+ int[] handlers = new int[mSensorPositions.size()];
+ int[] uidDurationPositions = new int[mSensorPositions.size()];
+
+ for (int i = 0; i < mSensorPositions.size(); i++) {
+ handlers[i] = mSensorPositions.keyAt(i);
+ uidDurationPositions[i] = mSensorPositions.valueAt(i);
+ }
+
+ extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
+ extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
+ }
+
+ @Override
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+
+ int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
+ int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
+
+ for (int i = 0; i < handlers.length; i++) {
+ mSensorPositions.put(handlers[i], uidDurationPositions[i]);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
new file mode 100644
index 0000000..5bd3288
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class SensorPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "SensorPowerStatsProcessor";
+ private static final String ANDROID_SENSOR_TYPE_PREFIX = "android.sensor.";
+
+ private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60;
+ private static final String SENSOR_EVENT_TAG_PREFIX = "sensor:0x";
+ private final Supplier<SensorManager> mSensorManagerSupplier;
+
+ private static final long INITIAL_TIMESTAMP = -1;
+ private SensorManager mSensorManager;
+ private SensorPowerStatsLayout mStatsLayout;
+ private PowerStats mPowerStats;
+ private boolean mIsInitialized;
+ private PowerStats.Descriptor mDescriptor;
+ private long mLastUpdateTimestamp;
+ private PowerEstimationPlan mPlan;
+
+ private static class SensorState {
+ public int sensorHandle;
+ public boolean stateOn;
+ public int uid;
+ public long startTime = INITIAL_TIMESTAMP;
+ }
+
+ private static class Intermediates {
+ public double power;
+ }
+
+ private final SparseArray<SensorState> mSensorStates = new SparseArray<>();
+ private long[] mTmpDeviceStatsArray;
+ private long[] mTmpUidStatsArray;
+
+ public SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
+ mSensorManagerSupplier = sensorManagerSupplier;
+ }
+
+ private boolean ensureInitialized() {
+ if (mIsInitialized) {
+ return true;
+ }
+
+ mSensorManager = mSensorManagerSupplier.get();
+ if (mSensorManager == null) {
+ return false;
+ }
+
+ mStatsLayout = new SensorPowerStatsLayout();
+ List<Sensor> sensorList = new ArrayList<>(mSensorManager.getSensorList(Sensor.TYPE_ALL));
+ sensorList.sort(Comparator.comparingInt(Sensor::getId));
+ for (int i = 0; i < sensorList.size(); i++) {
+ Sensor sensor = sensorList.get(i);
+ String label = makeLabel(sensor, sensorList);
+ mStatsLayout.addUidSensorSection(sensor.getHandle(), label);
+ }
+ mStatsLayout.addUidSectionPowerEstimate();
+ mStatsLayout.addDeviceSectionPowerEstimate();
+
+ PersistableBundle extras = new PersistableBundle();
+ mStatsLayout.toExtras(extras);
+ mDescriptor = new PowerStats.Descriptor(
+ BatteryConsumer.POWER_COMPONENT_SENSORS, mStatsLayout.getDeviceStatsArrayLength(),
+ null, 0, mStatsLayout.getUidStatsArrayLength(),
+ extras);
+
+ mPowerStats = new PowerStats(mDescriptor);
+ mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
+ mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
+
+ mIsInitialized = true;
+ return true;
+ }
+
+ private String makeLabel(Sensor sensor, List<Sensor> sensorList) {
+ int type = sensor.getType();
+ String label = sensor.getStringType();
+
+ boolean isSingleton = true;
+ for (int i = sensorList.size() - 1; i >= 0; i--) {
+ Sensor s = sensorList.get(i);
+ if (s == sensor) {
+ continue;
+ }
+ if (s.getType() == type) {
+ isSingleton = false;
+ break;
+ }
+ }
+ if (!isSingleton) {
+ StringBuilder sb = new StringBuilder(label).append('.');
+ if (sensor.getId() > 0) { // 0 and -1 are reserved
+ sb.append(sensor.getId());
+ } else {
+ sb.append(sensor.getName());
+ }
+ label = sb.toString();
+ }
+ if (label.startsWith(ANDROID_SENSOR_TYPE_PREFIX)) {
+ label = label.substring(ANDROID_SENSOR_TYPE_PREFIX.length());
+ }
+ return label.replace(' ', '_');
+ }
+
+ @Override
+ void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ if (!ensureInitialized()) {
+ return;
+ }
+
+ // Establish a baseline at the beginning of an accumulation pass
+ mLastUpdateTimestamp = timestampMs;
+ flushPowerStats(stats, timestampMs);
+ }
+
+ @Override
+ void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
+ if (!mIsInitialized) {
+ return;
+ }
+
+ if (item.eventTag == null || item.eventTag.string == null
+ || !item.eventTag.string.startsWith(SENSOR_EVENT_TAG_PREFIX)) {
+ return;
+ }
+
+ int sensorHandle;
+ try {
+ sensorHandle = Integer.parseInt(item.eventTag.string, SENSOR_EVENT_TAG_PREFIX.length(),
+ item.eventTag.string.length(), 16);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Bad format of event tag: " + item.eventTag.string);
+ return;
+ }
+
+ SensorState sensor = mSensorStates.get(sensorHandle);
+ if (sensor == null) {
+ sensor = new SensorState();
+ sensor.sensorHandle = sensorHandle;
+ mSensorStates.put(sensorHandle, sensor);
+ }
+
+ int uid = item.eventTag.uid;
+ boolean sensorOn = (item.states & BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG) != 0;
+ if (sensorOn) {
+ if (!sensor.stateOn) {
+ sensor.stateOn = true;
+ sensor.uid = uid;
+ sensor.startTime = item.time;
+ } else if (sensor.uid != uid) {
+ recordUsageDuration(sensor, item.time);
+ sensor.uid = uid;
+ }
+ } else {
+ if (sensor.stateOn) {
+ recordUsageDuration(sensor, item.time);
+ sensor.stateOn = false;
+ }
+ }
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ if (!mIsInitialized) {
+ return;
+ }
+
+ for (int i = mSensorStates.size() - 1; i >= 0; i--) {
+ SensorState sensor = mSensorStates.valueAt(i);
+ if (sensor.stateOn) {
+ recordUsageDuration(sensor, timestampMs);
+ }
+ }
+ flushPowerStats(stats, timestampMs);
+
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ }
+
+ List<Integer> uids = new ArrayList<>();
+ stats.collectUids(uids);
+
+ computeUidPowerEstimates(stats, uids);
+ computeDevicePowerEstimates(stats);
+
+ mPlan.resetIntermediates();
+ }
+
+ protected void recordUsageDuration(SensorState sensorState, long time) {
+ long durationMs = Math.max(0, time - sensorState.startTime);
+ if (durationMs > 0) {
+ long[] uidStats = mPowerStats.uidStats.get(sensorState.uid);
+ if (uidStats == null) {
+ uidStats = new long[mDescriptor.uidStatsArrayLength];
+ mPowerStats.uidStats.put(sensorState.uid, uidStats);
+ }
+ mStatsLayout.addUidSensorDuration(uidStats, sensorState.sensorHandle, durationMs);
+ }
+ sensorState.startTime = time;
+ }
+
+ private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
+ mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+ stats.addPowerStats(mPowerStats, timestamp);
+
+ Arrays.fill(mPowerStats.stats, 0);
+ mPowerStats.uidStats.clear();
+ mLastUpdateTimestamp = timestamp;
+ }
+
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+ List<Integer> uids) {
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ int[] uidSensorDurationPositions = new int[sensorList.size()];
+ double[] sensorPower = new double[sensorList.size()];
+ for (int i = sensorList.size() - 1; i >= 0; i--) {
+ Sensor sensor = sensorList.get(i);
+ uidSensorDurationPositions[i] =
+ mStatsLayout.getUidSensorDurationPosition(sensor.getHandle());
+ sensorPower[i] = sensor.getPower() / MILLIS_IN_HOUR;
+ }
+
+ for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+ UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+ List<UidStateProportionalEstimate> proportionalEstimates =
+ uidStateEstimate.proportionalEstimates;
+ for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+ UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+ for (int k = uids.size() - 1; k >= 0; k--) {
+ int uid = uids.get(k);
+ if (!stats.getUidStats(mTmpUidStatsArray, uid,
+ proportionalEstimate.stateValues)) {
+ continue;
+ }
+ double power = 0;
+ for (int m = 0; m < uidSensorDurationPositions.length; m++) {
+ int position = uidSensorDurationPositions[m];
+ if (position == PowerStatsLayout.UNSUPPORTED
+ || mTmpUidStatsArray[position] == 0) {
+ continue;
+ }
+ power += sensorPower[m] * mTmpUidStatsArray[position];
+ }
+ if (power == 0) {
+ continue;
+ }
+
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+ Intermediates intermediates = (Intermediates) uidStateEstimate
+ .combinedDeviceStateEstimate.intermediates;
+ if (intermediates == null) {
+ intermediates = new Intermediates();
+ uidStateEstimate.combinedDeviceStateEstimate.intermediates = intermediates;
+ }
+ intermediates.power += power;
+ }
+ }
+ }
+ }
+
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+ for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ CombinedDeviceStateEstimate estimation =
+ mPlan.combinedDeviceStateEstimations.get(i);
+ if (estimation.intermediates == null) {
+ continue;
+ }
+
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ continue;
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ ((Intermediates) estimation.intermediates).power);
+ stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 444097a..291eab1 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -325,11 +325,6 @@
: mAppCompatConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode);
}
- float getFixedOrientationLetterboxAspectRatio(@NonNull Configuration parentConfiguration) {
- return mActivityRecord.mAppCompatController.getAppCompatAspectRatioOverrides()
- .getFixedOrientationLetterboxAspectRatio(parentConfiguration);
- }
-
boolean isLetterboxEducationEnabled() {
return mAppCompatConfiguration.getIsEducationEnabled();
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java
new file mode 100644
index 0000000..4a2396d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.pm.PackageInfoLite;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.UUID;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageManagerServiceUtilsTest {
+
+ private static final String PACKAGE_NAME = "com.android.app";
+ private static final File CODE_PATH =
+ InstrumentationRegistry.getInstrumentation().getContext().getFilesDir();
+
+ @Test
+ public void testCheckDowngrade_packageSetting_versionCodeSmaller_throwException()
+ throws Exception {
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(2);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_baseRevisionCodeSmaller_throwException()
+ throws Exception {
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setBaseRevisionCode(2);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.baseRevisionCode = 1;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_splitArraySizeIsDifferent_throwException()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne };
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setSplitNames(splitNames);
+ before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_splitRevisionCodeSmaller_throwException()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionTwo, revisionThree};
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setSplitNames(splitNames);
+ before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_sameSplitNameRevisionsBigger()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionThree };
+
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setSplitNames(splitNames);
+ before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_hasDifferentSplitNames() throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] beforeSplitNames = new String[] { splitOne, splitTwo };
+ final String[] afterSplitNames = new String[] { splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+ final int[] afterSplitRevisionCodes = new int[] { revisionThree };
+
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setSplitNames(beforeSplitNames);
+ before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = afterSplitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ @Test
+ public void testCheckDowngrade_packageSetting_newSplitName() throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final String[] beforeSplitNames = new String[] { splitOne };
+ final String[] afterSplitNames = new String[] { splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionTwo };
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne };
+
+ final PackageSetting before = createPackageSetting();
+ before.setLongVersionCode(1);
+ before.setSplitNames(beforeSplitNames);
+ before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = afterSplitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_versionCodeSmaller_throwException()
+ throws Exception {
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME).hideAsParsed()
+ .setVersionCode(2).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_baseRevisionCodeSmaller_throwException()
+ throws Exception {
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME).setBaseRevisionCode(2)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.baseRevisionCode = 1;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_splitArraySizeIsDifferent_throwException()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne };
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+ .asSplit(splitNames, /* splitCodePaths= */ null,
+ beforeSplitRevisionCodes, /* splitDependencies= */ null)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_splitRevisionCodeSmaller_throwException()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionTwo, revisionThree};
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+ .asSplit(splitNames, /* splitCodePaths= */ null,
+ beforeSplitRevisionCodes, /* splitDependencies= */ null)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ assertThrows(PackageManagerException.class,
+ () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_sameSplitNameRevisionsBigger()
+ throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] splitNames = new String[] { splitOne, splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionThree };
+
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+ .asSplit(splitNames, /* splitCodePaths= */ null,
+ beforeSplitRevisionCodes, /* splitDependencies= */ null)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = splitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_hasDifferentSplitNames() throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final int revisionThree = 360;
+ final String[] beforeSplitNames = new String[] { splitOne, splitTwo };
+ final String[] afterSplitNames = new String[] { splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+ final int[] afterSplitRevisionCodes = new int[] { revisionThree };
+
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+ .asSplit(beforeSplitNames, /* splitCodePaths= */ null,
+ beforeSplitRevisionCodes, /* splitDependencies= */ null)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = afterSplitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ @Test
+ public void testCheckDowngrade_androidPackage_newSplitName() throws Exception {
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ final String[] beforeSplitNames = new String[] { splitOne };
+ final String[] afterSplitNames = new String[] { splitTwo };
+ final int[] beforeSplitRevisionCodes = new int[] { revisionTwo };
+ final int[] afterSplitRevisionCodes = new int[] { revisionOne };
+
+ final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+ .asSplit(beforeSplitNames, /* splitCodePaths= */ null,
+ beforeSplitRevisionCodes, /* splitDependencies= */ null)
+ .hideAsParsed().setVersionCode(1).hideAsFinal();
+ final PackageInfoLite after = new PackageInfoLite();
+ after.versionCode = 1;
+ after.splitNames = afterSplitNames;
+ after.splitRevisionCodes = afterSplitRevisionCodes;
+
+ PackageManagerServiceUtils.checkDowngrade(before, after);
+ }
+
+ private PackageSetting createPackageSetting() {
+ return new PackageSetting(PACKAGE_NAME, PACKAGE_NAME, CODE_PATH, /* pkgFlags= */ 0,
+ /* privateFlags= */ 0 , UUID.randomUUID());
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index dec4634..d7af443 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -1092,7 +1092,7 @@
}
@Test
- public void testNoPkg_writeReadSplitVersions() {
+ public void testNoPkgDifferentRevisions_writeReadSplitVersions() {
Settings settings = makeSettings();
PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
@@ -1117,6 +1117,54 @@
}
@Test
+ public void testNoPkgSameRevisions_writeReadSplitVersions() {
+ Settings settings = makeSettings();
+ PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+ packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+
+ final String splitOne = "one";
+ final String splitTwo = "two";
+ final int revisionOne = 311;
+ packageSetting.setSplitNames(new String[] { splitOne, splitTwo});
+ packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionOne});
+ settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+ settings.writeLPr(computer, /* sync= */ true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+ assertThat(resultSetting.getSplitNames()[0], is(splitOne));
+ assertThat(resultSetting.getSplitNames()[1], is(splitTwo));
+ assertThat(resultSetting.getSplitRevisionCodes()[0], is(revisionOne));
+ assertThat(resultSetting.getSplitRevisionCodes()[1], is(revisionOne));
+ }
+
+ @Test
+ public void testNoPkgSameSplitNames_writeReadSplitVersions() {
+ Settings settings = makeSettings();
+ PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+ packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+
+ final String splitOne = "one";
+ final int revisionOne = 311;
+ final int revisionTwo = 330;
+ packageSetting.setSplitNames(new String[] { splitOne, splitOne});
+ packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionTwo});
+ settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+ settings.writeLPr(computer, /* sync= */ true);
+ settings.mPackages.clear();
+
+ assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+ PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+ assertThat(resultSetting.getSplitNames().length, is(1));
+ assertThat(resultSetting.getSplitRevisionCodes().length, is(1));
+ assertThat(resultSetting.getSplitNames()[0], is(splitOne));
+ assertThat(resultSetting.getSplitRevisionCodes()[0], is(revisionTwo));
+ }
+
+ @Test
public void testWriteReadArchiveState() {
Settings settings = makeSettings();
PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
new file mode 100644
index 0000000..7000487
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+public class SensorPowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .initMeasuredEnergyStatsLocked();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int SENSOR_HANDLE_1 = 77;
+ private static final int SENSOR_HANDLE_2 = 88;
+ private static final int SENSOR_HANDLE_3 = 99;
+
+ @Mock
+ private SensorManager mSensorManager;
+
+ private MonotonicClock mMonotonicClock;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+ Sensor sensor1 = createSensor(SENSOR_HANDLE_1, Sensor.TYPE_STEP_COUNTER,
+ Sensor.STRING_TYPE_STEP_COUNTER, "dancing", 100);
+ Sensor sensor2 = createSensor(SENSOR_HANDLE_2, Sensor.TYPE_MOTION_DETECT,
+ "com.example", "tango", 200);
+ Sensor sensor3 = createSensor(SENSOR_HANDLE_3, Sensor.TYPE_MOTION_DETECT,
+ "com.example", "waltz", 300);
+ when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(
+ List.of(sensor1, sensor2, sensor3));
+ }
+
+ @Test
+ public void testPowerEstimation() {
+ SensorPowerStatsProcessor processor = new SensorPowerStatsProcessor(() -> mSensorManager);
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1, SENSOR_HANDLE_1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1, SENSOR_HANDLE_1));
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2, SENSOR_HANDLE_1));
+ processor.noteStateChange(stats, buildHistoryItem(8000, true, APP_UID2, SENSOR_HANDLE_2));
+ processor.noteStateChange(stats, buildHistoryItem(9000, false, APP_UID2, SENSOR_HANDLE_1));
+
+ processor.finish(stats, 10000);
+
+ PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+ SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout();
+ statsLayout.fromExtras(descriptor.extras);
+
+ String dump = stats.toString();
+ assertThat(dump).contains(" step_counter: ");
+ assertThat(dump).contains(" com.example.tango: ");
+
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+
+ // For UID1:
+ // SENSOR1 was on for 6000 ms.
+ // Estimated power: 6000 * 100 = 0.167 mAh
+ // split between three different states
+ // fg screen-on: 6000 * 2500/10000
+ // bg screen-off: 6000 * 2500/10000
+ // fgs screen-off: 6000 * 5000/10000
+ double expectedPower1 = 0.166666;
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 5000 / 10000);
+
+ // For UID2:
+ // SENSOR1 was on for 2000 ms.
+ // Estimated power: 2000 * 100 = 0.0556 mAh
+ // split between three different states
+ // cached screen-on: 2000 * 2500/10000
+ // cached screen-off: 2000 * 7500/10000
+ // SENSOR2 was on for 2000 ms.
+ // Estimated power: 2000 * 200 = 0.11111 mAh
+ // split between three different states
+ // cached screen-on: 2000 * 2500/10000
+ // cached screen-off: 2000 * 7500/10000
+ double expectedPower2 = 0.05555 + 0.11111;
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2 * 2500 / 10000);
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2 * 7500 / 10000);
+
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 2500 / 10000);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 7500 / 10000);
+ }
+
+ private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+ int uid, int sensor) {
+ mStatsRule.setTime(timestamp, timestamp);
+ BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+ historyItem.time = mMonotonicClock.monotonicTime();
+ historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG : 0;
+ if (stateOn) {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_START;
+ } else {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+ }
+ historyItem.eventTag = historyItem.localEventTag;
+ historyItem.eventTag.uid = uid;
+ historyItem.eventTag.string = "sensor:0x" + Integer.toHexString(sensor);
+ return historyItem;
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+ SensorPowerStatsProcessor processor) {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ aggregatedPowerStats.getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_SENSORS);
+ processor.start(powerComponentStats, 0);
+
+ powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ return powerComponentStats;
+ }
+
+ private Sensor createSensor(int handle, int type, String stringType, String name, float power) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ Sensor sensor = mock(Sensor.class);
+ when(sensor.getHandle()).thenReturn(handle);
+ when(sensor.getType()).thenReturn(type);
+ when(sensor.getStringType()).thenReturn(stringType);
+ when(sensor.getName()).thenReturn(name);
+ when(sensor.getPower()).thenReturn(power);
+ return sensor;
+ } else {
+ return new Sensor(new InputSensorInfo(name, "vendor", 0 /* version */,
+ handle, type, 100.0f /*maxRange */, 0.02f /* resolution */,
+ (float) power, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
+ 0 /* fifoMaxEventCount */, stringType /* stringType */,
+ "" /* requiredPermission */, 0 /* maxDelay */, 0 /* flags */, 0 /* id */));
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
index ddd6d56..a6fd112 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -26,6 +26,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import android.compat.testing.PlatformCompatChangeRule;
import android.platform.test.annotations.Presubmit;
@@ -246,7 +247,6 @@
});
}
-
@Test
@EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse() {
@@ -269,6 +269,24 @@
});
}
+ @Test
+ public void testGetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
+ runTestScenario((robot)-> {
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(/* enabled */ true);
+ c.enableCameraCompatTreatmentAtBuildTime(/* enabled */ true);
+ c.enableCameraCompatSplitScreenAspectRatio(/* enabled */ true);
+ c.enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(/* enabled */ false);
+ c.setFixedOrientationLetterboxAspectRatio(/* aspectRatio */ 1.5f);
+ });
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkFixedOrientationLetterboxAspectRatioForTopParent(/* expected */ 1.5f);
+
+ robot.activity().enableTreatmentForTopActivity(/* enabled */ true);
+ robot.checkAspectRatioForTopParentIsSplitScreenRatio(/* expected */ true);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -308,6 +326,28 @@
}
@NonNull
+ void checkFixedOrientationLetterboxAspectRatioForTopParent(float expected) {
+ assertEquals(expected,
+ getTopActivityAppCompatAspectRatioOverrides()
+ .getFixedOrientationLetterboxAspectRatio(
+ activity().top().getParent().getConfiguration()),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkAspectRatioForTopParentIsSplitScreenRatio(boolean expected) {
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ getTopActivityAppCompatAspectRatioOverrides();
+ if (expected) {
+ assertEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
+ aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
+ activity().top().getParent().getConfiguration()), FLOAT_TOLLERANCE);
+ } else {
+ assertNotEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
+ aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
+ activity().top().getParent().getConfiguration()), FLOAT_TOLLERANCE);
+ }
+ }
+
private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 00a8771..6592f26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -70,4 +70,14 @@
void enableCompatFakeFocus(boolean enabled) {
doReturn(enabled).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
}
+
+ void enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) {
+ doReturn(enabled).when(mAppCompatConfiguration)
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
+ }
+
+ void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+ doReturn(aspectRatio).when(mAppCompatConfiguration)
+ .getFixedOrientationLetterboxAspectRatio();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
index 92f246b..6939f97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -28,6 +28,8 @@
private static final int DEFAULT_DISPLAY_WIDTH = 1000;
private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
+ static final float FLOAT_TOLLERANCE = 0.01f;
+
@NonNull
private final AppCompatActivityRobot mActivityRobot;
@NonNull
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index e2c0f6c2..61a6f31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -19,7 +19,6 @@
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -297,37 +296,6 @@
}
@Test
- public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
- doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
- .isCameraCompatTreatmentEnabled();
- doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
- doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
- .isCameraCompatSplitScreenAspectRatioEnabled();
- doReturn(false).when(mActivity.mWmService.mAppCompatConfiguration)
- .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
- doReturn(1.5f).when(mActivity.mWmService.mAppCompatConfiguration)
- .getFixedOrientationLetterboxAspectRatio();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertEquals(1.5f, mController.getFixedOrientationLetterboxAspectRatio(
- mActivity.getParent().getConfiguration()), /* delta */ 0.01);
-
- spyOn(mDisplayContent.mAppCompatCameraPolicy);
- doReturn(true).when(mDisplayContent.mAppCompatCameraPolicy)
- .isTreatmentEnabledForActivity(eq(mActivity));
-
- final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides();
- assertEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
- aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
- mActivity.getParent().getConfiguration()), /* delta */ 0.01);
- }
-
- @Test
public void testIsVerticalThinLetterboxed() {
// Vertical thin letterbox disabled
doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration)