diff options
42 files changed, 588 insertions, 232 deletions
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 8ee23aa8e67b..aba4b2c0d591 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -188,6 +188,14 @@ public class TaskInfo { public int launchIntoPipHostTaskId; /** + * The task id of the parent Task of the launch-into-pip Activity, i.e., if task have more than + * one activity it will create new task for this activity, this id is the origin task id and + * the pip activity will be reparent to origin task when it exit pip mode. + * @hide + */ + public int lastParentTaskIdBeforePip; + + /** * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS), * {@code null} otherwise. @@ -503,6 +511,7 @@ public class TaskInfo { pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR); shouldDockBigOverlays = source.readBoolean(); launchIntoPipHostTaskId = source.readInt(); + lastParentTaskIdBeforePip = source.readInt(); displayCutoutInsets = source.readTypedObject(Rect.CREATOR); topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR); isResizeable = source.readBoolean(); @@ -549,6 +558,7 @@ public class TaskInfo { dest.writeTypedObject(pictureInPictureParams, flags); dest.writeBoolean(shouldDockBigOverlays); dest.writeInt(launchIntoPipHostTaskId); + dest.writeInt(lastParentTaskIdBeforePip); dest.writeTypedObject(displayCutoutInsets, flags); dest.writeTypedObject(topActivityInfo, flags); dest.writeBoolean(isResizeable); @@ -589,6 +599,7 @@ public class TaskInfo { + " pictureInPictureParams=" + pictureInPictureParams + " shouldDockBigOverlays=" + shouldDockBigOverlays + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId + + " lastParentTaskIdBeforePip=" + lastParentTaskIdBeforePip + " displayCutoutSafeInsets=" + displayCutoutInsets + " topActivityInfo=" + topActivityInfo + " launchCookies=" + launchCookies diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl index 34d016adbc06..7c54a9b01dde 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl +++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl @@ -36,4 +36,5 @@ parcelable CameraOutputConfig int surfaceGroupId; String physicalCameraId; List<CameraOutputConfig> sharedSurfaceConfigs; + boolean isMultiResolutionOutput; } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index c8dc2d0b0b91..77def20179ae 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -226,6 +226,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId, outputSurface); + if (output.isMultiResolutionOutput) { + cameraOutput.setMultiResolutionOutput(); + } if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) { cameraOutput.enableSurfaceSharing(); for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) { diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 90e92dbe2ab0..9868d87460e0 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -421,7 +421,7 @@ public final class OutputConfiguration implements Parcelable { * call, or no non-negative group ID has been set. * @hide */ - void setMultiResolutionOutput() { + public void setMultiResolutionOutput() { if (mIsShared) { throw new IllegalStateException("Multi-resolution output flag must not be set for " + "configuration with surface sharing"); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 76475f2142c0..4f49f12691d0 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2808,6 +2808,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long getMobileRadioMeasuredBatteryConsumptionUC(); /** + * Returns the battery consumption (in microcoulombs) of the phone calls, derived from on device + * power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getPhoneEnergyConsumptionUC(); + + /** * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on * device power measurement data. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 36e0dc35cb8e..b02e123e5254 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -141,12 +141,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba private int mRingerMode; private int mZenMode; private boolean mPlaySample; + private final boolean mDeviceHasProductStrategies; private static final int MSG_SET_STREAM_VOLUME = 0; private static final int MSG_START_SAMPLE = 1; private static final int MSG_STOP_SAMPLE = 2; private static final int MSG_INIT_SAMPLE = 3; + private static final int MSG_UPDATE_SLIDER_MAYBE_LATER = 4; private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000; + private static final int CHECK_UPDATE_SLIDER_LATER_MS = 500; private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500); private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500); private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000); @@ -170,6 +173,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba boolean playSample) { mContext = context; mAudioManager = context.getSystemService(AudioManager.class); + mDeviceHasProductStrategies = hasAudioProductStrategies(); mNotificationManager = context.getSystemService(NotificationManager.class); mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy(); mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy @@ -186,7 +190,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } mZenMode = mNotificationManager.getZenMode(); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { mVolumeGroupId = getVolumeGroupIdForLegacyStreamType(mStreamType); mAttributes = getAudioAttributesForLegacyStreamType( mStreamType); @@ -213,6 +217,12 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mDefaultUri = defaultUri; } + /** + * DO NOT CALL every time this is needed, use once in constructor, + * read mDeviceHasProductStrategies instead + * @return true if stream types are used for volume management, false if volume groups are + * used for volume management + */ private boolean hasAudioProductStrategies() { return AudioManager.getAudioProductStrategies().size() > 0; } @@ -330,6 +340,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba onInitSample(); } break; + case MSG_UPDATE_SLIDER_MAYBE_LATER: + onUpdateSliderMaybeLater(); + break; default: Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what); } @@ -353,6 +366,21 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba : isDelay() ? START_SAMPLE_DELAY_MS : 0); } + private void onUpdateSliderMaybeLater() { + if (isDelay()) { + postUpdateSliderMaybeLater(); + return; + } + updateSlider(); + } + + private void postUpdateSliderMaybeLater() { + if (mHandler == null) return; + mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SLIDER_MAYBE_LATER), + CHECK_UPDATE_SLIDER_LATER_MS); + } + // After stop volume it needs to add a small delay when playing volume or set stream. // It is because the call volume is from the earpiece and the alarm/ring/media // is from the speaker. If play the alarm volume or set alarm stream right after stop @@ -422,7 +450,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba postStopSample(); mContext.getContentResolver().unregisterContentObserver(mVolumeObserver); mReceiver.setListening(false); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { unregisterVolumeGroupCb(); } mSeekBar.setOnSeekBarChangeListener(null); @@ -442,7 +470,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]), false, mVolumeObserver); mReceiver.setListening(true); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { registerVolumeGroupCb(); } } @@ -466,6 +494,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mLastProgress = progress; mHandler.removeMessages(MSG_SET_STREAM_VOLUME); mHandler.removeMessages(MSG_START_SAMPLE); + mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER); mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME), isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0); } @@ -608,7 +637,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); - if (hasAudioProductStrategies() && !isDelay()) { + if (mDeviceHasProductStrategies && !isDelay()) { updateVolumeSlider(streamType, streamValue); } } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { @@ -620,9 +649,16 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); - if (hasAudioProductStrategies() && !isDelay()) { - int streamVolume = mAudioManager.getStreamVolume(streamType); - updateVolumeSlider(streamType, streamVolume); + + if (mDeviceHasProductStrategies) { + if (isDelay()) { + // not the right time to update the sliders, try again later + postUpdateSliderMaybeLater(); + } else { + int streamVolume = mAudioManager.getStreamVolume(streamType); + updateVolumeSlider(streamType, streamVolume); + } + } else { int volumeGroup = getVolumeGroupIdForLegacyStreamType(streamType); if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP diff --git a/core/java/com/android/internal/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java index 8761b7470cd3..49113465c26a 100644 --- a/core/java/com/android/internal/app/procstats/UidState.java +++ b/core/java/com/android/internal/app/procstats/UidState.java @@ -150,6 +150,7 @@ public final class UidState { public void resetSafely(long now) { mDurations.resetTable(); mStartTime = now; + mProcesses.removeIf(p -> !p.isInUse()); } /** diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 6fed26c4a81d..e1e57de15346 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -166,8 +166,8 @@ public class BatteryStatsImpl extends BatteryStats { // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' - // Current on-disk Parcel version - static final int VERSION = 210; + // Current on-disk Parcel version. Must be updated when the format of the parcelable changes + public static final int VERSION = 211; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -6491,6 +6491,9 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mPhoneOn = true; mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs); + if (mConstants.PHONE_ON_EXTERNAL_STATS_COLLECTION) { + scheduleSyncExternalStatsLocked("phone-on", ExternalStatsSync.UPDATE_RADIO); + } } } @@ -6509,6 +6512,7 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mPhoneOn = false; mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs); + scheduleSyncExternalStatsLocked("phone-off", ExternalStatsSync.UPDATE_RADIO); } } @@ -8475,6 +8479,12 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") @Override + public long getPhoneEnergyConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_PHONE); + } + + @GuardedBy("this") + @Override public long getScreenOnMeasuredBatteryConsumptionUC() { return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); } @@ -13717,18 +13727,36 @@ public class BatteryStatsImpl extends BatteryStats { } synchronized (this) { + final long totalRadioDurationMs = + mMobileRadioActiveTimer.getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + mMobileRadioActiveTimer.setMark(elapsedRealtimeMs); + final long phoneOnDurationMs = Math.min(totalRadioDurationMs, + mPhoneOnTimer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000); + mPhoneOnTimer.setMark(elapsedRealtimeMs); + if (!mOnBatteryInternal || mIgnoreNextExternalStats) { return; } final SparseDoubleArray uidEstimatedConsumptionMah; + final long dataConsumedChargeUC; if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null && mGlobalMeasuredEnergyStats != null) { + // Crudely attribute power consumption. Added (totalRadioDurationMs / 2) to the + // numerator for long rounding. + final long phoneConsumedChargeUC = + (consumedChargeUC * phoneOnDurationMs + totalRadioDurationMs / 2) + / totalRadioDurationMs; + dataConsumedChargeUC = consumedChargeUC - phoneConsumedChargeUC; mGlobalMeasuredEnergyStats.updateStandardBucket( - MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC); + MeasuredEnergyStats.POWER_BUCKET_PHONE, phoneConsumedChargeUC); + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, dataConsumedChargeUC); uidEstimatedConsumptionMah = new SparseDoubleArray(); } else { uidEstimatedConsumptionMah = null; + dataConsumedChargeUC = POWER_DATA_UNAVAILABLE; } if (deltaInfo != null) { @@ -13888,14 +13916,9 @@ public class BatteryStatsImpl extends BatteryStats { // Update the MeasuredEnergyStats information. if (uidEstimatedConsumptionMah != null) { double totalEstimatedConsumptionMah = 0.0; - - // Estimate total active radio power consumption since last mark. - final long totalRadioTimeMs = mMobileRadioActiveTimer.getTimeSinceMarkLocked( - elapsedRealtimeMs * 1000) / 1000; - mMobileRadioActiveTimer.setMark(elapsedRealtimeMs); totalEstimatedConsumptionMah += mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah( - totalRadioTimeMs); + totalRadioDurationMs); // Estimate idle power consumption at each signal strength level final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length; @@ -13919,7 +13942,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs); distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, - consumedChargeUC, uidEstimatedConsumptionMah, + dataConsumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah, elapsedRealtimeMs); } @@ -16651,6 +16674,8 @@ public class BatteryStatsImpl extends BatteryStats { public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb"; public static final String KEY_BATTERY_CHARGED_DELAY_MS = "battery_charged_delay_ms"; + public static final String KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION = + "phone_on_external_stats_collection"; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000; @@ -16663,6 +16688,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64; private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/ private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */ + private static final boolean DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION = true; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an @@ -16678,6 +16704,8 @@ public class BatteryStatsImpl extends BatteryStats { public int MAX_HISTORY_FILES; public int MAX_HISTORY_BUFFER; /*Bytes*/ public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS; + public boolean PHONE_ON_EXTERNAL_STATS_COLLECTION = + DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -16754,6 +16782,11 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB : DEFAULT_MAX_HISTORY_BUFFER_KB) * 1024; + + PHONE_ON_EXTERNAL_STATS_COLLECTION = mParser.getBoolean( + KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION, + DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION); + updateBatteryChargedDelayMsLocked(); } } @@ -16808,6 +16841,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(MAX_HISTORY_BUFFER/1024); pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("="); pw.println(BATTERY_CHARGED_DELAY_MS); + pw.print(KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION); pw.print("="); + pw.println(PHONE_ON_EXTERNAL_STATS_COLLECTION); } } diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index cb893defab14..f1c4ffe07788 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -40,14 +40,27 @@ public class PhonePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long energyConsumerUC = batteryStats.getPhoneEnergyConsumptionUC(); + final int powerModel = getPowerModel(energyConsumerUC, query); + final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); - if (phoneOnPower != 0) { - builder.getAggregateBatteryConsumerBuilder( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower) - .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs); + final double phoneOnPower; + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + phoneOnPower = uCtoMah(energyConsumerUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); } + + if (phoneOnPower == 0.0) return; + + builder.getAggregateBatteryConsumerBuilder( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower, powerModel) + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs); + } } diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index 7fb8696a217d..5bfdd62592c8 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -59,7 +59,9 @@ public class MeasuredEnergyStats { public static final int POWER_BUCKET_BLUETOOTH = 5; public static final int POWER_BUCKET_GNSS = 6; public static final int POWER_BUCKET_MOBILE_RADIO = 7; - public static final int NUMBER_STANDARD_POWER_BUCKETS = 8; // Buckets above this are custom. + public static final int POWER_BUCKET_CAMERA = 8; + public static final int POWER_BUCKET_PHONE = 9; + public static final int NUMBER_STANDARD_POWER_BUCKETS = 10; // Buckets above this are custom. @IntDef(prefix = {"POWER_BUCKET_"}, value = { POWER_BUCKET_UNKNOWN, diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java index d4276efe39c2..61899143b9c5 100644 --- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java @@ -186,4 +186,19 @@ public class ProcessStatsTest extends TestCase { eq(0), eq(APP_1_PROCESS_NAME)); } + + @SmallTest + public void testSafelyResetClearsProcessInUidState() throws Exception { + ProcessStats processStats = new ProcessStats(); + ProcessState processState = + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processState.makeActive(); + UidState uidState = processStats.mUidStates.get(APP_1_UID); + assertTrue(uidState.isInUse()); + processState.makeInactive(); + uidState.resetSafely(NOW_MS); + processState.makeActive(); + assertFalse(uidState.isInUse()); + } } diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index 00ac1985f897..0bdf491e6377 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -245,6 +245,8 @@ public class MobileRadioPowerCalculatorTest { stats.noteNetworkInterfaceForTransports("cellular", new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + stats.notePhoneOnLocked(9800, 9800); + // Note application network activity NetworkStats networkStats = new NetworkStats(10000, 1) .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, @@ -257,27 +259,33 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setTime(12_000, 12_000); - MobileRadioPowerCalculator calculator = + MobileRadioPowerCalculator mobileRadioPowerCalculator = new MobileRadioPowerCalculator(mStatsRule.getPowerProfile()); - - mStatsRule.apply(calculator); + PhonePowerCalculator phonePowerCalculator = + new PhonePowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(mobileRadioPowerCalculator, phonePowerCalculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(1.53934); + .isWithin(PRECISION).of(1.38541); assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(2.77778); + .isWithin(PRECISION).of(2.5); assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) + .isWithin(PRECISION).of(0.27778); + assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) + .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(1.53934); + .isWithin(PRECISION).of(1.38541); assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java index cb1a6e7ace6b..ac6e4c2a6521 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TabletopModeController.java @@ -59,7 +59,7 @@ public class TabletopModeController implements */ private static final boolean ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP = SystemProperties.getBoolean( - "persist.wm.debug.enable_move_floating_window_in_tabletop", false); + "persist.wm.debug.enable_move_floating_window_in_tabletop", true); /** * Prefer the {@link #PREFERRED_TABLETOP_HALF_TOP} if this flag is enabled, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 6831a47c3f1a..1dd2ef94a959 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -538,6 +539,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); return; } + if (mSplitScreenOptional.isPresent()) { + // If pip activity will reparent to origin task case and if the origin task still under + // split root, just exit split screen here to ensure it could expand to fullscreen. + SplitScreenController split = mSplitScreenOptional.get(); + if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) { + split.exitSplitScreen(INVALID_TASK_ID, + SplitScreenController.EXIT_REASON_APP_FINISHED); + } + } mSyncTransactionQueue.queue(wct); mSyncTransactionQueue.runInSync(t -> { // Make sure to grab the latest source hint rect as it could have been @@ -1481,9 +1491,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, applyFinishBoundsResize(wct, direction, false); } } else { - final boolean isPipTopLeft = - direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft(); - applyFinishBoundsResize(wct, direction, isPipTopLeft); + applyFinishBoundsResize(wct, direction, isPipToTopLeft()); + // Use sync transaction to apply finish transaction for enter split case. + if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) { + mSyncTransactionQueue.runInSync(t -> { + t.merge(tx); + }); + } } finishResizeForMenu(destinationBounds); @@ -1520,7 +1534,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper.round(tx, mLeash, isInPip()); wct.setBounds(mToken, taskBounds); - wct.setBoundsChangeTransaction(mToken, tx); + // Pip to split should use sync transaction to sync split bounds change. + if (direction != TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) { + wct.setBoundsChangeTransaction(mToken, tx); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 979b7c7dc31f..167c0321d3ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -282,7 +282,8 @@ public class PipMenuView extends FrameLayout { public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent() - && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId); + && mSplitScreenControllerOptional.get().isTaskInSplitScreenForeground( + taskInfo.taskId); mFocusedTaskAllowSplitScreen = isSplitScreen || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN && taskInfo.supportsMultiWindow diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 94b9e907fa76..7d5ab8428a3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -31,7 +31,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; @@ -89,7 +88,6 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -329,9 +327,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mTaskOrganizer.getRunningTaskInfo(taskId); } + /** Check task is under split or not by taskId. */ public boolean isTaskInSplitScreen(int taskId) { - return isSplitScreenVisible() - && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED; + return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED; + } + + /** Check split is foreground and task is under split or not by taskId. */ + public boolean isTaskInSplitScreenForeground(int taskId) { + return isTaskInSplitScreen(taskId) && isSplitScreenVisible(); } public @SplitPosition int getSplitPosition(int taskId) { @@ -339,8 +342,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) { - return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition, - new WindowContainerTransaction()); + return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction()); } /** @@ -351,13 +353,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.updateSurfaces(transaction); } - private boolean moveToStage(int taskId, @StageType int stageType, - @SplitPosition int stagePosition, WindowContainerTransaction wct) { + private boolean moveToStage(int taskId, @SplitPosition int stagePosition, + WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); if (task == null) { throw new IllegalArgumentException("Unknown taskId" + taskId); } - return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct); + return mStageCoordinator.moveToStage(task, stagePosition, wct); } public boolean removeFromSideStage(int taskId) { @@ -382,10 +384,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) { - final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE; final int stagePosition = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT; - moveToStage(taskId, stageType, stagePosition, wct); + moveToStage(taskId, stagePosition, wct); } public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 71ee690146f9..e2d7a7764808 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -399,56 +399,43 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return STAGE_TYPE_UNDEFINED; } - boolean moveToStage(ActivityManager.RunningTaskInfo task, @StageType int stageType, - @SplitPosition int stagePosition, WindowContainerTransaction wct) { + boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, + WindowContainerTransaction wct) { StageTaskListener targetStage; int sideStagePosition; - if (stageType == STAGE_TYPE_MAIN) { - targetStage = mMainStage; - sideStagePosition = reverseSplitPosition(stagePosition); - } else if (stageType == STAGE_TYPE_SIDE) { + if (isSplitScreenVisible()) { + // If the split screen is foreground, retrieves target stage based on position. + targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage; + sideStagePosition = mSideStagePosition; + } else { targetStage = mSideStage; sideStagePosition = stagePosition; - } else { - if (isSplitScreenVisible()) { - // If the split screen is activated, retrieves target stage based on position. - targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage; - sideStagePosition = mSideStagePosition; - } else { - // Exit split if it running background. - exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT); - - targetStage = mSideStage; - sideStagePosition = stagePosition; - } } if (!isSplitActive()) { - // prevent the fling divider to center transitioni if split screen didn't active. - mIsDropEntering = true; - } - - setSideStagePosition(sideStagePosition, wct); - final WindowContainerTransaction evictWct = new WindowContainerTransaction(); - targetStage.evictAllChildren(evictWct); - - // Apply surface bounds before animation start. - SurfaceControl.Transaction startT = mTransactionPool.acquire(); - if (startT != null) { - updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */); - startT.apply(); - mTransactionPool.release(startT); + mSplitLayout.init(); + prepareEnterSplitScreen(wct, task, stagePosition); + mSyncQueue.queue(wct); + mSyncQueue.runInSync(t -> { + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); + }); + } else { + setSideStagePosition(sideStagePosition, wct); + targetStage.addTask(task, wct); + targetStage.evictAllChildren(wct); + if (!isSplitScreenVisible()) { + final StageTaskListener anotherStage = targetStage == mMainStage + ? mSideStage : mMainStage; + anotherStage.reparentTopTask(wct); + anotherStage.evictAllChildren(wct); + wct.reorder(mRootTaskInfo.token, true); + } + setRootForceTranslucent(false, wct); + mSyncQueue.queue(wct); } - // reparent the task to an invisible split root will make the activity invisible. Reorder - // the root task to front to make the entering transition from pip to split smooth. - wct.reorder(mRootTaskInfo.token, true); - wct.reorder(targetStage.mRootTaskInfo.token, true); - targetStage.addTask(task, wct); - if (!evictWct.isEmpty()) { - wct.merge(evictWct, true /* transfer */); - } - mTaskOrganizer.applyTransaction(wct); + // Due to drag already pip task entering split by this method so need to reset flag here. + mIsDropEntering = false; return true; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 0bb809d354dc..0b528ba3a9ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -144,39 +144,48 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test - public void testMoveToStage() { + public void testMoveToStage_splitActiveBackground() { + when(mStageCoordinator.isSplitActive()).thenReturn(true); + + final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + + mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + verify(mSideStage).addTask(eq(task), eq(wct)); + assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); + assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); + } + + @Test + public void testMoveToStage_splitActiveForeground() { + when(mStageCoordinator.isSplitActive()).thenReturn(true); + when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true); + // Assume current side stage is top or left. + mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null); + final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); - mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT, - new WindowContainerTransaction()); - verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class)); + mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + verify(mMainStage).addTask(eq(task), eq(wct)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); + assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); - mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT, - new WindowContainerTransaction()); - verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class)); - assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); + mStageCoordinator.moveToStage(task, SPLIT_POSITION_TOP_OR_LEFT, wct); + verify(mSideStage).addTask(eq(task), eq(wct)); + assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); + assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); } @Test - public void testMoveToUndefinedStage() { + public void testMoveToStage_splitInctive() { final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Verify move to undefined stage while split screen not activated moves task to side stage. - when(mStageCoordinator.isSplitScreenVisible()).thenReturn(false); - mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null); - mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT, - new WindowContainerTransaction()); - verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class)); + mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); - - // Verify move to undefined stage after split screen activated moves task based on position. - when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true); - assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); - mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, - new WindowContainerTransaction()); - verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class)); - assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); } @Test diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index fc3263fd3d11..f0aefb5bc0df 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -398,7 +398,8 @@ public class DozeMachine { } if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING || mState == State.DOZE_AOD || mState == State.DOZE - || mState == State.DOZE_AOD_DOCKED) && requestedState == State.DOZE_PULSE_DONE) { + || mState == State.DOZE_AOD_DOCKED || mState == State.DOZE_SUSPEND_TRIGGERS) + && requestedState == State.DOZE_PULSE_DONE) { Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); return mState; } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index e15ca46b6c59..cc56c488aa4f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -258,20 +258,12 @@ object Flags { // TODO(b/270223352): Tracking Bug @JvmField val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = - unreleasedFlag( - 404, - "hide_smartspace_on_dream_overlay", - teamfood = true - ) + releasedFlag(404, "hide_smartspace_on_dream_overlay") // TODO(b/271460958): Tracking Bug @JvmField val SHOW_WEATHER_COMPLICATION_ON_DREAM_OVERLAY = - unreleasedFlag( - 405, - "show_weather_complication_on_dream_overlay", - teamfood = true - ) + releasedFlag(405, "show_weather_complication_on_dream_overlay") // 500 - quick settings @@ -425,7 +417,7 @@ object Flags { @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple") // TODO(b/270882464): Tracking Bug - val ENABLE_DOCK_SETUP_V2 = unreleasedFlag(1005, "enable_dock_setup_v2", teamfood = true) + val ENABLE_DOCK_SETUP_V2 = releasedFlag(1005, "enable_dock_setup_v2") // TODO(b/265045965): Tracking Bug val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot") @@ -527,7 +519,7 @@ object Flags { @JvmField val ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP = sysPropBooleanFlag( - 1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = false) + 1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = true) // 1200 - predictive back @Keep diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 98b6d70317b8..60192b8c33df 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -152,6 +152,14 @@ public class LogModule { return factory.create("QSLog", 700 /* maxSize */, false /* systrace */); } + /** Provides a logging buffer for logs related to Quick Settings configuration. */ + @Provides + @SysUISingleton + @QSConfigLog + public static LogBuffer provideQSConfigLogBuffer(LogBufferFactory factory) { + return factory.create("QSConfigLog", 100 /* maxSize */, true /* systrace */); + } + /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java new file mode 100644 index 000000000000..295bf88d498f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.android.systemui.plugins.log.LogBuffer; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** A {@link LogBuffer} for QS configuration changed messages. */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface QSConfigLog { +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 2668d2e36731..fdab9b16c7a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -91,16 +91,19 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr new QSPanel.OnConfigurationChangedListener() { @Override public void onConfigurationChange(Configuration newConfig) { - mQSLogger.logOnConfigurationChanged( - /* lastOrientation= */ mLastOrientation, - /* newOrientation= */ newConfig.orientation, - /* containerName= */ mView.getDumpableTag()); - - boolean previousSplitShadeState = mShouldUseSplitNotificationShade; + final boolean previousSplitShadeState = mShouldUseSplitNotificationShade; + final int previousOrientation = mLastOrientation; mShouldUseSplitNotificationShade = - LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); + LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); mLastOrientation = newConfig.orientation; + mQSLogger.logOnConfigurationChanged( + /* oldOrientation= */ previousOrientation, + /* newOrientation= */ mLastOrientation, + /* oldShouldUseSplitShade= */ previousSplitShadeState, + /* newShouldUseSplitShade= */ mShouldUseSplitNotificationShade, + /* containerName= */ mView.getDumpableTag()); + switchTileLayoutIfNeeded(); onConfigurationChanged(); if (previousSplitShadeState != mShouldUseSplitNotificationShade) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt index 23c41db6d5a6..5b461a6d8bad 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt @@ -16,8 +16,12 @@ package com.android.systemui.qs.logging +import android.content.res.Configuration.ORIENTATION_LANDSCAPE +import android.content.res.Configuration.ORIENTATION_PORTRAIT +import android.content.res.Configuration.Orientation import android.service.quicksettings.Tile import android.view.View +import com.android.systemui.log.dagger.QSConfigLog import com.android.systemui.log.dagger.QSLog import com.android.systemui.plugins.log.ConstantStringsLogger import com.android.systemui.plugins.log.ConstantStringsLoggerImpl @@ -32,8 +36,12 @@ import javax.inject.Inject private const val TAG = "QSLog" -class QSLogger @Inject constructor(@QSLog private val buffer: LogBuffer) : - ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) { +class QSLogger +@Inject +constructor( + @QSLog private val buffer: LogBuffer, + @QSConfigLog private val configChangedBuffer: LogBuffer, +) : ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) { fun logException(@CompileTimeConstant logMsg: String, ex: Exception) { buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex) @@ -264,19 +272,28 @@ class QSLogger @Inject constructor(@QSLog private val buffer: LogBuffer) : } fun logOnConfigurationChanged( - lastOrientation: Int, - newOrientation: Int, + @Orientation oldOrientation: Int, + @Orientation newOrientation: Int, + newShouldUseSplitShade: Boolean, + oldShouldUseSplitShade: Boolean, containerName: String ) { - buffer.log( + configChangedBuffer.log( TAG, DEBUG, { str1 = containerName - int1 = lastOrientation + int1 = oldOrientation int2 = newOrientation + bool1 = oldShouldUseSplitShade + bool2 = newShouldUseSplitShade }, - { "configuration change: $str1 orientation was $int1, now $int2" } + { + "config change: " + + "$str1 orientation=${toOrientationString(int2)} " + + "(was ${toOrientationString(int1)}), " + + "splitShade=$bool2 (was $bool1)" + } ) } @@ -353,3 +370,11 @@ class QSLogger @Inject constructor(@QSLog private val buffer: LogBuffer) : } } } + +private inline fun toOrientationString(@Orientation orientation: Int): String { + return when (orientation) { + ORIENTATION_LANDSCAPE -> "land" + ORIENTATION_PORTRAIT -> "port" + else -> "undefined" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 2dc15d09afe2..71e2e405d071 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1452,11 +1452,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public boolean onTouch(MotionEvent event) { boolean handledTouch = false; if (mAlternateBouncerInteractor.isVisibleState()) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { + final boolean downThenUp = event.getActionMasked() == MotionEvent.ACTION_UP + && mAlternateBouncerReceivedDownTouch; + final boolean outsideTouch = event.getActionMasked() == MotionEvent.ACTION_OUTSIDE; + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mAlternateBouncerReceivedDownTouch = true; - } else if (event.getAction() == MotionEvent.ACTION_UP - && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime() - && mAlternateBouncerReceivedDownTouch) { + } else if ((downThenUp || outsideTouch) + && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { showPrimaryBouncer(true); } handledTouch = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index f866d65e2d2d..d0c6215a55d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -105,10 +105,14 @@ class MobileConnectionRepositoryImpl( * The reason we need to do this is because TelephonyManager limits the number of registered * listeners per-process, so we don't want to create a new listener for every callback. * - * A note on the design for back pressure here: We use the [coalesce] operator here to change - * the backpressure strategy to store exactly the last callback event of _each type_ here, as - * opposed to the default strategy which is to drop the oldest event (regardless of type). This - * means that we should never miss any single event as long as the flow has been started. + * A note on the design for back pressure here: We don't control _which_ telephony callback + * comes in first, since we register every relevant bit of information as a batch. E.g., if a + * downstream starts collecting on a field which is backed by + * [TelephonyCallback.ServiceStateListener], it's not possible for us to guarantee that _that_ + * callback comes in -- the first callback could very well be + * [TelephonyCallback.DataActivityListener], which would promptly be dropped if we didn't keep + * it tracked. We use the [scan] operator here to track the most recent callback of _each type_ + * here. See [TelephonyCallbackState] to see how the callbacks are stored. */ private val callbackEvents: StateFlow<TelephonyCallbackState> = run { val initial = TelephonyCallbackState() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 2ee52325ca4a..654ba04eba7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -57,6 +57,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.concurrent.GuardedBy; + /** * Default implementation of a {@link BatteryController}. This controller monitors for battery * level change events that are broadcasted by the system. @@ -94,7 +96,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private boolean mTestMode = false; @VisibleForTesting boolean mHasReceivedBattery = false; + @GuardedBy("mEstimateLock") private Estimate mEstimate; + private final Object mEstimateLock = new Object(); + private boolean mFetchingEstimate = false; // Use AtomicReference because we may request it from a different thread @@ -321,7 +326,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @Nullable private String generateTimeRemainingString() { - synchronized (mFetchCallbacks) { + synchronized (mEstimateLock) { if (mEstimate == null) { return null; } @@ -340,7 +345,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mFetchingEstimate = true; mBgHandler.post(() -> { // Only fetch the estimate if they are enabled - synchronized (mFetchCallbacks) { + synchronized (mEstimateLock) { mEstimate = null; if (mEstimates.isHybridNotificationEnabled()) { updateEstimate(); @@ -363,6 +368,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @WorkerThread + @GuardedBy("mEstimateLock") private void updateEstimate() { Assert.isNotMainThread(); // if the estimate has been cached we can just use that, otherwise get a new one and diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index a636b7f43648..b87647ef9839 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -408,6 +408,17 @@ public class DozeMachineTest extends SysuiTestCase { } @Test + public void testPulsing_dozeSuspendTriggers_pulseDone_doesntCrash() { + mMachine.requestState(INITIALIZED); + + mMachine.requestState(DOZE); + mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); + mMachine.requestState(DOZE_PULSING); + mMachine.requestState(DOZE_SUSPEND_TRIGGERS); + mMachine.requestState(DOZE_PULSE_DONE); + } + + @Test public void testSuppressingPulse_doesntCrash() { mMachine.requestState(INITIALIZED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 61286a43217c..346b90c1bd88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -786,4 +786,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // THEN the alternateBouncer doesn't hide verify(mAlternateBouncerInteractor, never()).hide(); } + + @Test + public void testAlternateBouncerOnTouch_actionOutside_hidesAlternateBouncer() { + reset(mAlternateBouncerInteractor); + + // GIVEN the alternate bouncer has shown for a minimum amount of time + when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + + // WHEN only ACTION_OUTSIDE touch event comes + mStatusBarKeyguardViewManager.onTouch( + MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_OUTSIDE, 0f, 0f, 0)); + + // THEN the alternateBouncer hides + verify(mAlternateBouncerInteractor).hide(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt index 542b688b162d..934e1c64c6da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt @@ -25,7 +25,6 @@ import android.telephony.ServiceState.STATE_OUT_OF_SERVICE import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback.DataActivityListener import android.telephony.TelephonyCallback.ServiceStateListener -import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE import android.telephony.TelephonyManager @@ -68,6 +67,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameMo import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength +import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel @@ -392,13 +392,17 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this) val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>() - val type = NETWORK_TYPE_UNKNOWN - val expected = UnknownNetworkType - val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) } + val ti = + telephonyDisplayInfo( + networkType = NETWORK_TYPE_UNKNOWN, + overrideNetworkType = NETWORK_TYPE_UNKNOWN, + ) + callback.onDisplayInfoChanged(ti) + val expected = UnknownNetworkType assertThat(latest).isEqualTo(expected) - assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(type)) + assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(NETWORK_TYPE_UNKNOWN)) job.cancel() } @@ -412,14 +416,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>() val overrideType = OVERRIDE_NETWORK_TYPE_NONE val type = NETWORK_TYPE_LTE - val expected = DefaultNetworkType(mobileMappings.toIconKey(type)) - val ti = - mock<TelephonyDisplayInfo>().also { - whenever(it.overrideNetworkType).thenReturn(overrideType) - whenever(it.networkType).thenReturn(type) - } + val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = overrideType) callback.onDisplayInfoChanged(ti) + val expected = DefaultNetworkType(mobileMappings.toIconKey(type)) assertThat(latest).isEqualTo(expected) job.cancel() @@ -433,14 +433,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>() val type = OVERRIDE_NETWORK_TYPE_LTE_CA - val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type)) - val ti = - mock<TelephonyDisplayInfo>().also { - whenever(it.networkType).thenReturn(type) - whenever(it.overrideNetworkType).thenReturn(type) - } + val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = type) callback.onDisplayInfoChanged(ti) + val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type)) assertThat(latest).isEqualTo(expected) job.cancel() @@ -455,14 +451,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>() val unknown = NETWORK_TYPE_UNKNOWN val type = OVERRIDE_NETWORK_TYPE_LTE_CA - val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type)) - val ti = - mock<TelephonyDisplayInfo>().also { - whenever(it.networkType).thenReturn(unknown) - whenever(it.overrideNetworkType).thenReturn(type) - } + val ti = telephonyDisplayInfo(unknown, type) callback.onDisplayInfoChanged(ti) + val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type)) assertThat(latest).isEqualTo(expected) job.cancel() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt index bbf04ed28fd7..9da9ff72d380 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt @@ -64,10 +64,14 @@ import org.mockito.MockitoAnnotations * * Kind of like an interaction test case build just for [TelephonyCallback] * - * The list of telephony callbacks we use is: [TelephonyCallback.CarrierNetworkListener] - * [TelephonyCallback.DataActivityListener] [TelephonyCallback.DataConnectionStateListener] - * [TelephonyCallback.DataEnabledListener] [TelephonyCallback.DisplayInfoListener] - * [TelephonyCallback.ServiceStateListener] [TelephonyCallback.SignalStrengthsListener] + * The list of telephony callbacks we use is: + * - [TelephonyCallback.CarrierNetworkListener] + * - [TelephonyCallback.DataActivityListener] + * - [TelephonyCallback.DataConnectionStateListener] + * - [TelephonyCallback.DataEnabledListener] + * - [TelephonyCallback.DisplayInfoListener] + * - [TelephonyCallback.ServiceStateListener] + * - [TelephonyCallback.SignalStrengthsListener] * * Because each of these callbacks comes in on the same callbackFlow, collecting on a field backed * by only a single callback can immediately create backpressure on the other fields related to a @@ -201,7 +205,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() { 200 /* unused */ ) - // Send a bunch of events that we don't care about, to overrun the replay buffer flipActivity(100, activityCallback) val connectionJob = underTest.dataConnectionState.onEach { latest = it }.launchIn(this) @@ -225,7 +228,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() { enabledCallback.onDataEnabledChanged(true, 1 /* unused */) - // Send a bunch of events that we don't care about, to overrun the replay buffer flipActivity(100, activityCallback) val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this) @@ -252,7 +254,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() { val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) } displayInfoCallback.onDisplayInfoChanged(ti) - // Send a bunch of events that we don't care about, to overrun the replay buffer flipActivity(100, activityCallback) val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt index d07b96f6609e..cf815c27a0bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod import android.telephony.CellSignalStrengthCdma import android.telephony.SignalStrength import android.telephony.TelephonyCallback +import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyManager import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -48,6 +49,12 @@ object MobileTelephonyHelpers { return signalStrength } + fun telephonyDisplayInfo(networkType: Int, overrideNetworkType: Int) = + mock<TelephonyDisplayInfo>().also { + whenever(it.networkType).thenReturn(networkType) + whenever(it.overrideNetworkType).thenReturn(overrideNetworkType) + } + inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T { val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>() assertThat(cbs.size).isEqualTo(1) diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index 3d24588c9763..aafcd9dcbff9 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -19,7 +19,6 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.graphics.Camera; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.hardware.HardwareBuffer; @@ -1855,6 +1854,7 @@ public class CameraExtensionsProxyService extends Service { ret.outputId.id = output.getId(); ret.physicalCameraId = output.getPhysicalCameraId(); ret.surfaceGroupId = output.getSurfaceGroupId(); + ret.isMultiResolutionOutput = false; if (output instanceof SurfaceOutputConfigImpl) { SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output; ret.type = CameraOutputConfig.TYPE_SURFACE; @@ -1874,6 +1874,7 @@ public class CameraExtensionsProxyService extends Service { ret.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER; ret.imageFormat = multiResReaderConfig.getImageFormat(); ret.capacity = multiResReaderConfig.getMaxImages(); + ret.isMultiResolutionOutput = true; } else { throw new IllegalStateException("Unknown output config type!"); } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 702526a4beab..dc1ef7eee0b6 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -891,6 +891,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { break; case EnergyConsumerType.MOBILE_RADIO: buckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO] = true; + buckets[MeasuredEnergyStats.POWER_BUCKET_PHONE] = true; break; case EnergyConsumerType.DISPLAY: buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index ff18167ee86f..158492056fb5 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1017,9 +1017,14 @@ public class AudioService extends IAudioService.Stub mSfxHelper = new SoundEffectsHelper(mContext, playerBase -> ignorePlayerLogs(playerBase)); - final boolean headTrackingDefault = mContext.getResources().getBoolean( + final boolean binauralEnabledDefault = SystemProperties.getBoolean( + "ro.audio.spatializer_binaural_enabled_default", true); + final boolean transauralEnabledDefault = SystemProperties.getBoolean( + "ro.audio.spatializer_transaural_enabled_default", true); + final boolean headTrackingEnabledDefault = mContext.getResources().getBoolean( com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default); - mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, headTrackingDefault); + mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, + binauralEnabledDefault, transauralEnabledDefault, headTrackingEnabledDefault); mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator(); diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 2b566668a7c7..a9e7d4b0bac8 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -171,13 +171,17 @@ public class SpatializerHelper { // initialization @SuppressWarnings("StaticAssignmentInConstructor") SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa, - boolean headTrackingEnabledByDefault) { + boolean binauralEnabledDefault, + boolean transauralEnabledDefault, + boolean headTrackingEnabledDefault) { mAudioService = mother; mASA = asa; // "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being // constructed here is the factory for SADeviceState, thus SADeviceState and its // private static field sHeadTrackingEnabledDefault should never be accessed directly. - SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledByDefault; + SADeviceState.sBinauralEnabledDefault = binauralEnabledDefault; + SADeviceState.sTransauralEnabledDefault = transauralEnabledDefault; + SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault; } synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) { @@ -1539,10 +1543,12 @@ public class SpatializerHelper { } /*package*/ static final class SADeviceState { + private static boolean sBinauralEnabledDefault = true; + private static boolean sTransauralEnabledDefault = true; private static boolean sHeadTrackingEnabledDefault = false; final @AudioDeviceInfo.AudioDeviceType int mDeviceType; final @NonNull String mDeviceAddress; - boolean mEnabled = true; // by default, SA is enabled on any device + boolean mEnabled; boolean mHasHeadTracker = false; boolean mHeadTrackerEnabled; static final String SETTING_FIELD_SEPARATOR = ","; @@ -1558,6 +1564,12 @@ public class SpatializerHelper { SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) { mDeviceType = deviceType; mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : ""; + final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); + mEnabled = spatMode == SpatializationMode.SPATIALIZER_BINAURAL + ? sBinauralEnabledDefault + : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL + ? sTransauralEnabledDefault + : false; mHeadTrackerEnabled = sHeadTrackingEnabledDefault; } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 1bbdc20c81e5..8c83be3ea8dd 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -1230,13 +1230,12 @@ public class DisplayModeDirector { } public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) { - if (defaultPeakRefreshRate == null) { - defaultPeakRefreshRate = (float) mContext.getResources().getInteger( - R.integer.config_defaultPeakRefreshRate); - } - - if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) { - synchronized (mLock) { + synchronized (mLock) { + if (defaultPeakRefreshRate == null) { + setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig, + /* attemptLoadingFromDeviceConfig= */ false); + updateRefreshRateSettingLocked(); + } else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) { mDefaultPeakRefreshRate = defaultPeakRefreshRate; updateRefreshRateSettingLocked(); } @@ -1869,11 +1868,20 @@ public class DisplayModeDirector { mLowDisplayBrightnessThresholds = displayThresholds; mLowAmbientBrightnessThresholds = ambientThresholds; } else { - // Invalid or empty. Use device default. - mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray( - R.array.config_brightnessThresholdsOfPeakRefreshRate); - mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray( - R.array.config_ambientThresholdsOfPeakRefreshRate); + DisplayDeviceConfig displayDeviceConfig; + synchronized (mLock) { + displayDeviceConfig = mDefaultDisplayDeviceConfig; + } + mLowDisplayBrightnessThresholds = loadBrightnessThresholds( + () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(), + () -> displayDeviceConfig.getLowDisplayBrightnessThresholds(), + R.array.config_brightnessThresholdsOfPeakRefreshRate, + displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false); + mLowAmbientBrightnessThresholds = loadBrightnessThresholds( + () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(), + () -> displayDeviceConfig.getLowAmbientBrightnessThresholds(), + R.array.config_ambientThresholdsOfPeakRefreshRate, + displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false); } restartObserver(); } @@ -1883,24 +1891,41 @@ public class DisplayModeDirector { * DeviceConfig properties. */ public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) { - if (refreshRate != mRefreshRateInLowZone) { + if (refreshRate == -1) { + // Given there is no value available in DeviceConfig, lets not attempt loading it + // from there. + synchronized (mLock) { + loadRefreshRateInLowZone(mDefaultDisplayDeviceConfig, + /* attemptLoadingFromDeviceConfig= */ false); + } + restartObserver(); + } else if (refreshRate != mRefreshRateInLowZone) { mRefreshRateInLowZone = refreshRate; restartObserver(); } } - public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, + private void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { if (displayThresholds != null && ambientThresholds != null && displayThresholds.length == ambientThresholds.length) { mHighDisplayBrightnessThresholds = displayThresholds; mHighAmbientBrightnessThresholds = ambientThresholds; } else { - // Invalid or empty. Use device default. - mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray( - R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); - mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray( - R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + DisplayDeviceConfig displayDeviceConfig; + synchronized (mLock) { + displayDeviceConfig = mDefaultDisplayDeviceConfig; + } + mHighDisplayBrightnessThresholds = loadBrightnessThresholds( + () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(), + () -> displayDeviceConfig.getHighDisplayBrightnessThresholds(), + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate, + displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false); + mHighAmbientBrightnessThresholds = loadBrightnessThresholds( + () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(), + () -> displayDeviceConfig.getHighAmbientBrightnessThresholds(), + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate, + displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false); } restartObserver(); } @@ -1910,7 +1935,15 @@ public class DisplayModeDirector { * DeviceConfig properties. */ public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) { - if (refreshRate != mRefreshRateInHighZone) { + if (refreshRate == -1) { + // Given there is no value available in DeviceConfig, lets not attempt loading it + // from there. + synchronized (mLock) { + loadRefreshRateInHighZone(mDefaultDisplayDeviceConfig, + /* attemptLoadingFromDeviceConfig= */ false); + } + restartObserver(); + } else if (refreshRate != mRefreshRateInHighZone) { mRefreshRateInHighZone = refreshRate; restartObserver(); } @@ -2870,10 +2903,8 @@ public class DisplayModeDirector { new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds)) .sendToTarget(); - if (refreshRateInLowZone != -1) { - mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, - 0).sendToTarget(); - } + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, + 0).sendToTarget(); int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds(); int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds(); @@ -2883,10 +2914,8 @@ public class DisplayModeDirector { new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds)) .sendToTarget(); - if (refreshRateInHighZone != -1) { - mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, - 0).sendToTarget(); - } + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, + 0).sendToTarget(); synchronized (mLock) { final int refreshRateInHbmSunlight = diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 3404279d2c59..c0ed08d2607d 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -88,6 +88,10 @@ import java.util.Set; public class DisplayRotation { private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM; + // Delay in milliseconds when updating config due to folding events. This prevents + // config changes and unexpected jumps while folding the device to closed state. + private static final int FOLDING_RECOMPUTE_CONFIG_DELAY_MS = 800; + private static class RotationAnimationPair { @AnimRes int mEnter; @@ -1618,6 +1622,7 @@ public class DisplayRotation { private boolean mInHalfFoldTransition = false; private final boolean mIsDisplayAlwaysSeparatingHinge; private final Set<Integer> mTabletopRotations; + private final Runnable mActivityBoundsUpdateCallback; FoldController() { mTabletopRotations = new ArraySet<>(); @@ -1652,6 +1657,26 @@ public class DisplayRotation { } mIsDisplayAlwaysSeparatingHinge = mContext.getResources().getBoolean( R.bool.config_isDisplayHingeAlwaysSeparating); + + mActivityBoundsUpdateCallback = new Runnable() { + public void run() { + if (mDeviceState == DeviceStateController.DeviceState.OPEN + || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) { + synchronized (mLock) { + final Task topFullscreenTask = + mDisplayContent.getTask( + t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + if (topFullscreenTask != null) { + final ActivityRecord top = + topFullscreenTask.topRunningActivity(); + if (top != null) { + top.recomputeConfiguration(); + } + } + } + } + } + }; } boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) { @@ -1723,14 +1748,9 @@ public class DisplayRotation { false /* forceRelayout */); } // Alert the activity of possible new bounds. - final Task topFullscreenTask = - mDisplayContent.getTask(t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); - if (topFullscreenTask != null) { - final ActivityRecord top = topFullscreenTask.topRunningActivity(); - if (top != null) { - top.recomputeConfiguration(); - } - } + UiThread.getHandler().removeCallbacks(mActivityBoundsUpdateCallback); + UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback, + FOLDING_RECOMPUTE_CONFIG_DELAY_MS); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3461bc27b40c..18d6dea927c1 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3448,6 +3448,8 @@ class Task extends TaskFragment { && info.pictureInPictureParams.isLaunchIntoPip() && top.getLastParentBeforePip() != null) ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID; + info.lastParentTaskIdBeforePip = top != null && top.getLastParentBeforePip() != null + ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID; info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays; info.mTopActivityLocusId = top != null ? top.getLocusId() : null; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 41e0fd715889..2d849b3a6b95 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2049,16 +2049,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * Like isOnScreen(), but we don't return true if the window is part - * of a transition that has not yet been started. + * of a transition but has not yet started animating. */ boolean isReadyForDisplay() { - if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) { + if (!mHasSurface || mDestroying || !isVisibleByPolicy()) { + return false; + } + if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet() + && !isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) { return false; } final boolean parentAndClientVisible = !isParentWindowHidden() && mViewVisibility == View.VISIBLE && mToken.isVisible(); - return mHasSurface && isVisibleByPolicy() && !mDestroying - && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS)); + return parentAndClientVisible || isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL); } boolean isFullyTransparent() { diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java index 428eaff9e5bc..dea31d764522 100644 --- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java @@ -56,7 +56,9 @@ public class SpatializerHelperTest { mSpyAudioSystem = spy(new NoOpAudioSystemAdapter()); mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem, - false /*headTrackingEnabledByDefault*/); + true /*binauralEnabledDefault*/, + true /*transauralEnabledDefault*/, + false /*headTrackingEnabledDefault*/); } /** diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index ff37564f46a4..58f3db959afe 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -1905,7 +1905,7 @@ public class DisplayModeDirectorTest { // We don't expect any interaction with DeviceConfig when the director is initialized // because we explicitly avoid doing this as this can lead to a latency spike in the // startup of DisplayManagerService - // Verify all the loaded values are from DisplayDeviceConfig + // Verify all the loaded values are from config.xml assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0); assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75, 0.0); @@ -1937,6 +1937,7 @@ public class DisplayModeDirectorTest { when(displayDeviceConfig.getDefaultRefreshRateInHbmSunlight()).thenReturn(75); director.defaultDisplayDeviceUpdated(displayDeviceConfig); + // Verify the new values are from the freshly loaded DisplayDeviceConfig. assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0); assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 65, 0.0); @@ -1966,6 +1967,7 @@ public class DisplayModeDirectorTest { config.setRefreshRateInHbmSunlight(80); director.defaultDisplayDeviceUpdated(displayDeviceConfig); + // Verify the values are loaded from the DeviceConfig. assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0); assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60, 0.0); @@ -1981,6 +1983,35 @@ public class DisplayModeDirectorTest { new int[]{20}); assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 70); assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 80); + + // Reset the DeviceConfig + config.setDefaultPeakRefreshRate(null); + config.setRefreshRateInHighZone(null); + config.setRefreshRateInLowZone(null); + config.setLowAmbientBrightnessThresholds(new int[]{}); + config.setLowDisplayBrightnessThresholds(new int[]{}); + config.setHighDisplayBrightnessThresholds(new int[]{}); + config.setHighAmbientBrightnessThresholds(new int[]{}); + config.setRefreshRateInHbmHdr(null); + config.setRefreshRateInHbmSunlight(null); + waitForIdleSync(); + + // verify the new values now fallback to DisplayDeviceConfig + assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0); + assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 65, + 0.0); + assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55); + assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50); + assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(), + new int[]{210}); + assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(), + new int[]{2100}); + assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(), + new int[]{25}); + assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(), + new int[]{30}); + assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65); + assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75); } @Test @@ -2140,18 +2171,18 @@ public class DisplayModeDirectorTest { super.addOnPropertiesChangedListener(namespace, executor, listener); } - void setRefreshRateInLowZone(int fps) { + void setRefreshRateInLowZone(Integer fps) { putPropertyAndNotify( DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE, String.valueOf(fps)); } - void setRefreshRateInHbmSunlight(int fps) { + void setRefreshRateInHbmSunlight(Integer fps) { putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps)); } - void setRefreshRateInHbmHdr(int fps) { + void setRefreshRateInHbmHdr(Integer fps) { putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps)); } @@ -2187,13 +2218,13 @@ public class DisplayModeDirectorTest { thresholds); } - void setRefreshRateInHighZone(int fps) { + void setRefreshRateInHighZone(Integer fps) { putPropertyAndNotify( DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE, String.valueOf(fps)); } - void setDefaultPeakRefreshRate(int fps) { + void setDefaultPeakRefreshRate(Integer fps) { putPropertyAndNotify( DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_PEAK_REFRESH_RATE_DEFAULT, String.valueOf(fps)); |