Merge "Store trusted AttributionSources without token" into udc-qpr-dev
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5940819..703f165 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1468,6 +1468,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2140,6 +2147,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 905f98d..746648b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -887,6 +887,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2365,6 +2372,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index 7a153ef..c5f5614 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -116,8 +116,9 @@
* Builder for DeviceBatteryConsumer.
*/
public static final class Builder extends BaseBuilder<AggregateBatteryConsumer.Builder> {
- public Builder(BatteryConsumer.BatteryConsumerData data, int scope) {
- super(data, CONSUMER_TYPE_AGGREGATE);
+ public Builder(BatteryConsumer.BatteryConsumerData data, int scope,
+ double minConsumedPowerThreshold) {
+ super(data, CONSUMER_TYPE_AGGREGATE, minConsumedPowerThreshold);
data.putInt(COLUMN_INDEX_SCOPE, scope);
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 0ba8d51..ca84b35 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -795,11 +795,12 @@
protected final BatteryConsumer.BatteryConsumerData mData;
protected final PowerComponents.Builder mPowerComponentsBuilder;
- public BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType) {
+ public BaseBuilder(BatteryConsumer.BatteryConsumerData data, int consumerType,
+ double minConsumedPowerThreshold) {
mData = data;
data.putLong(COLUMN_INDEX_BATTERY_CONSUMER_TYPE, consumerType);
- mPowerComponentsBuilder = new PowerComponents.Builder(data);
+ mPowerComponentsBuilder = new PowerComponents.Builder(data, minConsumedPowerThreshold);
}
@Nullable
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index e2c52ce..cc37b54 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -707,7 +707,7 @@
XML_ATTR_PREFIX_INCLUDES_PROC_STATE_DATA, false);
builder = new Builder(customComponentNames.toArray(new String[0]), true,
- includesProcStateData);
+ includesProcStateData, 0);
builder.setStatsStartTimestamp(
parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
@@ -782,6 +782,7 @@
private final String[] mCustomPowerComponentNames;
private final boolean mIncludePowerModels;
private final boolean mIncludesProcessStateData;
+ private final double mMinConsumedPowerThreshold;
private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout;
private long mStatsStartTimestampMs;
private long mStatsEndTimestampMs;
@@ -802,11 +803,11 @@
private BatteryStatsHistory mBatteryStatsHistory;
public Builder(@NonNull String[] customPowerComponentNames) {
- this(customPowerComponentNames, false, false);
+ this(customPowerComponentNames, false, false, 0);
}
public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
- boolean includeProcessStateData) {
+ boolean includeProcessStateData, double minConsumedPowerThreshold) {
mBatteryConsumersCursorWindow =
new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE);
mBatteryConsumerDataLayout =
@@ -817,12 +818,14 @@
mCustomPowerComponentNames = customPowerComponentNames;
mIncludePowerModels = includePowerModels;
mIncludesProcessStateData = includeProcessStateData;
+ mMinConsumedPowerThreshold = minConsumedPowerThreshold;
for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
final BatteryConsumer.BatteryConsumerData data =
BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
mBatteryConsumerDataLayout);
mAggregateBatteryConsumersBuilders[scope] =
- new AggregateBatteryConsumer.Builder(data, scope);
+ new AggregateBatteryConsumer.Builder(
+ data, scope, mMinConsumedPowerThreshold);
}
}
@@ -961,7 +964,8 @@
final BatteryConsumer.BatteryConsumerData data =
BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
mBatteryConsumerDataLayout);
- builder = new UidBatteryConsumer.Builder(data, batteryStatsUid);
+ builder = new UidBatteryConsumer.Builder(data, batteryStatsUid,
+ mMinConsumedPowerThreshold);
mUidBatteryConsumerBuilders.put(uid, builder);
}
return builder;
@@ -979,7 +983,7 @@
final BatteryConsumer.BatteryConsumerData data =
BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
mBatteryConsumerDataLayout);
- builder = new UidBatteryConsumer.Builder(data, uid);
+ builder = new UidBatteryConsumer.Builder(data, uid, mMinConsumedPowerThreshold);
mUidBatteryConsumerBuilders.put(uid, builder);
}
return builder;
@@ -996,7 +1000,7 @@
final BatteryConsumer.BatteryConsumerData data =
BatteryConsumer.BatteryConsumerData.create(mBatteryConsumersCursorWindow,
mBatteryConsumerDataLayout);
- builder = new UserBatteryConsumer.Builder(data, userId);
+ builder = new UserBatteryConsumer.Builder(data, userId, mMinConsumedPowerThreshold);
mUserBatteryConsumerBuilders.put(userId, builder);
}
return builder;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index b3f4d98..49d7e8b 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -80,6 +80,7 @@
private final long mMaxStatsAgeMs;
private final long mFromTimestamp;
private final long mToTimestamp;
+ private final double mMinConsumedPowerThreshold;
private final @BatteryConsumer.PowerComponent int[] mPowerComponents;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
@@ -87,6 +88,7 @@
mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
: new int[]{UserHandle.USER_ALL};
mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
+ mMinConsumedPowerThreshold = builder.mMinConsumedPowerThreshold;
mFromTimestamp = builder.mFromTimestamp;
mToTimestamp = builder.mToTimestamp;
mPowerComponents = builder.mPowerComponents;
@@ -137,6 +139,14 @@
}
/**
+ * Returns the minimal power component consumed power threshold. The small power consuming
+ * components will be reported as zero.
+ */
+ public double getMinConsumedPowerThreshold() {
+ return mMinConsumedPowerThreshold;
+ }
+
+ /**
* Returns the exclusive lower bound of the stored snapshot timestamps that should be included
* in the aggregation. Ignored if {@link #getToTimestamp()} is zero.
*/
@@ -158,6 +168,7 @@
mUserIds = new int[in.readInt()];
in.readIntArray(mUserIds);
mMaxStatsAgeMs = in.readLong();
+ mMinConsumedPowerThreshold = in.readDouble();
mFromTimestamp = in.readLong();
mToTimestamp = in.readLong();
mPowerComponents = in.createIntArray();
@@ -169,6 +180,7 @@
dest.writeInt(mUserIds.length);
dest.writeIntArray(mUserIds);
dest.writeLong(mMaxStatsAgeMs);
+ dest.writeDouble(mMinConsumedPowerThreshold);
dest.writeLong(mFromTimestamp);
dest.writeLong(mToTimestamp);
dest.writeIntArray(mPowerComponents);
@@ -202,6 +214,7 @@
private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
private long mFromTimestamp;
private long mToTimestamp;
+ private double mMinConsumedPowerThreshold = 0;
private @BatteryConsumer.PowerComponent int[] mPowerComponents;
/**
@@ -301,5 +314,14 @@
mMaxStatsAgeMs = maxStatsAgeMs;
return this;
}
+
+ /**
+ * Set the minimal power component consumed power threshold. The small power consuming
+ * components will be reported as zero.
+ */
+ public Builder setMinConsumedPowerThreshold(double minConsumedPowerThreshold) {
+ mMinConsumedPowerThreshold = minConsumedPowerThreshold;
+ return this;
+ }
}
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 5dffa0a..9e5f539 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -461,9 +461,11 @@
private static final byte POWER_MODEL_UNINITIALIZED = -1;
private final BatteryConsumer.BatteryConsumerData mData;
+ private final double mMinConsumedPowerThreshold;
- Builder(BatteryConsumer.BatteryConsumerData data) {
+ Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold) {
mData = data;
+ mMinConsumedPowerThreshold = minConsumedPowerThreshold;
for (BatteryConsumer.Key[] keys : mData.layout.keys) {
for (BatteryConsumer.Key key : keys) {
if (key.mPowerModelColumnIndex != -1) {
@@ -476,6 +478,9 @@
@NonNull
public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
int powerModel) {
+ if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
+ componentPower = 0;
+ }
mData.putDouble(key.mPowerColumnIndex, componentPower);
if (key.mPowerModelColumnIndex != -1) {
mData.putInt(key.mPowerModelColumnIndex, powerModel);
@@ -491,6 +496,9 @@
*/
@NonNull
public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
+ componentPower = 0;
+ }
final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
if (index < 0 || index >= mData.layout.customPowerComponentCount) {
throw new IllegalArgumentException(
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 103452d..03a1b6f 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -207,17 +207,18 @@
private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED;
private boolean mExcludeFromBatteryUsageStats;
- public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid) {
- this(data, batteryStatsUid, batteryStatsUid.getUid());
+ public Builder(BatteryConsumerData data, @NonNull BatteryStats.Uid batteryStatsUid,
+ double minConsumedPowerThreshold) {
+ this(data, batteryStatsUid, batteryStatsUid.getUid(), minConsumedPowerThreshold);
}
- public Builder(BatteryConsumerData data, int uid) {
- this(data, null, uid);
+ public Builder(BatteryConsumerData data, int uid, double minConsumedPowerThreshold) {
+ this(data, null, uid, minConsumedPowerThreshold);
}
private Builder(BatteryConsumerData data, @Nullable BatteryStats.Uid batteryStatsUid,
- int uid) {
- super(data, CONSUMER_TYPE_UID);
+ int uid, double minConsumedPowerThreshold) {
+ super(data, CONSUMER_TYPE_UID, minConsumedPowerThreshold);
mBatteryStatsUid = batteryStatsUid;
mUid = uid;
mIsVirtualUid = mUid == Process.SDK_SANDBOX_VIRTUAL_UID;
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
index 6b4a5cf..a2ff078 100644
--- a/core/java/android/os/UserBatteryConsumer.java
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -107,8 +107,8 @@
public static final class Builder extends BaseBuilder<Builder> {
private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
- Builder(BatteryConsumerData data, int userId) {
- super(data, CONSUMER_TYPE_USER);
+ Builder(BatteryConsumerData data, int userId, double minConsumedPowerThreshold) {
+ super(data, CONSUMER_TYPE_USER, minConsumedPowerThreshold);
data.putLong(COLUMN_INDEX_USER_ID, userId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cf41cf..d66ffce 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5164,7 +5164,6 @@
public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
/** {@hide} */
- @Readable
public static final String RINGTONE_CACHE = "ringtone_cache";
/** {@hide} */
public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d9ac4850..8ffb022 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1292,9 +1292,9 @@
visibleFrame.intersect(mInsetsState.getDisplayFrame());
WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
null /* ignoringVisibilityState */, config.isScreenRound(),
- false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
- mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
- config.windowConfiguration.getWindowingMode(), null /* idSideMap */);
+ mLayout.softInputMode, mLayout.flags, SYSTEM_UI_FLAG_VISIBLE,
+ mLayout.type, config.windowConfiguration.getActivityType(),
+ null /* idSideMap */);
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 5127f05..dfa0000 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -1155,9 +1155,15 @@
}
private void allocateFrameTimelines(int length) {
- mFrameTimelines = new FrameTimeline[length];
- for (int i = 0; i < mFrameTimelines.length; i++) {
- mFrameTimelines[i] = new FrameTimeline();
+ // Maintain one default frame timeline for API (such as getFrameTimelines and
+ // getPreferredFrameTimeline) consistency. It should have default data when accessed.
+ length = Math.max(1, length);
+
+ if (mFrameTimelines == null || mFrameTimelines.length != length) {
+ mFrameTimelines = new FrameTimeline[length];
+ for (int i = 0; i < mFrameTimelines.length; i++) {
+ mFrameTimelines[i] = new FrameTimeline();
+ }
}
}
@@ -1167,12 +1173,7 @@
*/
FrameTimeline update(
long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
- // Even if the frame timelines length is 0, continue with allocation for API
- // FrameData.getFrameTimelines consistency. The 0 length frame timelines code path
- // should only occur when USE_VSYNC property is false.
- if (mFrameTimelines.length != vsyncEventData.frameTimelinesLength) {
- allocateFrameTimelines(vsyncEventData.frameTimelinesLength);
- }
+ allocateFrameTimelines(vsyncEventData.frameTimelinesLength);
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = vsyncEventData.preferredFrameTimelineIndex;
for (int i = 0; i < mFrameTimelines.length; i++) {
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6c5f195..b2e164d 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,7 +16,7 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.EventLogTags.IMF_IME_ANIM_CANCEL;
import static android.view.EventLogTags.IMF_IME_ANIM_FINISH;
import static android.view.EventLogTags.IMF_IME_ANIM_START;
@@ -40,6 +40,7 @@
import static android.view.InsetsState.ISIDE_RIGHT;
import static android.view.InsetsState.ISIDE_TOP;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
@@ -63,7 +64,6 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
-import android.view.WindowManager.LayoutParams;
import android.view.animation.Interpolator;
import android.view.inputmethod.ImeTracker;
@@ -401,10 +401,9 @@
private Insets getInsetsFromState(InsetsState state, Rect frame,
@Nullable @InternalInsetsSide SparseIntArray idSideMap) {
return state.calculateInsets(frame, null /* ignoringVisibilityState */,
- false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
- LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
+ false /* isScreenRound */, SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode */,
0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, idSideMap).getInsets(mTypes);
+ ACTIVITY_TYPE_UNDEFINED, idSideMap).getInsets(mTypes);
}
/** Computes the insets relative to the given frame. */
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c6d8bd1..62243e2 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -652,7 +652,7 @@
private int mLastLegacySoftInputMode;
private int mLastLegacyWindowFlags;
private int mLastLegacySystemUiFlags;
- private int mLastWindowingMode;
+ private int mLastActivityType;
private boolean mStartingAnimation;
private int mCaptionInsetsHeight = 0;
private int mImeCaptionBarInsetsHeight = 0;
@@ -803,10 +803,10 @@
}
}
- WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
- mLastInsets.isRound(), false /* alwaysConsumeSystemBars */,
+ WindowInsets insets = state.calculateInsets(mFrame,
+ mState /* ignoringVisibilityState */, mLastInsets.isRound(),
mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags,
- mWindowType, mLastWindowingMode, null /* idSideMap */);
+ mWindowType, mLastActivityType, null /* idSideMap */);
mHost.dispatchWindowInsetsAnimationProgress(insets,
Collections.unmodifiableList(runningAnimations));
if (DEBUG) {
@@ -969,30 +969,29 @@
}
/**
- * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, boolean, int, int, int, int,
- * int, android.util.SparseIntArray)
+ * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
+ * android.util.SparseIntArray)
*/
@VisibleForTesting
- public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars,
- int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags,
- int legacySystemUiFlags) {
+ public WindowInsets calculateInsets(boolean isScreenRound, int windowType, int activityType,
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
mWindowType = windowType;
- mLastWindowingMode = windowingMode;
+ mLastActivityType = activityType;
mLastLegacySoftInputMode = legacySoftInputMode;
mLastLegacyWindowFlags = legacyWindowFlags;
mLastLegacySystemUiFlags = legacySystemUiFlags;
- mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
- isScreenRound, alwaysConsumeSystemBars, legacySoftInputMode, legacyWindowFlags,
- legacySystemUiFlags, windowType, windowingMode, null /* idSideMap */);
+ mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState */,
+ isScreenRound, legacySoftInputMode, legacyWindowFlags,
+ legacySystemUiFlags, windowType, activityType, null /* idSideMap */);
return mLastInsets;
}
/**
* @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int)
*/
- public Insets calculateVisibleInsets(int windowType, int windowingMode,
+ public Insets calculateVisibleInsets(int windowType, int activityType,
@SoftInputModeFlags int softInputMode, int windowFlags) {
- return mState.calculateVisibleInsets(mFrame, windowType, windowingMode, softInputMode,
+ return mState.calculateVisibleInsets(mFrame, windowType, activityType, softInputMode,
windowFlags);
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index af24140..59e0932 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
@@ -39,7 +40,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.ActivityType;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
@@ -136,9 +137,8 @@
* @return The calculated insets.
*/
public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
- boolean isScreenRound, boolean alwaysConsumeSystemBars,
- int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
- int windowType, @WindowConfiguration.WindowingMode int windowingMode,
+ boolean isScreenRound, int legacySoftInputMode, int legacyWindowFlags,
+ int legacySystemUiFlags, int windowType, @ActivityType int activityType,
@Nullable @InternalInsetsSide SparseIntArray idSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
@@ -185,9 +185,8 @@
if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
compatInsetsTypes &= ~statusBars();
}
- if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
- // Clear all types but forceConsumingTypes.
- compatInsetsTypes &= forceConsumingTypes;
+ if (clearsCompatInsets(windowType, legacyWindowFlags, activityType, forceConsumingTypes)) {
+ compatInsetsTypes = 0;
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
@@ -295,26 +294,27 @@
return insets;
}
- public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode,
+ public Insets calculateVisibleInsets(Rect frame, int windowType, @ActivityType int activityType,
@SoftInputModeFlags int softInputMode, int windowFlags) {
- final boolean clearsCompatInsets = clearsCompatInsets(
- windowType, windowFlags, windowingMode);
final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
? systemBars() | ime()
: systemBars();
+ @InsetsType int forceConsumingTypes = 0;
Insets insets = Insets.NONE;
for (int i = mSources.size() - 1; i >= 0; i--) {
final InsetsSource source = mSources.valueAt(i);
if ((source.getType() & visibleInsetsTypes) == 0) {
continue;
}
- if (clearsCompatInsets && !source.hasFlags(FLAG_FORCE_CONSUMING)) {
- continue;
+ if (source.hasFlags(FLAG_FORCE_CONSUMING)) {
+ forceConsumingTypes |= source.getType();
}
insets = Insets.max(source.calculateVisibleInsets(frame), insets);
}
- return insets;
+ return clearsCompatInsets(windowType, windowFlags, activityType, forceConsumingTypes)
+ ? Insets.NONE
+ : insets;
}
/**
@@ -662,10 +662,15 @@
mSources.put(source.getId(), source);
}
- public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) {
+ public static boolean clearsCompatInsets(int windowType, int windowFlags,
+ @ActivityType int activityType, @InsetsType int forceConsumingTypes) {
return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0
+ // For compatibility reasons, this excludes the wallpaper, the system error windows,
+ // and the app windows while any system bar is forcibly consumed.
&& windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR
- && !WindowConfiguration.inMultiWindowMode(windowingMode);
+ // This ensures the app content won't be obscured by compat insets even if the app
+ // has FLAG_LAYOUT_NO_LIMITS.
+ && (forceConsumingTypes == 0 || activityType != ACTIVITY_TYPE_STANDARD);
}
public void dump(String prefix, PrintWriter pw) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c1ce5e0..d680d04 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -50,6 +50,8 @@
import static android.view.ViewRootImplProto.WIDTH;
import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
import static android.view.ViewRootImplProto.WIN_FRAME;
+import static android.view.ViewRootRefreshRateController.RefreshRatePref.LOWER;
+import static android.view.ViewRootRefreshRateController.RefreshRatePref.RESTORE;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -96,6 +98,7 @@
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UiContext;
+import android.annotation.UiThread;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ICompatCameraControlCallback;
@@ -240,6 +243,7 @@
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
@@ -423,6 +427,74 @@
}
/**
+ * Used to notify if the user is typing or not.
+ * @hide
+ */
+ public interface TypingHintNotifier {
+ /**
+ * Called when the typing hint is changed. This would be invoked by the
+ * {@link android.view.inputmethod.RemoteInputConnectionImpl}
+ * to hint if the user is typing when the it is {@link #isActive() active}.
+ *
+ * This can be only happened on the UI thread. The behavior won't be guaranteed if
+ * invoking this on a non-UI thread.
+ *
+ * @param isTyping {@code true} if the user is typing.
+ */
+ @UiThread
+ void onTypingHintChanged(boolean isTyping);
+
+ /**
+ * Indicates whether the notifier is currently in active state or not.
+ *
+ * @see #deactivate()
+ */
+ boolean isActive();
+
+ /**
+ * Deactivate the notifier when no longer in use. Mostly invoked when finishing the typing.
+ */
+ void deactivate();
+ }
+
+ /**
+ * The {@link TypingHintNotifier} implementation used to handle
+ * the refresh rate preference when the typing state is changed.
+ */
+ private static class TypingHintNotifierImpl implements TypingHintNotifier {
+
+ private final AtomicReference<TypingHintNotifier> mActiveNotifier;
+
+ @NonNull
+ private final ViewRootRefreshRateController mController;
+
+ TypingHintNotifierImpl(@NonNull AtomicReference<TypingHintNotifier> notifier,
+ @NonNull ViewRootRefreshRateController controller) {
+ mController = controller;
+ mActiveNotifier = notifier;
+ }
+
+ @Override
+ public void onTypingHintChanged(boolean isTyping) {
+ if (!isActive()) {
+ // No-op when the listener was deactivated.
+ return;
+ }
+ mController.updateRefreshRatePreference(isTyping ? LOWER : RESTORE);
+ }
+
+ @Override
+ public boolean isActive() {
+ return mActiveNotifier.get() == this;
+ }
+
+ @Override
+ public void deactivate() {
+ mActiveNotifier.compareAndSet(this, null);
+ }
+ }
+
+ /**
* Callback used to notify corresponding activity about camera compat control changes, override
* configuration change and make sure that all resources are set correctly before updating the
* ViewRootImpl's internal state.
@@ -430,6 +502,32 @@
private ActivityConfigCallback mActivityConfigCallback;
/**
+ * The current active {@link TypingHintNotifier} to handle
+ * typing hint change operations.
+ */
+ private final AtomicReference<TypingHintNotifier> mActiveTypingHintNotifier =
+ new AtomicReference<>(null);
+
+ /**
+ * Create a {@link TypingHintNotifier} if the client support variable
+ * refresh rate for typing. The {@link TypingHintNotifier} is created
+ * and mapped to a new active input connection each time.
+ *
+ * @hide
+ */
+ @Nullable
+ public TypingHintNotifier createTypingHintNotifierIfSupported() {
+ if (mRefreshRateController == null) {
+ return null;
+ }
+ final TypingHintNotifier newNotifier = new TypingHintNotifierImpl(mActiveTypingHintNotifier,
+ mRefreshRateController);
+ mActiveTypingHintNotifier.set(newNotifier);
+
+ return newNotifier;
+ }
+
+ /**
* Used when configuration change first updates the config of corresponding activity.
* In that case we receive a call back from {@link ActivityThread} and this flag is used to
* preserve the initial value.
@@ -858,6 +956,8 @@
private final InsetsController mInsetsController;
private final ImeFocusController mImeFocusController;
+ private ViewRootRefreshRateController mRefreshRateController;
+
private boolean mIsSurfaceOpaque;
private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =
@@ -1048,6 +1148,13 @@
mViewConfiguration,
mContext.getSystemService(InputMethodManager.class));
+ // Whether the variable refresh rate for typing is supported.
+ boolean useVariableRefreshRateWhenTyping = context.getResources().getBoolean(
+ R.bool.config_variableRefreshRateTypingSupported);
+ if (useVariableRefreshRateWhenTyping) {
+ mRefreshRateController = new ViewRootRefreshRateController(this);
+ }
+
mViewBoundsSandboxingEnabled = getViewBoundsSandboxingEnabled();
mIsStylusPointerIconEnabled =
InputSettings.isStylusPointerIconEnabled(mContext);
@@ -2089,6 +2196,10 @@
if (!mIsInTraversal) {
scheduleTraversals();
}
+
+ if (!mInsetsController.getState().isSourceOrDefaultVisible(ID_IME, Type.ime())) {
+ notifyLeaveTypingEvent();
+ }
}
@Override
@@ -2851,16 +2962,15 @@
if (mLastWindowInsets == null || forceConstruct) {
final Configuration config = getConfiguration();
mLastWindowInsets = mInsetsController.calculateInsets(
- config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
- mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
- mWindowAttributes.softInputMode, mWindowAttributes.flags,
- (mWindowAttributes.systemUiVisibility
+ config.isScreenRound(), mWindowAttributes.type,
+ config.windowConfiguration.getActivityType(), mWindowAttributes.softInputMode,
+ mWindowAttributes.flags, (mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets(
- mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
+ mWindowAttributes.type, config.windowConfiguration.getActivityType(),
mWindowAttributes.softInputMode, mWindowAttributes.flags).toRect());
}
return mLastWindowInsets;
@@ -6851,6 +6961,17 @@
}
/**
+ * Restores the refresh rate after leaving typing, the leaving typing cases like
+ * the IME insets is invisible or the user interacts the screen outside keyboard.
+ */
+ @UiThread
+ private void notifyLeaveTypingEvent() {
+ if (mRefreshRateController != null && mActiveTypingHintNotifier.get() != null) {
+ mRefreshRateController.updateRefreshRatePreference(RESTORE);
+ }
+ }
+
+ /**
* Delivers post-ime input events to the view hierarchy.
*/
final class ViewPostImeInputStage extends InputStage {
@@ -7067,6 +7188,10 @@
mLastClickToolType = event.getToolType(event.getActionIndex());
}
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ notifyLeaveTypingEvent();
+ }
+
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
// If the event was fully handled by the handwriting initiator, then don't dispatch it
diff --git a/core/java/android/view/ViewRootRefreshRateController.java b/core/java/android/view/ViewRootRefreshRateController.java
new file mode 100644
index 0000000..cb9a81c
--- /dev/null
+++ b/core/java/android/view/ViewRootRefreshRateController.java
@@ -0,0 +1,220 @@
+/*
+ * 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 android.view;
+
+import static android.os.Trace.TRACE_TAG_VIEW;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Trace;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller to request refresh rate preference operations to the {@link ViewRootImpl}.
+ *
+ * @hide
+ */
+public class ViewRootRefreshRateController {
+
+ private static final String TAG = "VRRefreshRateController";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final float TARGET_REFRESH_RATE_UPPER_BOUND = 60f;
+
+ @NonNull
+ private final ViewRootImpl mViewRootImpl;
+
+ private final RefreshRateParams mRateParams;
+
+ private final boolean mHasPreferredRefreshRate;
+
+ private int mRefreshRatePref = RefreshRatePref.NONE;
+
+ private boolean mMaxRefreshRateOverride = false;
+
+ @IntDef(value = {
+ RefreshRatePref.NONE,
+ RefreshRatePref.LOWER,
+ RefreshRatePref.RESTORE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RefreshRatePref {
+ /**
+ * Indicates that no refresh rate preference.
+ */
+ int NONE = 0;
+
+ /**
+ * Indicates that apply the lower refresh rate.
+ */
+ int LOWER = 1;
+
+ /**
+ * Indicates that restore to previous refresh rate.
+ */
+ int RESTORE = 2;
+ }
+
+ public ViewRootRefreshRateController(@NonNull ViewRootImpl viewRoot) {
+ mViewRootImpl = viewRoot;
+ mRateParams = new RefreshRateParams(getLowerSupportedRefreshRate());
+ mHasPreferredRefreshRate = hasPreferredRefreshRate();
+ if (mHasPreferredRefreshRate && DEBUG) {
+ Log.d(TAG, "App has preferred refresh rate. name:" + viewRoot);
+ }
+ }
+
+ /**
+ * Updates the preference to {@link ViewRootRefreshRateController#mRefreshRatePref},
+ * and check if it's needed to update the preferred refresh rate on demand. Like if the
+ * user is typing, try to apply the {@link RefreshRateParams#mTargetRefreshRate}.
+ *
+ * @param refreshRatePref to indicate the refresh rate preference
+ */
+ public void updateRefreshRatePreference(@RefreshRatePref int refreshRatePref) {
+ mRefreshRatePref = refreshRatePref;
+ doRefreshRateCheck();
+ }
+
+ private void doRefreshRateCheck() {
+ if (mRefreshRatePref == RefreshRatePref.NONE) {
+ return;
+ }
+ if (mHasPreferredRefreshRate) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "mMaxRefreshRateOverride:" + mMaxRefreshRateOverride
+ + ", mRefreshRatePref:" + refreshRatePrefToString(mRefreshRatePref));
+ }
+
+ switch (mRefreshRatePref) {
+ case RefreshRatePref.LOWER :
+ if (!mMaxRefreshRateOverride) {
+ // Save previous preferred rate before update
+ mRateParams.savePreviousRefreshRateParams(mViewRootImpl.mWindowAttributes);
+ updateMaxRefreshRate();
+ } else if (mViewRootImpl.mDisplay.getRefreshRate()
+ > mRateParams.mTargetRefreshRate) {
+ // Boosted, try to update again.
+ updateMaxRefreshRate();
+ }
+ break;
+ case RefreshRatePref.RESTORE :
+ resetRefreshRate();
+ break;
+ default :
+ throw new RuntimeException("Unexpected value: " + mRefreshRatePref);
+ }
+ }
+
+ private void updateMaxRefreshRate() {
+ Trace.traceBegin(TRACE_TAG_VIEW, "VRRC.updateMaxRefreshRate");
+ WindowManager.LayoutParams params = mViewRootImpl.mWindowAttributes;
+ params.preferredMaxDisplayRefreshRate = mRateParams.mTargetRefreshRate;
+ mViewRootImpl.setLayoutParams(params, false);
+ mMaxRefreshRateOverride = true;
+ Trace.instant(TRACE_TAG_VIEW, "VRRC update preferredMax="
+ + mRateParams.mTargetRefreshRate);
+ Trace.traceEnd(TRACE_TAG_VIEW);
+ if (DEBUG) {
+ Log.d(TAG, "update max refresh rate to: " + params.preferredMaxDisplayRefreshRate);
+ }
+ }
+
+ private void resetRefreshRate() {
+ if (!mMaxRefreshRateOverride) {
+ return;
+ }
+ Trace.traceBegin(TRACE_TAG_VIEW, "VRRC.resetRefreshRate");
+ WindowManager.LayoutParams params = mViewRootImpl.mWindowAttributes;
+ params.preferredMaxDisplayRefreshRate = mRateParams.mPreviousPreferredMaxRefreshRate;
+ mViewRootImpl.setLayoutParams(params, false);
+ mMaxRefreshRateOverride = false;
+ Trace.instant(TRACE_TAG_VIEW, "VRRC restore previous="
+ + mRateParams.mPreviousPreferredMaxRefreshRate);
+ Trace.traceEnd(TRACE_TAG_VIEW);
+ if (DEBUG) {
+ Log.d(TAG, "reset max refresh rate to: " + params.preferredMaxDisplayRefreshRate);
+ }
+ }
+
+ private boolean hasPreferredRefreshRate() {
+ WindowManager.LayoutParams params = mViewRootImpl.mWindowAttributes;
+ return params.preferredRefreshRate > 0
+ || params.preferredMaxDisplayRefreshRate > 0
+ || params.preferredMinDisplayRefreshRate > 0
+ || params.preferredDisplayModeId > 0;
+ }
+
+ private float getLowerSupportedRefreshRate() {
+ final Display display = mViewRootImpl.mDisplay;
+ final Display.Mode defaultMode = display.getDefaultMode();
+ float targetRefreshRate = defaultMode.getRefreshRate();
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (mode.getRefreshRate() < targetRefreshRate) {
+ targetRefreshRate = mode.getRefreshRate();
+ }
+ }
+ if (targetRefreshRate < TARGET_REFRESH_RATE_UPPER_BOUND) {
+ targetRefreshRate = TARGET_REFRESH_RATE_UPPER_BOUND;
+ }
+ return targetRefreshRate;
+ }
+
+ private static String refreshRatePrefToString(@RefreshRatePref int pref) {
+ switch (pref) {
+ case RefreshRatePref.NONE:
+ return "NONE";
+ case RefreshRatePref.LOWER:
+ return "LOWER";
+ case RefreshRatePref.RESTORE:
+ return "RESTORE";
+ default:
+ return "Unknown pref=" + pref;
+ }
+ }
+
+ /**
+ * A class for recording refresh rate parameters of the target view, including the target
+ * refresh rate we want to apply when entering particular states, and the original preferred
+ * refresh rate for restoring when leaving the state.
+ */
+ private static class RefreshRateParams {
+ float mTargetRefreshRate;
+
+ float mPreviousPreferredMaxRefreshRate = 0;
+
+ RefreshRateParams(float targetRefreshRate) {
+ mTargetRefreshRate = targetRefreshRate;
+ if (DEBUG) {
+ Log.d(TAG, "The target rate: " + targetRefreshRate);
+ }
+ }
+ void savePreviousRefreshRateParams(WindowManager.LayoutParams param) {
+ mPreviousPreferredMaxRefreshRate = param.preferredMaxDisplayRefreshRate;
+ if (DEBUG) {
+ Log.d(TAG, "Save previous params, preferred: " + param.preferredRefreshRate
+ + ", Max: " + param.preferredMaxDisplayRefreshRate);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index e9d7b9b..364adc7 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,6 +28,7 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UiThread;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -182,6 +183,8 @@
private CancellationSignalBeamer.Receiver mBeamer;
+ private ViewRootImpl.TypingHintNotifier mTypingHintNotifier;
+
RemoteInputConnectionImpl(@NonNull Looper looper,
@NonNull InputConnection inputConnection,
@NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
@@ -190,6 +193,12 @@
mH = new Handler(mLooper);
mParentInputMethodManager = inputMethodManager;
mServedView = new WeakReference<>(servedView);
+ if (servedView != null) {
+ final ViewRootImpl viewRoot = servedView.getViewRootImpl();
+ if (viewRoot != null) {
+ mTypingHintNotifier = viewRoot.createTypingHintNotifierIfSupported();
+ }
+ }
}
/**
@@ -364,6 +373,12 @@
return;
}
dispatch(() -> {
+ notifyTypingHint(false /* isTyping */);
+ // Deactivate the notifier when finishing typing.
+ if (mTypingHintNotifier != null) {
+ mTypingHintNotifier.deactivate();
+ }
+
// Note that we do not need to worry about race condition here, because 1) mFinished is
// updated only inside this block, and 2) the code here is running on a Handler hence we
// assume multiple closeConnection() tasks will not be handled at the same time.
@@ -628,6 +643,7 @@
return;
}
ic.commitText(text, newCursorPosition);
+ notifyTypingHint(true /* isTyping */);
});
}
@@ -783,6 +799,7 @@
return;
}
ic.setComposingText(text, newCursorPosition);
+ notifyTypingHint(true /* isTyping */);
});
}
@@ -910,6 +927,7 @@
return;
}
ic.deleteSurroundingText(beforeLength, afterLength);
+ notifyTypingHint(true /* isTyping */);
});
}
@@ -1473,4 +1491,16 @@
private static boolean useImeTracing() {
return ImeTracing.getInstance().isEnabled();
}
+
+ /**
+ * Dispatch the typing hint to {@link ViewRootImpl.TypingHintNotifier}.
+ * The input connection indicates that the user is typing when {@link #commitText} or
+ * {@link #setComposingText)} and the user finish typing when {@link #deactivate()}.
+ */
+ @UiThread
+ private void notifyTypingHint(boolean isTyping) {
+ if (mTypingHintNotifier != null) {
+ mTypingHintNotifier.onTypingHintChanged(isTyping);
+ }
+ }
}
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 2858f0a..e32c8e5 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -16,7 +16,7 @@
package android.window;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -80,7 +80,7 @@
final Rect bounds;
final float density;
final boolean isScreenRound;
- final int windowingMode;
+ final int activityType;
synchronized (ResourcesManager.getInstance()) {
final Configuration config = mContext.getResources().getConfiguration();
final WindowConfiguration winConfig = config.windowConfiguration;
@@ -90,11 +90,11 @@
// as DisplayMetrics#density
density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
isScreenRound = config.isScreenRound();
- windowingMode = winConfig.getWindowingMode();
+ activityType = winConfig.getActivityType();
}
final IBinder token = Context.getToken(mContext);
final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
- mContext.getDisplayId(), token, bounds, isScreenRound, windowingMode);
+ mContext.getDisplayId(), token, bounds, isScreenRound, activityType);
return new WindowMetrics(new Rect(bounds), insetsSupplier, density);
}
@@ -105,23 +105,22 @@
* @param token the token of Activity or WindowContext
* @param bounds the window bounds to calculate insets for
* @param isScreenRound if the display identified by displayId is round
- * @param windowingMode the windowing mode of the window to calculate insets for
+ * @param activityType the activity type of the window to calculate insets for
* @return WindowInsets calculated for the given window bounds, on the given display
*/
private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId, IBinder token,
- Rect bounds, boolean isScreenRound, int windowingMode) {
+ Rect bounds, boolean isScreenRound, int activityType) {
try {
final InsetsState insetsState = new InsetsState();
- final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
- .getWindowInsets(displayId, token, insetsState);
+ WindowManagerGlobal.getWindowManagerService().getWindowInsets(
+ displayId, token, insetsState);
final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale();
if (overrideInvScale != 1f) {
insetsState.scale(overrideInvScale);
}
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState */,
- isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING,
- 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
- WindowManager.LayoutParams.INVALID_WINDOW_TYPE, windowingMode,
+ isScreenRound, SOFT_INPUT_ADJUST_NOTHING, 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
+ WindowManager.LayoutParams.INVALID_WINDOW_TYPE, activityType,
null /* idSideMap */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -157,7 +156,7 @@
currentDisplayInfo.displayId, null /* token */,
new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
currentDisplayInfo.getNaturalHeight()), isScreenRound,
- WINDOWING_MODE_FULLSCREEN);
+ ACTIVITY_TYPE_UNDEFINED);
// Set the hardware-provided insets.
windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
currentDisplayInfo.roundedCorners)
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bb86801..86ca077 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1113,15 +1113,14 @@
if (insets != null) {
mLastForceConsumingTypes = insets.getForceConsumingTypes();
- @InsetsType int compatInsetsTypes =
+ final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags,
+ getResources().getConfiguration().windowConfiguration.getActivityType(),
+ mLastForceConsumingTypes);
+ final @InsetsType int compatInsetsTypes =
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
- if (clearsCompatInsets(attrs.type, attrs.flags,
- getResources().getConfiguration().windowConfiguration.getWindowingMode())) {
- compatInsetsTypes &= mLastForceConsumingTypes;
- }
final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars());
- final Insets systemInsets = compatInsetsTypes == 0
+ final Insets systemInsets = clearsCompatInsets
? Insets.NONE
: Insets.min(insets.getInsets(compatInsetsTypes), stableBarInsets);
mLastTopInset = systemInsets.top;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4184e79..72e7459 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6089,6 +6089,8 @@
<!-- Default value for Settings.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED -->
<bool name="config_searchPressHoldNavHandleEnabledDefault">true</bool>
+ <!-- Default value for Settings.ASSIST_LONG_PRESS_HOME_ENABLED for search overlay -->
+ <bool name="config_searchLongPressHomeEnabledDefault">true</bool>
<!-- The maximum byte size of the information contained in the bundle of
HotwordDetectedResult. -->
@@ -6562,6 +6564,9 @@
device. -->
<bool name="config_enableAppCloningBuildingBlocks">true</bool>
+ <!-- Whether the variable refresh rate when typing feature is enabled for the device. -->
+ <bool name="config_variableRefreshRateTypingSupported">false</bool>
+
<!-- Enables or disables support for repair mode. The feature creates a secure
environment to protect the user's privacy when the device is being repaired.
Off by default, since OEMs may have had a similar feature on their devices. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 81fbc38..7eec74a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4866,6 +4866,7 @@
<java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
<java-symbol type="bool" name="config_searchPressHoldNavHandleEnabledDefault" />
+ <java-symbol type="bool" name="config_searchLongPressHomeEnabledDefault" />
<java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
@@ -4942,6 +4943,8 @@
<java-symbol type="bool" name="config_repairModeSupported" />
+ <java-symbol type="bool" name="config_variableRefreshRateTypingSupported" />
+
<java-symbol type="string" name="config_devicePolicyManagementUpdater" />
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index 0676f89..aaaa3c7 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -241,7 +241,8 @@
final BatteryUsageStats.Builder builder =
new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"},
/* includePowerModels */ true,
- /* includeProcessStats */true)
+ /* includeProcessStats */ true,
+ /* minConsumedPowerThreshold */ 0)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
.setDischargeDurationMs(1234)
@@ -325,7 +326,7 @@
@Test
public void testLargeAtomTruncated() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[0], true, false);
+ new BatteryUsageStats.Builder(new String[0], true, false, 0);
// If not truncated, this BatteryUsageStats object would generate a proto buffer
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index a358c4f..57f0920 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1389,6 +1389,15 @@
android:resource="@xml/accessibility_shortcut_test_activity"/>
</activity>
+ <activity android:name="android.view.ViewRefreshRateTestActivity"
+ android:label="ViewRefreshRateTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<!-- Activity-level metadata -->
<meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" />
<meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
diff --git a/core/tests/coretests/res/layout/activity_refresh_rate_test.xml b/core/tests/coretests/res/layout/activity_refresh_rate_test.xml
new file mode 100644
index 0000000..ad57fc1
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_refresh_rate_test.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layout"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</LinearLayout>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index f45db23..8c93fbb 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -16,7 +16,7 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.InsetsSource.ID_IME;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -81,8 +81,7 @@
Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
mController.calculateInsets(
false,
- false,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mImeConsumer = mController.getImeSourceConsumer();
});
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index b8f0d5c..1568174 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -16,7 +16,7 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
@@ -171,8 +171,7 @@
mController.onStateChanged(state);
mController.calculateInsets(
false,
- false,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
});
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index b06cd39..906d84e 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -16,8 +16,9 @@
package android.view;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_TOP;
@@ -101,7 +102,7 @@
.setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
@@ -120,7 +121,7 @@
.setFrame(new Rect(0, 100, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(systemBars()));
@@ -139,7 +140,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -154,7 +155,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -169,7 +170,7 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
null);
assertEquals(0, insets.getSystemWindowInsetBottom());
assertEquals(100, insets.getInsets(ime()).bottom);
@@ -185,12 +186,12 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
+ SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
+ ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
+ ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
}
@@ -200,12 +201,12 @@
.setFrame(new Rect(0, 0, 100, 100))
.setVisible(false);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
+ TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
+ ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
}
@@ -213,22 +214,23 @@
public void testCalculateInsets_flagLayoutNoLimits() {
mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
.setFrame(new Rect(0, 0, 100, 100))
- .setVisible(true);
+ .setVisible(true)
+ .setFlags(FLAG_FORCE_CONSUMING);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
- 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ 0 /* legacySystemUiFlags */, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
- 0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, WINDOWING_MODE_UNDEFINED, null);
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ 0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
- 0 /* legacySystemUiFlags */, TYPE_WALLPAPER, WINDOWING_MODE_UNDEFINED, null);
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ 0 /* legacySystemUiFlags */, TYPE_WALLPAPER, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
- 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_FREEFORM, null);
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ 0 /* legacySystemUiFlags */, TYPE_APPLICATION, ACTIVITY_TYPE_STANDARD, null);
assertEquals(100, insets.getSystemWindowInsetTop());
}
@@ -243,7 +245,7 @@
.setVisible(true);
Insets visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ new Rect(0, 0, 100, 400), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
}
@@ -255,7 +257,7 @@
.setVisible(true);
Insets visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ new Rect(0, 0, 150, 400), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
}
@@ -269,7 +271,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -284,7 +286,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -292,11 +294,11 @@
@Test
public void testCalculateInsets_emptyIme() {
- WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false, false,
- SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false,
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
mState.getOrCreateSource(ID_IME, ime());
- WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false, false,
- SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false,
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(Insets.NONE, insets1.getInsets(ime()));
assertEquals(Insets.NONE, insets2.getInsets(ime()));
assertEquals(insets1, insets2);
@@ -311,8 +313,8 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
mState.removeSource(ID_IME);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
@@ -527,7 +529,7 @@
.setFrame(new Rect(0, 100, 100, 300))
.setVisible(true);
Insets visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_PAN, 0 /* windowFlags */);
assertEquals(Insets.of(0, 100, 0, 100), visibleInsets);
}
@@ -546,7 +548,7 @@
.setFrame(new Rect(0, 100, 100, 300))
.setVisible(true);
Insets visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
assertEquals(Insets.of(0, 100, 0, 0), visibleInsets);
}
@@ -565,7 +567,7 @@
.setFrame(new Rect(0, 100, 100, 300))
.setVisible(true);
Insets visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ new Rect(0, 0, 100, 300), TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
SOFT_INPUT_ADJUST_PAN, FLAG_LAYOUT_NO_LIMITS);
assertEquals(Insets.NONE, visibleInsets);
}
@@ -599,8 +601,8 @@
new Rect(0, 0, 1, 2),
new Rect(197, 296, 200, 300),
new Rect(197, 296, 200, 300)));
- DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false, false,
- SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false,
+ SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
new SparseIntArray()).getDisplayCutout();
assertEquals(0, cutout.getSafeInsetLeft());
assertEquals(1, cutout.getSafeInsetTop());
@@ -625,8 +627,8 @@
new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
- false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+ SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ ACTIVITY_TYPE_UNDEFINED, new SparseIntArray());
assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
@@ -642,8 +644,8 @@
mState.setDisplayFrame(new Rect(0, 0, 200, 400));
mState.setDisplayShape(DisplayShape.createDefaultDisplayShape(200, 400, false));
WindowInsets windowInsets = mState.calculateInsets(new Rect(10, 20, 200, 400), null, false,
- false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+ SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ ACTIVITY_TYPE_UNDEFINED, new SparseIntArray());
final DisplayShape expect =
DisplayShape.createDefaultDisplayShape(200, 400, false).setOffset(-10, -20);
diff --git a/core/tests/coretests/src/android/view/ViewRefreshRateTestActivity.java b/core/tests/coretests/src/android/view/ViewRefreshRateTestActivity.java
new file mode 100644
index 0000000..2b11851
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewRefreshRateTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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 android.view;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class ViewRefreshRateTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_refresh_rate_test);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootRefreshRateControllerTest.java b/core/tests/coretests/src/android/view/ViewRootRefreshRateControllerTest.java
new file mode 100644
index 0000000..d278bc3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewRootRefreshRateControllerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.view;
+
+import static android.view.ViewRootRefreshRateController.RefreshRatePref.LOWER;
+import static android.view.ViewRootRefreshRateController.RefreshRatePref.RESTORE;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
+import android.widget.EditText;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewRootRefreshRateControllerTest {
+
+ private static final float TARGET_REFRESH_RATE_UPPER_BOUND = 60f;
+
+ private boolean mUseVariableRefreshRateWhenTyping;
+
+ private ViewRootRefreshRateController mRefreshRateController;
+
+ @Rule
+ public ActivityTestRule<ViewRefreshRateTestActivity> mActivityRule =
+ new ActivityTestRule<>(ViewRefreshRateTestActivity.class);
+
+ private ViewRefreshRateTestActivity mActivity;
+
+ private float mLowestSupportRefreshRate;
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ mLowestSupportRefreshRate = getLowerSupportedRefreshRate();
+ mUseVariableRefreshRateWhenTyping = mInstrumentation.getContext().getResources()
+ .getBoolean(com.android.internal.R.bool.config_variableRefreshRateTypingSupported);
+ }
+
+ @Test
+ public void testUpdateRefreshRatePreference_shouldLowerThenRestore() throws Throwable {
+ // Ignored if the feature is not enabled.
+ assumeTrue(mUseVariableRefreshRateWhenTyping);
+
+ final ViewGroup viewGroup = mActivity.findViewById(R.id.layout);
+ final EditText editText = new EditText(mActivity);
+
+ mActivityRule.runOnUiThread(() -> viewGroup.addView(editText));
+ mInstrumentation.waitForIdleSync();
+
+ final ViewRootImpl viewRootImpl = editText.getViewRootImpl();
+ mRefreshRateController = new ViewRootRefreshRateController(viewRootImpl);
+ final float originalPreferredMaxDisplayRefreshRate =
+ viewRootImpl.mWindowAttributes.preferredMaxDisplayRefreshRate;
+
+ mRefreshRateController.updateRefreshRatePreference(LOWER);
+
+ // Update to lower rate.
+ assertEquals(viewRootImpl.mWindowAttributes.preferredMaxDisplayRefreshRate,
+ mLowestSupportRefreshRate);
+
+ mRefreshRateController.updateRefreshRatePreference(RESTORE);
+
+ // Restore to previous preferred rate.
+ assertEquals(viewRootImpl.mWindowAttributes.preferredMaxDisplayRefreshRate,
+ originalPreferredMaxDisplayRefreshRate);
+ }
+
+ private float getLowerSupportedRefreshRate() {
+ final Display display = mActivity.getDisplay();
+ final Display.Mode defaultMode = display.getDefaultMode();
+ float targetRefreshRate = defaultMode.getRefreshRate();
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (mode.getRefreshRate() < targetRefreshRate) {
+ targetRefreshRate = mode.getRefreshRate();
+ }
+ }
+ if (targetRefreshRate < TARGET_REFRESH_RATE_UPPER_BOUND) {
+ targetRefreshRate = TARGET_REFRESH_RATE_UPPER_BOUND;
+ }
+ return targetRefreshRate;
+ }
+}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 43acdd5..ff369c8 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -40,7 +40,6 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -934,18 +933,6 @@
Settings.System.putStringForUser(resolver, setting,
ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
-
- // Stream selected ringtone into cache so it's available for playback
- // when CE storage is still locked
- if (ringtoneUri != null) {
- final Uri cacheUri = getCacheForType(type, context.getUserId());
- try (InputStream in = openRingtone(context, ringtoneUri);
- OutputStream out = resolver.openOutputStream(cacheUri, "wt")) {
- FileUtils.copy(in, out);
- } catch (IOException e) {
- Log.w(TAG, "Failed to cache ringtone: " + e);
- }
- }
}
private static boolean isInternalRingtoneUri(Uri uri) {
@@ -1041,28 +1028,6 @@
}
}
- /**
- * Try opening the given ringtone locally first, but failover to
- * {@link IRingtonePlayer} if we can't access it directly. Typically happens
- * when process doesn't hold
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
- */
- private static InputStream openRingtone(Context context, Uri uri) throws IOException {
- final ContentResolver resolver = context.getContentResolver();
- try {
- return resolver.openInputStream(uri);
- } catch (SecurityException | IOException e) {
- Log.w(TAG, "Failed to open directly; attempting failover: " + e);
- final IRingtonePlayer player = context.getSystemService(AudioManager.class)
- .getRingtonePlayer();
- try {
- return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
- } catch (Exception e2) {
- throw new IOException(e2);
- }
- }
- }
-
private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 15446b6..3790490 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -34,7 +34,6 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -53,11 +52,10 @@
private static final String DOWNLOADS_AUTHORITY = "downloads";
- private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = 1;
- private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = 2;
private PackageManager mPackageManager;
private UserManager mUserManager;
private boolean mAbortInstall = false;
+ private boolean mShouldFinish = true;
private final boolean mLocalLOGV = false;
@@ -130,7 +128,7 @@
mAbortInstall = true;
}
- checkDevicePolicyRestriction();
+ checkDevicePolicyRestrictions();
final String installerPackageNameFromIntent = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
@@ -149,7 +147,9 @@
if (mAbortInstall) {
setResult(RESULT_CANCELED);
- finish();
+ if (mShouldFinish) {
+ finish();
+ }
return;
}
@@ -291,58 +291,52 @@
return originatingUid == installerUid;
}
- private void checkDevicePolicyRestriction() {
- // Check for install apps user restriction first.
- final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
- if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
- if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
- mAbortInstall = true;
- showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
- return;
- } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- if (mLocalLOGV) {
- Log.i(TAG, "install not allowed by admin; showing "
- + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
- }
- mAbortInstall = true;
- startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
- return;
- }
+ private void checkDevicePolicyRestrictions() {
+ final String[] restrictions = new String[] {
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+ };
- final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
- final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
- final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
- & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
- if (systemRestriction != 0) {
- if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
+ final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
+ for (String restriction : restrictions) {
+ if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
+ continue;
+ }
+
mAbortInstall = true;
- showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
- } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- mAbortInstall = true;
- startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- mAbortInstall = true;
- startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+
+ // If the given restriction is set by an admin, display information about the
+ // admin enforcing the restriction for the affected user. If not enforced by the admin,
+ // show the system dialog.
+ final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
+ if (showAdminSupportDetailsIntent != null) {
+ if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
+ startActivity(showAdminSupportDetailsIntent);
+ } else {
+ if (mLocalLOGV) Log.i(TAG, "Restriction set by system: " + restriction);
+ mShouldFinish = false;
+ showDialogInner(restriction);
+ }
+ break;
}
}
/**
- * Replace any dialog shown by the dialog with the one for the given {@link #createDialog(int)}.
+ * Replace any dialog shown by the dialog with the one for the given
+ * {@link #createDialog(String)}.
*
- * @param id The dialog type to add
+ * @param restriction The restriction to create the dialog for
*/
- private void showDialogInner(int id) {
- if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")");
+ private void showDialogInner(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + restriction + ")");
DialogFragment currentDialog =
(DialogFragment) getFragmentManager().findFragmentByTag("dialog");
if (currentDialog != null) {
currentDialog.dismissAllowingStateLoss();
}
- DialogFragment newDialog = createDialog(id);
+ DialogFragment newDialog = createDialog(restriction);
if (newDialog != null) {
getFragmentManager().beginTransaction()
.add(newDialog, "dialog").commitAllowingStateLoss();
@@ -352,35 +346,20 @@
/**
* Create a new dialog.
*
- * @param id The id of the dialog (determines dialog type)
- *
+ * @param restriction The restriction to create the dialog for
* @return The dialog
*/
- private DialogFragment createDialog(int id) {
- if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
- switch (id) {
- case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
+ private DialogFragment createDialog(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "createDialog(" + restriction + ")");
+ switch (restriction) {
+ case UserManager.DISALLOW_INSTALL_APPS:
return PackageUtil.SimpleErrorDialog.newInstance(
R.string.install_apps_user_restriction_dlg_text);
- case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY:
return PackageUtil.SimpleErrorDialog.newInstance(
R.string.unknown_apps_user_restriction_dlg_text);
}
return null;
}
-
- private void startAdminSupportDetailsActivity(String restriction) {
- if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
-
- // If the given restriction is set by an admin, display information about the
- // admin enforcing the restriction for the affected user.
- final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
- final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
- if (showAdminSupportDetailsIntent != null) {
- if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
- startActivity(showAdminSupportDetailsIntent);
- } else {
- if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
- }
- }
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index e071c11..c4780b7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -16,9 +16,7 @@
*/
package com.android.packageinstaller;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
-import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.Manifest;
@@ -790,8 +788,12 @@
}
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (!isDestroyed()) {
- startActivity(getIntent().addFlags(
- FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP));
+ startActivity(getIntent());
+ // The start flag (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) doesn't
+ // work for the multiple user case, i.e. the caller task user and started
+ // Activity user are not the same. To avoid having multiple PIAs in the task,
+ // finish the current PackageInstallerActivity
+ finish();
}
}, 500);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f03ff00..ceda902 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -728,6 +728,14 @@
void onAudioModeChanged() {
dispatchAttributesChanged();
}
+
+ /**
+ * Notify that the audio category has changed.
+ */
+ public void onAudioDeviceCategoryChanged() {
+ dispatchAttributesChanged();
+ }
+
/**
* Get the device status as active or non-active per Bluetooth profile.
*
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 765edd7..e45913c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -58,6 +58,7 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -76,6 +77,7 @@
import android.database.sqlite.SQLiteQueryBuilder;
import android.hardware.camera2.utils.ArrayUtils;
import android.media.AudioManager;
+import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -110,6 +112,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -129,7 +132,10 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
@@ -841,29 +847,68 @@
uri = ContentProvider.getUriWithoutUserId(uri);
final String cacheRingtoneSetting;
- final String cacheName;
if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.RINGTONE;
- cacheName = Settings.System.RINGTONE_CACHE;
} else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.NOTIFICATION_SOUND;
- cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
} else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.ALARM_ALERT;
- cacheName = Settings.System.ALARM_ALERT_CACHE;
} else {
throw new FileNotFoundException("Direct file access no longer supported; "
+ "ringtone playback is available through android.media.Ringtone");
}
+ final File cacheFile = getCacheFile(cacheRingtoneSetting, userId);
+ return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+ }
+
+ @Nullable
+ private String getCacheName(String setting) {
+ if (Settings.System.RINGTONE.equals(setting)) {
+ return Settings.System.RINGTONE_CACHE;
+ } else if (Settings.System.NOTIFICATION_SOUND.equals(setting)) {
+ return Settings.System.NOTIFICATION_SOUND_CACHE;
+ } else if (Settings.System.ALARM_ALERT.equals(setting)) {
+ return Settings.System.ALARM_ALERT_CACHE;
+ }
+ return null;
+ }
+
+ @Nullable
+ private File getCacheFile(String setting, int userId) {
int actualCacheOwner;
// Redirect cache to parent if ringtone setting is owned by profile parent
synchronized (mLock) {
- actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId,
- cacheRingtoneSetting);
+ actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId, setting);
+ }
+ final String cacheName = getCacheName(setting);
+ if (cacheName == null) {
+ return null;
}
final File cacheFile = new File(getRingtoneCacheDir(actualCacheOwner), cacheName);
- return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+ return cacheFile;
+ }
+
+
+ /**
+ * Try opening the given ringtone locally first, but failover to
+ * {@link IRingtonePlayer} if we can't access it directly. Typically, happens
+ * when process doesn't hold {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+ */
+ private static InputStream openRingtone(Context context, Uri uri) throws IOException {
+ final ContentResolver resolver = context.getContentResolver();
+ try {
+ return resolver.openInputStream(uri);
+ } catch (SecurityException | IOException e) {
+ Log.w(LOG_TAG, "Failed to open directly; attempting failover: " + e);
+ final IRingtonePlayer player = context.getSystemService(AudioManager.class)
+ .getRingtonePlayer();
+ try {
+ return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
+ } catch (Exception e2) {
+ throw new IOException(e2);
+ }
+ }
}
private File getRingtoneCacheDir(int userId) {
@@ -1938,49 +1983,65 @@
return false;
}
- // Invalidate any relevant cache files
- String cacheName = null;
- if (Settings.System.RINGTONE.equals(name)) {
- cacheName = Settings.System.RINGTONE_CACHE;
- } else if (Settings.System.NOTIFICATION_SOUND.equals(name)) {
- cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
- } else if (Settings.System.ALARM_ALERT.equals(name)) {
- cacheName = Settings.System.ALARM_ALERT_CACHE;
- }
- if (cacheName != null) {
+ File cacheFile = getCacheFile(name, callingUserId);
+ if (cacheFile != null) {
if (!isValidAudioUri(name, value)) {
return false;
}
- final File cacheFile = new File(
- getRingtoneCacheDir(owningUserId), cacheName);
+ // Invalidate any relevant cache files
cacheFile.delete();
}
+ final boolean success;
// Mutate the value.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null, overrideableByRestore);
+ break;
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, false, null);
+ break;
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null);
+ break;
+ }
+
+ default: {
+ success = false;
+ Slog.e(LOG_TAG, "Unknown operation code: " + operation);
}
}
- Slog.e(LOG_TAG, "Unknown operation code: " + operation);
+ }
+
+ if (!success) {
return false;
}
+
+ if ((operation == MUTATION_OPERATION_INSERT || operation == MUTATION_OPERATION_UPDATE)
+ && cacheFile != null && value != null) {
+ final Uri ringtoneUri = Uri.parse(value);
+ // Stream selected ringtone into cache, so it's available for playback
+ // when CE storage is still locked
+ try (InputStream in = openRingtone(getContext(), ringtoneUri);
+ OutputStream out = new FileOutputStream(cacheFile)) {
+ FileUtils.copy(in, out);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
+ }
+ }
+ return true;
}
private boolean isValidAudioUri(String name, String uri) {
@@ -3267,20 +3328,21 @@
return Global.SECURE_FRP_MODE.equals(setting.getName());
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
- resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+ return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
null);
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
- return;
+ return false;
}
+ boolean success = false;
banConfigurationIfNecessary(type, prefix, settingsState);
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
@@ -3300,6 +3362,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3321,6 +3384,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3348,6 +3412,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3372,10 +3437,12 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
}
+ return success;
}
public void removeSettingsForPackageLocked(String packageName, int userId) {
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
deleted file mode 100644
index 3baae33..0000000
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/Theme.SystemUI.QuickSettings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:layout_marginStart="1dp"
- android:layout_marginEnd="0dp"
- android:layout_weight="1"
- android:gravity="center_vertical" >
-
- <LinearLayout
- android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:gravity="center_vertical"
- android:layout_centerVertical="true"
- android:orientation="vertical"
- android:layout_toEndOf="@android:id/checkbox"
- android:layout_toStartOf="@android:id/button1">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/zen_mode_condition_detail_item_interline_spacing"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@android:id/button1"
- style="@style/QSBorderlessButton"
- android:background="@drawable/ripple_drawable_20dp"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_centerVertical="true"
- android:scaleType="center"
- android:layout_toStartOf="@android:id/button2"
- android:contentDescription="@string/accessibility_quick_settings_less_time"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_qs_minus" />
-
- <ImageView
- android:id="@android:id/button2"
- style="@style/QSBorderlessButton"
- android:background="@drawable/ripple_drawable_20dp"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:scaleType="center"
- android:layout_centerVertical="true"
- android:contentDescription="@string/accessibility_quick_settings_more_time"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_qs_plus" />
-
-</RelativeLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index a9d2b30..4be572f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -55,7 +55,6 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -73,7 +72,6 @@
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
- private final ActivityStarter mActivityStarter;
private String mPackageName;
private int mUid;
@@ -89,10 +87,8 @@
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
- Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
- ActivityStarter activityStarter) {
+ Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
mFeatureFlags = featureFlags;
- mActivityStarter = activityStarter;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
}
@@ -313,16 +309,8 @@
// Start activity from the current foreground user to avoid creating a separate
// SystemUI process without access to recent tasks because it won't have
// WM Shell running inside.
- // It is also important to make sure the shade is dismissed, otherwise users won't
- // see the app selector.
mUserSelectingTask = true;
- mActivityStarter.startActivity(
- intent,
- /* dismissShade= */ true,
- /* animationController= */ null,
- /* showOverLockscreenWhenLocked= */ false,
- UserHandle.of(ActivityManager.getCurrentUser())
- );
+ startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
}
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 37f032b..2c15e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -125,6 +125,7 @@
// FrameCallback used to delay starting the light reveal animation until the next frame
private val startLightRevealCallback = TraceUtils.namedRunnable("startLightReveal") {
+ lightRevealAnimationPlaying = true
lightRevealAnimator.start()
}
@@ -268,7 +269,6 @@
decidedToAnimateGoingToSleep = true
shouldAnimateInKeyguard = true
- lightRevealAnimationPlaying = true
// Start the animation on the next frame. startAnimation() is called after
// PhoneWindowManager makes a binder call to System UI on
@@ -283,7 +283,8 @@
// dispatched, a race condition could make it possible for this callback to be run
// as the device is waking up. That results in the AOD UI being shown while we wake
// up, with unpredictable consequences.
- if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY)) {
+ if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY) &&
+ shouldAnimateInKeyguard) {
aodUiAnimationPlaying = true
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index e76f26d..e6f8c48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -134,6 +134,22 @@
verify(shadeViewController, times(1)).showAodUi()
}
+ @Test
+ fun testAodUiShowNotInvokedIfWakingUp() {
+ `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true)
+ `when`(powerManager.isInteractive).thenReturn(false)
+
+ val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ controller.startAnimation()
+ controller.onStartedWakingUp()
+
+ verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
+
+ callbackCaptor.value.run()
+
+ verify(shadeViewController, never()).showAodUi()
+ }
+
/**
* The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
* animation to start. If the device is woken up during the screen off, we should *never* do
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dc6f858..2c745ae 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.Manifest.permission.BATTERY_STATS;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.POWER_SAVER;
@@ -31,6 +30,7 @@
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
+import android.annotation.SuppressLint;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -82,6 +82,7 @@
import android.os.health.HealthStatsWriter;
import android.os.health.UidHealthStats;
import android.power.PowerStatsInternal;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -169,6 +170,7 @@
.replaceWith("?");
private static final int MAX_LOW_POWER_STATS_SIZE = 32768;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
+ private static final String MIN_CONSUMED_POWER_THRESHOLD_KEY = "min_consumed_power_threshold";
private static final String EMPTY = "Empty";
private final HandlerThread mHandlerThread;
@@ -855,12 +857,17 @@
final BatteryUsageStats bus;
switch (atomTag) {
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET:
+ @SuppressLint("MissingPermission")
+ final double minConsumedPowerThreshold =
+ DeviceConfig.getFloat(DeviceConfig.NAMESPACE_BATTERY_STATS,
+ MIN_CONSUMED_POWER_THRESHOLD_KEY, 0);
final BatteryUsageStatsQuery querySinceReset =
new BatteryUsageStatsQuery.Builder()
.setMaxStatsAgeMs(0)
.includeProcessStateData()
.includeVirtualUids()
.includePowerModels()
+ .setMinConsumedPowerThreshold(minConsumedPowerThreshold)
.build();
bus = getBatteryUsageStats(List.of(querySinceReset)).get(0);
break;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0bb78f0..bf3f63c 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -772,7 +772,6 @@
final int mVolume;
final boolean mIsLeOutput;
final @NonNull String mEventSource;
- final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
final int mAudioSystemDevice;
final int mMusicDevice;
@@ -787,7 +786,6 @@
mEventSource = d.mEventSource;
mAudioSystemDevice = audioDevice;
mMusicDevice = AudioSystem.DEVICE_NONE;
- mCodec = codec;
}
// constructor used by AudioDeviceBroker to search similar message
@@ -796,7 +794,6 @@
mProfile = profile;
mEventSource = "";
mMusicDevice = AudioSystem.DEVICE_NONE;
- mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
mAudioSystemDevice = 0;
mState = 0;
mSupprNoisy = false;
@@ -811,7 +808,6 @@
mProfile = profile;
mEventSource = "";
mMusicDevice = musicDevice;
- mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
mAudioSystemDevice = audioSystemDevice;
mState = state;
mSupprNoisy = false;
@@ -829,7 +825,6 @@
mEventSource = src.mEventSource;
mAudioSystemDevice = src.mAudioSystemDevice;
mMusicDevice = src.mMusicDevice;
- mCodec = src.mCodec;
}
// redefine equality op so we can match messages intended for this device
@@ -847,6 +842,19 @@
}
return false;
}
+
+ @Override
+ public String toString() {
+ return "BtDeviceInfo: device=" + mDevice.toString()
+ + " state=" + mState
+ + " prof=" + mProfile
+ + " supprNoisy=" + mSupprNoisy
+ + " volume=" + mVolume
+ + " isLeOutput=" + mIsLeOutput
+ + " eventSource=" + mEventSource
+ + " audioSystemDevice=" + mAudioSystemDevice
+ + " musicDevice=" + mMusicDevice;
+ }
}
BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device,
@@ -859,9 +867,6 @@
break;
case BluetoothProfile.A2DP:
audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
- synchronized (mDeviceStateLock) {
- codec = mBtHelper.getA2dpCodec(device);
- }
break;
case BluetoothProfile.HEARING_AID:
audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
@@ -1696,8 +1701,11 @@
case MSG_L_SET_BT_ACTIVE_DEVICE:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
- mDeviceInventory.onSetBtActiveDevice(btInfo,
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
+ mBtHelper.getA2dpCodecWithFallbackToSBC(
+ btInfo.mDevice, "MSG_L_SET_BT_ACTIVE_DEVICE");
+ mDeviceInventory.onSetBtActiveDevice(btInfo, codec,
(btInfo.mProfile
!= BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
? mAudioService.getBluetoothContextualVolumeStream()
@@ -1730,12 +1738,16 @@
(String) msg.obj, msg.arg1);
}
break;
- case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+ case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: {
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
synchronized (mDeviceStateLock) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
+ mBtHelper.getA2dpCodecWithFallbackToSBC(
+ btInfo.mDevice, "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
mDeviceInventory.onBluetoothDeviceConfigChange(
- (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ btInfo, codec, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
- break;
+ } break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
onSendBecomingNoisyIntent();
break;
@@ -1831,20 +1843,12 @@
}
break;
case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
- final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
- if (info.mDevice == null) break;
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ if (btInfo.mDevice == null) break;
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "msg: onBluetoothActiveDeviceChange "
- + " state=" + info.mState
- // only querying address as this is the only readily available
- // field on the device
- + " addr=" + info.mDevice.getAddress()
- + " prof=" + info.mProfile
- + " supprNoisy=" + info.mSupprNoisy
- + " src=" + info.mEventSource
- )).printLog(TAG));
+ "msg: onBluetoothActiveDeviceChange " + btInfo)).printLog(TAG));
synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothActiveDevice(info);
+ mDeviceInventory.setBluetoothActiveDevice(btInfo);
}
} break;
case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index da89502..60af280 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -503,9 +503,11 @@
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
+ int streamType) {
if (AudioService.DEBUG_DEVICES) {
Log.d(TAG, "onSetBtActiveDevice"
+ " btDevice=" + btInfo.mDevice
@@ -518,10 +520,7 @@
}
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent("BT connected:"
- + " addr=" + address
- + " profile=" + btInfo.mProfile
- + " state=" + btInfo.mState
- + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec)));
+ + btInfo + " codec=" + AudioSystem.audioFormatToString(codec)));
new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice")
.set(MediaMetrics.Property.STATUS, btInfo.mProfile)
@@ -529,7 +528,7 @@
AudioSystem.getDeviceName(btInfo.mAudioSystemDevice))
.set(MediaMetrics.Property.ADDRESS, address)
.set(MediaMetrics.Property.ENCODING,
- AudioSystem.audioFormatToString(btInfo.mCodec))
+ AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice")
.set(MediaMetrics.Property.STREAM_TYPE,
AudioSystem.streamToString(streamType))
@@ -568,7 +567,7 @@
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
}
- makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
+ makeA2dpDeviceAvailable(btInfo, codec, "onSetBtActiveDevice");
}
break;
case BluetoothProfile.HEARING_AID:
@@ -594,9 +593,10 @@
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ void onBluetoothDeviceConfigChange(
- @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
+ @NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
+ "onBluetoothDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
@@ -610,7 +610,6 @@
Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
}
int volume = btInfo.mVolume;
- @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
String address = btDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -639,8 +638,7 @@
}
mmi.set(MediaMetrics.Property.ADDRESS, address)
- .set(MediaMetrics.Property.ENCODING,
- AudioSystem.audioFormatToString(audioCodec))
+ .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.INDEX, volume)
.set(MediaMetrics.Property.NAME, di.mDeviceName);
@@ -648,20 +646,19 @@
if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
boolean a2dpCodecChange = false;
if (btInfo.mProfile == BluetoothProfile.A2DP) {
- if (di.mDeviceCodecFormat != audioCodec) {
- di.mDeviceCodecFormat = audioCodec;
+ if (di.mDeviceCodecFormat != codec) {
+ di.mDeviceCodecFormat = codec;
mConnectedDevices.replace(key, di);
a2dpCodecChange = true;
}
final int res = mAudioSystem.handleDeviceConfigChange(
- btInfo.mAudioSystemDevice, address,
- BtHelper.getName(btDevice), audioCodec);
+ btInfo.mAudioSystemDevice, address, BtHelper.getName(btDevice), codec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM handleDeviceConfigChange failed for A2DP device addr="
+ address + " codec="
- + AudioSystem.audioFormatToString(audioCodec))
+ + AudioSystem.audioFormatToString(codec))
.printLog(TAG));
// force A2DP device disconnection in case of error so that AudioService
@@ -672,7 +669,7 @@
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM handleDeviceConfigChange success for A2DP device addr="
+ address
- + " codec=" + AudioSystem.audioFormatToString(audioCodec))
+ + " codec=" + AudioSystem.audioFormatToString(codec))
.printLog(TAG));
}
@@ -1338,7 +1335,7 @@
* @param device the device whose connection state is queried
* @return true if connected
*/
- // called with AudioDeviceBroker.mDeviceStateLock lock held
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
public boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) {
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
@@ -1489,7 +1486,7 @@
}
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ void onBtProfileDisconnected(int profile) {
switch (profile) {
case BluetoothProfile.HEADSET:
@@ -1554,7 +1551,7 @@
disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void disconnectHeadset() {
boolean disconnect = false;
synchronized (mDevicesLock) {
@@ -1594,9 +1591,10 @@
return mCurAudioRoutes;
}
- // only public for mocking/spying
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @VisibleForTesting
+ /**
+ * Set a Bluetooth device to active.
+ */
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) {
int delay;
synchronized (mDevicesLock) {
@@ -1617,12 +1615,7 @@
}
if (AudioService.DEBUG_DEVICES) {
- Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice
- + " profile: " + BluetoothProfile.getProfileName(info.mProfile)
- + " state: " + BluetoothProfile.getConnectionStateName(info.mState)
- + " delay(ms): " + delay
- + " codec:" + Integer.toHexString(info.mCodec)
- + " suppressNoisyIntent: " + info.mSupprNoisy);
+ Log.i(TAG, "setBluetoothActiveDevice " + info.toString() + " delay(ms): " + delay);
}
mDeviceBroker.postBluetoothActiveDevice(info, delay);
if (info.mProfile == BluetoothProfile.HEARING_AID
@@ -1658,10 +1651,10 @@
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
String eventSource) {
final String address = btInfo.mDevice.getAddress();
final String name = BtHelper.getName(btInfo.mDevice);
- final int a2dpCodec = btInfo.mCodec;
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
@@ -1671,7 +1664,7 @@
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
+ AudioSystem.DEVICE_STATE_AVAILABLE, codec);
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
@@ -1694,7 +1687,7 @@
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, a2dpCodec, sensorUuid);
+ address, codec, sensorUuid);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1910,9 +1903,9 @@
}
@GuardedBy("mDevicesLock")
- private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
+ private void makeA2dpDeviceUnavailableNow(String address, int codec) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
- .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec))
+ .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow");
if (address == null) {
@@ -1939,7 +1932,7 @@
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, codec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ef098e3..58f48f3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -10732,6 +10732,7 @@
mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
mDeviceBroker.persistAudioDeviceSettings();
+ mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
mSoundDoseHelper.setAudioDeviceCategory(addr, internalType,
btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES);
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 1462b3c..6af409e 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -173,8 +173,8 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -263,8 +263,20 @@
return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec
+ int getA2dpCodecWithFallbackToSBC(
+ @NonNull BluetoothDevice device, @NonNull String source) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec = getA2dpCodec(device);
+ if (codec == AudioSystem.AUDIO_FORMAT_DEFAULT) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "getA2dpCodec DEFAULT from " + source + " fallback to SBC"));
+ return AudioSystem.AUDIO_FORMAT_SBC;
+ }
+ return codec;
+ }
+
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onReceiveBtEvent(Intent intent) {
final String action = intent.getAction();
@@ -283,8 +295,8 @@
* Exclusively called from AudioDeviceBroker when handling MSG_L_RECEIVED_BT_EVENT
* as part of the serialization of the communication route selection
*/
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void onScoAudioStateChanged(int state) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
@@ -355,16 +367,16 @@
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
@@ -435,8 +447,8 @@
mScoConnectionState = state;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -445,7 +457,8 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileDisconnected(int profile) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " disconnected"));
@@ -474,7 +487,8 @@
}
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
@@ -522,8 +536,8 @@
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -642,8 +656,8 @@
return btDevice == null ? "(null)" : btDevice.getAnonymizedAddress();
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package */ synchronized void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice)
+ " -> " + getAnonymizedAddress(btDevice));
@@ -712,8 +726,8 @@
//----------------------------------------------------------------------
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 0eb459f..35260ed 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
+import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
import static android.media.AudioSystem.isBluetoothDevice;
import android.annotation.NonNull;
@@ -540,7 +542,7 @@
return;
}
loglogi("addCompatibleAudioDevice: dev=" + ada);
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
AdiDeviceState updatedDevice = null; // non-null on update.
if (deviceState != null) {
if (forceEnable && !deviceState.isSAEnabled()) {
@@ -609,7 +611,7 @@
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
loglogi("removeCompatibleAudioDevice: dev=" + ada);
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
if (deviceState != null && deviceState.isSAEnabled()) {
deviceState.setSAEnabled(false);
onRoutingUpdated();
@@ -636,14 +638,25 @@
}
/**
- * Returns the Spatial Audio device state for an audio device attributes
- * or null if it does not exist.
+ * Returns the audio device state for the audio device attributes in case
+ * spatial audio is supported or null otherwise.
*/
@GuardedBy("this")
@Nullable
- private AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
- return mDeviceBroker.findDeviceStateForAudioDeviceAttributes(ada,
- getCanonicalDeviceType(ada.getType(), ada.getInternalType()));
+ private AdiDeviceState findSACompatibleDeviceStateForAudioDeviceAttributes(
+ AudioDeviceAttributes ada) {
+ final AdiDeviceState deviceState =
+ mDeviceBroker.findDeviceStateForAudioDeviceAttributes(ada,
+ getCanonicalDeviceType(ada.getType(), ada.getInternalType()));
+ if (deviceState == null) {
+ return null;
+ }
+
+ if (!isSADevice(deviceState)) {
+ return null;
+ }
+
+ return deviceState;
}
/**
@@ -665,21 +678,33 @@
Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
return new Pair<>(false, false);
}
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
if (deviceState == null) {
// no matching device state?
Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada);
return new Pair<>(false, false);
}
+ boolean available = true;
+ if (isBluetoothDevice(deviceType)) {
+ // only checking headphones/binaural because external speakers cannot use transaural
+ // since their physical characteristics are unknown
+ if (deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_UNKNOWN
+ || deviceState.getAudioDeviceCategory() == AUDIO_DEVICE_CATEGORY_HEADPHONES) {
+ available = (spatMode == SpatializationMode.SPATIALIZER_BINAURAL)
+ && mBinauralSupported;
+ } else {
+ available = false;
+ }
+ }
// found the matching device state.
- return new Pair<>(deviceState.isSAEnabled(), true /* available */);
+ return new Pair<>(deviceState.isSAEnabled(), available);
}
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
return;
}
- if (findDeviceStateForAudioDeviceAttributes(ada) == null) {
+ if (findSACompatibleDeviceStateForAudioDeviceAttributes(ada) == null) {
// wireless device types should be canonical, but we translate to be sure.
final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(),
ada.getInternalType());
@@ -729,11 +754,36 @@
}
}
+ synchronized void refreshDevice(@NonNull AudioDeviceAttributes ada) {
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
+ if (isAvailableForAdiDeviceState(deviceState)) {
+ addCompatibleAudioDevice(ada, /*forceEnable=*/deviceState.isSAEnabled());
+ setHeadTrackerEnabled(deviceState.isHeadTrackerEnabled(), ada);
+ } else {
+ removeCompatibleAudioDevice(ada);
+ }
+ }
+
synchronized boolean isAvailableForDevice(@NonNull AudioDeviceAttributes ada) {
if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
return false;
}
- return findDeviceStateForAudioDeviceAttributes(ada) != null;
+
+ return isAvailableForAdiDeviceState(
+ findSACompatibleDeviceStateForAudioDeviceAttributes(ada));
+ }
+
+ private boolean isAvailableForAdiDeviceState(AdiDeviceState deviceState) {
+ if (deviceState == null) {
+ return false;
+ }
+
+ if (isBluetoothDevice(deviceState.getInternalDeviceType())
+ && deviceState.getAudioDeviceCategory() != AUDIO_DEVICE_CATEGORY_UNKNOWN
+ && deviceState.getAudioDeviceCategory() != AUDIO_DEVICE_CATEGORY_HEADPHONES) {
+ return false;
+ }
+ return true;
}
private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
@@ -1149,7 +1199,7 @@
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
+ " for " + ada);
}
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
if (deviceState == null) return;
if (!deviceState.hasHeadTracker()) {
Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
@@ -1182,7 +1232,7 @@
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
return false;
}
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
return deviceState != null && deviceState.hasHeadTracker();
}
@@ -1196,7 +1246,7 @@
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
return false;
}
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
if (deviceState != null) {
if (!deviceState.hasHeadTracker()) {
deviceState.setHasHeadTracker(true);
@@ -1214,7 +1264,7 @@
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
return false;
}
- final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findSACompatibleDeviceStateForAudioDeviceAttributes(ada);
return deviceState != null
&& deviceState.hasHeadTracker() && deviceState.isHeadTrackerEnabled();
}
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 ebd4aec..5e5f6f2 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -166,10 +166,11 @@
&& mStats.isProcessStateDataAvailable();
final boolean includeVirtualUids = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
+ final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
mStats.getCustomEnergyConsumerNames(), includePowerModels,
- includeProcessStateData);
+ includeProcessStateData, minConsumedPowerThreshold);
// TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
// of stats sessions to wall-clock adjustments
batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
@@ -303,10 +304,12 @@
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
&& mStats.isProcessStateDataAvailable();
+ final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- customEnergyConsumerNames, includePowerModels, includeProcessStateData);
+ customEnergyConsumerNames, includePowerModels, includeProcessStateData,
+ minConsumedPowerThreshold);
if (mBatteryUsageStatsStore == null) {
Log.e(TAG, "BatteryUsageStatsStore is unavailable");
return builder.build();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 9e3a611..97b3e32 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -880,6 +880,58 @@
false /* forTabletopMode */);
}
+ /**
+ * Overrides persistent horizontal position of the letterboxed app window when horizontal
+ * reachability is enabled.
+ */
+ void setPersistentLetterboxPositionForHorizontalReachability(boolean forBookMode,
+ @LetterboxHorizontalReachabilityPosition int position) {
+ mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(
+ forBookMode, position);
+ }
+
+ /**
+ * Overrides persistent vertical position of the letterboxed app window when vertical
+ * reachability is enabled.
+ */
+ void setPersistentLetterboxPositionForVerticalReachability(boolean forTabletopMode,
+ @LetterboxVerticalReachabilityPosition int position) {
+ mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(
+ forTabletopMode, position);
+ }
+
+ /**
+ * Resets persistent horizontal position of the letterboxed app window when horizontal
+ * reachability
+ * is enabled to default position.
+ */
+ void resetPersistentLetterboxPositionForHorizontalReachability() {
+ mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(
+ false /* forBookMode */,
+ readLetterboxHorizontalReachabilityPositionFromConfig(mContext,
+ false /* forBookMode */));
+ mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(
+ true /* forBookMode */,
+ readLetterboxHorizontalReachabilityPositionFromConfig(mContext,
+ true /* forBookMode */));
+ }
+
+ /**
+ * Resets persistent vertical position of the letterboxed app window when vertical reachability
+ * is
+ * enabled to default position.
+ */
+ void resetPersistentLetterboxPositionForVerticalReachability() {
+ mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(
+ false /* forTabletopMode */,
+ readLetterboxVerticalReachabilityPositionFromConfig(mContext,
+ false /* forTabletopMode */));
+ mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(
+ true /* forTabletopMode */,
+ readLetterboxVerticalReachabilityPositionFromConfig(mContext,
+ true /* forTabletopMode */));
+ }
+
@LetterboxHorizontalReachabilityPosition
private static int readLetterboxHorizontalReachabilityPositionFromConfig(Context context,
boolean forBookMode) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0f0189e61..9f2aff2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2813,7 +2813,7 @@
final WindowManager.LayoutParams attrs = win.mAttrs;
visibleFrame.set(win.getFrame());
visibleFrame.inset(win.getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
- visibleFrame, attrs.type, win.getWindowingMode(), attrs.softInputMode,
+ visibleFrame, attrs.type, win.getActivityType(), attrs.softInputMode,
attrs.flags));
out.union(visibleFrame);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index ceebb27..bfe0553 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -911,6 +911,70 @@
return 0;
}
+ private int runSetPersistentLetterboxPositionForHorizontalReachability(PrintWriter pw)
+ throws RemoteException {
+ @LetterboxHorizontalReachabilityPosition final int position;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "left":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
+ break;
+ case "center":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+ break;
+ case "right":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setPersistentLetterboxPositionForHorizontalReachability(
+ false /* IsInBookMode */, position);
+ }
+ return 0;
+ }
+
+ private int runSetPersistentLetterboxPositionForVerticalReachability(PrintWriter pw)
+ throws RemoteException {
+ @LetterboxVerticalReachabilityPosition final int position;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "top":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+ break;
+ case "center":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+ break;
+ case "bottom":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'top', 'center' or 'bottom' are expected as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'top', 'center' or 'bottom' are expected as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setPersistentLetterboxPositionForVerticalReachability(
+ false /* forTabletopMode */, position);
+ }
+ return 0;
+ }
+
private int runSetBooleanFlag(PrintWriter pw, Consumer<Boolean> setter)
throws RemoteException {
String arg = getNextArg();
@@ -994,6 +1058,12 @@
case "--defaultPositionForVerticalReachability":
runSetLetterboxDefaultPositionForVerticalReachability(pw);
break;
+ case "--persistentPositionForHorizontalReachability":
+ runSetPersistentLetterboxPositionForHorizontalReachability(pw);
+ break;
+ case "--persistentPositionForVerticalReachability":
+ runSetPersistentLetterboxPositionForVerticalReachability(pw);
+ break;
case "--isEducationEnabled":
runSetBooleanFlag(pw, mLetterboxConfiguration::setIsEducationEnabled);
break;
@@ -1080,6 +1150,14 @@
case "defaultPositionForVerticalReachability":
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
break;
+ case "persistentPositionForHorizontalReachability":
+ mLetterboxConfiguration
+ .resetPersistentLetterboxPositionForHorizontalReachability();
+ break;
+ case "persistentPositionForVerticalReachability":
+ mLetterboxConfiguration
+ .resetPersistentLetterboxPositionForVerticalReachability();
+ break;
case "isEducationEnabled":
mLetterboxConfiguration.resetIsEducationEnabled();
break;
@@ -1206,6 +1284,8 @@
mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode();
mLetterboxConfiguration.resetDefaultPositionForHorizontalReachability();
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
+ mLetterboxConfiguration.resetPersistentLetterboxPositionForHorizontalReachability();
+ mLetterboxConfiguration.resetPersistentLetterboxPositionForVerticalReachability();
mLetterboxConfiguration.resetIsEducationEnabled();
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
@@ -1233,6 +1313,12 @@
pw.println("Vertical position multiplier (tabletop mode): "
+ mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier(
true /* isInTabletopMode */));
+ pw.println("Horizontal position multiplier for reachability: "
+ + mLetterboxConfiguration.getHorizontalMultiplierForReachability(
+ false /* isInBookMode */));
+ pw.println("Vertical position multiplier for reachability: "
+ + mLetterboxConfiguration.getVerticalMultiplierForReachability(
+ false /* isInTabletopMode */));
pw.println("Aspect ratio: "
+ mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
pw.println("Default min aspect ratio for unresizable apps: "
@@ -1472,6 +1558,12 @@
pw.println(" --defaultPositionForVerticalReachability [top|center|bottom]");
pw.println(" Default position of app window when vertical reachability is.");
pw.println(" enabled.");
+ pw.println(" --persistentPositionForHorizontalReachability [left|center|right]");
+ pw.println(" Persistent position of app window when horizontal reachability is.");
+ pw.println(" enabled.");
+ pw.println(" --persistentPositionForVerticalReachability [top|center|bottom]");
+ pw.println(" Persistent position of app window when vertical reachability is.");
+ pw.println(" enabled.");
pw.println(" --isEducationEnabled [true|1|false|0]");
pw.println(" Whether education is allowed for letterboxed fullscreen apps.");
pw.println(" --isSplitScreenAspectRatioForUnresizableAppsEnabled [true|1|false|0]");
@@ -1493,8 +1585,10 @@
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier");
pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled");
- pw.println(" |isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
+ pw.println(" |isEducationEnabled|defaultPositionMultiplierForHorizontalReachability");
pw.println(" |isTranslucentLetterboxingEnabled|isUserAppAspectRatioSettingsEnabled");
+ pw.println(" |persistentPositionMultiplierForHorizontalReachability");
+ pw.println(" |persistentPositionMultiplierForVerticalReachability");
pw.println(" |defaultPositionMultiplierForVerticalReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c09e6a3..d283df2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1762,7 +1762,7 @@
bounds.set(mWindowFrames.mFrame);
bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
- bounds, mAttrs.type, getWindowingMode(), mAttrs.softInputMode, mAttrs.flags));
+ bounds, mAttrs.type, getActivityType(), mAttrs.softInputMode, mAttrs.flags));
if (intersectWithRootTaskBounds) {
bounds.intersect(mTmpRect);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f3c2de6..b104185 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -679,7 +679,7 @@
// to decide whether an existing policy in the {@link #DEVICE_POLICIES_XML} needs to
// be upgraded. See {@link PolicyVersionUpgrader} on instructions how to add an upgrade
// step.
- static final int DPMS_VERSION = 5;
+ static final int DPMS_VERSION = 6;
static {
SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
@@ -875,8 +875,7 @@
private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = true;
// TODO(b/265683382) remove the flag after rollout.
- private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
- public static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = true;
+ public static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
// TODO(b/261999445) remove the flag after rollout.
private static final String HEADLESS_FLAG = "headless";
@@ -23839,10 +23838,7 @@
}
private static boolean isKeepProfilesRunningFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- KEEP_PROFILES_RUNNING_FLAG,
- DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
+ return DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
}
private boolean isUnicornFlagEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index 06f11be..913860c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -116,6 +116,19 @@
currentVersion = 5;
}
+ if (currentVersion == 5) {
+ Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
+ // No-op upgrade here:
+ // DevicePolicyData.mEffectiveKeepProfilesRunning is only stored in XML file when it is
+ // different from its default value, otherwise the tag is not written. When loading, if
+ // the tag is missing, the field retains the value previously assigned in the
+ // constructor, which is the default value.
+ // In version 5 the default value was 'true', in version 6 it is 'false', so when
+ // loading XML version 5 we need to initialize the field to 'true' for it to be restored
+ // correctly in case the tag is missing. This is done in loadDataForUser().
+ currentVersion = 6;
+ }
+
writePoliciesAndVersion(allUsers, allUsersData, ownersData, currentVersion);
}
@@ -281,6 +294,10 @@
private DevicePolicyData loadDataForUser(
int userId, int loadVersion, ComponentName ownerComponent) {
DevicePolicyData policy = new DevicePolicyData(userId);
+ // See version 5 -> 6 step in upgradePolicy()
+ if (loadVersion == 5 && userId == UserHandle.USER_SYSTEM) {
+ policy.mEffectiveKeepProfilesRunning = true;
+ }
DevicePolicyData.load(policy,
mProvider.makeDevicePoliciesJournaledFile(userId),
mProvider.getAdminInfoSupplier(userId),
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_false.xml b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_false.xml
new file mode 100644
index 0000000..4785a88
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_false.xml
@@ -0,0 +1,10 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+ <keep-profiles-running value="false" />
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+ <policies flags="991" />
+ <strong-auth-unlock-timeout value="0" />
+ <organization-color value="-16738680" />
+ <active-password value="0" />
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_true.xml b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_true.xml
new file mode 100644
index 0000000..07ec229
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/device_policies_keep_profiles_running_true.xml
@@ -0,0 +1,10 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+ <keep-profiles-running value="true" />
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+ <policies flags="991" />
+ <strong-auth-unlock-timeout value="0" />
+ <organization-color value="-16738680" />
+ <active-password value="0" />
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index aba24fb..7f8ad45 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -261,7 +261,8 @@
// Verify disconnection has been cancelled and we're seeing two connections attempts,
// with the device connected at the end of the test
verify(mSpyDevInventory, times(2)).onSetBtActiveDevice(
- any(AudioDeviceBroker.BtDeviceInfo.class), anyInt());
+ any(AudioDeviceBroker.BtDeviceInfo.class), anyInt() /*codec*/,
+ anyInt() /*streamType*/);
Assert.assertTrue("Mock device not connected",
mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index eb2ee35..d2b921d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -76,7 +76,7 @@
public class PolicyVersionUpgraderTest extends DpmTestBase {
// NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
// to the new version.
- private static final int LATEST_TESTED_VERSION = 5;
+ private static final int LATEST_TESTED_VERSION = 6;
public static final String PERMISSIONS_TAG = "admin-can-grant-sensors-permissions";
public static final String DEVICE_OWNER_XML = "device_owner_2.xml";
private ComponentName mFakeAdmin;
@@ -313,7 +313,7 @@
}
@Test
- public void testEffectiveKeepProfilesRunningSet() throws Exception {
+ public void testEffectiveKeepProfilesRunningSetToFalse4To5() throws Exception {
writeVersionToXml(4);
final int userId = UserHandle.USER_SYSTEM;
@@ -327,8 +327,109 @@
Document policies = readPolicies(userId);
Element keepProfilesRunning = (Element) policies.getDocumentElement()
.getElementsByTagName("keep-profiles-running").item(0);
- assertThat(keepProfilesRunning.getAttribute("value")).isEqualTo("false");
+
+ // Default value (false) is not serialized.
+ assertThat(keepProfilesRunning).isNull();
}
+ @Test
+ public void testEffectiveKeepProfilesRunningIsToFalse4To6() throws Exception {
+ writeVersionToXml(4);
+
+ final int userId = UserHandle.USER_SYSTEM;
+ mProvider.mUsers = new int[]{userId};
+ preparePoliciesFile(userId, "device_policies.xml");
+
+ mUpgrader.upgradePolicy(6);
+
+ assertThat(readVersionFromXml()).isAtLeast(6);
+
+ Document policies = readPolicies(userId);
+ Element keepProfilesRunning = (Element) policies.getDocumentElement()
+ .getElementsByTagName("keep-profiles-running").item(0);
+
+ // Default value (false) is not serialized.
+ assertThat(keepProfilesRunning).isNull();
+ }
+
+ /**
+ * Verify correct behaviour when upgrading from Android 13
+ */
+ @Test
+ public void testEffectiveKeepProfilesRunningIsToFalse3To6() throws Exception {
+ writeVersionToXml(3);
+
+ final int userId = UserHandle.USER_SYSTEM;
+ mProvider.mUsers = new int[]{userId};
+ preparePoliciesFile(userId, "device_policies.xml");
+
+ mUpgrader.upgradePolicy(6);
+
+ assertThat(readVersionFromXml()).isAtLeast(6);
+
+ Document policies = readPolicies(userId);
+ Element keepProfilesRunning = (Element) policies.getDocumentElement()
+ .getElementsByTagName("keep-profiles-running").item(0);
+
+ // Default value (false) is not serialized.
+ assertThat(keepProfilesRunning).isNull();
+ }
+
+ @Test
+ public void testEffectiveKeepProfilesRunningMissingInV5() throws Exception {
+ writeVersionToXml(5);
+
+ final int userId = UserHandle.USER_SYSTEM;
+ mProvider.mUsers = new int[]{userId};
+ preparePoliciesFile(userId, "device_policies.xml");
+
+ mUpgrader.upgradePolicy(6);
+
+ assertThat(readVersionFromXml()).isAtLeast(6);
+
+ Document policies = readPolicies(userId);
+ Element keepProfilesRunning = (Element) policies.getDocumentElement()
+ .getElementsByTagName("keep-profiles-running").item(0);
+ assertThat(keepProfilesRunning.getAttribute("value")).isEqualTo("true");
+ }
+
+ @Test
+ public void testEffectiveKeepProfilesRunningTrueInV5() throws Exception {
+ writeVersionToXml(5);
+
+ final int userId = UserHandle.USER_SYSTEM;
+ mProvider.mUsers = new int[]{userId};
+ preparePoliciesFile(userId, "device_policies_keep_profiles_running_true.xml");
+
+ mUpgrader.upgradePolicy(6);
+
+ assertThat(readVersionFromXml()).isAtLeast(6);
+
+ Document policies = readPolicies(userId);
+ Element keepProfilesRunning = (Element) policies.getDocumentElement()
+ .getElementsByTagName("keep-profiles-running").item(0);
+ assertThat(keepProfilesRunning.getAttribute("value")).isEqualTo("true");
+ }
+
+ @Test
+ public void testEffectiveKeepProfilesRunningFalseInV5() throws Exception {
+ writeVersionToXml(5);
+
+ final int userId = UserHandle.USER_SYSTEM;
+ mProvider.mUsers = new int[]{userId};
+ preparePoliciesFile(userId, "device_policies_keep_profiles_running_false.xml");
+
+ mUpgrader.upgradePolicy(6);
+
+ assertThat(readVersionFromXml()).isAtLeast(6);
+
+ Document policies = readPolicies(userId);
+ Element keepProfilesRunning = (Element) policies.getDocumentElement()
+ .getElementsByTagName("keep-profiles-running").item(0);
+
+ // Default value (false) is not serialized.
+ assertThat(keepProfilesRunning).isNull();
+ }
+
@Test
public void isLatestVersionTested() {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 3135215..a1e1da6 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -222,8 +222,10 @@
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0;
+ final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- customPowerComponentNames, includePowerModels, includeProcessStateData);
+ customPowerComponentNames, includePowerModels, includeProcessStateData,
+ minConsumedPowerThreshold);
SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
for (int i = 0; i < uidStats.size(); i++) {
builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 266a226..07c486c 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -181,7 +181,7 @@
final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[]{"FOO"}, true).build();
final BatteryUsageStats sum =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true)
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, 0)
.add(stats1)
.add(stats2)
.build();
@@ -222,7 +222,7 @@
@Test
public void testAdd_customComponentMismatch() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true);
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, 0);
final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"BAR"}, false).build();
assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
@@ -231,7 +231,7 @@
@Test
public void testAdd_processStateDataMismatch() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true);
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, 0);
final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"FOO"}, false).build();
assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
@@ -260,7 +260,7 @@
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true)
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, 0)
.setBatteryCapacity(4000)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
@@ -305,7 +305,7 @@
final BatteryUsageStats.Builder builder =
new BatteryUsageStats.Builder(customPowerComponentNames, true,
- includeProcessStateData);
+ includeProcessStateData, 0);
builder.setDischargePercentage(30)
.setDischargedPowerRange(1234, 2345)
.setStatsStartTimestamp(2000)
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index e1fc0cf..80e169d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -250,4 +250,42 @@
times(expectedTime)).setLetterboxPositionForVerticalReachability(halfFoldPose,
expected);
}
+
+ @Test
+ public void test_letterboxPositionWhenReachabilityEnabledIsReset() {
+ // Check that horizontal reachability is set with correct arguments
+ mLetterboxConfiguration.resetPersistentLetterboxPositionForHorizontalReachability();
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForHorizontalReachability(
+ false /* forBookMode */,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER);
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForHorizontalReachability(
+ true /* forBookMode */,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
+
+ // Check that vertical reachability is set with correct arguments
+ mLetterboxConfiguration.resetPersistentLetterboxPositionForVerticalReachability();
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForVerticalReachability(
+ false /* forTabletopMode */,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER);
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForVerticalReachability(
+ true /* forTabletopMode */,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
+ }
+
+ @Test
+ public void test_lettterboxPositionWhenReachabilityEnabledIsSet() {
+ // Check that horizontal reachability is set with correct arguments
+ mLetterboxConfiguration.setPersistentLetterboxPositionForHorizontalReachability(
+ false /* forBookMode */, LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForHorizontalReachability(
+ false /* forBookMode */,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
+
+ // Check that vertical reachability is set with correct arguments
+ mLetterboxConfiguration.setPersistentLetterboxPositionForVerticalReachability(
+ false /* forTabletopMode */, LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
+ verify(mLetterboxConfigurationPersister).setLetterboxPositionForVerticalReachability(
+ false /* forTabletopMode */,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
+ }
}
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index fe2fe0b..08430f2 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -159,7 +159,7 @@
private static BatteryUsageStats buildBatteryUsageStats() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false)
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, 0)
.setBatteryCapacity(4000)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)