Merge "Add dream scene transitions for Flexiglass" into main
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index d3068d7..a6e9807 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -2,16 +2,6 @@
container: "system"
flag {
- name: "use_frozen_state_to_drop_listener_alarms"
- namespace: "backstage_power"
- description: "Use frozen state callback to drop listener alarms for cached apps"
- bug: "324470945"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "start_user_before_scheduled_alarms"
namespace: "multiuser"
description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 033da2d..60ba3b8 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -282,7 +282,6 @@
private final Injector mInjector;
int mBroadcastRefCount = 0;
- boolean mUseFrozenStateToDropListenerAlarms;
MetricsHelper mMetricsHelper;
PowerManager.WakeLock mWakeLock;
SparseIntArray mAlarmsPerUid = new SparseIntArray();
@@ -1784,40 +1783,37 @@
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
- mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms();
mStartUserBeforeScheduledAlarms = Flags.startUserBeforeScheduledAlarms()
&& UserManager.supportsMultipleUsers();
if (mStartUserBeforeScheduledAlarms) {
mUserWakeupStore = new UserWakeupStore();
mUserWakeupStore.init();
}
- if (mUseFrozenStateToDropListenerAlarms) {
- final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
- final int size = frozenStates.length;
- if (uids.length != size) {
- Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
- + " uids.length: " + uids.length + " frozenStates.length: " + size);
- // Cannot process received data in any meaningful way.
- return;
+ final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
+ final int size = frozenStates.length;
+ if (uids.length != size) {
+ Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
+ + " uids.length: " + uids.length + " frozenStates.length: " + size);
+ // Cannot process received data in any meaningful way.
+ return;
+ }
+ final IntArray affectedUids = new IntArray();
+ for (int i = 0; i < size; i++) {
+ if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
+ continue;
}
- final IntArray affectedUids = new IntArray();
- for (int i = 0; i < size; i++) {
- if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
- continue;
- }
- if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
- uids[i])) {
- continue;
- }
- affectedUids.add(uids[i]);
+ if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
+ uids[i])) {
+ continue;
}
- if (affectedUids.size() > 0) {
- removeExactListenerAlarms(affectedUids.toArray());
- }
- };
- final ActivityManager am = getContext().getSystemService(ActivityManager.class);
- am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
- }
+ affectedUids.add(uids[i]);
+ }
+ if (affectedUids.size() > 0) {
+ removeExactListenerAlarms(affectedUids.toArray());
+ }
+ };
+ final ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
mListenerDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -2994,13 +2990,10 @@
pw.println("Feature Flags:");
pw.increaseIndent();
- pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
- mUseFrozenStateToDropListenerAlarms);
- pw.println();
pw.print(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS,
Flags.startUserBeforeScheduledAlarms());
- pw.decreaseIndent();
pw.println();
+ pw.decreaseIndent();
pw.println();
pw.println("App Standby Parole: " + mAppStandbyParole);
@@ -5146,38 +5139,6 @@
removeForStoppedLocked(uid);
}
}
-
- @Override
- public void handleUidCachedChanged(int uid, boolean cached) {
- if (mUseFrozenStateToDropListenerAlarms) {
- // Use ActivityManager#UidFrozenStateChangedCallback instead.
- return;
- }
- if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) {
- return;
- }
- // Apps can quickly get frozen after being cached, breaking the exactness guarantee on
- // listener alarms. So going forward, the contract of exact listener alarms explicitly
- // states that they will be removed as soon as the app goes out of lifecycle. We still
- // allow a short grace period for quick shuffling of proc-states that may happen
- // unexpectedly when switching between different lifecycles and is generally hard for
- // apps to avoid.
-
- final long delay;
- synchronized (mLock) {
- delay = mConstants.CACHED_LISTENER_REMOVAL_DELAY;
- }
- final Integer uidObj = uid;
-
- if (cached && !mHandler.hasEqualMessages(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED,
- uidObj)) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED, uidObj),
- delay);
- } else {
- mHandler.removeEqualMessages(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED, uidObj);
- }
- }
};
private final BroadcastStats getStatsLocked(PendingIntent pi) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 06cf9a5..59dc314 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1276,6 +1276,7 @@
field public static final int paddingStart = 16843699; // 0x10103b3
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int paddingVertical = 16844094; // 0x101053e
+ field @FlaggedApi("android.content.pm.app_compat_option_16kb") public static final int pageSizeCompat;
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
field public static final int panelColorForeground = 16842848; // 0x1010060
@@ -18826,6 +18827,19 @@
field public static final int TRANSFER_UNSPECIFIED = 0; // 0x0
}
+ @FlaggedApi("android.hardware.flags.luts_api") public final class DisplayLuts {
+ ctor @FlaggedApi("android.hardware.flags.luts_api") public DisplayLuts();
+ method @FlaggedApi("android.hardware.flags.luts_api") public void set(@NonNull android.hardware.DisplayLuts.Entry);
+ method @FlaggedApi("android.hardware.flags.luts_api") public void set(@NonNull android.hardware.DisplayLuts.Entry, @NonNull android.hardware.DisplayLuts.Entry);
+ }
+
+ @FlaggedApi("android.hardware.flags.luts_api") public static class DisplayLuts.Entry {
+ ctor @FlaggedApi("android.hardware.flags.luts_api") public DisplayLuts.Entry(@NonNull float[], int, int);
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public float[] getBuffer();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getDimension();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getSamplingKey();
+ }
+
public class GeomagneticField {
ctor public GeomagneticField(float, float, float, long);
method public float getDeclination();
@@ -18887,8 +18901,19 @@
field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c
}
+ @FlaggedApi("android.hardware.flags.luts_api") public final class LutProperties {
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getDimension();
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public int[] getSamplingKeys();
+ method @FlaggedApi("android.hardware.flags.luts_api") public int getSize();
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int ONE_DIMENSION = 1; // 0x1
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_MAX_RGB = 1; // 0x1
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_RGB = 0; // 0x0
+ field @FlaggedApi("android.hardware.flags.luts_api") public static final int THREE_DIMENSION = 3; // 0x3
+ }
+
@FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable {
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public int describeContents();
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public android.hardware.LutProperties[] getLutProperties();
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isCombinationSupported(int, int);
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isMixedColorSpacesSupported();
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -21025,6 +21050,7 @@
method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
+ method @FlaggedApi("android.view.inputmethod.verify_key_event") public boolean onShouldVerifyKeyEvent(@NonNull android.view.KeyEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
}
@@ -21042,6 +21068,7 @@
method public void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback);
method public boolean isEnabled();
method public boolean isRevoked();
+ method @FlaggedApi("android.view.inputmethod.verify_key_event") public boolean onShouldVerifyKeyEvent(@NonNull android.view.KeyEvent);
method public void revokeSelf();
method public void setEnabled(boolean);
}
@@ -21982,7 +22009,7 @@
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioAttributes getAudioAttributes();
- method @Nullable public android.media.AudioDeviceInfo getAudioDeviceInfo();
+ method @Deprecated @FlaggedApi("android.media.audio.routed_device_ids") @Nullable public android.media.AudioDeviceInfo getAudioDeviceInfo();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
@@ -22065,6 +22092,7 @@
method public android.media.AudioDeviceInfo getPreferredDevice();
method public int getRecordingState();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSampleRate();
method public int getState();
method public int getTimestamp(@NonNull android.media.AudioTimestamp, int);
@@ -22159,6 +22187,7 @@
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public default java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
}
@@ -22217,6 +22246,7 @@
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSampleRate();
method @IntRange(from=1) public int getStartThresholdInFrames();
method public int getState();
@@ -24385,6 +24415,7 @@
method @NonNull public android.media.PlaybackParams getPlaybackParams();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method @NonNull public android.media.SyncParams getSyncParams();
method @Nullable public android.media.MediaTimestamp getTimestamp();
@@ -24598,6 +24629,7 @@
method public android.os.PersistableBundle getMetrics();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull public java.util.List<android.media.AudioDeviceInfo> getRoutedDevices();
method public android.view.Surface getSurface();
method public boolean isPrivacySensitive();
method public void pause() throws java.lang.IllegalStateException;
@@ -28529,6 +28561,8 @@
method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.ad.TvAdView.TvAdCallback);
method public void setOnUnhandledInputEventListener(@NonNull android.media.tv.ad.TvAdView.OnUnhandledInputEventListener);
method public boolean setTvView(@Nullable android.media.tv.TvView);
+ method @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public void setZOrderMediaOverlay(boolean);
+ method @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public void setZOrderOnTop(boolean);
method public void startAdService();
method public void stopAdService();
field public static final String ERROR_KEY_ERROR_CODE = "error_code";
@@ -28801,6 +28835,8 @@
method public void setOnUnhandledInputEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.OnUnhandledInputEventListener);
method public void setTeletextAppEnabled(boolean);
method public int setTvView(@Nullable android.media.tv.TvView);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void setZOrderMediaOverlay(boolean);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void setZOrderOnTop(boolean);
method public void startInteractiveApp();
method public void stopInteractiveApp();
field public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias";
@@ -33313,6 +33349,14 @@
method public final android.os.CountDownTimer start();
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
+ ctor public CpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
+ field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ }
+
public final class CpuUsageInfo implements android.os.Parcelable {
method public int describeContents();
method public long getActive();
@@ -33560,6 +33604,14 @@
method public void onProgress(long);
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
+ ctor public GpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ }
+
public class Handler {
ctor @Deprecated public Handler();
ctor @Deprecated public Handler(@Nullable android.os.Handler.Callback);
@@ -34810,6 +34862,10 @@
}
public class SystemHealthManager {
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getCpuHeadroom(@Nullable android.os.CpuHeadroomParams);
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getCpuHeadroomMinIntervalMillis();
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getGpuHeadroom(@Nullable android.os.GpuHeadroomParams);
+ method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getGpuHeadroomMinIntervalMillis();
method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>);
method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
method public android.os.health.HealthStats takeMyUidSnapshot();
@@ -46672,6 +46728,8 @@
method public long getDataUsageBytes();
method public long getDataUsageTime();
method @NonNull public int[] getNetworkTypes();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @Nullable public java.time.ZonedDateTime getPlanEndDate();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public int getSubscriptionStatus();
method @Nullable public CharSequence getSummary();
method @Nullable public CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
@@ -46682,6 +46740,11 @@
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_ACTIVE = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_INACTIVE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_TRIAL = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0; // 0x0
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
@@ -46693,6 +46756,7 @@
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@NonNull int[]);
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @NonNull public android.telephony.SubscriptionPlan.Builder setSubscriptionStatus(int);
method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
@@ -53168,6 +53232,7 @@
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setFrameTimeline(long);
method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public android.view.SurfaceControl.Transaction setLuts(@NonNull android.view.SurfaceControl, @Nullable android.hardware.DisplayLuts);
method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
@@ -56960,6 +57025,7 @@
method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public boolean isStylusHandwritingEnabled();
+ method @FlaggedApi("android.view.inputmethod.writing_tools") public boolean isWritingToolsEnabled();
method public final void makeCompatible(int);
method @FlaggedApi("android.view.inputmethod.public_autofill_id_in_editorinfo") public void setAutofillId(@Nullable android.view.autofill.AutofillId);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
@@ -56968,6 +57034,7 @@
method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public void setStylusHandwritingEnabled(boolean);
method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
+ method @FlaggedApi("android.view.inputmethod.writing_tools") public void setWritingToolsEnabled(boolean);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bc73220..dcb4a40 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -379,6 +379,13 @@
field public static final int DEVICE_INITIAL_SDK_INT;
}
+ public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
+ method @FlaggedApi("android.os.enable_has_binders") public int hasBinders();
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_NOT_PRESENT = 0; // 0x0
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_PRESENT = 1; // 0x1
+ field @FlaggedApi("android.os.enable_has_binders") public static final int STATUS_BINDERS_UNKNOWN = 2; // 0x2
+ }
+
public class Handler {
method @FlaggedApi("android.os.mainline_vcn_platform_api") public final boolean hasMessagesOrCallbacks();
method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeCallbacksAndEqualMessages(@Nullable Object);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ed95fdd..fee53d6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7464,6 +7464,7 @@
}
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
+ method @FlaggedApi("android.media.audio.routed_device_ids") @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceInfo> getAudioDeviceInfos();
method public int getChannelMask();
method public int getClientPid();
method public int getClientUid();
@@ -7618,7 +7619,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
- method @FlaggedApi("com.android.media.flags.update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
+ method @FlaggedApi("android.media.tv.flags.mediacas_update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
}
public final class MediaCodec {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index cf5ebbaa3..bc38294 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -232,6 +232,8 @@
"android.hardware.power-aidl",
],
srcs: [
+ "android/os/CpuHeadroomParamsInternal.aidl",
+ "android/os/GpuHeadroomParamsInternal.aidl",
"android/os/IHintManager.aidl",
"android/os/IHintSession.aidl",
],
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e451116..ee0c38c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -190,6 +190,7 @@
import android.os.IBinder;
import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
+import android.os.IHintManager;
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
@@ -1195,8 +1196,10 @@
public SystemHealthManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder batteryStats = ServiceManager.getServiceOrThrow(BatteryStats.SERVICE_NAME);
IBinder powerStats = ServiceManager.getService(Context.POWER_STATS_SERVICE);
+ IBinder perfHint = ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE);
return new SystemHealthManager(IBatteryStats.Stub.asInterface(batteryStats),
- IPowerStatsService.Stub.asInterface(powerStats));
+ IPowerStatsService.Stub.asInterface(powerStats),
+ IHintManager.Stub.asInterface(perfHint));
}});
registerService(Context.CONTEXTHUB_SERVICE, ContextHubManager.class,
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 9ba5a35..e181ae8 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -366,3 +366,13 @@
description: "Block app installations that specify an incompatible minor SDK version"
bug: "377474232"
}
+
+flag {
+ name: "app_compat_option_16kb"
+ is_exported: true
+ namespace: "devoptions_settings"
+ description: "Feature flag to enable page size app compat mode from manifest, package manager and settings level."
+ bug: "371049373"
+ is_fixed_read_only: true
+}
+
diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java
index b162ad6..6343ba1 100644
--- a/core/java/android/hardware/DisplayLuts.java
+++ b/core/java/android/hardware/DisplayLuts.java
@@ -16,116 +16,294 @@
package android.hardware;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.hardware.flags.Flags;
import android.util.IntArray;
import java.util.ArrayList;
-import java.util.List;
/**
- * @hide
+ * DisplayLuts provides the developers to apply Lookup Tables (Luts) to a
+ * {@link android.view.SurfaceControl}. Luts provides ways to control tonemapping
+ * for specific content.
+ *
+ * The general flow is as follows:
+ * <p>
+ * <img src="{@docRoot}reference/android/images/graphics/DisplayLuts.png" />
+ * <figcaption style="text-align: center;">DisplayLuts flow</figcaption>
+ * </p>
+ *
+ * @see LutProperties
*/
+@FlaggedApi(Flags.FLAG_LUTS_API)
public final class DisplayLuts {
+ private ArrayList<Entry> mEntries;
private IntArray mOffsets;
private int mTotalLength;
- private List<float[]> mLutBuffers;
- private IntArray mLutDimensions;
- private IntArray mLutSizes;
- private IntArray mLutSamplingKeys;
- private static final int LUT_LENGTH_LIMIT = 100000;
-
+ /**
+ * Create a {@link DisplayLuts} instance.
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public DisplayLuts() {
+ mEntries = new ArrayList<>();
mOffsets = new IntArray();
mTotalLength = 0;
-
- mLutBuffers = new ArrayList<>();
- mLutDimensions = new IntArray();
- mLutSizes = new IntArray();
- mLutSamplingKeys = new IntArray();
}
- /**
- * Add the lut to be applied.
- *
- * @param buffer
- * @param dimension either 1D or 3D
- * @param size
- * @param samplingKey
- */
- public void addLut(@NonNull float[] buffer, @LutProperties.Dimension int dimension,
- int size, @LutProperties.SamplingKey int samplingKey) {
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public static class Entry {
+ private float[] mBuffer;
+ private @LutProperties.Dimension int mDimension;
+ private int mSize;
+ private @LutProperties.SamplingKey int mSamplingKey;
- int lutLength = 0;
- if (dimension == LutProperties.ONE_DIMENSION) {
- lutLength = size;
- } else if (dimension == LutProperties.THREE_DIMENSION) {
- lutLength = size * size * size;
- } else {
- clear();
- throw new IllegalArgumentException("The dimension is either 1D or 3D!");
+ private static final int LUT_LENGTH_LIMIT = 100000;
+
+ /**
+ * Create a Lut entry.
+ *
+ * <p>
+ * Noted that 1D Lut(s) are treated as gain curves.
+ * For 3D Lut(s), 3D Lut(s) are used for direct color manipulations.
+ * The values of 3D Lut(s) data should be normalized to the range {@code 0.0}
+ * to {@code 1.0}, inclusive. And 3D Lut(s) data is organized in the order of
+ * R, G, B channels.
+ *
+ * @param buffer The raw lut data
+ * @param dimension Either 1D or 3D
+ * @param samplingKey The sampling kay used for the Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public Entry(@NonNull float[] buffer,
+ @LutProperties.Dimension int dimension,
+ @LutProperties.SamplingKey int samplingKey) {
+ if (buffer == null || buffer.length < 1) {
+ throw new IllegalArgumentException("The buffer cannot be empty!");
+ }
+
+ if (buffer.length >= LUT_LENGTH_LIMIT) {
+ throw new IllegalArgumentException("The lut length is too big to handle!");
+ }
+
+ if (dimension != LutProperties.ONE_DIMENSION
+ && dimension != LutProperties.THREE_DIMENSION) {
+ throw new IllegalArgumentException("The dimension should be either 1D or 3D!");
+ }
+
+ if (dimension == LutProperties.THREE_DIMENSION) {
+ if (buffer.length <= 3) {
+ throw new IllegalArgumentException(
+ "The 3d lut size of each dimension should be over 1!");
+ }
+ int lengthPerChannel = buffer.length;
+ if (lengthPerChannel % 3 != 0) {
+ throw new IllegalArgumentException(
+ "The lut buffer of 3dlut should have 3 channels!");
+ }
+ lengthPerChannel /= 3;
+
+ double size = Math.cbrt(lengthPerChannel);
+ if (size == (int) size) {
+ mSize = (int) size;
+ } else {
+ throw new IllegalArgumentException(
+ "Cannot get the cube root of the 3d lut buffer!");
+ }
+ } else {
+ mSize = buffer.length;
+ }
+
+ mBuffer = buffer;
+ mDimension = dimension;
+ mSamplingKey = samplingKey;
}
- if (lutLength >= LUT_LENGTH_LIMIT) {
- clear();
- throw new IllegalArgumentException("The lut length is too big to handle!");
+ /**
+ * @return the dimension of the lut entry
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public int getDimension() {
+ return mDimension;
}
+ /**
+ * @return the size of the lut for each dimension
+ * @hide
+ */
+ public int getSize() {
+ return mSize;
+ }
+
+ /**
+ * @return the lut raw data of the lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public @NonNull float[] getBuffer() {
+ return mBuffer;
+ }
+
+ /**
+ * @return the sampling key used by the lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public int getSamplingKey() {
+ return mSamplingKey;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{"
+ + "dimension=" + DisplayLuts.Entry.dimensionToString(getDimension())
+ + ", size(each dimension)=" + getSize()
+ + ", samplingKey=" + samplingKeyToString(getSamplingKey()) + "}";
+ }
+
+ private static String dimensionToString(int dimension) {
+ switch(dimension) {
+ case LutProperties.ONE_DIMENSION:
+ return "ONE_DIMENSION";
+ case LutProperties.THREE_DIMENSION:
+ return "THREE_DIMENSION";
+ default:
+ return "";
+ }
+ }
+
+ private static String samplingKeyToString(int key) {
+ switch(key) {
+ case LutProperties.SAMPLING_KEY_RGB:
+ return "SAMPLING_KEY_RGB";
+ case LutProperties.SAMPLING_KEY_MAX_RGB:
+ return "SAMPLING_KEY_MAX_RGB";
+ default:
+ return "";
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("DisplayLuts{");
+ sb.append("\n");
+ for (DisplayLuts.Entry entry: mEntries) {
+ sb.append(entry.toString());
+ sb.append("\n");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private void addEntry(Entry entry) {
+ mEntries.add(entry);
mOffsets.add(mTotalLength);
- mTotalLength += lutLength;
-
- mLutBuffers.add(buffer);
- mLutDimensions.add(dimension);
- mLutSizes.add(size);
- mLutSamplingKeys.add(samplingKey);
+ mTotalLength += entry.getBuffer().length;
}
private void clear() {
- mTotalLength = 0;
mOffsets.clear();
- mLutBuffers.clear();
- mLutDimensions.clear();
- mLutSamplingKeys.clear();
+ mTotalLength = 0;
+ mEntries.clear();
}
/**
- * @return the array of Lut buffers
+ * Set a Lut to be applied.
+ *
+ * <p>Use either this or {@link #set(Entry, Entry)}. The function will
+ * replace any previously set lut(s).</p>
+ *
+ * @param entry Either an 1D Lut or a 3D Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public void set(@NonNull Entry entry) {
+ if (entry == null) {
+ throw new IllegalArgumentException("The entry is null!");
+ }
+ clear();
+ addEntry(entry);
+ }
+
+ /**
+ * Set Luts in order to be applied.
+ *
+ * <p> An 1D Lut and 3D Lut will be applied in order. Use either this or
+ * {@link #set(Entry)}. The function will replace any previously set lut(s)</p>
+ *
+ * @param first An 1D Lut
+ * @param second A 3D Lut
+ */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ public void set(@NonNull Entry first, @NonNull Entry second) {
+ if (first == null || second == null) {
+ throw new IllegalArgumentException("The entry is null!");
+ }
+ if (first.getDimension() != LutProperties.ONE_DIMENSION
+ || second.getDimension() != LutProperties.THREE_DIMENSION) {
+ throw new IllegalArgumentException("The entries should be 1D and 3D in order!");
+ }
+ clear();
+ addEntry(first);
+ addEntry(second);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean valid() {
+ return mEntries.size() > 0;
+ }
+
+ /**
+ * @hide
*/
public float[] getLutBuffers() {
float[] buffer = new float[mTotalLength];
- for (int i = 0; i < mLutBuffers.size(); i++) {
- float[] lutBuffer = mLutBuffers.get(i);
+ for (int i = 0; i < mEntries.size(); i++) {
+ float[] lutBuffer = mEntries.get(i).getBuffer();
System.arraycopy(lutBuffer, 0, buffer, mOffsets.get(i), lutBuffer.length);
}
return buffer;
}
/**
- * @return the starting point of each lut memory region of the lut buffer
+ * @hide
*/
public int[] getOffsets() {
return mOffsets.toArray();
}
/**
- * @return the array of Lut size
+ * @hide
*/
public int[] getLutSizes() {
- return mLutSizes.toArray();
+ int[] sizes = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ sizes[i] = mEntries.get(i).getSize();
+ }
+ return sizes;
}
/**
- * @return the array of Lut dimension
+ * @hide
*/
public int[] getLutDimensions() {
- return mLutDimensions.toArray();
+ int[] dimensions = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ dimensions[i] = mEntries.get(i).getDimension();
+ }
+ return dimensions;
}
/**
- * @return the array of sampling key
+ * @hide
*/
public int[] getLutSamplingKeys() {
- return mLutSamplingKeys.toArray();
+ int[] samplingKeys = new int[mEntries.size()];
+ for (int i = 0; i < mEntries.size(); i++) {
+ samplingKeys[i] = mEntries.get(i).getSamplingKey();
+ }
+ return samplingKeys;
}
}
diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java
index c9c6d6d..bf40a41 100644
--- a/core/java/android/hardware/LutProperties.java
+++ b/core/java/android/hardware/LutProperties.java
@@ -16,23 +16,31 @@
package android.hardware;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Lut properties class.
+ * Provides Lut properties of the device.
*
- * A Lut (Look-Up Table) is a pre-calculated table for color transformation.
- *
- * @hide
+ * <p>
+ * A Lut (Look-Up Table) is a pre-calculated table for color correction.
+ * Applications may be interested in the Lut properties exposed by
+ * this class to determine if the Lut(s) they select using
+ * {@link android.view.SurfaceControl.Transaction#setLuts} are by the HWC.
+ * </p>
*/
+@FlaggedApi(Flags.FLAG_LUTS_API)
public final class LutProperties {
private final @Dimension int mDimension;
private final int mSize;
private final @SamplingKey int[] mSamplingKeys;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SAMPLING_KEY_"}, value = {
SAMPLING_KEY_RGB,
@@ -42,11 +50,14 @@
}
/** use r,g,b channel as the gain value of a Lut */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int SAMPLING_KEY_RGB = 0;
/** use max of r,g,b channel as the gain value of a Lut */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int SAMPLING_KEY_MAX_RGB = 1;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ONE_DIMENSION,
@@ -56,18 +67,22 @@
}
/** The Lut is one dimensional */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int ONE_DIMENSION = 1;
/** The Lut is three dimensional */
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public static final int THREE_DIMENSION = 3;
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public @Dimension int getDimension() {
return mDimension;
}
/**
- * @return the size of the Lut.
+ * @return the size of the Lut for each dimension
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
public int getSize() {
return mSize;
}
@@ -75,6 +90,8 @@
/**
* @return the list of sampling keys
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ @NonNull
public @SamplingKey int[] getSamplingKeys() {
if (mSamplingKeys.length == 0) {
throw new IllegalStateException("no sampling key!");
diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java
index 24cfc1b..d42bfae 100644
--- a/core/java/android/hardware/OverlayProperties.java
+++ b/core/java/android/hardware/OverlayProperties.java
@@ -18,6 +18,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.hardware.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -72,9 +73,11 @@
}
/**
- * Gets the lut properties of the display.
- * @hide
+ * Returns the lut properties of the device.
*/
+ @FlaggedApi(Flags.FLAG_LUTS_API)
+ @SuppressLint("ArrayReturn")
+ @NonNull
public LutProperties[] getLutProperties() {
if (mNativeObject == 0) {
return null;
diff --git a/core/java/android/hardware/flags/overlayproperties_flags.aconfig b/core/java/android/hardware/flags/flags.aconfig
similarity index 63%
rename from core/java/android/hardware/flags/overlayproperties_flags.aconfig
rename to core/java/android/hardware/flags/flags.aconfig
index 6c86108..5ca6c6b 100644
--- a/core/java/android/hardware/flags/overlayproperties_flags.aconfig
+++ b/core/java/android/hardware/flags/flags.aconfig
@@ -2,6 +2,15 @@
container: "system"
flag {
+ name: "luts_api"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "core_graphics"
+ description: "public Luts related Apis"
+ bug: "349667978"
+}
+
+flag {
name: "overlayproperties_class_api"
is_exported: true
namespace: "core_graphics"
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 4b2f2c2..fee0749 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -170,4 +170,11 @@
namespace: "input"
description: "Adds key gestures for talkback and magnifier"
bug: "375277034"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "can_window_override_power_gesture_api"
+ namespace: "wallet_integration"
+ description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
+ bug: "378736024"
+ }
\ No newline at end of file
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 4bc5bd2..26308f6 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -16,6 +16,9 @@
package android.inputmethodservice;
+import static android.view.inputmethod.Flags.FLAG_VERIFY_KEY_EVENT;
+
+import android.annotation.FlaggedApi;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -193,6 +196,12 @@
}
}
+ @FlaggedApi(FLAG_VERIFY_KEY_EVENT)
+ @Override
+ public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) {
+ return AbstractInputMethodService.this.onShouldVerifyKeyEvent(event);
+ }
+
/**
* Take care of dispatching incoming trackball events to the appropriate
* callbacks on the service, and tell the client when this is done.
@@ -308,6 +317,14 @@
return false;
}
+ /**
+ * @see InputMethodService#onShouldVerifyKeyEvent(KeyEvent)
+ */
+ @FlaggedApi(FLAG_VERIFY_KEY_EVENT)
+ public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) {
+ return false;
+ }
+
/** @hide */
@Override
public final int getWindowType() {
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 62b131a..9b37533 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -16,12 +16,16 @@
package android.inputmethodservice;
+import static android.view.inputmethod.Flags.verifyKeyEvent;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import android.view.InputChannel;
@@ -41,6 +45,8 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.util.Objects;
+
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
@@ -56,6 +62,7 @@
private static final int DO_REMOVE_IME_SURFACE = 130;
private static final int DO_FINISH_INPUT = 140;
private static final int DO_INVALIDATE_INPUT = 150;
+ private final Context mContext;
@UnsupportedAppUsage
@@ -66,6 +73,7 @@
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession, InputChannel channel) {
+ mContext = context;
mCaller = new HandlerCaller(context, null,
this, true /*asyncHandler*/);
mInputMethodSession = inputMethodSession;
@@ -233,6 +241,8 @@
}
private final class ImeInputEventReceiver extends InputEventReceiver
implements InputMethodSession.EventCallback {
+ // Time after which a KeyEvent is invalid
+ private static final long KEY_EVENT_ALLOW_PERIOD_MS = 100L;
private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
@@ -247,10 +257,23 @@
return;
}
+ if (event instanceof KeyEvent keyEvent && needsVerification(keyEvent)) {
+ // any KeyEvent with modifiers (e.g. Ctrl/Alt/Fn) must be verified that
+ // they originated from system.
+ InputManager im = mContext.getSystemService(InputManager.class);
+ Objects.requireNonNull(im);
+ final long age = SystemClock.uptimeMillis() - keyEvent.getEventTime();
+ if (age >= KEY_EVENT_ALLOW_PERIOD_MS && im.verifyInputEvent(keyEvent) == null) {
+ Log.w(TAG, "Unverified or Invalid KeyEvent injected into IME. Dropping "
+ + keyEvent);
+ finishInputEvent(event, false /* handled */);
+ return;
+ }
+ }
+
final int seq = event.getSequenceNumber();
mPendingEvents.put(seq, event);
- if (event instanceof KeyEvent) {
- KeyEvent keyEvent = (KeyEvent)event;
+ if (event instanceof KeyEvent keyEvent) {
mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
} else {
MotionEvent motionEvent = (MotionEvent)event;
@@ -271,5 +294,21 @@
finishInputEvent(event, handled);
}
}
+
+ private boolean hasKeyModifiers(KeyEvent event) {
+ if (event.hasNoModifiers()) {
+ return false;
+ }
+ return event.hasModifiers(KeyEvent.META_CTRL_ON)
+ || event.hasModifiers(KeyEvent.META_ALT_ON)
+ || event.hasModifiers(KeyEvent.KEYCODE_FUNCTION);
+ }
+
+ private boolean needsVerification(KeyEvent event) {
+ //TODO(b/331730488): Handle a11y events as well.
+ return verifyKeyEvent()
+ && (hasKeyModifiers(event)
+ || mInputMethodSession.onShouldVerifyKeyEvent(event));
+ }
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index dadb5c38..a8fde4a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -56,6 +56,7 @@
import static android.view.inputmethod.ConnectionlessHandwritingCallback.CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED;
import static android.view.inputmethod.Flags.FLAG_CONNECTIONLESS_HANDWRITING;
import static android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API;
+import static android.view.inputmethod.Flags.FLAG_VERIFY_KEY_EVENT;
import static android.view.inputmethod.Flags.ctrlShiftShortcut;
import static android.view.inputmethod.Flags.predictiveBackIme;
@@ -3735,6 +3736,23 @@
}
/**
+ * Received by the IME before dispatch to {@link #onKeyDown(int, KeyEvent)} to let the system
+ * know if the {@link KeyEvent} needs to be verified that it originated from the system.
+ * {@link KeyEvent}s may originate from outside of the system and any sensitive keys should be
+ * marked for verification. One example of this could be using key shortcuts for switching to
+ * another IME.
+ *
+ * @param keyEvent the event that may need verification.
+ * @return {@code true} if {@link KeyEvent} should have its HMAC verified before dispatch,
+ * {@code false} otherwise.
+ */
+ @FlaggedApi(FLAG_VERIFY_KEY_EVENT)
+ @Override
+ public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent keyEvent) {
+ return false;
+ }
+
+ /**
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
* the event).
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 99e7d166..05bd10b 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -18,10 +18,12 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Size;
@@ -72,16 +74,18 @@
/**
* Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
* Binder object(s).
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_NOT_PRESENT = 0;
/**
* Status when the Bundle can <b>assert</b> that there are Binder object(s) in the Parcel.
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_PRESENT = 1;
/**
@@ -94,9 +98,10 @@
* object to the Bundle but it is not possible to assert this fact unless the Bundle is written
* to a Parcel.
* </p>
- *
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int STATUS_BINDERS_UNKNOWN = 2;
/** @hide */
@@ -417,6 +422,8 @@
* Returns a status indicating whether the bundle contains any parcelled Binder objects.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_HAS_BINDERS)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public @HasBinderStatus int hasBinders() {
if ((mFlags & FLAG_HAS_BINDERS_KNOWN) != 0) {
if ((mFlags & FLAG_HAS_BINDERS) != 0) {
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
new file mode 100644
index 0000000..f0d4f7d
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}.
+ */
+@FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
+public final class CpuHeadroomParams {
+ final CpuHeadroomParamsInternal mInternal;
+
+ public CpuHeadroomParams() {
+ mInternal = new CpuHeadroomParamsInternal();
+ }
+
+ /** @hide */
+ @IntDef(flag = false, prefix = {"CPU_HEADROOM_CALCULATION_TYPE_"}, value = {
+ CPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
+ CPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ public void setCalculationType(@CpuHeadroomCalculationType int calculationType) {
+ switch (calculationType) {
+ case CPU_HEADROOM_CALCULATION_TYPE_MIN:
+ case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
+ mInternal.calculationType = (byte) calculationType;
+ return;
+ }
+ throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #CPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+ */
+ public @CpuHeadroomCalculationType int getCalculationType() {
+ @CpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
+ case CPU_HEADROOM_CALCULATION_TYPE_MIN, CPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
+ mInternal.calculationType;
+ default -> CPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public CpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl
new file mode 100644
index 0000000..6cc4699
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.CpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#CpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable CpuHeadroomParamsInternal {
+ boolean usesDeviceHeadroom = false;
+ CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN;
+ CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL;
+}
+
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
new file mode 100644
index 0000000..efb2a28
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getGpuHeadroom(GpuHeadroomParams)}.
+ */
+@FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
+public final class GpuHeadroomParams {
+ final GpuHeadroomParamsInternal mInternal;
+
+ public GpuHeadroomParams() {
+ mInternal = new GpuHeadroomParamsInternal();
+ }
+
+ /** @hide */
+ @IntDef(flag = false, prefix = {"GPU_HEADROOM_CALCULATION_TYPE_"}, value = {
+ GPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
+ GPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ public void setCalculationType(@GpuHeadroomCalculationType int calculationType) {
+ switch (calculationType) {
+ case GPU_HEADROOM_CALCULATION_TYPE_MIN:
+ case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
+ mInternal.calculationType = (byte) calculationType;
+ return;
+ }
+ throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+ */
+ public @GpuHeadroomCalculationType int getCalculationType() {
+ @GpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
+ case GPU_HEADROOM_CALCULATION_TYPE_MIN, GPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
+ mInternal.calculationType;
+ default -> GPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public GpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl
new file mode 100644
index 0000000..20309e7
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.GpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#GpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable GpuHeadroomParamsInternal {
+ GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN;
+}
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 73cdd56..3312055 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -17,6 +17,8 @@
package android.os;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IHintSession;
import android.hardware.power.ChannelConfig;
import android.hardware.power.SessionConfig;
@@ -50,4 +52,8 @@
*/
@nullable ChannelConfig getSessionChannel(in IBinder token);
oneway void closeSessionChannel();
+ float[] getCpuHeadroom(in CpuHeadroomParamsInternal params);
+ long getCpuHeadroomMinIntervalMillis();
+ float getGpuHeadroom(in GpuHeadroomParamsInternal params);
+ long getGpuHeadroomMinIntervalMillis();
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 590ddb4..24e1d66 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -78,6 +78,9 @@
# PermissionEnforcer
per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
+# RemoteCallbackList
+per-file RemoteCallbackList.java = shayba@google.com
+
# ART
per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9ab9228..5a53bc15 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5308,7 +5308,13 @@
Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS,
Manifest.permission.QUERY_USERS}, conditional = true)
+ @CachedProperty(api = "user_manager_user_data")
public List<UserInfo> getProfiles(@UserIdInt int userId) {
+ if (android.multiuser.Flags.cacheProfilesReadOnly()) {
+ return UserManagerCache.getProfiles(
+ (Integer userIdentifier) -> mService.getProfiles(userIdentifier, false),
+ userId);
+ }
try {
return mService.getProfiles(userId, false /* enabledOnly */);
} catch (RemoteException re) {
@@ -6484,6 +6490,19 @@
}
/**
+ * This method is used to invalidate caches, when UserManagerService.mUsers
+ * {@link UserManagerService.UserData} is modified, including changes to {@link UserInfo}.
+ * In practice we determine modification by when that data is persisted, or scheduled to be
+ * presisted, to xml.
+ * @hide
+ */
+ public static final void invalidateCacheOnUserDataChanged() {
+ if (android.multiuser.Flags.cacheProfilesReadOnly()) {
+ UserManagerCache.invalidateProfiles();
+ }
+ }
+
+ /**
* Returns a serial number on this device for a given userId. User handles can be recycled
* when deleting and creating users, but serial numbers are not reused until the device is
* wiped.
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index d9db28e..118167d 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -66,6 +66,14 @@
}
flag {
+ name: "adpf_use_load_hints"
+ namespace: "game"
+ description: "Guards use of the ADPF public load hints behind a readonly flag"
+ is_fixed_read_only: true
+ bug: "367803904"
+}
+
+flag {
name: "allow_consentless_bugreport_delegated_consent"
namespace: "crumpet"
description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
@@ -148,6 +156,13 @@
}
flag {
+ name: "cpu_gpu_headrooms"
+ namespace: "game"
+ description: "Feature flag for adding CPU/GPU headroom API"
+ bug: "346604998"
+}
+
+flag {
name: "disallow_cellular_null_ciphers_restriction"
namespace: "cellular_security"
description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
@@ -253,6 +268,15 @@
flag {
namespace: "system_performance"
+ name: "enable_has_binders"
+ is_exported: true
+ description: "Add hasBinders to Public API under a flag."
+ is_fixed_read_only: true
+ bug: "330345513"
+}
+
+flag {
+ namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
bug: "303199244"
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index deabfed..4db9bc3 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -17,6 +17,7 @@
package android.os.health;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -25,6 +26,11 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
+import android.os.CpuHeadroomParams;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParams;
+import android.os.GpuHeadroomParamsInternal;
+import android.os.IHintManager;
import android.os.IPowerStatsService;
import android.os.OutcomeReceiver;
import android.os.PowerMonitor;
@@ -68,6 +74,8 @@
private final IBatteryStats mBatteryStats;
@Nullable
private final IPowerStatsService mPowerStats;
+ @Nullable
+ private final IHintManager mHintManager;
private List<PowerMonitor> mPowerMonitorsInfo;
private final Object mPowerMonitorsLock = new Object();
private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000;
@@ -88,14 +96,111 @@
public SystemHealthManager() {
this(IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME)),
IPowerStatsService.Stub.asInterface(
- ServiceManager.getService(Context.POWER_STATS_SERVICE)));
+ ServiceManager.getService(Context.POWER_STATS_SERVICE)),
+ IHintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE)));
}
/** {@hide} */
public SystemHealthManager(@NonNull IBatteryStats batteryStats,
- @Nullable IPowerStatsService powerStats) {
+ @Nullable IPowerStatsService powerStats, @Nullable IHintManager hintManager) {
mBatteryStats = batteryStats;
mPowerStats = powerStats;
+ mHintManager = hintManager;
+ }
+
+ /**
+ * Provides an estimate of global available CPU headroom of the calling thread.
+ * <p>
+ *
+ * @param params params to customize the CPU headroom calculation, null to use default params.
+ * @return a single value a {@code Float.NaN} if it's temporarily unavailable.
+ * A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be
+ * granted.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom(
+ @Nullable CpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroom(
+ params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0];
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+
+
+ /**
+ * Provides an estimate of global available GPU headroom of the device.
+ * <p>
+ *
+ * @param params params to customize the GPU headroom calculation, null to use default params.
+ * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
+ * A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be
+ * granted.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom(
+ @Nullable GpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroom(
+ params != null ? params.getInternal() : new GpuHeadroomParamsInternal());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getCpuHeadroom(CpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getCpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getGpuHeadroom(GpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getGpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -261,7 +366,7 @@
mPowerMonitorsInfo = result;
}
if (executor != null) {
- executor.execute(()-> onResult.accept(result));
+ executor.execute(() -> onResult.accept(result));
} else {
onResult.accept(result);
}
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index ce90121..09004b3 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -115,6 +115,14 @@
}
flag {
+ name: "protect_device_config_flags"
+ namespace: "psap_ai"
+ description: "Feature flag to limit adb shell to allowlisted flags"
+ bug: "364083026"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keystore_grant_api"
namespace: "hardware_backed_security"
description: "Feature flag for exposing KeyStore grant APIs"
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
index 07311d5..75a9309 100644
--- a/core/java/android/service/quickaccesswallet/flags.aconfig
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -3,7 +3,7 @@
flag {
name: "launch_wallet_option_on_power_double_tap"
- namespace: "wallet_integrations"
+ namespace: "wallet_integration"
description: "Option to launch the Wallet app on double-tap of the power button"
bug: "378469025"
}
\ No newline at end of file
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index 7b48a16..4c59a85 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -18,6 +18,7 @@
import android.annotation.BytesLong;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,7 @@
import android.util.Range;
import android.util.RecurrenceRule;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -83,6 +85,33 @@
/** Value indicating a timestamp is unknown. */
public static final long TIME_UNKNOWN = -1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SUBSCRIPTION_STATUS_" }, value = {
+ SUBSCRIPTION_STATUS_UNKNOWN,
+ SUBSCRIPTION_STATUS_ACTIVE,
+ SUBSCRIPTION_STATUS_INACTIVE,
+ SUBSCRIPTION_STATUS_TRIAL,
+ SUBSCRIPTION_STATUS_SUSPENDED
+ })
+ public @interface SubscriptionStatus {}
+
+ /** Subscription status is unknown. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0;
+ /** Subscription is active. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_ACTIVE = 1;
+ /** Subscription is inactive. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_INACTIVE = 2;
+ /** Subscription is in a trial period. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_TRIAL = 3;
+ /** Subscription is suspended. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4;
+
private final RecurrenceRule cycleRule;
private CharSequence title;
private CharSequence summary;
@@ -91,6 +120,7 @@
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
private @NetworkType int[] networkTypes;
+ private int mSubscriptionStatus = SUBSCRIPTION_STATUS_UNKNOWN;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -107,6 +137,7 @@
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
networkTypes = source.createIntArray();
+ mSubscriptionStatus = source.readInt();
}
@Override
@@ -124,6 +155,7 @@
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
dest.writeIntArray(networkTypes);
+ dest.writeInt(mSubscriptionStatus);
}
@Override
@@ -137,13 +169,14 @@
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
.append(" networkTypes=").append(Arrays.toString(networkTypes))
+ .append(" subscriptionStatus=").append(mSubscriptionStatus)
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
- dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
+ dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes), mSubscriptionStatus);
}
@Override
@@ -157,7 +190,8 @@
&& dataLimitBehavior == other.dataLimitBehavior
&& dataUsageBytes == other.dataUsageBytes
&& dataUsageTime == other.dataUsageTime
- && Arrays.equals(networkTypes, other.networkTypes);
+ && Arrays.equals(networkTypes, other.networkTypes)
+ && mSubscriptionStatus == other.mSubscriptionStatus;
}
return false;
}
@@ -179,6 +213,13 @@
return cycleRule;
}
+ /** Return the end date of this plan, or null if no end date exists. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @Nullable ZonedDateTime getPlanEndDate() {
+ // ZonedDateTime is immutable, so no need to create a defensive copy.
+ return cycleRule.end;
+ }
+
/** Return the short title of this plan. */
public @Nullable CharSequence getTitle() {
return title;
@@ -238,6 +279,16 @@
}
/**
+ * Returns the status of the subscription plan.
+ *
+ * @return The subscription status, or {@link #SUBSCRIPTION_STATUS_UNKNOWN} if not available.
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @SubscriptionStatus int getSubscriptionStatus() {
+ return mSubscriptionStatus;
+ }
+
+ /**
* Builder for a {@link SubscriptionPlan}.
*/
public static class Builder {
@@ -382,5 +433,21 @@
TelephonyManager.getAllNetworkTypes().length);
return this;
}
+
+ /**
+ * Set the subscription status.
+ *
+ * @param subscriptionStatus the current subscription status
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @NonNull Builder setSubscriptionStatus(@SubscriptionStatus int subscriptionStatus) {
+ if (subscriptionStatus < SUBSCRIPTION_STATUS_UNKNOWN
+ || subscriptionStatus > SUBSCRIPTION_STATUS_SUSPENDED) {
+ throw new IllegalArgumentException(
+ "Subscription status must be defined with a valid value");
+ }
+ plan.mSubscriptionStatus = subscriptionStatus;
+ return this;
+ }
}
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
deleted file mode 100644
index ca88764..0000000
--- a/core/java/android/text/ClientFlags.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.text;
-
-/**
- * An aconfig feature flags that can be accessible from application process without
- * ContentProvider IPCs.
- *
- * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public class ClientFlags {
-}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
deleted file mode 100644
index f69a333..0000000
--- a/core/java/android/text/TextFlags.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.text;
-
-import android.annotation.NonNull;
-import android.app.AppGlobals;
-
-/**
- * Flags in the "text" namespace.
- *
- * TODO(nona): Remove this class.
- * @hide
- */
-public final class TextFlags {
-
- /**
- * The name space of the "text" feature.
- *
- * This needs to move to DeviceConfig constant.
- */
- public static final String NAMESPACE = "text";
-
- /**
- * Whether we use the new design of context menu.
- */
- public static final String ENABLE_NEW_CONTEXT_MENU =
- "TextEditing__enable_new_context_menu";
-
- /**
- * The key name used in app core settings for {@link #ENABLE_NEW_CONTEXT_MENU}.
- */
- public static final String KEY_ENABLE_NEW_CONTEXT_MENU = "text__enable_new_context_menu";
-
- /**
- * Default value for the flag {@link #ENABLE_NEW_CONTEXT_MENU}.
- */
- public static final boolean ENABLE_NEW_CONTEXT_MENU_DEFAULT = true;
-
- /**
- * List of text flags to be transferred to the application process.
- */
- public static final String[] TEXT_ACONFIGS_FLAGS = {
- };
-
- /**
- * List of the default values of the text flags.
- *
- * The order must be the same to the TEXT_ACONFIG_FLAGS.
- */
- public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
- };
-
- /**
- * Get a key for the feature flag.
- */
- public static String getKeyForFlag(@NonNull String flag) {
- return "text__" + flag;
- }
-
- /**
- * Return true if the feature flag is enabled.
- */
- public static boolean isFeatureEnabled(@NonNull String flag) {
- return AppGlobals.getIntCoreSetting(
- getKeyForFlag(flag), 0 /* aconfig is false by default */) != 0;
- }
-}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 68efa79..d56768d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4566,14 +4566,31 @@
return this;
}
- /** @hide */
+ /**
+ * Sets the Luts for the layer.
+ *
+ * <p> The function also allows to clear previously applied lut(s). To do this,
+ * set the displayluts to be either {@code nullptr} or
+ * an empty {@link android.hardware.DisplayLuts} instance.
+ *
+ * @param sc The SurfaceControl to update
+ *
+ * @param displayLuts The selected Lut(s)
+ *
+ * @return this
+ * @see DisplayLuts
+ */
+ @FlaggedApi(android.hardware.flags.Flags.FLAG_LUTS_API)
public @NonNull Transaction setLuts(@NonNull SurfaceControl sc,
- @NonNull DisplayLuts displayLuts) {
+ @Nullable DisplayLuts displayLuts) {
checkPreconditions(sc);
-
- nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
- displayLuts.getOffsets(), displayLuts.getLutDimensions(),
- displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+ if (displayLuts != null && displayLuts.valid()) {
+ nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
+ displayLuts.getOffsets(), displayLuts.getLutDimensions(),
+ displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+ } else {
+ nativeSetLuts(mNativeObject, sc.mNativeObject, null, null, null, null, null);
+ }
return this;
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index a560339..afe195c 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -523,7 +523,6 @@
@Nullable
public LocaleList hintLocales = null;
-
/**
* List of acceptable MIME types for
* {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}.
@@ -758,6 +757,30 @@
return mIsStylusHandwritingEnabled;
}
+ private boolean mWritingToolsEnabled = true;
+
+ /**
+ * Returns {@code true} when an {@code Editor} has writing tools enabled.
+ * {@code true} by default for all editors. Toolkits can optionally disable them where not
+ * relevant e.g. passwords, number input, etc.
+ * @see #setWritingToolsEnabled(boolean)
+ */
+ @FlaggedApi(Flags.FLAG_WRITING_TOOLS)
+ public boolean isWritingToolsEnabled() {
+ return mWritingToolsEnabled;
+ }
+
+ /**
+ * Set {@code false} if {@code Editor} opts-out of writing tools, that enable IMEs to replace
+ * text with generative AI text.
+ * @param enabled set {@code true} to enabled or {@code false to disable} support.
+ * @see #isWritingToolsEnabled()
+ */
+ @FlaggedApi(Flags.FLAG_WRITING_TOOLS)
+ public void setWritingToolsEnabled(boolean enabled) {
+ mWritingToolsEnabled = enabled;
+ }
+
/**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
@@ -1276,6 +1299,7 @@
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGesturePreviewTypes));
pw.println(prefix + "isStylusHandwritingEnabled=" + mIsStylusHandwritingEnabled);
+ pw.println(prefix + "writingToolsEnabled=" + mWritingToolsEnabled);
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1356,6 +1380,7 @@
}
dest.writeStringArray(contentMimeTypes);
UserHandle.writeToParcel(targetInputMethodUser, dest);
+ dest.writeBoolean(mWritingToolsEnabled);
}
/**
@@ -1396,6 +1421,7 @@
res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
res.contentMimeTypes = source.readStringArray();
res.targetInputMethodUser = UserHandle.readFromParcel(source);
+ res.mWritingToolsEnabled = source.readBoolean();
return res;
}
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index 4f48cb6..0f48f12 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
@@ -125,6 +126,11 @@
public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback);
/**
+ * @hide
+ */
+ boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event);
+
+ /**
* This method is called when there is a track ball event.
*
* <p>
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e619ab0..63f8c80 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -175,3 +175,11 @@
bug: "342672560"
is_fixed_read_only: true
}
+
+flag {
+ name: "verify_key_event"
+ namespace: "input_method"
+ description: "Verify KeyEvents in IME"
+ bug: "331730488"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d7750bd..cb70466 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -106,6 +106,7 @@
import android.os.ParcelableParcel;
import android.os.Process;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
@@ -9229,174 +9230,179 @@
@Override
protected void onDraw(Canvas canvas) {
- restartMarqueeIfNeeded();
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onDraw");
+ try {
+ restartMarqueeIfNeeded();
- // Draw the background for this view
- super.onDraw(canvas);
+ // Draw the background for this view
+ super.onDraw(canvas);
- final int compoundPaddingLeft = getCompoundPaddingLeft();
- final int compoundPaddingTop = getCompoundPaddingTop();
- final int compoundPaddingRight = getCompoundPaddingRight();
- final int compoundPaddingBottom = getCompoundPaddingBottom();
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int right = mRight;
- final int left = mLeft;
- final int bottom = mBottom;
- final int top = mTop;
- final boolean isLayoutRtl = isLayoutRtl();
- final int offset = getHorizontalOffsetForDrawables();
- final int leftOffset = isLayoutRtl ? 0 : offset;
- final int rightOffset = isLayoutRtl ? offset : 0;
+ final int compoundPaddingLeft = getCompoundPaddingLeft();
+ final int compoundPaddingTop = getCompoundPaddingTop();
+ final int compoundPaddingRight = getCompoundPaddingRight();
+ final int compoundPaddingBottom = getCompoundPaddingBottom();
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
+ final int right = mRight;
+ final int left = mLeft;
+ final int bottom = mBottom;
+ final int top = mTop;
+ final boolean isLayoutRtl = isLayoutRtl();
+ final int offset = getHorizontalOffsetForDrawables();
+ final int leftOffset = isLayoutRtl ? 0 : offset;
+ final int rightOffset = isLayoutRtl ? offset : 0;
- final Drawables dr = mDrawables;
- if (dr != null) {
- /*
- * Compound, not extended, because the icon is not clipped
- * if the text height is smaller.
- */
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ /*
+ * Compound, not extended, because the icon is not clipped
+ * if the text height is smaller.
+ */
- int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
- int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
+ int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
+ int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.LEFT] != null) {
- canvas.save();
- canvas.translate(scrollX + mPaddingLeft + leftOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
- dr.mShowing[Drawables.LEFT].draw(canvas);
- canvas.restore();
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.LEFT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + mPaddingLeft + leftOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
+ dr.mShowing[Drawables.LEFT].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.RIGHT] != null) {
+ canvas.save();
+ canvas.translate(scrollX + right - left - mPaddingRight
+ - dr.mDrawableSizeRight - rightOffset,
+ scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
+ dr.mShowing[Drawables.RIGHT].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.TOP] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
+ dr.mShowing[Drawables.TOP].draw(canvas);
+ canvas.restore();
+ }
+
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (dr.mShowing[Drawables.BOTTOM] != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft
+ + (hspace - dr.mDrawableWidthBottom) / 2,
+ scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
+ dr.mShowing[Drawables.BOTTOM].draw(canvas);
+ canvas.restore();
+ }
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.RIGHT] != null) {
- canvas.save();
- canvas.translate(scrollX + right - left - mPaddingRight
- - dr.mDrawableSizeRight - rightOffset,
- scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
- dr.mShowing[Drawables.RIGHT].draw(canvas);
- canvas.restore();
+ int color = mCurTextColor;
+
+ if (mLayout == null) {
+ assumeLayout();
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.TOP] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
- dr.mShowing[Drawables.TOP].draw(canvas);
- canvas.restore();
+ Layout layout = mLayout;
+
+ if (mHint != null && !mHideHint && mText.length() == 0) {
+ if (mHintTextColor != null) {
+ color = mCurHintTextColor;
+ }
+
+ layout = mHintLayout;
}
- // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
- // Make sure to update invalidateDrawable() when changing this code.
- if (dr.mShowing[Drawables.BOTTOM] != null) {
- canvas.save();
- canvas.translate(scrollX + compoundPaddingLeft
- + (hspace - dr.mDrawableWidthBottom) / 2,
- scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
- dr.mShowing[Drawables.BOTTOM].draw(canvas);
- canvas.restore();
- }
- }
+ mTextPaint.setColor(color);
+ mTextPaint.drawableState = getDrawableState();
- int color = mCurTextColor;
+ canvas.save();
+ /* Would be faster if we didn't have to do this. Can we chop the
+ (displayable) text so that we don't need to do this ever?
+ */
- if (mLayout == null) {
- assumeLayout();
- }
+ int extendedPaddingTop = getExtendedPaddingTop();
+ int extendedPaddingBottom = getExtendedPaddingBottom();
- Layout layout = mLayout;
+ final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
+ final int maxScrollY = mLayout.getHeight() - vspace;
- if (mHint != null && !mHideHint && mText.length() == 0) {
- if (mHintTextColor != null) {
- color = mCurHintTextColor;
+ float clipLeft = compoundPaddingLeft + scrollX;
+ float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
+ float clipRight = right - left - getCompoundPaddingRight() + scrollX;
+ float clipBottom = bottom - top + scrollY
+ - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
+
+ if (mShadowRadius != 0) {
+ clipLeft += Math.min(0, mShadowDx - mShadowRadius);
+ clipRight += Math.max(0, mShadowDx + mShadowRadius);
+
+ clipTop += Math.min(0, mShadowDy - mShadowRadius);
+ clipBottom += Math.max(0, mShadowDy + mShadowRadius);
}
- layout = mHintLayout;
- }
+ canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
- mTextPaint.setColor(color);
- mTextPaint.drawableState = getDrawableState();
+ int voffsetText = 0;
+ int voffsetCursor = 0;
- canvas.save();
- /* Would be faster if we didn't have to do this. Can we chop the
- (displayable) text so that we don't need to do this ever?
- */
+ // translate in by our padding
+ /* shortcircuit calling getVerticaOffset() */
+ if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
+ voffsetText = getVerticalOffset(false);
+ voffsetCursor = getVerticalOffset(true);
+ }
+ canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
- int extendedPaddingTop = getExtendedPaddingTop();
- int extendedPaddingBottom = getExtendedPaddingBottom();
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+ if (isMarqueeFadeEnabled()) {
+ if (!mSingleLine && getLineCount() == 1 && canMarquee()
+ && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
+ final int width = mRight - mLeft;
+ final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
+ final float dx = mLayout.getLineRight(0) - (width - padding);
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
- final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
- final int maxScrollY = mLayout.getHeight() - vspace;
+ if (mMarquee != null && mMarquee.isRunning()) {
+ final float dx = -mMarquee.getScroll();
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ }
+ }
- float clipLeft = compoundPaddingLeft + scrollX;
- float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
- float clipRight = right - left - getCompoundPaddingRight() + scrollX;
- float clipBottom = bottom - top + scrollY
- - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
+ final int cursorOffsetVertical = voffsetCursor - voffsetText;
- if (mShadowRadius != 0) {
- clipLeft += Math.min(0, mShadowDx - mShadowRadius);
- clipRight += Math.max(0, mShadowDx + mShadowRadius);
+ maybeUpdateHighlightPaths();
+ // If there is a gesture preview highlight, then the selection or cursor is not drawn.
+ Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
+ if (mEditor != null) {
+ mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
+ mHighlightPaint, cursorOffsetVertical);
+ } else {
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
+ }
- clipTop += Math.min(0, mShadowDy - mShadowRadius);
- clipBottom += Math.max(0, mShadowDy + mShadowRadius);
- }
-
- canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
-
- int voffsetText = 0;
- int voffsetCursor = 0;
-
- // translate in by our padding
- /* shortcircuit calling getVerticaOffset() */
- if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
- voffsetText = getVerticalOffset(false);
- voffsetCursor = getVerticalOffset(true);
- }
- canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
- if (isMarqueeFadeEnabled()) {
- if (!mSingleLine && getLineCount() == 1 && canMarquee()
- && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
- final int width = mRight - mLeft;
- final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
- final float dx = mLayout.getLineRight(0) - (width - padding);
+ if (mMarquee != null && mMarquee.shouldDrawGhost()) {
+ final float dx = mMarquee.getGhostOffset();
canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
}
- if (mMarquee != null && mMarquee.isRunning()) {
- final float dx = -mMarquee.getScroll();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- }
+ canvas.restore();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
-
- final int cursorOffsetVertical = voffsetCursor - voffsetText;
-
- maybeUpdateHighlightPaths();
- // If there is a gesture preview highlight, then the selection or cursor is not drawn.
- Path highlight = hasGesturePreviewHighlight() ? null : getUpdatedHighlightPath();
- if (mEditor != null) {
- mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
- mHighlightPaint, cursorOffsetVertical);
- } else {
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- if (mMarquee != null && mMarquee.shouldDrawGhost()) {
- final float dx = mMarquee.getGhostOffset();
- canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
- cursorOffsetVertical);
- }
-
- canvas.restore();
}
@Override
@@ -11254,192 +11260,201 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextView.onMeasure");
+ try {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height;
+ int width;
+ int height;
- BoringLayout.Metrics boring = UNKNOWN_BORING;
- BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
+ BoringLayout.Metrics boring = UNKNOWN_BORING;
+ BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
- if (mTextDir == null) {
- mTextDir = getTextDirectionHeuristic();
- }
-
- int des = -1;
- boolean fromexisting = false;
- final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
- ? (float) widthSize : Float.MAX_VALUE;
-
- if (widthMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- width = widthSize;
- } else {
- if (mLayout != null && mEllipsize == null) {
- des = desired(mLayout, mUseBoundsForWidth);
+ if (mTextDir == null) {
+ mTextDir = getTextDirectionHeuristic();
}
- if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
- isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
- mBoring);
- if (boring != null) {
- mBoring = boring;
- }
+ int des = -1;
+ boolean fromexisting = false;
+ final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
+ ? (float) widthSize : Float.MAX_VALUE;
+
+ if (widthMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ width = widthSize;
} else {
- fromexisting = true;
- }
+ if (mLayout != null && mEllipsize == null) {
+ des = desired(mLayout, mUseBoundsForWidth);
+ }
- if (boring == null || boring == UNKNOWN_BORING) {
if (des < 0) {
- des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
- mTransformed.length(), mTextPaint, mTextDir, widthLimit,
- mUseBoundsForWidth));
- }
- width = des;
- } else {
- if (mUseBoundsForWidth) {
- RectF bbox = boring.getDrawingBoundingBox();
- float rightMax = Math.max(bbox.right, boring.width);
- float leftMin = Math.min(bbox.left, 0);
- width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
- } else {
- width = boring.width;
- }
- }
-
- final Drawables dr = mDrawables;
- if (dr != null) {
- width = Math.max(width, dr.mDrawableWidthTop);
- width = Math.max(width, dr.mDrawableWidthBottom);
- }
-
- if (mHint != null) {
- int hintDes = -1;
- int hintWidth;
-
- if (mHintLayout != null && mEllipsize == null) {
- hintDes = desired(mHintLayout, mUseBoundsForWidth);
- }
-
- if (hintDes < 0) {
- hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
isFallbackLineSpacingForBoringLayout(), getResolvedMinimumFontMetrics(),
- mHintBoring);
- if (hintBoring != null) {
- mHintBoring = hintBoring;
+ mBoring);
+ if (boring != null) {
+ mBoring = boring;
}
+ } else {
+ fromexisting = true;
}
- if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
- if (hintDes < 0) {
- hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
- mHint.length(), mTextPaint, mTextDir, widthLimit,
+ if (boring == null || boring == UNKNOWN_BORING) {
+ if (des < 0) {
+ des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
+ mTransformed.length(), mTextPaint, mTextDir, widthLimit,
mUseBoundsForWidth));
}
- hintWidth = hintDes;
+ width = des;
} else {
- hintWidth = hintBoring.width;
+ if (mUseBoundsForWidth) {
+ RectF bbox = boring.getDrawingBoundingBox();
+ float rightMax = Math.max(bbox.right, boring.width);
+ float leftMin = Math.min(bbox.left, 0);
+ width = Math.max(boring.width, (int) Math.ceil(rightMax - leftMin));
+ } else {
+ width = boring.width;
+ }
}
- if (hintWidth > width) {
- width = hintWidth;
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ width = Math.max(width, dr.mDrawableWidthTop);
+ width = Math.max(width, dr.mDrawableWidthBottom);
}
- }
- width += getCompoundPaddingLeft() + getCompoundPaddingRight();
+ if (mHint != null) {
+ int hintDes = -1;
+ int hintWidth;
- if (mMaxWidthMode == EMS) {
- width = Math.min(width, mMaxWidth * getLineHeight());
- } else {
- width = Math.min(width, mMaxWidth);
- }
+ if (mHintLayout != null && mEllipsize == null) {
+ hintDes = desired(mHintLayout, mUseBoundsForWidth);
+ }
- if (mMinWidthMode == EMS) {
- width = Math.max(width, mMinWidth * getLineHeight());
- } else {
- width = Math.max(width, mMinWidth);
- }
+ if (hintDes < 0) {
+ hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(),
+ getResolvedMinimumFontMetrics(),
+ mHintBoring);
+ if (hintBoring != null) {
+ mHintBoring = hintBoring;
+ }
+ }
- // Check against our minimum width
- width = Math.max(width, getSuggestedMinimumWidth());
+ if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
+ if (hintDes < 0) {
+ hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
+ mHint.length(), mTextPaint, mTextDir, widthLimit,
+ mUseBoundsForWidth));
+ }
+ hintWidth = hintDes;
+ } else {
+ hintWidth = hintBoring.width;
+ }
- if (widthMode == MeasureSpec.AT_MOST) {
- width = Math.min(widthSize, width);
- }
- }
+ if (hintWidth > width) {
+ width = hintWidth;
+ }
+ }
- int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
- int unpaddedWidth = want;
+ width += getCompoundPaddingLeft() + getCompoundPaddingRight();
- if (mHorizontallyScrolling) want = VERY_WIDE;
-
- int hintWant = want;
- int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
-
- if (mLayout == null) {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
- } else {
- final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant)
- || (mLayout.getEllipsizedWidth()
- != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
-
- final boolean widthChanged = (mHint == null) && (mEllipsize == null)
- && (want > mLayout.getWidth())
- && (mLayout instanceof BoringLayout
- || (fromexisting && des >= 0 && des <= want));
-
- final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
-
- if (layoutChanged || maximumChanged) {
- if (!maximumChanged && widthChanged) {
- mLayout.increaseWidthTo(want);
+ if (mMaxWidthMode == EMS) {
+ width = Math.min(width, mMaxWidth * getLineHeight());
} else {
- makeNewLayout(want, hintWant, boring, hintBoring,
- width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
+ width = Math.min(width, mMaxWidth);
}
+
+ if (mMinWidthMode == EMS) {
+ width = Math.max(width, mMinWidth * getLineHeight());
+ } else {
+ width = Math.max(width, mMinWidth);
+ }
+
+ // Check against our minimum width
+ width = Math.max(width, getSuggestedMinimumWidth());
+
+ if (widthMode == MeasureSpec.AT_MOST) {
+ width = Math.min(widthSize, width);
+ }
+ }
+
+ int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
+ int unpaddedWidth = want;
+
+ if (mHorizontallyScrolling) want = VERY_WIDE;
+
+ int hintWant = want;
+ int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
+
+ if (mLayout == null) {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
} else {
- // Nothing has changed
+ final boolean layoutChanged =
+ (mLayout.getWidth() != want) || (hintWidth != hintWant)
+ || (mLayout.getEllipsizedWidth()
+ != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
+
+ final boolean widthChanged = (mHint == null) && (mEllipsize == null)
+ && (want > mLayout.getWidth())
+ && (mLayout instanceof BoringLayout
+ || (fromexisting && des >= 0 && des <= want));
+
+ final boolean maximumChanged =
+ (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
+
+ if (layoutChanged || maximumChanged) {
+ if (!maximumChanged && widthChanged) {
+ mLayout.increaseWidthTo(want);
+ } else {
+ makeNewLayout(want, hintWant, boring, hintBoring,
+ width - getCompoundPaddingLeft() - getCompoundPaddingRight(),
+ false);
+ }
+ } else {
+ // Nothing has changed
+ }
}
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- // Parent has told us how big to be. So be it.
- height = heightSize;
- mDesiredHeightAtMeasure = -1;
- } else {
- int desired = getDesiredHeight();
+ if (heightMode == MeasureSpec.EXACTLY) {
+ // Parent has told us how big to be. So be it.
+ height = heightSize;
+ mDesiredHeightAtMeasure = -1;
+ } else {
+ int desired = getDesiredHeight();
- height = desired;
- mDesiredHeightAtMeasure = desired;
+ height = desired;
+ mDesiredHeightAtMeasure = desired;
- if (heightMode == MeasureSpec.AT_MOST) {
- height = Math.min(desired, heightSize);
+ if (heightMode == MeasureSpec.AT_MOST) {
+ height = Math.min(desired, heightSize);
+ }
}
- }
- int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
- if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
- unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
- }
+ int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
+ if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
+ unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
+ }
- /*
- * We didn't let makeNewLayout() register to bring the cursor into view,
- * so do it here if there is any possibility that it is needed.
- */
- if (mMovement != null
- || mLayout.getWidth() > unpaddedWidth
- || mLayout.getHeight() > unpaddedHeight) {
- registerForPreDraw();
- } else {
- scrollTo(0, 0);
- }
+ /*
+ * We didn't let makeNewLayout() register to bring the cursor into view,
+ * so do it here if there is any possibility that it is needed.
+ */
+ if (mMovement != null
+ || mLayout.getWidth() > unpaddedWidth
+ || mLayout.getHeight() > unpaddedHeight) {
+ registerForPreDraw();
+ } else {
+ scrollTo(0, 0);
+ }
- setMeasuredDimension(width, height);
+ setMeasuredDimension(width, height);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
/**
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0f2dd10..2c21417 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -49,6 +49,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
+import android.os.BinderProxy;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1089,8 +1090,13 @@
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- sb.append('{'); sb.append(mContainer);
- sb.append(" m="); sb.append(modeToString(mMode));
+ sb.append('{');
+ if (mContainer != null && !(mContainer.asBinder() instanceof BinderProxy)) {
+ // Only log the token if it is not a binder proxy and has additional container info
+ sb.append(mContainer);
+ sb.append(" ");
+ }
+ sb.append("m="); sb.append(modeToString(mMode));
sb.append(" f="); sb.append(flagsToString(mFlags));
if (mParent != null) {
sb.append(" p="); sb.append(mParent);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 3fe63ab..a88a172 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1120,8 +1120,8 @@
@NonNull
public String toString() {
return "WindowContainerTransaction {"
- + " changes = " + mChanges
- + " hops = " + mHierarchyOps
+ + " changes= " + mChanges
+ + " hops= " + mHierarchyOps
+ " errorCallbackToken=" + mErrorCallbackToken
+ " taskFragmentOrganizer=" + mTaskFragmentOrganizer
+ " }";
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index bb4084e..f64dec8 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -106,7 +106,7 @@
jlong nativeObject) {
gui::OverlayProperties* overlayProperties =
reinterpret_cast<gui::OverlayProperties*>(nativeObject);
- if (overlayProperties->lutProperties.has_value()) {
+ if (!overlayProperties || !overlayProperties->lutProperties) {
return NULL;
}
auto& lutProperties = overlayProperties->lutProperties.value();
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 49191ee..7ef7829 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <array>
+#include <cctype>
#include <cstring>
#include <limits>
#include <memory>
@@ -1008,6 +1009,8 @@
}
}
if ((mode&PROC_OUT_STRING) != 0 && di < NS) {
+ std::replace_if(buffer+start, buffer+end,
+ [](unsigned char c){ return !std::isprint(c); }, '?');
jstring str = env->NewStringUTF(buffer+start);
env->SetObjectArrayElement(outStrings, di, str);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 82b463e..450b88b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -758,54 +758,64 @@
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- ScopedIntArrayRW joffsets(env, joffsetArray);
- if (joffsets.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
- return;
- }
- ScopedIntArrayRW jdimensions(env, jdimensionArray);
- if (jdimensions.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
- return;
- }
- ScopedIntArrayRW jsizes(env, jsizeArray);
- if (jsizes.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
- return;
- }
- ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
- if (jsamplingKeys.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
- return;
- }
+ std::vector<int32_t> offsets;
+ std::vector<int32_t> dimensions;
+ std::vector<int32_t> sizes;
+ std::vector<int32_t> samplingKeys;
+ int32_t fd = -1;
- jsize numLuts = env->GetArrayLength(jdimensionArray);
- std::vector<int32_t> offsets(joffsets.get(), joffsets.get() + numLuts);
- std::vector<int32_t> dimensions(jdimensions.get(), jdimensions.get() + numLuts);
- std::vector<int32_t> sizes(jsizes.get(), jsizes.get() + numLuts);
- std::vector<int32_t> samplingKeys(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
+ if (jdimensionArray) {
+ jsize numLuts = env->GetArrayLength(jdimensionArray);
+ ScopedIntArrayRW joffsets(env, joffsetArray);
+ if (joffsets.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+ return;
+ }
+ ScopedIntArrayRW jdimensions(env, jdimensionArray);
+ if (jdimensions.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+ return;
+ }
+ ScopedIntArrayRW jsizes(env, jsizeArray);
+ if (jsizes.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+ return;
+ }
+ ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+ if (jsamplingKeys.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+ return;
+ }
- ScopedFloatArrayRW jbuffers(env, jbufferArray);
- if (jbuffers.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
- return;
- }
+ if (numLuts > 0) {
+ offsets = std::vector<int32_t>(joffsets.get(), joffsets.get() + numLuts);
+ dimensions = std::vector<int32_t>(jdimensions.get(), jdimensions.get() + numLuts);
+ sizes = std::vector<int32_t>(jsizes.get(), jsizes.get() + numLuts);
+ samplingKeys = std::vector<int32_t>(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
- // create the shared memory and copy jbuffers
- size_t bufferSize = jbuffers.size() * sizeof(float);
- int32_t fd = ashmem_create_region("lut_shread_mem", bufferSize);
- if (fd < 0) {
- jniThrowRuntimeException(env, "ashmem_create_region() failed");
- return;
+ ScopedFloatArrayRW jbuffers(env, jbufferArray);
+ if (jbuffers.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+ return;
+ }
+
+ // create the shared memory and copy jbuffers
+ size_t bufferSize = jbuffers.size() * sizeof(float);
+ fd = ashmem_create_region("lut_shared_mem", bufferSize);
+ if (fd < 0) {
+ jniThrowRuntimeException(env, "ashmem_create_region() failed");
+ return;
+ }
+ void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ jniThrowRuntimeException(env, "Failed to map the shared memory");
+ return;
+ }
+ memcpy(ptr, jbuffers.get(), bufferSize);
+ // unmap
+ munmap(ptr, bufferSize);
+ }
}
- void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (ptr == MAP_FAILED) {
- jniThrowRuntimeException(env, "Failed to map the shared memory");
- return;
- }
- memcpy(ptr, jbuffers.get(), bufferSize);
- // unmap
- munmap(ptr, bufferSize);
transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 41dec37..7ef5394 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1855,13 +1855,23 @@
{@link android.R.styleable#AndroidManifestProcess process} tag, or to an
{@link android.R.styleable#AndroidManifestApplication application} tag (to supply
a default setting for all application components). -->
- <attr name="memtagMode">
+ <attr name="memtagMode">
<enum name="default" value="-1" />
<enum name="off" value="0" />
<enum name="async" value="1" />
<enum name="sync" value="2" />
</attr>
+ <!-- This attribute will be used to override app compatibility mode on 16 KB devices.
+ If set to enabled, Natives lib will be extracted from APK if they are not page aligned on
+ 16 KB device. 4 KB natives libs will be loaded app-compat mode if they are eligible.
+ @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <attr name="pageSizeCompat">
+ <enum name="enabled" value="5" />
+ <enum name="disabled" value="6" />
+ </attr>
+
+
<!-- Attribution tag to be used for permission sub-attribution if a
permission is checked in {@link android.content.Context#sendBroadcast(Intent, String)}.
Multiple tags can be specified separated by '|'.
@@ -2212,6 +2222,9 @@
<attr name="memtagMode" />
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <attr name="pageSizeCompat" />
+
<!-- If {@code true} enables automatic zero initialization of all native heap
allocations. -->
<attr name="nativeHeapZeroInitialized" format="boolean" />
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b6436d0..a0bf89d 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -131,6 +131,8 @@
<public name="alternateLauncherIcons"/>
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
<public name="alternateLauncherLabels"/>
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) -->
+ <public name="pageSizeCompat" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
index 9dd196d..f62d420 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
@@ -507,7 +507,8 @@
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: isStylusHandwritingEnabled=false\n"
- + "prefix: contentMimeTypes=null\n");
+ + "prefix: contentMimeTypes=null\n"
+ + "prefix: writingToolsEnabled=true\n");
}
@Test
@@ -539,6 +540,7 @@
info.hintLocales = LocaleList.forLanguageTags("en,es,zh");
info.contentMimeTypes = new String[] {"image/png"};
info.targetInputMethodUser = UserHandle.of(10);
+ info.setWritingToolsEnabled(false);
final StringBuilder sb = new StringBuilder();
info.dump(new StringBuilderPrinter(sb), "prefix2: ");
assertThat(sb.toString()).isEqualTo(
@@ -555,7 +557,8 @@
+ "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ "prefix2: isStylusHandwritingEnabled=" + isStylusHandwritingEnabled + "\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
- + "prefix2: targetInputMethodUserId=10\n");
+ + "prefix2: targetInputMethodUserId=10\n"
+ + "prefix2: writingToolsEnabled=false\n");
}
@Test
@@ -576,7 +579,8 @@
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: isStylusHandwritingEnabled=false\n"
- + "prefix: contentMimeTypes=null\n");
+ + "prefix: contentMimeTypes=null\n"
+ + "prefix: writingToolsEnabled=true\n");
}
@Test
@@ -621,4 +625,9 @@
infoCopy.extras.putString("testKey2", "testValue");
assertFalse(TEST_EDITOR_INFO.kindofEquals(infoCopy));
}
+
+ @Test
+ public void testWritingToolsEnabledbyDefault() {
+ assertTrue(TEST_EDITOR_INFO.isWritingToolsEnabled());
+ }
}
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 6149382..4620cb8 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -12,3 +12,6 @@
# Caching
per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
+
+# RemoteCallbackList
+per-file RemoteCallbackListTest.java = shayba@google.com
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 03e0ab0..a31acc8 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -249,9 +249,10 @@
return null;
}
return "id=" + taskInfo.taskId
- + " baseIntent=" + (taskInfo.baseIntent != null
- ? taskInfo.baseIntent.getComponent()
- : "null")
+ + " baseIntent=" +
+ (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
+ ? taskInfo.baseIntent.getComponent().flattenToString()
+ : "null")
+ " winMode=" + WindowConfiguration.windowingModeToString(
taskInfo.getWindowingMode());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 39dc267..823c533 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -274,8 +274,11 @@
private final DragAndDropController mDragAndDropController;
/** Used to send bubble events to launcher. */
private Bubbles.BubbleStateListener mBubbleStateListener;
- /** Used to track previous navigation mode to detect switch to buttons navigation. */
- private boolean mIsPrevNavModeGestures;
+ /**
+ * Used to track previous navigation mode to detect switch to buttons navigation. Set to
+ * true to switch the bubble bar to the opposite side for 3 nav buttons mode on device boot.
+ */
+ private boolean mIsPrevNavModeGestures = true;
/** Used to send updates to the views from {@link #mBubbleDataListener}. */
private BubbleViewCallback mBubbleViewCallback;
@@ -357,7 +360,6 @@
}
};
mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this);
- mIsPrevNavModeGestures = ContextUtils.isGestureNavigationMode(mContext);
}
private void registerOneHandedState(OneHandedController oneHanded) {
@@ -593,9 +595,9 @@
if (mBubbleStateListener != null) {
boolean isCurrentNavModeGestures = ContextUtils.isGestureNavigationMode(mContext);
if (mIsPrevNavModeGestures && !isCurrentNavModeGestures) {
- BubbleBarLocation navButtonsLocation = ContextUtils.isRtl(mContext)
+ BubbleBarLocation bubbleBarLocation = ContextUtils.isRtl(mContext)
? BubbleBarLocation.RIGHT : BubbleBarLocation.LEFT;
- mBubblePositioner.setBubbleBarLocation(navButtonsLocation);
+ mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
}
mIsPrevNavModeGestures = isCurrentNavModeGestures;
BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 9911669..cea4d33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -205,7 +205,7 @@
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
notifyRecentTasksChanged();
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Add split pair: %d, %d, %s",
taskId1, taskId2, splitBounds);
return true;
}
@@ -221,7 +221,7 @@
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
notifyRecentTasksChanged();
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Remove split pair: %d, %d",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Remove split pair: %d, %d",
taskId, pairedTaskId);
}
}
@@ -234,7 +234,17 @@
// We could do extra verification of requiring both taskIds of a pair and verifying that
// the same split bounds object is returned... but meh. Seems unnecessary.
- return mTaskSplitBoundsMap.get(taskId);
+ SplitBounds splitBounds = mTaskSplitBoundsMap.get(taskId);
+ if (splitBounds != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "getSplitBoundsForTaskId: taskId=%d splitBoundsTasks=[%d, %d]", taskId,
+ splitBounds.leftTopTaskId, splitBounds.rightBottomTaskId);
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "getSplitBoundsForTaskId: expected split bounds for taskId=%d but not found",
+ taskId);
+ }
+ return splitBounds;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index cc0e1df..7d1ffb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1362,7 +1362,11 @@
}
void clearSplitPairedInRecents(@ExitReason int exitReason) {
- if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
+ if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: skipping reason=%s",
+ !mShouldUpdateRecents ? "shouldn't update" : exitReasonToString(exitReason));
+ return;
+ }
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
exitReasonToString(exitReason));
@@ -1608,6 +1612,8 @@
private void updateRecentTasksSplitPair() {
// Preventing from single task update while processing recents.
if (!mShouldUpdateRecents || !mPausingTasks.isEmpty()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateRecentTasksSplitPair: skipping reason=%s",
+ !mShouldUpdateRecents ? "shouldn't update" : "no pausing tasks");
return;
}
mRecentTasks.ifPresent(recentTasks -> {
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 3cd5f52..da50f2c 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -19,6 +19,7 @@
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -39,6 +40,8 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -461,8 +464,12 @@
/**
* Returns information about the {@link AudioDeviceInfo} used for this playback.
- * @return the audio playback device or null if the device is not available at the time of query
+ * @return the audio playback device or null if the device is not available at the time of
+ * query.
+ * @deprecated this information was never populated
*/
+ @Deprecated
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
public @Nullable AudioDeviceInfo getAudioDeviceInfo() {
final int deviceId;
synchronized (mUpdateablePropLock) {
@@ -476,6 +483,23 @@
/**
* @hide
+ * Returns information about the List of {@link AudioDeviceInfo} used for this playback.
+ * @return the audio playback devices
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioDeviceInfo> getAudioDeviceInfos() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getAudioDeviceInfo();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
+ * @hide
* Return the audio session ID associated with this player.
* See {@link AudioManager#generateAudioSessionId()}.
* @return an audio session ID
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 80e5719..9394941 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,8 +20,10 @@
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -1920,6 +1922,23 @@
}
/**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioRecord.
+ * Note: The query is only valid if the AudioRecord is currently playing. If it is not,
+ * <code>getRoutedDevices()</code> will return an empty list.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return audioDeviceInfos;
+ }
+
+ /**
* Must match the native definition in frameworks/av/service/audioflinger/Audioflinger.h.
*/
private static final long MAX_SHARED_AUDIO_HISTORY_MS = 5000;
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 26fa631..22aa9a0 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -16,9 +16,16 @@
package android.media;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* AudioRouting defines an interface for controlling routing and routing notifications in
* AudioTrack and AudioRecord objects.
@@ -49,6 +56,22 @@
public AudioDeviceInfo getRoutedDevice();
/**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioTrack/AudioRecord.
+ * Note: The query is only valid if the AudioTrack/AudioRecord is currently playing.
+ * If it is not, <code>getRoutedDevices()</code> will return an empty List.
+ */
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ default @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return new ArrayList<AudioDeviceInfo>();
+ }
+
+ /**
* Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
* changes on this AudioTrack/AudioRecord.
* @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 03cd535..93a1831 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -17,8 +17,10 @@
package android.media;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -54,7 +56,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
+import java.util.ArrayList;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -3783,6 +3787,8 @@
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
* Note: The query is only valid if the AudioTrack is currently playing. If it is not,
* <code>getRoutedDevice()</code> will return null.
+ * Audio may play on multiple devices simultaneously (e.g. an alarm playing on headphones and
+ * speaker on a phone), so prefer using {@link #getRoutedDevices}.
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
@@ -3793,6 +3799,23 @@
return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
}
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * AudioTrack.
+ * Note: The query is only valid if the AudioTrack is currently playing. If it is not,
+ * <code>getRoutedDevices()</code> will return an empty list.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return audioDeviceInfos;
+ }
+
private void tryToDisableNativeRoutingCallback() {
synchronized (mRoutingChangeListeners) {
if (mEnableSelfRoutingMonitor) {
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 3f9126a..1ecba31 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -16,10 +16,9 @@
package android.media;
+import static android.media.tv.flags.Flags.FLAG_MEDIACAS_UPDATE_CLIENT_PROFILE_PRIORITY;
import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
-import static com.android.media.flags.Flags.FLAG_UPDATE_CLIENT_PROFILE_PRIORITY;
-
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -996,7 +995,7 @@
* @param niceValue the nice value.
* @hide
*/
- @FlaggedApi(FLAG_UPDATE_CLIENT_PROFILE_PRIORITY)
+ @FlaggedApi(FLAG_MEDIACAS_UPDATE_CLIENT_PROFILE_PRIORITY)
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public boolean updateResourcePriority(int priority, int niceValue) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index a0f8ae5..158bc7f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -18,7 +18,9 @@
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -84,6 +86,7 @@
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.ByteOrder;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
@@ -1542,6 +1545,8 @@
* Note: The query is only valid if the MediaPlayer is currently playing.
* If the player is not playing, the returned device can be null or correspond to previously
* selected device when the player was last active.
+ * Audio may play on multiple devices simultaneously (e.g. an alarm playing on headphones and
+ * speaker on a phone), so prefer using {@link #getRoutedDevices}.
*/
@Override
public AudioDeviceInfo getRoutedDevice() {
@@ -1552,6 +1557,23 @@
return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
}
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * MediaPlayer.
+ * Note: The query is only valid if the MediaPlayer is currently playing.
+ * If the player is not playing, the returned devices can be empty or correspond to previously
+ * selected devices when the player was last active.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return audioDeviceInfos;
+ }
/**
* Sends device list change notification to all listeners.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2d17bf5..f75bcf3 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,7 +16,10 @@
package android.media;
+import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
+
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1695,6 +1698,24 @@
return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
}
+ /**
+ * Returns a List of {@link AudioDeviceInfo} identifying the current routing of this
+ * MediaRecorder.
+ * Note: The query is only valid if the MediaRecorder is currently recording.
+ * If the recorder is not recording, the returned devices can be empty or correspond to
+ * previously selected devices when the recorder was last active.
+ */
+ @Override
+ @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
+ public @NonNull List<AudioDeviceInfo> getRoutedDevices() {
+ List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
+ AudioDeviceInfo audioDeviceInfo = getRoutedDevice();
+ if (audioDeviceInfo != null) {
+ audioDeviceInfos.add(audioDeviceInfo);
+ }
+ return audioDeviceInfos;
+ }
+
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 7895eb2..5b1ea8b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -78,13 +78,6 @@
}
flag {
- name: "update_client_profile_priority"
- namespace: "media_solutions"
- description : "Feature flag to add updateResourcePriority api to MediaCas"
- bug: "300565729"
-}
-
-flag {
name: "enable_built_in_speaker_route_suitability_statuses"
is_exported: true
namespace: "media_solutions"
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
index dd2a534..ff0279f 100644
--- a/media/java/android/media/tv/ad/TvAdView.java
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -240,6 +240,38 @@
}
}
+ /**
+ * Controls whether the TvAdView's surface is placed on top of other regular surface views in
+ * the window (but still behind the window itself).
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+ * otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderOnTop(false);
+ mSurfaceView.setZOrderMediaOverlay(isMediaOverlay);
+ }
+ }
+
+ /**
+ * Controls whether the TvAdView's surface is placed on top of its window.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ *
+ * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public void setZOrderOnTop(boolean onTop) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderMediaOverlay(false);
+ mSurfaceView.setZOrderOnTop(onTop);
+ }
+ }
+
private void resetSurfaceView() {
if (mSurfaceView != null) {
mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 6441652..4b832ae 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -2,6 +2,22 @@
container: "system"
flag {
+ name: "enable_le_audio_broadcast_ui"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enable Broadcast UI for LE Audio on TV."
+ bug: "378732734"
+}
+
+flag {
+ name: "enable_le_audio_unicast_ui"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enable Unicast UI for LE Audio on TV."
+ bug: "378732734"
+}
+
+flag {
name: "broadcast_visibility_types"
is_exported: true
namespace: "media_tv"
@@ -77,11 +93,19 @@
name: "set_resource_holder_retain"
is_exported: true
namespace: "media_tv"
- description : "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
+ description: "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
bug: "372973197"
}
flag {
+ name: "mediacas_update_client_profile_priority"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Feature flag to add updateResourcePriority api to MediaCas"
+ bug: "372971241"
+}
+
+flag {
name: "apply_picture_profiles"
is_exported: true
namespace: "media_tv"
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 635572d..9e9699f 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -271,6 +271,38 @@
}
}
+ /**
+ * Controls whether the TvInteractiveAppView's surface is placed on top of other regular surface
+ * views in the window (but still behind the window itself).
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+ * otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderOnTop(false);
+ mSurfaceView.setZOrderMediaOverlay(isMediaOverlay);
+ }
+ }
+
+ /**
+ * Controls whether the TvInterActiveAppView's surface is placed on top of its window.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ *
+ * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void setZOrderOnTop(boolean onTop) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setZOrderMediaOverlay(false);
+ mSurfaceView.setZOrderOnTop(onTop);
+ }
+ }
+
private void resetSurfaceView() {
if (mSurfaceView != null) {
mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index a046057..2d1fbf9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -361,6 +361,8 @@
APerformanceHint_setThreads; # introduced=UpsideDownCake
APerformanceHint_setPreferPowerEfficiency; # introduced=VanillaIceCream
APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
+ APerformanceHint_notifyWorkloadIncrease; # introduced=36
+ APerformanceHint_notifyWorkloadReset; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -379,6 +381,8 @@
APerformanceHint_getThreadIds;
APerformanceHint_createSessionInternal;
APerformanceHint_setUseFMQForTesting;
+ APerformanceHint_getRateLimiterPropertiesForTesting;
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 15f77ce..e2fa94d 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -33,12 +33,14 @@
#include <android/performance_hint.h>
#include <android/trace.h>
#include <android_os.h>
+#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
#include <inttypes.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
#include <chrono>
+#include <format>
#include <future>
#include <set>
#include <utility>
@@ -63,6 +65,22 @@
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
+// A pair of values that determine the behavior of the
+// load hint rate limiter, to only allow "X hints every Y seconds"
+constexpr double kLoadHintInterval = std::chrono::nanoseconds(2s).count();
+constexpr double kMaxLoadHintsPerInterval = 20;
+constexpr double kReplenishRate = kMaxLoadHintsPerInterval / kLoadHintInterval;
+bool kForceNewHintBehavior = false;
+
+template <class T>
+constexpr int32_t enum_size() {
+ return static_cast<int32_t>(*(ndk::enum_range<T>().end() - 1)) + 1;
+}
+
+bool useNewLoadHintBehavior() {
+ return android::os::adpf_use_load_hints() || kForceNewHintBehavior;
+}
+
// Shared lock for the whole PerformanceHintManager and sessions
static std::mutex sHintMutex = std::mutex{};
class FMQWrapper {
@@ -76,7 +94,8 @@
hal::WorkDuration* durations, size_t count) REQUIRES(sHintMutex);
bool updateTargetWorkDuration(std::optional<hal::SessionConfig>& config,
int64_t targetDurationNanos) REQUIRES(sHintMutex);
- bool sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) REQUIRES(sHintMutex);
+ bool sendHints(std::optional<hal::SessionConfig>& config, std::vector<hal::SessionHint>& hint,
+ int64_t now) REQUIRES(sHintMutex);
bool setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode, bool enabled)
REQUIRES(sHintMutex);
void setToken(ndk::SpAIBinder& token);
@@ -86,10 +105,11 @@
private:
template <HalChannelMessageContents::Tag T, bool urgent = false,
class C = HalChannelMessageContents::_at<T>>
- bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1)
- REQUIRES(sHintMutex);
+ bool sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count = 1,
+ int64_t now = ::android::uptimeNanos()) REQUIRES(sHintMutex);
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
- void writeBuffer(C* message, hal::SessionConfig& config, size_t count) REQUIRES(sHintMutex);
+ void writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now)
+ REQUIRES(sHintMutex);
bool isActiveLocked() REQUIRES(sHintMutex);
bool updatePersistentTransaction() REQUIRES(sHintMutex);
@@ -120,6 +140,7 @@
hal::SessionTag tag = hal::SessionTag::APP);
int64_t getPreferredRateNanos() const;
FMQWrapper& getFMQWrapper();
+ bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
private:
// Necessary to create an empty binder object
@@ -138,6 +159,8 @@
ndk::SpAIBinder mToken;
const int64_t mPreferredRateNanos;
FMQWrapper mFMQWrapper;
+ double mHintBudget = kMaxLoadHintsPerInterval;
+ int64_t mLastBudgetReplenish = 0;
};
struct APerformanceHintSession {
@@ -151,7 +174,9 @@
int updateTargetWorkDuration(int64_t targetDurationNanos);
int reportActualWorkDuration(int64_t actualDurationNanos);
- int sendHint(SessionHint hint);
+ int sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char* debugName);
+ int notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName);
+ int notifyWorkloadReset(bool cpu, bool gpu, const char* debugName);
int setThreads(const int32_t* threadIds, size_t size);
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
@@ -173,6 +198,8 @@
// Last target hit timestamp
int64_t mLastTargetMetTimestamp GUARDED_BY(sHintMutex);
// Last hint reported from sendHint indexed by hint value
+ // This is only used by the old rate limiter impl and is replaced
+ // with the new rate limiter under a flag
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
@@ -255,6 +282,21 @@
return new APerformanceHintManager(manager, preferredRateNanos);
}
+bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) {
+ mHintBudget =
+ std::max(kMaxLoadHintsPerInterval,
+ mHintBudget +
+ static_cast<double>(now - mLastBudgetReplenish) * kReplenishRate);
+ mLastBudgetReplenish = now;
+
+ // If this youngest timestamp isn't older than the timeout time, we can't send
+ if (hints.size() > mHintBudget) {
+ return false;
+ }
+ mHintBudget -= hints.size();
+ return true;
+}
+
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
hal::SessionTag tag) {
@@ -292,9 +334,7 @@
// ===================================== APerformanceHintSession implementation
-constexpr int kNumEnums =
- ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
-
+constexpr int kNumEnums = enum_size<hal::SessionHint>();
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
@@ -361,31 +401,83 @@
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
-int APerformanceHintSession::sendHint(SessionHint hint) {
+int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
+ const char*) {
std::scoped_lock lock(sHintMutex);
- if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
- ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ if (hints.empty()) {
return EINVAL;
}
- int64_t now = uptimeNanos();
-
- // Limit sendHint to a pre-detemined rate for safety
- if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
- return 0;
- }
-
- if (!getFMQ().sendHint(mSessionConfig, hint)) {
- ndk::ScopedAStatus ret = mHintSession->sendHint(hint);
-
- if (!ret.isOk()) {
- ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
- return EPIPE;
+ for (auto&& hint : hints) {
+ if (static_cast<int32_t>(hint) < 0 || static_cast<int32_t>(hint) >= kNumEnums) {
+ ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
+ return EINVAL;
}
}
- mLastHintSentTimestamp[hint] = now;
+
+ if (useNewLoadHintBehavior()) {
+ if (!APerformanceHintManager::getInstance()->canSendLoadHints(hints, now)) {
+ return EBUSY;
+ }
+ }
+ // keep old rate limiter behavior for legacy flag
+ else {
+ for (auto&& hint : hints) {
+ if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + SEND_HINT_TIMEOUT)) {
+ return EBUSY;
+ }
+ }
+ }
+
+ if (!getFMQ().sendHints(mSessionConfig, hints, now)) {
+ for (auto&& hint : hints) {
+ ndk::ScopedAStatus ret = mHintSession->sendHint(static_cast<int32_t>(hint));
+
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.getMessage());
+ return EPIPE;
+ }
+ }
+ }
+
+ if (!useNewLoadHintBehavior()) {
+ for (auto&& hint : hints) {
+ mLastHintSentTimestamp[static_cast<int32_t>(hint)] = now;
+ }
+ }
+
+ if (ATrace_isEnabled()) {
+ ATRACE_INSTANT("Sending load hint");
+ }
+
return 0;
}
+int APerformanceHintSession::notifyWorkloadIncrease(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_UP);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_UP);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
+int APerformanceHintSession::notifyWorkloadReset(bool cpu, bool gpu, const char* debugName) {
+ std::vector<hal::SessionHint> hints(2);
+ hints.clear();
+ if (cpu) {
+ hints.push_back(hal::SessionHint::CPU_LOAD_RESET);
+ }
+ if (gpu) {
+ hints.push_back(hal::SessionHint::GPU_LOAD_RESET);
+ }
+ int64_t now = ::android::uptimeNanos();
+ return sendHints(hints, now, debugName);
+}
+
int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
if (size == 0) {
ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
@@ -565,24 +657,25 @@
}
template <HalChannelMessageContents::Tag T, class C>
-void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t) {
- new (mFmqTransaction.getSlot(0)) hal::ChannelMessage{
- .sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos = ::android::uptimeNanos(),
- .data = HalChannelMessageContents::make<T, C>(std::move(*message)),
- };
+void FMQWrapper::writeBuffer(C* message, hal::SessionConfig& config, size_t count, int64_t now) {
+ for (size_t i = 0; i < count; ++i) {
+ new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
+ .sessionID = static_cast<int32_t>(config.id),
+ .timeStampNanos = now,
+ .data = HalChannelMessageContents::make<T, C>(std::move(*(message + i))),
+ };
+ }
}
template <>
void FMQWrapper::writeBuffer<HalChannelMessageContents::workDuration>(hal::WorkDuration* messages,
hal::SessionConfig& config,
- size_t count) {
+ size_t count, int64_t now) {
for (size_t i = 0; i < count; ++i) {
hal::WorkDuration& message = messages[i];
new (mFmqTransaction.getSlot(i)) hal::ChannelMessage{
.sessionID = static_cast<int32_t>(config.id),
- .timeStampNanos =
- (i == count - 1) ? ::android::uptimeNanos() : message.timeStampNanos,
+ .timeStampNanos = (i == count - 1) ? now : message.timeStampNanos,
.data = HalChannelMessageContents::make<HalChannelMessageContents::workDuration,
hal::WorkDurationFixedV1>({
.durationNanos = message.cpuDurationNanos,
@@ -595,7 +688,8 @@
}
template <HalChannelMessageContents::Tag T, bool urgent, class C>
-bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count) {
+bool FMQWrapper::sendMessages(std::optional<hal::SessionConfig>& config, C* message, size_t count,
+ int64_t now) {
if (!isActiveLocked() || !config.has_value() || mCorrupted) {
return false;
}
@@ -609,7 +703,7 @@
return false;
}
}
- writeBuffer<T, C>(message, *config, count);
+ writeBuffer<T, C>(message, *config, count, now);
mQueue->commitWrite(count);
mEventFlag->wake(mWriteMask);
// Re-create the persistent transaction after writing
@@ -641,10 +735,9 @@
return sendMessages<HalChannelMessageContents::targetDuration>(config, &targetDurationNanos);
}
-bool FMQWrapper::sendHint(std::optional<hal::SessionConfig>& config, SessionHint hint) {
- return sendMessages<HalChannelMessageContents::hint>(config,
- reinterpret_cast<hal::SessionHint*>(
- &hint));
+bool FMQWrapper::sendHints(std::optional<hal::SessionConfig>& config,
+ std::vector<hal::SessionHint>& hints, int64_t now) {
+ return sendMessages<HalChannelMessageContents::hint>(config, hints.data(), hints.size(), now);
}
bool FMQWrapper::setMode(std::optional<hal::SessionConfig>& config, hal::SessionMode mode,
@@ -758,7 +851,9 @@
int APerformanceHint_sendHint(APerformanceHintSession* session, SessionHint hint) {
VALIDATE_PTR(session)
- return session->sendHint(hint);
+ std::vector<hal::SessionHint> hints{static_cast<hal::SessionHint>(hint)};
+ int64_t now = ::android::uptimeNanos();
+ return session->sendHints(hints, now, "HWUI hint");
}
int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
@@ -791,6 +886,26 @@
return session->reportActualWorkDuration(workDurationPtr);
}
+int APerformanceHint_notifyWorkloadIncrease(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadIncrease(cpu, gpu, debugName);
+}
+
+int APerformanceHint_notifyWorkloadReset(APerformanceHintSession* session, bool cpu, bool gpu,
+ const char* debugName) {
+ VALIDATE_PTR(session)
+ VALIDATE_PTR(debugName)
+ if (!useNewLoadHintBehavior()) {
+ return ENOTSUP;
+ }
+ return session->notifyWorkloadReset(cpu, gpu, debugName);
+}
+
AWorkDuration* AWorkDuration_create() {
return new AWorkDuration();
}
@@ -838,3 +953,13 @@
void APerformanceHint_setUseFMQForTesting(bool enabled) {
gForceFMQEnabled = enabled;
}
+
+void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPerInterval,
+ int64_t* loadHintInterval) {
+ *maxLoadHintsPerInterval = kMaxLoadHintsPerInterval;
+ *loadHintInterval = kLoadHintInterval;
+}
+
+void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
+ kForceNewHintBehavior = newBehavior;
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 9de3a6f..f707a0e 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -66,6 +66,18 @@
std::optional<hal::ChannelConfig>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params,
+ std::vector<float>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params,
+ float* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -90,7 +102,10 @@
public:
void SetUp() override {
mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+ APerformanceHint_getRateLimiterPropertiesForTesting(&mMaxLoadHintsPerInterval,
+ &mLoadHintInterval);
APerformanceHint_setIHintManagerForTesting(&mMockIHintManager);
+ APerformanceHint_setUseNewLoadHintBehaviorForTesting(true);
}
void TearDown() override {
@@ -176,6 +191,9 @@
int kMockQueueSize = 20;
bool mUsingFMQ = false;
+ int32_t mMaxLoadHintsPerInterval;
+ int64_t mLoadHintInterval;
+
template <HalChannelMessageContents::Tag T, class C = HalChannelMessageContents::_at<T>>
void expectToReadFromFmq(C expected) {
hal::ChannelMessage readData;
@@ -218,7 +236,6 @@
EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1));
result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
EXPECT_EQ(0, result);
-
result = APerformanceHint_updateTargetWorkDuration(session, -1L);
EXPECT_EQ(EINVAL, result);
result = APerformanceHint_reportActualWorkDuration(session, -1L);
@@ -228,18 +245,28 @@
EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
result = APerformanceHint_sendHint(session, hintId);
EXPECT_EQ(0, result);
- usleep(110000); // Sleep for longer than the update timeout.
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_UP))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, false, "Test hint");
EXPECT_EQ(0, result);
- // Expect to get rate limited if we try to send faster than the limiter allows
- EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(0));
- result = APerformanceHint_sendHint(session, hintId);
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_RESET))).Times(Exactly(1));
+ EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_EQ(0, result);
result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1));
EXPECT_EQ(EINVAL, result);
+ Mock::VerifyAndClearExpectations(mMockSession.get());
+ for (int i = 0; i < mMaxLoadHintsPerInterval; ++i) {
+ APerformanceHint_sendHint(session, hintId);
+ }
+
+ // Expect to get rate limited if we try to send faster than the limiter allows
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadIncrease(session, true, true, "Test hint");
+ EXPECT_EQ(result, EBUSY);
+ EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0));
+ result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint");
EXPECT_CALL(*mMockSession, close()).Times(Exactly(1));
APerformanceHint_closeSession(session);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 72163e4..f82c8b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -31,7 +31,8 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.whenever
@@ -62,7 +63,7 @@
private val testScope = kosmos.testScope
private val clock = FakeSystemClock()
private val userRepository = FakeUserRepository()
- private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.mobileConnectionsRepository
private lateinit var underTest: AuthenticationRepository
@@ -110,7 +111,7 @@
.isEqualTo(AuthenticationMethodModel.None)
currentSecurityMode = KeyguardSecurityModel.SecurityMode.SimPin
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Sim)
assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Sim)
}
@@ -199,7 +200,7 @@
}
private fun setSecurityModeAndDispatchBroadcast(
- securityMode: KeyguardSecurityModel.SecurityMode,
+ securityMode: KeyguardSecurityModel.SecurityMode
) {
currentSecurityMode = securityMode
dispatchBroadcast()
@@ -208,23 +209,15 @@
private fun dispatchBroadcast() {
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
- Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
)
}
companion object {
private val USER_INFOS =
listOf(
- UserInfo(
- /* id= */ 100,
- /* name= */ "First user",
- /* flags= */ 0,
- ),
- UserInfo(
- /* id= */ 101,
- /* name= */ "Second user",
- /* flags= */ 0,
- ),
+ UserInfo(/* id= */ 100, /* name= */ "First user", /* flags= */ 0),
+ UserInfo(/* id= */ 101, /* name= */ "Second user", /* flags= */ 0),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 566cd70..10bf523 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -35,7 +35,8 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -78,7 +79,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ mobileConnectionsRepository = kosmos.mobileConnectionsRepository.fake
overrideResource(R.string.lockscreen_emergency_call, MESSAGE_EMERGENCY_CALL)
overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
index 5efb617..f6a6e54 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt
@@ -119,22 +119,6 @@
}
@Test
- fun onLongClick_whenTileDoesNotHandleLongClick_playsFailureHaptics() =
- testScope.runTest {
- // WHEN the tile is long-clicked but the tile does not handle a long-click
- val state = QSTile.State().apply { handlesLongClick = false }
- qsTile.changeState(state)
- underTest.setTileInteractionState(
- TileHapticsViewModel.TileInteractionState.LONG_CLICKED
- )
- runCurrent()
-
- // THEN the failure token plays
- assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
- assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
- }
-
- @Test
fun whenLaunchingFromClick_doesNotPlayHaptics() =
testScope.runTest {
// WHEN the tile is clicked and its action state changes accordingly
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 4e429c3..69fb03d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -47,7 +47,8 @@
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.res.R
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -99,7 +100,7 @@
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
- private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
+ private val mobileConnectionsRepository = kosmos.mobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -142,7 +143,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
verify(authController, times(2)).addCallback(authControllerCallback.capture())
@@ -247,7 +248,7 @@
private fun deviceIsInPostureThatSupportsFaceAuth() {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_FLIPPED
+ DevicePostureController.DEVICE_POSTURE_FLIPPED,
)
devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED)
}
@@ -459,7 +460,7 @@
biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures()
- mobileConnectionsRepository.isAnySimSecure.value = false
+ mobileConnectionsRepository.fake.isAnySimSecure.value = false
runCurrent()
val isFaceAuthEnabledAndEnrolled by
@@ -467,7 +468,7 @@
assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
runCurrent()
assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
@@ -485,13 +486,13 @@
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
biometricsAreEnabledBySettings()
- mobileConnectionsRepository.isAnySimSecure.value = false
+ mobileConnectionsRepository.fake.isAnySimSecure.value = false
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
assertThat(isFaceAuthCurrentlyAllowed).isTrue()
- mobileConnectionsRepository.isAnySimSecure.value = true
+ mobileConnectionsRepository.fake.isAnySimSecure.value = true
assertThat(isFaceAuthCurrentlyAllowed).isFalse()
}
@@ -584,7 +585,7 @@
testScope.runTest {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_UNKNOWN
+ DevicePostureController.DEVICE_POSTURE_UNKNOWN,
)
createBiometricSettingsRepository()
@@ -597,7 +598,7 @@
testScope.runTest {
overrideResource(
R.integer.config_face_auth_supported_posture,
- DevicePostureController.DEVICE_POSTURE_FLIPPED
+ DevicePostureController.DEVICE_POSTURE_FLIPPED,
)
createBiometricSettingsRepository()
@@ -749,7 +750,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
// Non strong auth is not allowed now, FP is marked strong
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
@@ -761,7 +762,7 @@
1,
SensorStrength.CONVENIENCE,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
assertThat(isFingerprintCurrentlyAllowed).isFalse()
@@ -769,7 +770,7 @@
1,
SensorStrength.WEAK,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
assertThat(isFingerprintCurrentlyAllowed).isFalse()
}
@@ -791,7 +792,7 @@
1,
SensorStrength.STRONG,
FingerprintSensorType.UDFPS_OPTICAL,
- emptyMap()
+ emptyMap(),
)
// Non strong auth is not allowed now, FP is marked strong
onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, PRIMARY_USER_ID)
@@ -830,7 +831,7 @@
UserInfo(
/* id= */ PRIMARY_USER_ID,
/* name= */ "primary user",
- /* flags= */ UserInfo.FLAG_PRIMARY
+ /* flags= */ UserInfo.FLAG_PRIMARY,
)
private const val ANOTHER_USER_ID = 1
@@ -838,7 +839,7 @@
UserInfo(
/* id= */ ANOTHER_USER_ID,
/* name= */ "another user",
- /* flags= */ UserInfo.FLAG_PRIMARY
+ /* flags= */ UserInfo.FLAG_PRIMARY,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index f6d439a..5a77f3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.os.ParcelUuid
-import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -25,91 +24,78 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryLogbufferName
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconsInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private lateinit var underTest: MobileIconsInteractor
- private lateinit var connectivityRepository: FakeConnectivityRepository
- private lateinit var connectionsRepository: FakeMobileConnectionsRepository
- private val userSetupRepository = FakeUserSetupRepository()
- private val mobileMappingsProxy = FakeMobileMappingsProxy()
- private val flags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+ private val kosmos by lazy {
+ testKosmos().apply {
+ mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest"
+ mobileConnectionsRepository.fake.run {
+ setMobileConnectionRepositoryMap(
+ mapOf(
+ SUB_1_ID to CONNECTION_1,
+ SUB_2_ID to CONNECTION_2,
+ SUB_3_ID to CONNECTION_3,
+ SUB_4_ID to CONNECTION_4,
+ )
+ )
+ setActiveMobileDataSubscriptionId(SUB_1_ID)
+ }
+ featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
}
+ }
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ // shortcut rename
+ private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
- private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconsInteractorTest")
+ private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
- @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- connectivityRepository = FakeConnectivityRepository()
-
- connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer)
- connectionsRepository.setMobileConnectionRepositoryMap(
- mapOf(
- SUB_1_ID to CONNECTION_1,
- SUB_2_ID to CONNECTION_2,
- SUB_3_ID to CONNECTION_3,
- SUB_4_ID to CONNECTION_4,
- )
+ private val Kosmos.underTest by Fixture {
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ featureFlagsClassic,
)
- connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
-
- underTest =
- MobileIconsInteractorImpl(
- connectionsRepository,
- carrierConfigTracker,
- tableLogger = mock(),
- connectivityRepository,
- userSetupRepository,
- testScope.backgroundScope,
- context,
- flags,
- )
}
@Test
fun filteredSubscriptions_default() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
@@ -118,7 +104,7 @@
// Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
@Test
fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
@@ -129,7 +115,7 @@
@Test
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
val latest by collectLastValue(underTest.filteredSubscriptions)
@@ -139,7 +125,7 @@
@Test
fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
@@ -150,7 +136,7 @@
@Test
fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub2) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
@@ -167,7 +153,7 @@
@Test
fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
- testScope.runTest {
+ kosmos.runTest {
val (sub3, sub4) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
@@ -187,7 +173,7 @@
@Test
fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
- testScope.runTest {
+ kosmos.runTest {
val (sub3, sub4) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
@@ -207,7 +193,7 @@
@Test
fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -228,7 +214,7 @@
@Test
fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -249,7 +235,7 @@
@Test
fun filteredSubscriptions_vcnSubId_agreesWithActiveSubId_usesActiveAkaVcnSub() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -258,7 +244,7 @@
)
connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
- connectivityRepository.vcnSubId.value = SUB_3_ID
+ kosmos.connectivityRepository.fake.vcnSubId.value = SUB_3_ID
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -269,7 +255,7 @@
@Test
fun filteredSubscriptions_vcnSubId_disagreesWithActiveSubId_usesVcnSub() =
- testScope.runTest {
+ kosmos.runTest {
val (sub1, sub3) =
createSubscriptionPair(
subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
@@ -278,7 +264,7 @@
)
connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
- connectivityRepository.vcnSubId.value = SUB_1_ID
+ kosmos.connectivityRepository.fake.vcnSubId.value = SUB_1_ID
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -289,9 +275,9 @@
@Test
fun filteredSubscriptions_doesNotFilterProvisioningWhenFlagIsFalse() =
- testScope.runTest {
+ kosmos.runTest {
// GIVEN the flag is false
- flags.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
+ featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
// GIVEN 1 sub that is in PROFILE_CLASS_PROVISIONING
val sub1 =
@@ -313,7 +299,7 @@
@Test
fun filteredSubscriptions_filtersOutProvisioningSubs() =
- testScope.runTest {
+ kosmos.runTest {
val sub1 =
SubscriptionModel(
subscriptionId = SUB_1_ID,
@@ -326,7 +312,7 @@
subscriptionId = SUB_2_ID,
isOpportunistic = false,
carrierName = "Carrier 2",
- profileClass = SubscriptionManager.PROFILE_CLASS_PROVISIONING,
+ profileClass = PROFILE_CLASS_PROVISIONING,
)
connectionsRepository.setSubscriptions(listOf(sub1, sub2))
@@ -339,7 +325,7 @@
/** Note: I'm not sure if this will ever be the case, but we can test it at least */
@Test
fun filteredSubscriptions_filtersOutProvisioningSubsBeforeOpportunistic() =
- testScope.runTest {
+ kosmos.runTest {
// This is a contrived test case, where the active subId is the one that would
// also be filtered by opportunistic filtering.
@@ -376,7 +362,7 @@
@Test
fun filteredSubscriptions_groupedPairAndNonProvisioned_groupedFilteringStillHappens() =
- testScope.runTest {
+ kosmos.runTest {
// Grouped filtering only happens when the list of subs is length 2. In this case
// we'll show that filtering of provisioning subs happens before, and thus grouped
// filtering happens even though the unfiltered list is length 3
@@ -406,7 +392,7 @@
@Test
fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() =
- testScope.runTest {
+ kosmos.runTest {
val notExclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = false,
@@ -424,7 +410,7 @@
@Test
fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() =
- testScope.runTest {
+ kosmos.runTest {
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = true,
@@ -442,7 +428,7 @@
@Test
fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() =
- testScope.runTest {
+ kosmos.runTest {
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
isExclusivelyNonTerrestrial = true,
@@ -476,7 +462,7 @@
@Test
fun filteredSubscriptions_exclusivelyNonTerrestrialSub_andOpportunistic_bothFiltersHappen() =
- testScope.runTest {
+ kosmos.runTest {
// Exclusively non-terrestrial sub
val exclusivelyNonTerrestrialSub =
SubscriptionModel(
@@ -507,7 +493,7 @@
@Test
fun activeDataConnection_turnedOn() =
- testScope.runTest {
+ kosmos.runTest {
CONNECTION_1.setDataEnabled(true)
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
@@ -517,7 +503,7 @@
@Test
fun activeDataConnection_turnedOff() =
- testScope.runTest {
+ kosmos.runTest {
CONNECTION_1.setDataEnabled(true)
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
@@ -528,7 +514,7 @@
@Test
fun activeDataConnection_invalidSubId() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
connectionsRepository.setActiveMobileDataSubscriptionId(INVALID_SUBSCRIPTION_ID)
@@ -539,7 +525,7 @@
@Test
fun failedConnection_default_validated_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -550,7 +536,7 @@
@Test
fun failedConnection_notDefault_notValidated_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = false
@@ -561,7 +547,7 @@
@Test
fun failedConnection_default_notValidated_failed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -572,7 +558,7 @@
@Test
fun failedConnection_carrierMergedDefault_notValidated_failed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.hasCarrierMergedConnection.value = true
@@ -584,7 +570,7 @@
/** Regression test for b/275076959. */
@Test
fun failedConnection_dataSwitchInSameGroup_notFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -602,7 +588,7 @@
@Test
fun failedConnection_dataSwitchNotInSameGroup_isFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -618,7 +604,7 @@
@Test
fun alwaysShowDataRatIcon_configHasTrue() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
@@ -630,7 +616,7 @@
@Test
fun alwaysShowDataRatIcon_configHasFalse() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
@@ -642,7 +628,7 @@
@Test
fun alwaysUseCdmaLevel_configHasTrue() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
@@ -654,7 +640,7 @@
@Test
fun alwaysUseCdmaLevel_configHasFalse() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
@@ -666,7 +652,7 @@
@Test
fun isSingleCarrier_zeroSubscriptions_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(emptyList())
@@ -676,7 +662,7 @@
@Test
fun isSingleCarrier_oneSubscription_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
@@ -686,7 +672,7 @@
@Test
fun isSingleCarrier_twoSubscriptions_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
@@ -696,7 +682,7 @@
@Test
fun isSingleCarrier_updates() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
@@ -708,7 +694,7 @@
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
@@ -719,7 +705,7 @@
@Test
fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
@@ -731,7 +717,7 @@
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
@@ -742,7 +728,7 @@
@Test
fun mobileIsDefault_updatesWhenRepoUpdates() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
@@ -760,7 +746,7 @@
@Test
fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -774,17 +760,17 @@
// After 1s, the force validation bit is still present, so the connection is not marked
// as failed
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
assertThat(latest).isFalse()
// After 2s, the force validation expires so the connection updates to failed
- advanceTimeBy(1001)
+ testScope.advanceTimeBy(1001)
assertThat(latest).isTrue()
}
@Test
fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
@@ -798,7 +784,7 @@
@Test
fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
// GIVEN the network starts validated
@@ -819,17 +805,17 @@
// THEN the forced validation bit is still used...
assertThat(latest).isFalse()
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
assertThat(latest).isFalse()
// ... but expires after 2s
- advanceTimeBy(1001)
+ testScope.advanceTimeBy(1001)
assertThat(latest).isTrue()
}
@Test
fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
@@ -837,7 +823,7 @@
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
- advanceTimeBy(1000)
+ testScope.advanceTimeBy(1000)
// WHEN another change in same group event happens
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
@@ -847,37 +833,37 @@
// THEN the forced validation remains for exactly 2 more seconds from now
// 1.500s from second event
- advanceTimeBy(1500)
+ testScope.advanceTimeBy(1500)
assertThat(latest).isFalse()
// 2.001s from the second event
- advanceTimeBy(501)
+ testScope.advanceTimeBy(501)
assertThat(latest).isTrue()
}
@Test
fun isForceHidden_repoHasMobileHidden_true() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isForceHidden)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+ kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isTrue()
}
@Test
fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isForceHidden)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+ kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
assertThat(latest).isFalse()
}
@Test
fun iconInteractor_cachedPerSubId() =
- testScope.runTest {
+ kosmos.runTest {
val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
@@ -887,7 +873,7 @@
@Test
fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
connectionsRepository.isDeviceEmergencyCallCapable.value = true
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
index 7c59aad..71c77a5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
@@ -44,13 +44,13 @@
android:layout_height="wrap_content">
<com.android.systemui.statusbar.notification.row.FooterViewButton
- android:id="@+id/settings_button"
+ android:id="@+id/history_button"
style="@style/TextAppearance.NotificationFooterButtonRedesign"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/notif_footer_btn_background"
- android:contentDescription="@string/notification_settings_button_description"
- android:drawableStart="@drawable/notif_footer_btn_settings"
+ android:contentDescription="@string/notification_history_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_history"
android:focusable="true"
app:layout_constraintStart_toStartOf="parent" />
@@ -64,17 +64,17 @@
android:contentDescription="@string/accessibility_clear_all"
android:focusable="true"
android:text="@string/clear_all_notifications_text"
- app:layout_constraintEnd_toStartOf="@id/history_button"
- app:layout_constraintStart_toEndOf="@id/settings_button" />
+ app:layout_constraintEnd_toStartOf="@id/settings_button"
+ app:layout_constraintStart_toEndOf="@id/history_button" />
<com.android.systemui.statusbar.notification.row.FooterViewButton
- android:id="@+id/history_button"
+ android:id="@+id/settings_button"
style="@style/TextAppearance.NotificationFooterButtonRedesign"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/notif_footer_btn_background"
- android:contentDescription="@string/notification_history_button_description"
- android:drawableStart="@drawable/notif_footer_btn_history"
+ android:contentDescription="@string/notification_settings_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_settings"
android:focusable="true"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index b74ca03..35eed5e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -35,6 +35,7 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
@@ -363,6 +364,9 @@
private val interactor: DeviceUnlockedInteractor,
) : CoreStartable {
override fun start() {
+ if (!SceneContainerFlag.isEnabled)
+ return
+
applicationScope.launch { interactor.activate() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index ed7d182..316964a 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -83,9 +83,6 @@
interactionState == TileInteractionState.LONG_CLICKED &&
animationState == TileAnimationState.ACTIVITY_LAUNCH ->
TileHapticsState.LONG_PRESS
- interactionState == TileInteractionState.LONG_CLICKED &&
- !tileViewModel.currentState.handlesLongClick ->
- TileHapticsState.FAILED_LONGPRESS
else -> TileHapticsState.NO_HAPTICS
}
}
@@ -102,7 +99,6 @@
TileHapticsState.TOGGLE_ON -> MSDLToken.SWITCH_ON
TileHapticsState.TOGGLE_OFF -> MSDLToken.SWITCH_OFF
TileHapticsState.LONG_PRESS -> MSDLToken.LONG_PRESS
- TileHapticsState.FAILED_LONGPRESS -> MSDLToken.FAILURE
TileHapticsState.NO_HAPTICS -> null
}
tokenToPlay?.let {
@@ -154,7 +150,6 @@
TOGGLE_ON,
TOGGLE_OFF,
LONG_PRESS,
- FAILED_LONGPRESS,
NO_HAPTICS,
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 301ab2b..8f6c4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -1429,7 +1429,7 @@
void makeOverlayToast(int stringId) {
final Resources res = mContext.getResources();
- final SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
+ final SystemUIToast systemUIToast = mToastFactory.createToast(mContext, mContext,
res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
res.getConfiguration().orientation);
if (systemUIToast == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
index dfbdaa6..9dc2cba 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
@@ -41,7 +41,7 @@
// Show the brightness warning toast with passing the toast inflation required context,
// userId and resId from SystemUI package.
val systemUIToast = toastFactory.createToast(
- viewContext,
+ viewContext, viewContext,
res.getString(resId), viewContext.packageName, viewContext.getUserId(),
res.configuration.orientation
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index c7b6be3..a1b56d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -112,8 +112,7 @@
selectedUserContext
.flatMapLatest { currentContext
-> // flatMapLatest because when selectedUserContext emits a new value, we want
- // to
- // re-create a whole flow
+ // to re-create a whole flow
callbackFlow {
val callback =
object : WifiPickerTracker.WifiPickerTrackerCallback {
@@ -132,20 +131,16 @@
.map { it.toWifiNetworkModel() }
// [WifiPickerTracker.connectedWifiEntry] will return the
- // same
- // instance but with updated internals. For example, when
- // its
- // validation status changes from false to true, the same
- // instance is re-used but with the validated field updated.
+ // same instance but with updated internals. For example,
+ // when its validation status changes from false to true,
+ // the same instance is re-used but with the validated
+ // field updated.
//
// Because it's the same instance, the flow won't re-emit
- // the
- // value (even though the internals have changed). So, we
- // need
- // to transform it into our internal model immediately.
- // [toWifiNetworkModel] always returns a new instance, so
- // the
- // flow is guaranteed to emit.
+ // the value (even though the internals have changed). So,
+ // we need to transform it into our internal model
+ // immediately. [toWifiNetworkModel] always returns a new
+ // instance, so the flow is guaranteed to emit.
send(
newPrimaryNetwork =
connectedEntry?.toPrimaryWifiNetworkModel()
@@ -235,20 +230,15 @@
.map { it.toWifiNetworkModel() }
// [WifiPickerTracker.connectedWifiEntry] will return the same
- // instance
- // but with updated internals. For example, when its validation
- // status
- // changes from false to true, the same instance is re-used but
- // with the
- // validated field updated.
+ // instance but with updated internals. For example, when its
+ // validation status changes from false to true, the same
+ // instance is re-used but with the validated field updated.
//
// Because it's the same instance, the flow won't re-emit the
- // value
- // (even though the internals have changed). So, we need to
- // transform it
- // into our internal model immediately. [toWifiNetworkModel]
- // always
- // returns a new instance, so the flow is guaranteed to emit.
+ // value (even though the internals have changed). So, we need
+ // to transform it into our internal model immediately.
+ // [toWifiNetworkModel] always returns a new instance, so the
+ // flow is guaranteed to emit.
send(
newPrimaryNetwork =
connectedEntry?.toPrimaryWifiNetworkModel()
@@ -292,12 +282,10 @@
.create(applicationContext, lifecycle, callback, "WifiRepository")
.apply {
// By default, [WifiPickerTracker] will scan to see all
- // available wifi
- // networks in the area. Because SysUI only needs to display the
- // **connected** network, we don't need scans to be running (and
- // in fact,
- // running scans is costly and should be avoided whenever
- // possible).
+ // available wifi networks in the area. Because SysUI only
+ // needs to display the **connected** network, we don't
+ // need scans to be running (and in fact, running scans is
+ // costly and should be avoided whenever possible).
this?.disableScanning()
}
// The lifecycle must be STARTED in order for the callback to receive
@@ -382,16 +370,11 @@
private fun MergedCarrierEntry.convertCarrierMergedToModel(): WifiNetworkModel {
// WifiEntry instance values aren't guaranteed to be stable between method calls
- // because
- // WifiPickerTracker is continuously updating the same object. Save the level in a
- // local
- // variable so that checking the level validity here guarantees that the level will
- // still be
- // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the
- // level
- // could be valid here but become invalid later, and `WifiNetworkModel.Active` will
- // throw
- // an exception. See b/362384551.
+ // because WifiPickerTracker is continuously updating the same object. Save the
+ // level in a local variable so that checking the level validity here guarantees
+ // that the level will still be valid when we create the `WifiNetworkModel.Active`
+ // instance later. Otherwise, the level could be valid here but become invalid
+ // later, and `WifiNetworkModel.Active` will throw an exception. See b/362384551.
return WifiNetworkModel.CarrierMerged.of(
subscriptionId = this.subscriptionId,
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index d97cae2..d367455 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -50,6 +50,7 @@
public class SystemUIToast implements ToastPlugin.Toast {
static final String TAG = "SystemUIToast";
final Context mContext;
+ final Context mDisplayContext;
final CharSequence mText;
final ToastPlugin.Toast mPluginToast;
@@ -68,17 +69,18 @@
@Nullable private final Animator mInAnimator;
@Nullable private final Animator mOutAnimator;
- SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- String packageName, int userId, int orientation) {
- this(layoutInflater, context, text, null, packageName, userId,
+ SystemUIToast(LayoutInflater layoutInflater, Context applicationContext, Context displayContext,
+ CharSequence text, String packageName, int userId, int orientation) {
+ this(layoutInflater, applicationContext, displayContext, text, null, packageName, userId,
orientation);
}
- SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
- int orientation) {
+ SystemUIToast(LayoutInflater layoutInflater, Context applicationContext, Context displayContext,
+ CharSequence text, ToastPlugin.Toast pluginToast, String packageName,
+ @UserIdInt int userId, int orientation) {
mLayoutInflater = layoutInflater;
- mContext = context;
+ mContext = applicationContext;
+ mDisplayContext = displayContext;
mText = text;
mPluginToast = pluginToast;
mPackageName = packageName;
@@ -221,9 +223,9 @@
mPluginToast.onOrientationChange(orientation);
}
- mDefaultY = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ mDefaultY = mDisplayContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
mDefaultGravity =
- mContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
+ mDisplayContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
}
private Animator createInAnimator() {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index 9ae6674..388d4bd 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -65,15 +65,16 @@
/**
* Create a toast to be shown by ToastUI.
*/
- public SystemUIToast createToast(Context context, CharSequence text, String packageName,
- int userId, int orientation) {
- LayoutInflater layoutInflater = LayoutInflater.from(context);
+ public SystemUIToast createToast(Context applicationContext, Context displayContext,
+ CharSequence text, String packageName, int userId, int orientation) {
+ LayoutInflater layoutInflater = LayoutInflater.from(displayContext);
if (isPluginAvailable()) {
- return new SystemUIToast(layoutInflater, context, text, mPlugin.createToast(text,
- packageName, userId), packageName, userId, orientation);
+ return new SystemUIToast(layoutInflater, applicationContext, displayContext, text,
+ mPlugin.createToast(text, packageName, userId), packageName, userId,
+ orientation);
}
- return new SystemUIToast(layoutInflater, context, text, packageName, userId,
- orientation);
+ return new SystemUIToast(layoutInflater, applicationContext, displayContext, text,
+ packageName, userId, orientation);
}
private boolean isPluginAvailable() {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 32a4f12..12f73b8 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -135,8 +135,8 @@
return;
}
Context displayContext = context.createDisplayContext(display);
- mToast = mToastFactory.createToast(displayContext /* sysuiContext */, text, packageName,
- userHandle.getIdentifier(), mOrientation);
+ mToast = mToastFactory.createToast(mContext, displayContext /* sysuiContext */, text,
+ packageName, userHandle.getIdentifier(), mOrientation);
if (mToast.getInAnimation() != null) {
mToast.getInAnimation().start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 0b9c06f..5ada2f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -222,7 +222,7 @@
when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(SUB_ID);
SubscriptionInfo info = mock(SubscriptionInfo.class);
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
- when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
+ when(mToastFactory.createToast(any(), any(), anyString(), anyString(), anyInt(), anyInt()))
.thenReturn(mSystemUIToast);
when(mSystemUIToast.getView()).thenReturn(mToastView);
when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
@@ -275,8 +275,8 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory).createToast(any(), eq(TOAST_MESSAGE_STRING), anyString(), anyInt(),
- anyInt());
+ verify(mToastFactory).createToast(any(), any(), eq(TOAST_MESSAGE_STRING), anyString(),
+ anyInt(), anyInt());
}
@Test
@@ -288,7 +288,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
@@ -302,7 +302,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
@@ -321,7 +321,7 @@
mInternetDialogController.connectCarrierNetwork();
verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
- verify(mToastFactory, never()).createToast(any(), anyString(), anyString(), anyInt(),
+ verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
anyInt());
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index 32469b6..ad38bbe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -23,13 +23,13 @@
class FakeFeatureFlagsClassic : FakeFeatureFlags()
+val FeatureFlagsClassic.fake
+ get() = this as FakeFeatureFlagsClassic
+
@Deprecated(
message = "Use FakeFeatureFlagsClassic instead.",
replaceWith =
- ReplaceWith(
- "FakeFeatureFlagsClassic",
- "com.android.systemui.flags.FakeFeatureFlagsClassic",
- ),
+ ReplaceWith("FakeFeatureFlagsClassic", "com.android.systemui.flags.FakeFeatureFlagsClassic"),
)
open class FakeFeatureFlags : FeatureFlagsClassic {
private val booleanFlags = mutableMapOf<String, Boolean>()
@@ -105,6 +105,7 @@
listener.onFlagChanged(
object : FlagListenable.FlagEvent {
override val flagName = flag.name
+
override fun requestNoRestart() {}
}
)
@@ -165,7 +166,7 @@
@Module(includes = [FakeFeatureFlagsClassicModule.Bindings::class])
class FakeFeatureFlagsClassicModule(
- @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
+ @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
) {
constructor(
@@ -175,7 +176,9 @@
@Module
interface Bindings {
@Binds fun bindFake(fake: FakeFeatureFlagsClassic): FeatureFlagsClassic
+
@Binds fun bindClassic(classic: FeatureFlagsClassic): FeatureFlags
+
@Binds fun bindFakeClassic(fake: FakeFeatureFlagsClassic): FakeFeatureFlags
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 3cd613b..d1303d2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -78,7 +78,7 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.phone.scrimController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor
import com.android.systemui.statusbar.policy.configurationController
@@ -126,7 +126,7 @@
val keyguardStatusBarViewModel by lazy { kosmos.keyguardStatusBarViewModel }
val powerRepository by lazy { kosmos.fakePowerRepository }
val clock by lazy { kosmos.systemClock }
- val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
+ val mobileConnectionsRepository by lazy { kosmos.mobileConnectionsRepository }
val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
val statusBarStateController by lazy { kosmos.statusBarStateController }
val statusBarModePerDisplayRepository by lazy { kosmos.fakeStatusBarModePerDisplayRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
index d76defe..99ed4f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
@@ -18,7 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by
@@ -26,6 +26,6 @@
AirplaneModeInteractor(
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
- fakeMobileConnectionsRepository,
+ mobileConnectionsRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index de73d33..bfd46b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -77,11 +77,7 @@
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId]
- ?: FakeMobileConnectionRepository(
- subId,
- tableLogBuffer,
- )
- .also { subIdRepos[subId] = it }
+ ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
}
override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
@@ -135,3 +131,6 @@
const val LTE_ADVANCED_PRO = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO
}
}
+
+val MobileConnectionsRepository.fake
+ get() = this as FakeMobileConnectionsRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
index cd22f1d..b952d71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
@@ -19,12 +19,20 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+
+val Kosmos.mobileMappingsProxy: MobileMappingsProxy by Fixture { FakeMobileMappingsProxy() }
+
+var Kosmos.mobileConnectionsRepositoryLogbufferName by Fixture { "FakeMobileConnectionsRepository" }
val Kosmos.fakeMobileConnectionsRepository by Fixture {
FakeMobileConnectionsRepository(
- tableLogBuffer = logcatTableLogBuffer(this, "FakeMobileConnectionsRepository"),
+ mobileMappings = mobileMappingsProxy,
+ tableLogBuffer = logcatTableLogBuffer(this, mobileConnectionsRepositoryLogbufferName),
)
}
-val Kosmos.mobileConnectionsRepository by
- Fixture<MobileConnectionsRepository> { fakeMobileConnectionsRepository }
+val Kosmos.mobileConnectionsRepository: MobileConnectionsRepository by Fixture {
+ fakeMobileConnectionsRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
index 8e656cf..00bfa99 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
@@ -18,7 +18,5 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by
- Kosmos.Fixture { FakeConnectivityRepository() }
val Kosmos.connectivityRepository: ConnectivityRepository by
- Kosmos.Fixture { fakeConnectivityRepository }
+ Kosmos.Fixture { FakeConnectivityRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 331e2fa..c69d9a2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -42,10 +42,7 @@
* validated
*/
@JvmOverloads
- fun setMobileConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setMobileConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
mobile = DefaultConnectionModel.Mobile(default),
@@ -55,10 +52,7 @@
/** Similar convenience method for ethernet */
@JvmOverloads
- fun setEthernetConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setEthernetConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
ethernet = DefaultConnectionModel.Ethernet(default),
@@ -67,10 +61,7 @@
}
@JvmOverloads
- fun setWifiConnected(
- default: Boolean = true,
- validated: Boolean = true,
- ) {
+ fun setWifiConnected(default: Boolean = true, validated: Boolean = true) {
defaultConnections.value =
DefaultConnectionModel(
wifi = DefaultConnectionModel.Wifi(default),
@@ -78,3 +69,6 @@
)
}
}
+
+val ConnectivityRepository.fake
+ get() = this as FakeConnectivityRepository
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index fe2269a..27c5ea1 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -33,7 +33,7 @@
exclude_re=""
smoke_exclude_re=""
dry_run=""
-while getopts "sx:f:dt" opt; do
+while getopts "sx:f:dtb" opt; do
case "$opt" in
s)
# Remove slow tests.
@@ -52,8 +52,13 @@
dry_run="echo"
;;
t)
+ # Redirect log to terminal
export RAVENWOOD_LOG_OUT=$(tty)
;;
+ b)
+ # Build only
+ ATEST=m
+ ;;
'?')
exit 1
;;
@@ -99,11 +104,16 @@
# Calculate the removed tests.
-diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') )"
+diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') | grep -v [0-9] )"
if [[ "$diff" != "" ]]; then
echo "Excluded tests:"
echo "$diff"
fi
-$dry_run ${ATEST:-atest} "${targets[@]}"
+run() {
+ echo "Running: ${@}"
+ "${@}"
+}
+
+run $dry_run ${ATEST:-atest} "${targets[@]}"
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index cd2a535..e59bb42 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,8 +28,11 @@
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
+import android.app.slice.Slice;
+import android.app.slice.SliceItem;
import android.content.ComponentName;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.metrics.LogMaker;
import android.os.UserHandle;
@@ -97,11 +100,12 @@
@UserIdInt int userId, @NonNull RemoteViews rView) {
final AtomicBoolean permissionsOk = new AtomicBoolean(true);
- rView.visitUris(uri -> {
- int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri);
- boolean allowed = uriOwnerId == userId;
- permissionsOk.set(allowed & permissionsOk.get());
- });
+ rView.visitUris(
+ uri -> {
+ int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri, userId);
+ boolean allowed = uriOwnerId == userId;
+ permissionsOk.set(allowed & permissionsOk.get());
+ });
return permissionsOk.get();
}
@@ -150,6 +154,47 @@
return (ok ? rView : null);
}
+ /**
+ * Checks the URI permissions of the icon in the slice, to see if the current userId is able to
+ * access it.
+ *
+ * <p>Returns null if slice contains user inaccessible icons
+ *
+ * <p>TODO: instead of returning a null Slice when the current userId cannot access an icon,
+ * return a reconstructed Slice without the icons. This is currently non-trivial since there are
+ * no public methods to generically add SliceItems to Slices
+ */
+ public static @Nullable Slice sanitizeSlice(Slice slice) {
+ if (slice == null) {
+ return null;
+ }
+
+ int userId = ActivityManager.getCurrentUser();
+
+ // Recontruct the Slice, filtering out bad icons
+ for (SliceItem sliceItem : slice.getItems()) {
+ if (!sliceItem.getFormat().equals(SliceItem.FORMAT_IMAGE)) {
+ // Not an image slice
+ continue;
+ }
+
+ Icon icon = sliceItem.getIcon();
+ if (icon.getType() != Icon.TYPE_URI
+ && icon.getType() != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ // No URIs to sanitize
+ continue;
+ }
+
+ int iconUriId = android.content.ContentProvider.getUserIdFromUri(icon.getUri(), userId);
+
+ if (iconUriId != userId) {
+ Slog.w(TAG, "sanitizeSlice() user: " + userId + " cannot access icons in Slice");
+ return null;
+ }
+ }
+
+ return slice;
+ }
@Nullable
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
index 38a412f..50a26b3 100644
--- a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -27,6 +27,7 @@
import android.util.Slog;
import com.android.server.LocalServices;
+import com.android.server.autofill.Helper;
import com.android.server.autofill.RemoteInlineSuggestionRenderService;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -83,6 +84,10 @@
*/
public boolean renderSuggestion(int width, int height,
@NonNull IInlineSuggestionUiCallback callback) {
+ if (Helper.sanitizeSlice(mInlinePresentation.getSlice()) == null) {
+ if (sDebug) Slog.d(TAG, "Skipped rendering inline suggestion.");
+ return false;
+ }
if (mRemoteRenderService != null) {
if (sDebug) Slog.d(TAG, "Request to recreate the UI");
mRemoteRenderService.renderSuggestion(callback, mInlinePresentation, width, height,
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 446b367..c6cb67f 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -553,7 +553,7 @@
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
- exported, mService.mPlatformCompat);
+ exported, callerApp.info, mService.mPlatformCompat);
if (rl.containsFilter(filter)) {
Slog.w(TAG, "Receiver with filter " + filter
+ " already registered for pid " + rl.pid
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 3c7fb52..e20c46c 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -20,6 +20,8 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.os.Binder;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -40,7 +42,7 @@
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
- static final long CHANGE_RESTRICT_PRIORITY_VALUES = 371309185L;
+ static final long RESTRICT_PRIORITY_VALUES = 371309185L;
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
@@ -58,7 +60,7 @@
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
String _packageName, String _featureId, String _receiverId, String _requiredPermission,
int _owningUid, int _userId, boolean _instantApp, boolean _visibleToInstantApp,
- boolean _exported, PlatformCompat platformCompat) {
+ boolean _exported, ApplicationInfo applicationInfo, PlatformCompat platformCompat) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
@@ -71,7 +73,8 @@
visibleToInstantApp = _visibleToInstantApp;
exported = _exported;
initialPriority = getPriority();
- setPriority(calculateAdjustedPriority(owningUid, initialPriority, platformCompat));
+ setPriority(calculateAdjustedPriority(owningUid, initialPriority,
+ applicationInfo, platformCompat));
}
public @Nullable String getReceiverClassName() {
@@ -125,13 +128,18 @@
@VisibleForTesting
static int calculateAdjustedPriority(int owningUid, int priority,
- PlatformCompat platformCompat) {
+ ApplicationInfo applicationInfo, PlatformCompat platformCompat) {
if (!Flags.restrictPriorityValues()) {
return priority;
}
- if (!platformCompat.isChangeEnabledByUidInternalNoLogging(
- CHANGE_RESTRICT_PRIORITY_VALUES, owningUid)) {
- return priority;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!platformCompat.isChangeEnabledInternalNoLogging(
+ RESTRICT_PRIORITY_VALUES, applicationInfo)) {
+ return priority;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
if (!UserHandle.isCore(owningUid)) {
if (priority >= SYSTEM_HIGH_PRIORITY) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 38df10a..e8ce173 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -88,7 +88,7 @@
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
@VisibleForTesting
- static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L;
+ static final long LIMIT_PRIORITY_SCOPE = 371307720L;
final @NonNull Intent intent; // the original intent that generated us
final @Nullable ComponentName targetComp; // original component name set on the intent
@@ -781,7 +781,7 @@
} else {
if (Flags.limitPriorityScope()) {
final boolean[] changeEnabled = calculateChangeStateForReceivers(
- receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat);
+ receivers, LIMIT_PRIORITY_SCOPE, platformCompat);
// Priority of the previous tranche
int lastTranchePriority = 0;
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 5914dbe..7fd9620 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -92,10 +92,16 @@
static final int REGROUP_REASON_CHANNEL_UPDATE = 0;
// Regrouping needed because of notification bundling
static final int REGROUP_REASON_BUNDLE = 1;
+ // Regrouping needed because of notification unbundling
+ static final int REGROUP_REASON_UNBUNDLE = 2;
+ // Regrouping needed because of notification unbundling + the original group summary exists
+ static final int REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP = 3;
@IntDef(prefix = { "REGROUP_REASON_" }, value = {
REGROUP_REASON_CHANNEL_UPDATE,
REGROUP_REASON_BUNDLE,
+ REGROUP_REASON_UNBUNDLE,
+ REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP,
})
@Retention(RetentionPolicy.SOURCE)
@interface RegroupingReason {}
@@ -103,7 +109,6 @@
private final Callback mCallback;
private final int mAutoGroupAtCount;
private final int mAutogroupSparseGroupsAtCount;
- private final int mAutoGroupRegroupingAtCount;
private final Context mContext;
private final PackageManager mPackageManager;
private boolean mIsTestHarnessExempted;
@@ -190,11 +195,6 @@
mContext = context;
mPackageManager = packageManager;
mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
- if (notificationRegroupOnClassification()) {
- mAutoGroupRegroupingAtCount = 1;
- } else {
- mAutoGroupRegroupingAtCount = mAutoGroupAtCount;
- }
NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
}
@@ -797,7 +797,8 @@
Slog.v(TAG, "isGroupChildInDifferentBundleThanSummary: " + record);
}
moveNotificationsToNewSection(record.getUserId(), pkgName,
- List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)));
+ List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)),
+ REGROUP_REASON_BUNDLE);
return;
}
}
@@ -945,6 +946,27 @@
}
}
+ /**
+ * Called when a notification that was classified (bundled) is restored to its original channel.
+ * The notification will be restored to its original group, if any/if summary still exists.
+ * Otherwise it will be moved to the appropriate section as an ungrouped notification.
+ *
+ * @param record the notification which had its channel updated
+ * @param originalSummaryExists the original group summary exists
+ */
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING)
+ public void onNotificationUnbundled(final NotificationRecord record,
+ final boolean originalSummaryExists) {
+ synchronized (mAggregatedNotifications) {
+ ArrayMap<String, NotificationRecord> notificationsToCheck = new ArrayMap<>();
+ notificationsToCheck.put(record.getKey(), record);
+ regroupNotifications(record.getUserId(), record.getSbn().getPackageName(),
+ notificationsToCheck,
+ originalSummaryExists ? REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP
+ : REGROUP_REASON_UNBUNDLE);
+ }
+ }
+
@GuardedBy("mAggregatedNotifications")
private void regroupNotifications(int userId, String pkgName,
ArrayMap<String, NotificationRecord> notificationsToCheck,
@@ -973,7 +995,7 @@
// Batch move to new section
if (!notificationsToMove.isEmpty()) {
- moveNotificationsToNewSection(userId, pkgName, notificationsToMove);
+ moveNotificationsToNewSection(userId, pkgName, notificationsToMove, regroupingReason);
}
}
@@ -1093,7 +1115,7 @@
@GuardedBy("mAggregatedNotifications")
private void moveNotificationsToNewSection(final int userId, final String pkgName,
- final List<NotificationMoveOp> notificationsToMove) {
+ final List<NotificationMoveOp> notificationsToMove, int regroupingReason) {
record GroupUpdateOp(FullyQualifiedGroupKey groupKey, NotificationRecord record,
boolean hasSummary) { }
// Bundled operations to apply to groups affected by the channel update
@@ -1111,7 +1133,8 @@
if (DEBUG) {
Log.i(TAG,
"moveNotificationToNewSection: " + record + " " + newFullAggregateGroupKey
- + " from: " + oldFullAggregateGroupKey);
+ + " from: " + oldFullAggregateGroupKey + " regroupingReason: "
+ + regroupingReason);
}
// Update/remove aggregate summary for old group
@@ -1140,28 +1163,35 @@
// Add moved notifications to the ungrouped list for new group and do grouping
// after all notifications have been handled
if (newFullAggregateGroupKey != null) {
- final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
+ if (notificationRegroupOnClassification()
+ && regroupingReason == REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP) {
+ // Just reset override group key, original summary exists
+ // => will be grouped back to its original group
+ record.setOverrideGroupKey(null);
+ } else {
+ final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
mAggregatedNotifications.getOrDefault(newFullAggregateGroupKey,
new ArrayMap<>());
- boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
- ArrayMap<String, NotificationAttributes> ungrouped =
+ boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
+ ArrayMap<String, NotificationAttributes> ungrouped =
mUngroupedAbuseNotifications.getOrDefault(newFullAggregateGroupKey,
new ArrayMap<>());
- ungrouped.put(record.getKey(), new NotificationAttributes(
+ ungrouped.put(record.getKey(), new NotificationAttributes(
record.getFlags(),
record.getNotification().getSmallIcon(),
record.getNotification().color,
record.getNotification().visibility,
record.getNotification().getGroupAlertBehavior(),
record.getChannel().getId()));
- mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
+ mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
- record.setOverrideGroupKey(null);
+ record.setOverrideGroupKey(null);
- // Only add once, for triggering notification
- if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
- groupsToUpdate.put(newFullAggregateGroupKey,
- new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
+ // Only add once, for triggering notification
+ if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
+ groupsToUpdate.put(newFullAggregateGroupKey,
+ new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
+ }
}
}
}
@@ -1176,7 +1206,7 @@
NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
//Group needs to be created/updated
- if (ungrouped.size() >= mAutoGroupRegroupingAtCount
+ if (ungrouped.size() >= mAutoGroupAtCount
|| (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
NotificationSectioner sectioner = getSection(triggeringNotification);
if (sectioner == null) {
@@ -1436,7 +1466,8 @@
}
}
- private ArrayMap<String, NotificationRecord> getSparseGroups(
+ @VisibleForTesting
+ protected ArrayMap<String, NotificationRecord> getSparseGroups(
final FullyQualifiedGroupKey fullAggregateGroupKey,
final List<NotificationRecord> notificationList,
final Map<String, NotificationRecord> summaryByGroupKey,
@@ -1448,8 +1479,8 @@
&& summary.getUserId() == fullAggregateGroupKey.userId
&& summary.getSbn().isAppGroup()
&& !summary.getGroupKey().equals(fullAggregateGroupKey.toString())) {
- int numChildren = getNumChildrenForGroup(summary.getSbn().getGroup(),
- notificationList);
+ int numChildren = getNumChildrenForGroupWithSection(summary.getSbn().getGroup(),
+ notificationList, sectioner);
if (numChildren > 0 && numChildren < MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING) {
sparseGroups.put(summary.getGroupKey(), summary);
}
@@ -1459,6 +1490,43 @@
return sparseGroups;
}
+ /**
+ * Get the number of children of a group if all match a certain section.
+ * Used for force grouping sparse groups, where the summary may match a section but the
+ * child notifications do not: ie. conversations
+ *
+ * @param groupKey the group key (name)
+ * @param notificationList all notifications list
+ * @param sectioner the section to match
+ * @return number of children in that group or -1 if section does not match
+ */
+ private int getNumChildrenForGroupWithSection(final String groupKey,
+ final List<NotificationRecord> notificationList,
+ final NotificationSectioner sectioner) {
+ int numChildren = 0;
+ for (NotificationRecord r : notificationList) {
+ if (!r.getNotification().isGroupSummary() && groupKey.equals(r.getSbn().getGroup())) {
+ NotificationSectioner childSection = getSection(r);
+ if (childSection == null || childSection != sectioner) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "getNumChildrenForGroupWithSection skip because invalid section: "
+ + groupKey + " r: " + r);
+ }
+ return -1;
+ } else {
+ numChildren++;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG,
+ "getNumChildrenForGroupWithSection " + groupKey + " numChild: " + numChildren);
+ }
+ return numChildren;
+ }
+
@GuardedBy("mAggregatedNotifications")
private void cacheCanceledSummary(NotificationRecord record) {
final FullyQualifiedGroupKey groupKey = new FullyQualifiedGroupKey(record.getUserId(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0d8880a..5182dfe 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7180,13 +7180,16 @@
Slog.i(TAG, "Removing app summary (all children bundled): "
+ groupSummary);
}
- canceledSummary = groupSummary;
- mSummaryByGroupKey.remove(oldGroupKey);
- cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
+ if (convertSummaryToNotificationLocked(groupSummary.getKey())) {
+ groupSummary.isCanceled = true;
+ canceledSummary = groupSummary;
+ mSummaryByGroupKey.remove(oldGroupKey);
+ cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
groupSummary.getSbn().getPackageName(),
groupSummary.getSbn().getTag(),
groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(),
NotificationListenerService.REASON_GROUP_OPTIMIZATION, null);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 06e29c2..b2b8aaf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4984,7 +4984,10 @@
res.getValue(com.android.internal.R.string.owner_name, mOwnerNameTypedValue, true);
final CharSequence ownerName = mOwnerNameTypedValue.coerceToString();
mOwnerName.set(ownerName != null ? ownerName.toString() : null);
+ // Invalidate when owners name changes due to config change.
+ UserManager.invalidateCacheOnUserDataChanged();
}
+
}
private void scheduleWriteUserList() {
@@ -4997,6 +5000,8 @@
Message msg = mHandler.obtainMessage(WRITE_USER_LIST_MSG);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
}
+ // Invalidate cache when {@link UserData} changed, but write was scheduled for later.
+ UserManager.invalidateCacheOnUserDataChanged();
}
private void scheduleWriteUser(@UserIdInt int userId) {
@@ -5009,6 +5014,8 @@
Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userId);
mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
}
+ // Invalidate cache when {@link Data} changed, but write was scheduled for later.
+ UserManager.invalidateCacheOnUserDataChanged();
}
private ResilientAtomicFile getUserFile(int userId) {
@@ -5032,6 +5039,9 @@
if (DBG) {
debug("writeUserLP " + userData);
}
+ // invalidate caches related to any {@link UserData} change.
+ UserManager.invalidateCacheOnUserDataChanged();
+
try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
FileOutputStream fos = null;
try {
@@ -5196,6 +5206,8 @@
if (DBG) {
debug("writeUserList");
}
+ // invalidate caches related to any {@link UserData} change.
+ UserManager.invalidateCacheOnUserDataChanged();
try (ResilientAtomicFile file = getUserListFile()) {
FileOutputStream fos = null;
@@ -7958,7 +7970,7 @@
Settings.Secure.getIntForUser(mContext.getContentResolver(),
HIDE_PRIVATESPACE_ENTRY_POINT, parentId) == 1);
} catch (Settings.SettingNotFoundException e) {
- throw new RuntimeException(e);
+ config.putBoolean(PRIVATE_SPACE_ENTRYPOINT_HIDDEN, false);
}
}
return new LauncherUserInfo.Builder(userDetails.getName(),
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 2c0ce25..17459df 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -33,11 +33,15 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.power.ChannelConfig;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHintManager;
@@ -90,6 +94,10 @@
private static final int EVENT_CLEAN_UP_UID = 3;
@VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000;
+ private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1;
+ @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1;
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -160,10 +168,76 @@
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
+ private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
private Boolean mFMQUsesIntegratedEventFlag = false;
- @VisibleForTesting final IHintManager.Stub mService = new BinderService();
+ private final Object mCpuHeadroomLock = new Object();
+
+ private static class CpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ CpuHeadroomParamsInternal mParams;
+ float[] mHeadroom;
+ long mPid;
+
+ CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params,
+ float[] headroom, long pid) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mPid = pid;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(CpuHeadroomParamsInternal params, long pid) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params) && pid == mPid;
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mCpuHeadroomLock")
+ private final List<CpuHeadroomCacheItem> mCpuHeadroomCache;
+ private final long mCpuHeadroomIntervalMillis;
+
+ private final Object mGpuHeadroomLock = new Object();
+
+ private static class GpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ GpuHeadroomParamsInternal mParams;
+ float mHeadroom;
+
+ GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params,
+ float headroom) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(GpuHeadroomParamsInternal params) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params);
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mGpuHeadroomLock")
+ private final List<GpuHeadroomCacheItem> mGpuHeadroomCache;
+ private final long mGpuHeadroomIntervalMillis;
+
+ @VisibleForTesting
+ final IHintManager.Stub mService = new BinderService();
public HintManagerService(Context context) {
this(context, new Injector());
@@ -197,13 +271,72 @@
mPowerHal = injector.createIPower();
mPowerHalVersion = 0;
mUsesFmq = false;
+ long cpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
+ long gpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
if (mPowerHal != null) {
try {
mPowerHalVersion = mPowerHal.getInterfaceVersion();
+ if (mPowerHal.getInterfaceVersion() >= 6) {
+ if (SystemProperties.getBoolean(PROPERTY_USE_HAL_HEADROOMS, true)) {
+ cpuHeadroomIntervalMillis = checkCpuHeadroomSupport();
+ gpuHeadroomIntervalMillis = checkGpuHeadroomSupport();
+ }
+ }
} catch (RemoteException e) {
throw new IllegalStateException("Could not contact PowerHAL!", e);
}
}
+ mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis;
+ mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis;
+ if (mCpuHeadroomIntervalMillis > 0) {
+ mCpuHeadroomCache = new ArrayList<>(4);
+ } else {
+ mCpuHeadroomCache = null;
+ }
+ if (mGpuHeadroomIntervalMillis > 0) {
+ mGpuHeadroomCache = new ArrayList<>(2);
+ } else {
+ mGpuHeadroomCache = null;
+ }
+ }
+
+ private long checkCpuHeadroomSupport() {
+ try {
+ synchronized (mCpuHeadroomLock) {
+ final CpuHeadroomParams defaultParams = new CpuHeadroomParams();
+ defaultParams.pid = Process.myPid();
+ float[] ret = mPowerHal.getCpuHeadroom(defaultParams);
+ if (ret != null && ret.length > 0) {
+ return Math.max(
+ DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getCpuHeadroomMinIntervalMillis());
+ }
+ }
+
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
+ }
+
+ private long checkGpuHeadroomSupport() {
+ try {
+ synchronized (mGpuHeadroomLock) {
+ float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams());
+ if (!Float.isNaN(ret)) {
+ return Math.max(
+ DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getGpuHeadroomMinIntervalMillis());
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
}
private ServiceThread createCleanUpThread() {
@@ -738,7 +871,7 @@
mLinked = false;
}
if (mConfig != null) {
- try {
+ try {
mPowerHal.closeSessionChannel(mTgid, mUid);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to close session channel!", e);
@@ -982,13 +1115,13 @@
}
// returns the first invalid tid or null if not found
- private Integer checkTidValid(int uid, int tgid, int [] tids, IntArray nonIsolated) {
+ private Integer checkTidValid(int uid, int tgid, int[] tids, IntArray nonIsolated) {
// Make sure all tids belongs to the same UID (including isolated UID),
// tids can belong to different application processes.
List<Integer> isolatedPids = null;
for (int i = 0; i < tids.length; i++) {
int tid = tids[i];
- final String[] procStatusKeys = new String[] {
+ final String[] procStatusKeys = new String[]{
"Uid:",
"Tgid:"
};
@@ -1058,7 +1191,7 @@
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
@@ -1214,6 +1347,124 @@
}
@Override
+ public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.pid = Binder.getCallingPid();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ halParams.selectionType = params.selectionType;
+ if (params.usesDeviceHeadroom) {
+ halParams.pid = DEFAULT_HEADROOM_PID;
+ }
+ }
+ synchronized (mCpuHeadroomLock) {
+ while (!mCpuHeadroomCache.isEmpty()) {
+ if (mCpuHeadroomCache.getFirst().isExpired()) {
+ mCpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mCpuHeadroomCache.size(); ++i) {
+ final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i);
+ if (item.match(params, halParams.pid)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis;
+ mCpuHeadroomCache.remove(i);
+ mCpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float[] headroom = mPowerHal.getCpuHeadroom(halParams);
+ if (headroom == null || headroom.length == 0) {
+ Slog.wtf(TAG,
+ "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom));
+ return new float[]{Float.NaN};
+ }
+ synchronized (mCpuHeadroomLock) {
+ mCpuHeadroomCache.add(new CpuHeadroomCacheItem(
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis,
+ params, headroom, halParams.pid
+ ));
+ }
+ return headroom;
+
+ } catch (RemoteException e) {
+ return new float[]{Float.NaN};
+ }
+ }
+
+ @Override
+ public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ GpuHeadroomParams halParams = new GpuHeadroomParams();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ }
+ synchronized (mGpuHeadroomLock) {
+ while (!mGpuHeadroomCache.isEmpty()) {
+ if (mGpuHeadroomCache.getFirst().isExpired()) {
+ mGpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mGpuHeadroomCache.size(); ++i) {
+ final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i);
+ if (item.match(params)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis;
+ mGpuHeadroomCache.remove(i);
+ mGpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float headroom = mPowerHal.getGpuHeadroom(halParams);
+ if (Float.isNaN(headroom)) {
+ Slog.wtf(TAG,
+ "GPU headroom from Power HAL is NaN");
+ return Float.NaN;
+ }
+ synchronized (mGpuHeadroomLock) {
+ mGpuHeadroomCache.add(new GpuHeadroomCacheItem(
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis,
+ params, headroom
+ ));
+ }
+ return headroom;
+ } catch (RemoteException e) {
+ return Float.NaN;
+ }
+ }
+
+ @Override
+ public long getCpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mCpuHeadroomIntervalMillis;
+ }
+
+ @Override
+ public long getGpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mGpuHeadroomIntervalMillis;
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1235,6 +1486,25 @@
}
}
}
+ pw.println("CPU Headroom Interval: " + mCpuHeadroomIntervalMillis);
+ pw.println("GPU Headroom Interval: " + mGpuHeadroomIntervalMillis);
+ try {
+ CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params)));
+ params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params)));
+ } catch (Exception e) {
+ pw.println("CPU headroom: N/A");
+ }
+ try {
+ pw.println("GPU headroom: " + getGpuHeadroom(null));
+ } catch (Exception e) {
+ pw.println("GPU headroom: N/A");
+ }
}
private void logPerformanceHintSessionAtom(int uid, long sessionId,
@@ -1467,7 +1737,7 @@
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dff3438..28eec5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19148,6 +19148,14 @@
}
}
+ private boolean isAnyResetPasswordTokenActiveForUser(int userId) {
+ return mDevicePolicyEngine
+ .getLocalPoliciesSetByAdmins(PolicyDefinition.RESET_PASSWORD_TOKEN, userId)
+ .values()
+ .stream()
+ .anyMatch((p) -> isResetPasswordTokenActiveForUserLocked(p.getValue(), userId));
+ }
+
private boolean isResetPasswordTokenActiveForUserLocked(
long passwordTokenHandle, int userHandle) {
if (passwordTokenHandle != 0) {
@@ -21003,6 +21011,9 @@
Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
String.format(NOT_SYSTEM_CALLER_MSG,
"call canProfileOwnerResetPasswordWhenLocked"));
+ if (Flags.resetPasswordWithTokenCoexistence()) {
+ return isAnyResetPasswordTokenActiveForUser(userId);
+ }
synchronized (getLockObject()) {
final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
DevicePolicyData policy = getUserData(userId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 30de0e8..8dc8c14 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -67,7 +67,6 @@
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
-import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.TEMPORARY_QUOTA_CHANGED;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
@@ -152,7 +151,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.util.FlagSetException;
@@ -436,8 +434,7 @@
*/
private void disableFlagsNotSetByAnnotation() {
try {
- mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
- Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
} catch (FlagSetException fse) {
// Expected if the test about to be run requires this enabled.
}
@@ -523,13 +520,11 @@
mService.onStart();
- if (Flags.useFrozenStateToDropListenerAlarms()) {
- final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
- ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
- verify(mActivityManager).registerUidFrozenStateChangedCallback(
- any(HandlerExecutor.class), frozenCaptor.capture());
- mUidFrozenStateCallback = frozenCaptor.getValue();
- }
+ final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
+ ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
+ verify(mActivityManager).registerUidFrozenStateChangedCallback(
+ any(HandlerExecutor.class), frozenCaptor.capture());
+ mUidFrozenStateCallback = frozenCaptor.getValue();
// Unable to mock mMockContext to return a mock stats manager.
// So just mocking the whole MetricsHelper instance.
@@ -3744,79 +3739,11 @@
testTemporaryQuota_bumpedBeforeDeferral(STANDBY_BUCKET_RARE);
}
- @Test
- public void exactListenerAlarmsRemovedOnCached() {
- mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
-
- setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT,
- TEST_CALLING_UID);
- setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
- setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID, null);
- setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
-
- setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT,
- TEST_CALLING_UID_2);
- setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
- setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID_2, null);
- setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null);
-
- assertEquals(8, mService.mAlarmStore.size());
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(7, mService.mAlarmStore.size());
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(6, mService.mAlarmStore.size());
- }
-
- @Test
- public void alarmCountOnListenerCached() {
- mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
-
- // Set some alarms for TEST_CALLING_UID.
- final int numExactListenerUid1 = 14;
- for (int i = 0; i < numExactListenerUid1; i++) {
- setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
- getNewListener(() -> {}));
- }
- setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
- setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent());
- setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
-
- // Set some alarms for TEST_CALLING_UID_2.
- final int numExactListenerUid2 = 9;
- for (int i = 0; i < numExactListenerUid2; i++) {
- setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
- getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2);
- }
- setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
- setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
- TEST_CALLING_UID_2, null);
-
- assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
-
- mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
- assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
- assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
- }
-
private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) {
assertNotNull(mUidFrozenStateCallback);
mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates);
}
- @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
@DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void exactListenerAlarmsRemovedOnFrozen() {
@@ -3848,7 +3775,6 @@
assertEquals(6, mService.mAlarmStore.size());
}
- @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
@DisableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS)
@Test
public void alarmCountOnListenerFrozen() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index a1a8b0e..1caa02a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -185,10 +185,10 @@
doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker();
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), anyInt());
}
public void tearDown() throws Exception {
@@ -298,7 +298,7 @@
filter.setPriority(priority);
final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
receiverList.app.info.packageName, null, null, null, receiverList.uid,
- receiverList.userId, false, false, true,
+ receiverList.userId, false, false, true, receiverList.app.info,
mock(PlatformCompat.class));
receiverList.add(res);
return res;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
index e977a7d..5d106ac 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastFilterTest.java
@@ -20,10 +20,13 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -53,14 +56,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ doReturn(true).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
}
@Test
@EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority() {
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
-
{
// Pairs of {initial-priority, expected-adjusted-priority}
final Pair<Integer, Integer>[] priorities = new Pair[] {
@@ -95,8 +98,8 @@
@Test
@EnableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withChangeIdDisabled() {
- doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+ doReturn(false).when(mPlatformCompat).isChangeEnabledInternalNoLogging(
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), any(ApplicationInfo.class));
{
// Pairs of {initial-priority, expected-adjusted-priority}
@@ -132,9 +135,6 @@
@Test
@DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withFlagDisabled() {
- doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
-
{
// Pairs of {initial-priority, expected-adjusted-priority}
final Pair<Integer, Integer>[] priorities = new Pair[] {
@@ -170,7 +170,7 @@
@DisableFlags(Flags.FLAG_RESTRICT_PRIORITY_VALUES)
public void testCalculateAdjustedPriority_withFlagDisabled_withChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+ eq(BroadcastFilter.RESTRICT_PRIORITY_VALUES), anyInt());
{
// Pairs of {initial-priority, expected-adjusted-priority}
@@ -215,6 +215,7 @@
final String errorMsg = String.format("owner=%d; actualPriority=%d; expectedPriority=%d",
owningUid, priority, expectedAdjustedPriority);
assertWithMessage(errorMsg).that(BroadcastFilter.calculateAdjustedPriority(
- owningUid, priority, mPlatformCompat)).isEqualTo(expectedAdjustedPriority);
+ owningUid, priority, mock(ApplicationInfo.class), mPlatformCompat))
+ .isEqualTo(expectedAdjustedPriority);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 1481085..88caaa6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -717,7 +717,7 @@
@Test
public void testRunnableAt_Cached_Prioritized_NonDeferrable_changeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
eq(getUidForPackage(PACKAGE_GREEN)));
final List receivers = List.of(
withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
@@ -1289,7 +1289,7 @@
@Test
public void testDeliveryGroupPolicy_prioritized_diffReceivers_changeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
eq(getUidForPackage(PACKAGE_GREEN)));
final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
@@ -1824,7 +1824,7 @@
@Test
public void testDeliveryDeferredForCached_changeIdDisabled() throws Exception {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
eq(getUidForPackage(PACKAGE_GREEN)));
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
@@ -2028,7 +2028,7 @@
public void testDeliveryDeferredForCached_withInfiniteDeferred_changeIdDisabled()
throws Exception {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE),
eq(getUidForPackage(PACKAGE_GREEN)));
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 9d92d5f..a38ef78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1660,7 +1660,7 @@
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
// Enqueue a normal broadcast that will go to several processes, and
// then enqueue a foreground broadcast that risks reordering
@@ -2472,7 +2472,7 @@
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index a424bfdb..f9f3790 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -19,7 +19,7 @@
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.server.am.BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE;
+import static com.android.server.am.BroadcastRecord.LIMIT_PRIORITY_SCOPE;
import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
@@ -109,7 +109,7 @@
MockitoAnnotations.initMocks(this);
doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), anyInt());
}
@Test
@@ -223,7 +223,7 @@
@Test
public void testIsPrioritized_withDifferentPriorities_withFirstUidChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -257,7 +257,7 @@
@Test
public void testIsPrioritized_withDifferentPriorities_withLastUidChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -295,7 +295,7 @@
@Test
public void testIsPrioritized_withDifferentPriorities_withUidChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -329,9 +329,9 @@
@Test
public void testIsPrioritized_withDifferentPriorities_withMultipleUidChangeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
assertTrue(isPrioritized(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -593,7 +593,7 @@
@Test
public void testSetDeliveryState_DeferUntilActive_changeIdDisabled() {
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
final BroadcastRecord r = createBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
@@ -961,7 +961,7 @@
createResolveInfo(PACKAGE3, getAppId(3)))));
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
assertArrayEquals(new boolean[] {false, true, true}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -973,7 +973,7 @@
createResolveInfo(PACKAGE3, getAppId(3)))));
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
assertArrayEquals(new boolean[] {false, false, true}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -988,7 +988,7 @@
createResolveInfo(PACKAGE3, getAppId(3)))));
doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
- eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+ eq(BroadcastRecord.LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
assertArrayEquals(new boolean[] {false, false, false}, calculateChangeState(
List.of(createResolveInfo(PACKAGE1, getAppId(1)),
createResolveInfo(PACKAGE2, getAppId(2)),
@@ -1005,7 +1005,7 @@
private boolean[] calculateChangeState(List<Object> receivers) {
return BroadcastRecord.calculateChangeStateForReceivers(receivers,
- CHANGE_LIMIT_PRIORITY_SCOPE, mPlatformCompat);
+ LIMIT_PRIORITY_SCOPE, mPlatformCompat);
}
private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 58489f3..0881b4c 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -18,6 +18,7 @@
import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS;
+import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID;
import static com.google.common.truth.Truth.assertThat;
@@ -51,11 +52,15 @@
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.power.ChannelConfig;
import android.hardware.power.ChannelMessage;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IBinder;
import android.os.IHintSession;
import android.os.PerformanceHintManager;
@@ -128,11 +133,11 @@
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
private static final WorkDuration[] WORK_DURATIONS_FIVE = new WorkDuration[] {
- makeWorkDuration(1L, 11L, 1L, 8L, 4L),
- makeWorkDuration(2L, 13L, 2L, 8L, 6L),
- makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
- makeWorkDuration(2L, 13L, 2L, 0L, 6L),
- makeWorkDuration(2L, 13L, 2L, 8L, 0L),
+ makeWorkDuration(1L, 11L, 1L, 8L, 4L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 6L),
+ makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
+ makeWorkDuration(2L, 13L, 2L, 0L, 6L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 0L),
};
private static final String TEST_APP_NAME = "com.android.test.app";
@@ -187,17 +192,17 @@
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_A), eq(DEFAULT_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[0],
- SESSION_IDS[0]));
+ SESSION_IDS[0]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_B), eq(DOUBLED_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[1],
- SESSION_IDS[1]));
+ SESSION_IDS[1]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_C), eq(0L), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[2],
- SESSION_IDS[2]));
+ SESSION_IDS[2]));
- when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
@@ -217,8 +222,8 @@
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
eq(0L))).thenReturn(SESSION_PTRS[2]);
when(mNativeWrapperMock.halCreateHintSessionWithConfig(anyInt(), anyInt(),
- any(int[].class), anyLong(), anyInt(),
- any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
+ any(int[].class), anyLong(), anyInt(),
+ any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
}
static class NativeWrapperFake extends NativeWrapper {
@@ -337,7 +342,7 @@
SESSION_TIDS_C, 0L, SessionTag.OTHER, new SessionConfig());
assertNotNull(c);
verify(mNativeWrapperMock, times(3)).halCreateHintSession(anyInt(), anyInt(),
- any(int[].class), anyLong());
+ any(int[].class), anyLong());
}
@Test
@@ -487,7 +492,7 @@
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(),
@@ -514,7 +519,7 @@
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -1096,4 +1101,157 @@
verify(mIPowerMock, times(1)).getSessionChannel(eq(TGID), eq(UID));
assertTrue(service.hasChannel(TGID, UID));
}
+
+ @Test
+ public void testHeadroomPowerHalNotSupported() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ HintManagerService service = createService();
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ });
+ }
+
+ @Test
+ public void testCpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getCpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
+ CpuHeadroomParams halParams1 = new CpuHeadroomParams();
+ halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
+ halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ halParams1.pid = Process.myPid();
+
+ CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal();
+ params2.usesDeviceHeadroom = true;
+ params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ CpuHeadroomParams halParams2 = new CpuHeadroomParams();
+ halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ halParams2.pid = DEFAULT_HEADROOM_PID;
+
+ float[] headroom1 = new float[] {0.1f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float[] headroom2 = new float[] {0.1f, 0.5f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis();
+ service.getBinderServiceInstance().getCpuHeadroom(params1);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ service.getBinderServiceInstance().getCpuHeadroom(params2);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+ }
+
+ @Test
+ public void testGpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getGpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ GpuHeadroomParamsInternal params1 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams1 = new GpuHeadroomParams();
+ halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN;
+
+ GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams2 = new GpuHeadroomParams();
+ params2.calculationType =
+ halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+
+ float headroom1 = 0.1f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float headroom2 = 0.2f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis();
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index cb4269a..9524fb2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4425,6 +4425,7 @@
}
@Test
+ @Ignore("b/359188869")
public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index cc02865..6af6542 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -84,7 +84,9 @@
import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.GroupHelper.CachedSummary;
+import com.android.server.notification.GroupHelper.FullyQualifiedGroupKey;
import com.android.server.notification.GroupHelper.NotificationAttributes;
+import com.android.server.notification.GroupHelper.NotificationSectioner;
import org.junit.Before;
import org.junit.Rule;
@@ -2298,6 +2300,7 @@
final String pkg = "package";
final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ final int numNotifications = 2 * AUTOGROUP_AT_COUNT;
int numNotificationChannel1 = 0;
final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
"TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
@@ -2307,7 +2310,7 @@
final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
// Post notifications with different channels that autogroup within the same section
NotificationRecord r;
- for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < numNotifications; i++) {
if (i % 2 == 0) {
r = getNotificationRecord(pkg, i, String.valueOf(i),
UserHandle.SYSTEM, "testGrp " + i, false, channel1);
@@ -2324,12 +2327,12 @@
"TEST_CHANNEL_ID1");
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
- verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(numNotifications)).addAutoGroup(anyString(),
eq(expectedGroupKey_alerting), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
+ verify(mCallback, times(numNotifications - AUTOGROUP_AT_COUNT)).updateAutogroupSummary(
+ anyInt(), anyString(), anyString(), any());
Mockito.reset(mCallback);
// Update channel1's importance
@@ -2375,7 +2378,7 @@
final List<NotificationRecord> notificationList = new ArrayList<>();
final String pkg = "package";
final int summaryId = 0;
- final int numChildNotif = 4;
+ final int numChildNotif = 2 * AUTOGROUP_AT_COUNT;
// Create an app-provided group: summary + child notifications
final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
@@ -2435,8 +2438,8 @@
eq(expectedGroupKey_social), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(numChildNotif / 2)).updateAutogroupSummary(anyInt(), anyString(),
- anyString(), any());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification(
anyString(), eq(originalAppGroupKey));
}
@@ -2513,9 +2516,10 @@
final List<NotificationRecord> notificationList = new ArrayList<>();
final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
final String pkg = "package";
+ final int numChildNotifications = AUTOGROUP_AT_COUNT;
// Post singleton groups, above forced group limit
- for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ for (int i = 0; i < numChildNotifications; i++) {
NotificationRecord summary = getNotificationRecord(pkg, i,
String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
notificationList.add(summary);
@@ -2545,13 +2549,13 @@
// Check that notifications are forced grouped and app-provided summaries are canceled
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(numChildNotifications)).addAutoGroup(anyString(),
eq(expectedGroupKey_social), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
any());
- verify(mCallback, times(2)).removeAppProvidedSummaryOnClassification(
+ verify(mCallback, times(numChildNotifications)).removeAppProvidedSummaryOnClassification(
anyString(), anyString());
// Adjust group key and cancel summaries
@@ -2593,14 +2597,16 @@
AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
String expectedTriggeringKey = null;
// Post singleton groups, above forced group limit
- for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
NotificationRecord summary = getNotificationRecord(pkg, i,
String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
notificationList.add(summary);
NotificationRecord child = getNotificationRecord(pkg, i + 42,
String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
notificationList.add(child);
- expectedTriggeringKey = child.getKey();
+ if (i == AUTOGROUP_SINGLETONS_AT_COUNT - 1) {
+ expectedTriggeringKey = child.getKey();
+ }
summaryByGroup.put(summary.getGroupKey(), summary);
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
summary.isCanceled = true; // simulate removing the app summary
@@ -2611,14 +2617,8 @@
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(),
eq(getNotificationAttributes(BASE_FLAGS)));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
- eq(expectedGroupKey_alerting), eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
- any());
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
- anyString());
assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0,
UserHandle.SYSTEM.getIdentifier())).isNotNull();
assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1,
@@ -2645,12 +2645,12 @@
// Check that all notifications are moved to the social section group
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
eq(expectedGroupKey_social), eq(true));
// Check that the alerting section group is removed
verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
eq(expectedGroupKey_alerting));
- verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).updateAutogroupSummary(anyInt(),
+ verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(),
anyString(), anyString(), any());
}
@@ -2666,7 +2666,7 @@
final String pkg = "package";
final int summaryId = 0;
- final int numChildren = 3;
+ final int numChildren = AUTOGROUP_AT_COUNT;
// Post a regular/valid group: summary + notifications
NotificationRecord summary = getNotificationRecord(pkg, summaryId,
String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
@@ -2706,13 +2706,211 @@
eq(true));
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
- verify(mCallback, times(numChildren - 1)).updateAutogroupSummary(anyInt(), anyString(),
- anyString(), any());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(),
anyString());
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_CLASSIFICATION})
+ public void testUnbundleNotification_originalSummaryMissing_autogroupInNewSection() {
+ // Check that unbundled notifications are moved to the original section and aggregated
+ // with existing autogrouped notifications
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ final int summaryId = 0;
+ final int numChildren = AUTOGROUP_AT_COUNT - 1;
+ // Post a regular/valid group: summary + notifications (one less than autogroup limit)
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final NotificationChannel originalChannel = summary.getChannel();
+ for (int i = 0; i < numChildren; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp", false);
+ notificationList.add(child);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ }
+
+ // Classify/bundle all child notifications: original group & summary is removed
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that no autogroup summaries were created for the social section
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+
+ // Cancel summary
+ summary.isCanceled = true;
+ summaryByGroup.clear();
+ notificationList.remove(summary);
+
+ // Add 1 ungrouped notification in the original section
+ NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
+ String.valueOf(4242), UserHandle.SYSTEM);
+ notificationList.add(ungroupedNotification);
+ mGroupHelper.onNotificationPosted(ungroupedNotification, false);
+
+ // Unbundle the bundled notifications => notifications are moved back to the original group
+ // and an aggregate group is created because autogroup limit is reached
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(record, false);
+ }
+ }
+
+ // Check that a new aggregate group is created
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_alerting), anyInt(), any());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, times(numChildren)).removeAutoGroupSummary(anyInt(), anyString(),
+ anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_CLASSIFICATION})
+ public void testUnbundleNotification_originalSummaryExists() {
+ // Check that unbundled notifications are moved to the original section and original group
+ // when the original summary is still present
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ final int summaryId = 0;
+ final int numChildren = AUTOGROUP_AT_COUNT + 1;
+ // Post a regular/valid group: summary + notifications
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final NotificationChannel originalChannel = summary.getChannel();
+ for (int i = 0; i < numChildren; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp", false);
+ notificationList.add(child);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ }
+
+ // Classify/bundle child notifications: all except one, to keep the original group
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ int numChildrenBundled = 0;
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ numChildrenBundled++;
+ if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
+ break;
+ }
+ }
+ }
+
+ // Check that 1 autogroup summaries were created for the social section
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+
+ // Adjust group key and cancel summaries
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupSummary()) {
+ record.isCanceled = true;
+ } else if (record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.setOverrideGroupKey(expectedGroupKey_social);
+ }
+ }
+
+ // Add 1 ungrouped notification in the original section
+ NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
+ String.valueOf(4242), UserHandle.SYSTEM);
+ notificationList.add(ungroupedNotification);
+ mGroupHelper.onNotificationPosted(ungroupedNotification, false);
+
+ // Unbundle the bundled notifications => social section summary is destroyed
+ // and notifications are moved back to the original group
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
+ record.getChannel().getId())) {
+ record.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(record, true);
+ }
+ }
+
+ // Check that the autogroup summary for the social section was removed
+ // and that no new autogroup summaries were created
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_social));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_social), any());
+
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupChild()
+ && record.getOriginalGroupKey().contains("testGrp")) {
+ assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+ }
+ }
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testMoveAggregateGroups_updateChannel_groupsUngrouped() {
final String pkg = "package";
@@ -3059,6 +3257,120 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
@DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
+ public void testNonGroupableChildren_singletonGroups_disableConversations() {
+ // Check that singleton groups with children that are not groupable, is not grouped
+ // Even though the group summary is a regular (alerting) notification, the children are
+ // conversations => the group should not be forced grouped.
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Trigger notification, ungrouped
+ final int triggerId = 1;
+ NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
+ String.valueOf(triggerId), UserHandle.SYSTEM);
+ notificationList.add(triggerNotification);
+ final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
+ final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
+ triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
+ triggerSection);
+
+ // Add singleton group with alerting child
+ final String groupName_valid = "testGrp_valid";
+ final int summaryId_valid = 0;
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
+ String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String groupKey_valid = summary.getGroupKey();
+ NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
+ String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
+ notificationList.add(child);
+
+ // Add singleton group with conversation child
+ final String groupName_invalid = "testGrp_invalid";
+ final int summaryId_invalid = 100;
+ summary = getNotificationRecord(pkg, summaryId_invalid,
+ String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
+ notificationList.add(summary);
+ final String groupKey_invalid = summary.getGroupKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ child = getNotificationRecord(pkg, summaryId_invalid + 42,
+ String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
+ false);
+ child = spy(child);
+ when(child.isConversation()).thenReturn(true);
+ notificationList.add(child);
+
+ // Check that the invalid group will not be force grouped
+ final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
+ triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
+ assertThat(sparseGroups).containsKey(groupKey_valid);
+ assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+ public void testNonGroupableChildren_singletonGroups_enableConversations() {
+ // Check that singleton groups with children that are not groupable, is not grouped
+ // Conversations are groupable (FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS is enabled)
+ // The invalid group is the alerting notifications: because the triggering notifications'
+ // section is Conversations, so the alerting group should be skipped.
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Trigger notification, ungrouped conversation
+ final int triggerId = 1;
+ NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
+ String.valueOf(triggerId), UserHandle.SYSTEM);
+ triggerNotification = spy(triggerNotification);
+ when(triggerNotification.isConversation()).thenReturn(true);
+ notificationList.add(triggerNotification);
+ final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
+ final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
+ triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
+ triggerSection);
+
+ // Add singleton group with conversation child
+ final String groupName_valid = "testGrp_valid";
+ final int summaryId_valid = 0;
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
+ String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
+ summary = spy(summary);
+ when(summary.isConversation()).thenReturn(true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ final String groupKey_valid = summary.getGroupKey();
+ NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
+ String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
+ child = spy(child);
+ when(child.isConversation()).thenReturn(true);
+ notificationList.add(child);
+
+ // Add singleton group with non-conversation child
+ final String groupName_invalid = "testGrp_invalid";
+ final int summaryId_invalid = 100;
+ summary = getNotificationRecord(pkg, summaryId_invalid,
+ String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
+ notificationList.add(summary);
+ final String groupKey_invalid = summary.getGroupKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ child = getNotificationRecord(pkg, summaryId_invalid + 42,
+ String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
+ false);
+ notificationList.add(child);
+
+ // Check that the invalid group will not be force grouped
+ final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
+ triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
+ assertThat(sparseGroups).containsKey(groupKey_valid);
+ assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
+ }
+
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
public void testNonGroupableNotifications() {
// Check that there is no valid section for: conversations, calls, foreground services
NotificationRecord notification_conversation = mock(NotificationRecord.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 42e31de..69fe7c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -82,6 +82,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
@@ -1299,6 +1300,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
public void testEnterPipParams() {
final StubOrganizer o = new StubOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
@@ -1314,6 +1316,7 @@
}
@Test
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
public void testChangePipParams() {
class ChangeSavingOrganizer extends StubOrganizer {
RunningTaskInfo mChangedInfo;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1bef5f8..1d4adc4 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -207,14 +207,13 @@
Value* dst_value = dst_config_value->value.get();
Value* src_value = src_config_value->value.get();
- CollisionResult collision_result;
- if (overlay) {
- collision_result =
- ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
- } else {
- collision_result =
- ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
- if (collision_result == CollisionResult::kConflict) {
+ CollisionResult collision_result =
+ ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
+ if (collision_result == CollisionResult::kConflict) {
+ if (overlay) {
+ collision_result =
+ ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
+ } else {
collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
}
}